ICU-20651 add setContext/getContext for [U]DateIntervalFormat
diff --git a/icu4c/source/i18n/dtitvfmt.cpp b/icu4c/source/i18n/dtitvfmt.cpp
index 3b65c94..2433a80 100644
--- a/icu4c/source/i18n/dtitvfmt.cpp
+++ b/icu4c/source/i18n/dtitvfmt.cpp
@@ -23,6 +23,7 @@
#include "unicode/dtptngen.h"
#include "unicode/dtitvinf.h"
#include "unicode/simpleformatter.h"
+#include "unicode/udisplaycontext.h"
#include "cmemory.h"
#include "cstring.h"
#include "dtitv_impl.h"
@@ -143,7 +144,8 @@
fLocale(Locale::getRoot()),
fDatePattern(nullptr),
fTimePattern(nullptr),
- fDateTimeFormat(nullptr)
+ fDateTimeFormat(nullptr),
+ fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
{}
@@ -156,7 +158,8 @@
fLocale(itvfmt.fLocale),
fDatePattern(nullptr),
fTimePattern(nullptr),
- fDateTimeFormat(nullptr) {
+ fDateTimeFormat(nullptr),
+ fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) {
*this = itvfmt;
}
@@ -203,6 +206,7 @@
fDatePattern = (itvfmt.fDatePattern)? itvfmt.fDatePattern->clone(): nullptr;
fTimePattern = (itvfmt.fTimePattern)? itvfmt.fTimePattern->clone(): nullptr;
fDateTimeFormat = (itvfmt.fDateTimeFormat)? itvfmt.fDateTimeFormat->clone(): nullptr;
+ fCapitalizationContext = itvfmt.fCapitalizationContext;
}
return *this;
}
@@ -254,6 +258,7 @@
if (fIntervalPatterns[i].secondPart != fmt->fIntervalPatterns[i].secondPart ) {return FALSE;}
if (fIntervalPatterns[i].laterDateFirst != fmt->fIntervalPatterns[i].laterDateFirst) {return FALSE;}
}
+ if (fCapitalizationContext != fmt->fCapitalizationContext) {return FALSE;}
return TRUE;
}
@@ -409,6 +414,7 @@
}
+// The following is only called from within the gFormatterMutex lock
UnicodeString&
DateIntervalFormat::formatImpl(Calendar& fromCalendar,
Calendar& toCalendar,
@@ -464,6 +470,11 @@
if ( U_FAILURE(status) ) {
return appendTo;
}
+ UErrorCode tempStatus = U_ZERO_ERROR; // for setContext, ignored
+ // Set up fDateFormat to handle the first or only part of the interval
+ // (override later for any second part). Inside lock, OK to modify fDateFormat.
+ fDateFormat->setContext(fCapitalizationContext, tempStatus);
+
if ( field == UCAL_FIELD_COUNT ) {
/* ignore the millisecond etc. small fields' difference.
* use single date when all the above are the same.
@@ -521,6 +532,9 @@
if ( !intervalPattern.secondPart.isEmpty() ) {
fDateFormat->applyPattern(intervalPattern.secondPart);
+ // No capitalization for second part of interval
+ tempStatus = U_ZERO_ERROR;
+ fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus);
fDateFormat->_format(*secondCal, appendTo, fphandler, status);
}
fDateFormat->applyPattern(originalPattern);
@@ -622,6 +636,30 @@
return *(TimeZone::createDefault());
}
+void
+DateIntervalFormat::setContext(UDisplayContext value, UErrorCode& status)
+{
+ if (U_FAILURE(status))
+ return;
+ if ( (UDisplayContextType)((uint32_t)value >> 8) == UDISPCTX_TYPE_CAPITALIZATION ) {
+ fCapitalizationContext = value;
+ } else {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ }
+}
+
+UDisplayContext
+DateIntervalFormat::getContext(UDisplayContextType type, UErrorCode& status) const
+{
+ if (U_FAILURE(status))
+ return (UDisplayContext)0;
+ if (type != UDISPCTX_TYPE_CAPITALIZATION) {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return (UDisplayContext)0;
+ }
+ return fCapitalizationContext;
+}
+
DateIntervalFormat::DateIntervalFormat(const Locale& locale,
DateIntervalInfo* dtItvInfo,
const UnicodeString* skeleton,
@@ -633,7 +671,8 @@
fLocale(locale),
fDatePattern(nullptr),
fTimePattern(nullptr),
- fDateTimeFormat(nullptr)
+ fDateTimeFormat(nullptr),
+ fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
{
LocalPointer<DateIntervalInfo> info(dtItvInfo, status);
LocalPointer<SimpleDateFormat> dtfmt(static_cast<SimpleDateFormat *>(
@@ -1506,6 +1545,7 @@
return (i - count);
}
+// The following is only called from fallbackFormat, i.e. within the gFormatterMutex lock
void DateIntervalFormat::fallbackFormatRange(
Calendar& fromCalendar,
Calendar& toCalendar,
@@ -1522,12 +1562,15 @@
int32_t offsets[2];
UnicodeString patternBody = sf.getTextWithNoArguments(offsets, 2);
+ UErrorCode tempStatus = U_ZERO_ERROR; // for setContext, ignored
// TODO(ICU-20406): Use SimpleFormatter Iterator interface when available.
if (offsets[0] < offsets[1]) {
firstIndex = 0;
appendTo.append(patternBody.tempSubStringBetween(0, offsets[0]));
fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
appendTo.append(patternBody.tempSubStringBetween(offsets[0], offsets[1]));
+ // No capitalization for second part of interval
+ fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus);
fDateFormat->_format(toCalendar, appendTo, fphandler, status);
appendTo.append(patternBody.tempSubStringBetween(offsets[1]));
} else {
@@ -1535,11 +1578,14 @@
appendTo.append(patternBody.tempSubStringBetween(0, offsets[1]));
fDateFormat->_format(toCalendar, appendTo, fphandler, status);
appendTo.append(patternBody.tempSubStringBetween(offsets[1], offsets[0]));
+ // No capitalization for second part of interval
+ fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus);
fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
appendTo.append(patternBody.tempSubStringBetween(offsets[0]));
}
}
+// The following is only called from formatImpl, i.e. within the gFormatterMutex lock
UnicodeString&
DateIntervalFormat::fallbackFormat(Calendar& fromCalendar,
Calendar& toCalendar,
@@ -1564,6 +1610,7 @@
UnicodeString fullPattern; // for saving the pattern in fDateFormat
fDateFormat->toPattern(fullPattern); // save current pattern, restore later
+ UErrorCode tempStatus = U_ZERO_ERROR; // for setContext, ignored
// {0} is time range
// {1} is single date portion
// TODO(ICU-20406): Use SimpleFormatter Iterator interface when available.
@@ -1573,6 +1620,8 @@
fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status);
appendTo.append(patternBody.tempSubStringBetween(offsets[0], offsets[1]));
fDateFormat->applyPattern(*fDatePattern);
+ // No capitalization for second portion
+ fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus);
fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
appendTo.append(patternBody.tempSubStringBetween(offsets[1]));
} else {
@@ -1581,6 +1630,8 @@
fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
appendTo.append(patternBody.tempSubStringBetween(offsets[1], offsets[0]));
fDateFormat->applyPattern(*fTimePattern);
+ // No capitalization for second portion
+ fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus);
fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status);
appendTo.append(patternBody.tempSubStringBetween(offsets[0]));
}
diff --git a/icu4c/source/i18n/udateintervalformat.cpp b/icu4c/source/i18n/udateintervalformat.cpp
index 4bae622..3557443 100644
--- a/icu4c/source/i18n/udateintervalformat.cpp
+++ b/icu4c/source/i18n/udateintervalformat.cpp
@@ -18,6 +18,7 @@
#include "unicode/timezone.h"
#include "unicode/locid.h"
#include "unicode/unistr.h"
+#include "unicode/udisplaycontext.h"
#include "formattedval_impl.h"
U_NAMESPACE_USE
@@ -151,5 +152,25 @@
}
}
+U_CAPI void U_EXPORT2
+udtitvfmt_setContext(UDateIntervalFormat* formatter,
+ UDisplayContext value,
+ UErrorCode* status) {
+ if (U_FAILURE(*status)) {
+ return;
+ }
+ reinterpret_cast<DateIntervalFormat*>(formatter)->setContext( value, *status );
+}
+
+U_CAPI UDisplayContext U_EXPORT2
+udtitvfmt_getContext(const UDateIntervalFormat* formatter,
+ UDisplayContextType type,
+ UErrorCode* status) {
+ if (U_FAILURE(*status)) {
+ return (UDisplayContext)0;
+ }
+ return reinterpret_cast<const DateIntervalFormat*>(formatter)->getContext( type, *status );
+}
+
#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/unicode/dtitvfmt.h b/icu4c/source/i18n/unicode/dtitvfmt.h
index 3ff0aff..9fa1908 100644
--- a/icu4c/source/i18n/unicode/dtitvfmt.h
+++ b/icu4c/source/i18n/unicode/dtitvfmt.h
@@ -31,6 +31,7 @@
#include "unicode/dtitvinf.h"
#include "unicode/dtptngen.h"
#include "unicode/formattedvalue.h"
+#include "unicode/udisplaycontext.h"
U_NAMESPACE_BEGIN
@@ -652,6 +653,32 @@
virtual void setTimeZone(const TimeZone& zone);
/**
+ * Set a particular UDisplayContext value in the formatter, such as
+ * UDISPCTX_CAPITALIZATION_FOR_STANDALONE. This causes the formatted
+ * result to be capitalized appropriately for the context in which
+ * it is intended to be used, considering both the locale and the
+ * type of field at the beginning of the formatted result.
+ * @param value The UDisplayContext value to set.
+ * @param status Input/output status. If at entry this indicates a failure
+ * status, the function will do nothing; otherwise this will be
+ * updated with any new status from the function.
+ * @draft ICU 68
+ */
+ virtual void setContext(UDisplayContext value, UErrorCode& status);
+
+ /**
+ * Get the formatter's UDisplayContext value for the specified UDisplayContextType,
+ * such as UDISPCTX_TYPE_CAPITALIZATION.
+ * @param type The UDisplayContextType whose value to return
+ * @param status Input/output status. If at entry this indicates a failure
+ * status, the function will do nothing; otherwise this will be
+ * updated with any new status from the function.
+ * @return The UDisplayContextValue for the specified type.
+ * @draft ICU 68
+ */
+ virtual UDisplayContext getContext(UDisplayContextType type, UErrorCode& status) const;
+
+ /**
* Return the class ID for this class. This is useful only for comparing to
* a return value from getDynamicClassID(). For example:
* <pre>
@@ -1152,6 +1179,11 @@
UnicodeString* fDatePattern;
UnicodeString* fTimePattern;
UnicodeString* fDateTimeFormat;
+
+ /**
+ * Other formatting information
+ */
+ UDisplayContext fCapitalizationContext;
};
inline UBool
diff --git a/icu4c/source/i18n/unicode/udateintervalformat.h b/icu4c/source/i18n/unicode/udateintervalformat.h
index 03b7361..b100ea6 100644
--- a/icu4c/source/i18n/unicode/udateintervalformat.h
+++ b/icu4c/source/i18n/unicode/udateintervalformat.h
@@ -17,6 +17,7 @@
#include "unicode/ucal.h"
#include "unicode/umisc.h"
#include "unicode/uformattedvalue.h"
+#include "unicode/udisplaycontext.h"
#if U_SHOW_CPLUSPLUS_API
#include "unicode/localpointer.h"
@@ -302,6 +303,34 @@
UErrorCode* status);
#endif /* U_HIDE_DRAFT_API */
+#ifndef U_HIDE_DRAFT_API
+/**
+ * Set a particular UDisplayContext value in the formatter, such as
+ * UDISPCTX_CAPITALIZATION_FOR_STANDALONE. This causes the formatted
+ * result to be capitalized appropriately for the context in which
+ * it is intended to be used, considering both the locale and the
+ * type of field at the beginning of the formatted result.
+ * @param formatter The formatter for which to set a UDisplayContext value.
+ * @param value The UDisplayContext value to set.
+ * @param status A pointer to an UErrorCode to receive any errors
+ * @draft ICU 68
+ */
+U_CAPI void U_EXPORT2
+udtitvfmt_setContext(UDateIntervalFormat* formatter, UDisplayContext value, UErrorCode* status);
+
+/**
+ * Get the formatter's UDisplayContext value for the specified UDisplayContextType,
+ * such as UDISPCTX_TYPE_CAPITALIZATION.
+ * @param formatter The formatter to query.
+ * @param type The UDisplayContextType whose value to return
+ * @param status A pointer to an UErrorCode to receive any errors
+ * @return The UDisplayContextValue for the specified type.
+ * @draft ICU 68
+ */
+U_CAPI UDisplayContext U_EXPORT2
+udtitvfmt_getContext(const UDateIntervalFormat* formatter, UDisplayContextType type, UErrorCode* status);
+
+#endif /* U_HIDE_DRAFT_API */
#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/test/cintltst/cdateintervalformattest.c b/icu4c/source/test/cintltst/cdateintervalformattest.c
index fb5bc73..eb3877e 100644
--- a/icu4c/source/test/cintltst/cdateintervalformattest.c
+++ b/icu4c/source/test/cintltst/cdateintervalformattest.c
@@ -14,6 +14,7 @@
#include "unicode/udat.h"
#include "unicode/ucal.h"
#include "unicode/ustring.h"
+#include "unicode/udisplaycontext.h"
#include "cintltst.h"
#include "cmemory.h"
#include "cformtst.h"
@@ -48,23 +49,41 @@
typedef struct {
const char * locale;
const char * skeleton;
+ UDisplayContext context;
const char * tzid;
const UDate from;
const UDate to;
const char * resultExpected;
} DateIntervalFormatTestItem;
+#define CAP_NONE UDISPCTX_CAPITALIZATION_NONE
+#define CAP_BEGIN UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
+#define CAP_LIST UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
+#define CAP_ALONE UDISPCTX_CAPITALIZATION_FOR_STANDALONE
+
/* Just a small set of tests for now, the real functionality is tested in the C++ tests */
static const DateIntervalFormatTestItem testItems[] = {
- { "en", "MMMdHHmm", tzUSPacific, Date201103021030, Date201103021030 + 7.0*_HOUR, "Mar 2, 10:30 \\u2013 17:30" },
- { "en", "MMMdHHmm", tzAsiaTokyo, Date201103021030, Date201103021030 + 7.0*_HOUR, "Mar 3, 03:30 \\u2013 10:30" },
- { "en", "yMMMEd", tzUSPacific, Date201009270800, Date201009270800 + 12.0*_HOUR, "Mon, Sep 27, 2010" },
- { "en", "yMMMEd", tzUSPacific, Date201009270800, Date201009270800 + 31.0*_DAY, "Mon, Sep 27 \\u2013 Thu, Oct 28, 2010" },
- { "en", "yMMMEd", tzUSPacific, Date201009270800, Date201009270800 + 410.0*_DAY, "Mon, Sep 27, 2010 \\u2013 Fri, Nov 11, 2011" },
- { "de", "Hm", tzUSPacific, Date201009270800, Date201009270800 + 12.0*_HOUR, "08:00\\u201320:00 Uhr" },
- { "de", "Hm", tzUSPacific, Date201009270800, Date201009270800 + 31.0*_DAY, "27.9.2010, 08:00 \\u2013 28.10.2010, 08:00" },
- { "ja", "MMMd", tzUSPacific, Date201009270800, Date201009270800 + 1.0*_DAY, "9\\u670827\\u65E5\\uFF5E28\\u65E5" },
- { NULL, NULL, NULL, 0, 0, NULL }
+ { "en", "MMMdHHmm", CAP_NONE, tzUSPacific, Date201103021030, Date201103021030 + 7.0*_HOUR, "Mar 2, 10:30 \\u2013 17:30" },
+ { "en", "MMMdHHmm", CAP_NONE, tzAsiaTokyo, Date201103021030, Date201103021030 + 7.0*_HOUR, "Mar 3, 03:30 \\u2013 10:30" },
+ { "en", "yMMMEd", CAP_NONE, tzUSPacific, Date201009270800, Date201009270800 + 12.0*_HOUR, "Mon, Sep 27, 2010" },
+ { "en", "yMMMEd", CAP_NONE, tzUSPacific, Date201009270800, Date201009270800 + 31.0*_DAY, "Mon, Sep 27 \\u2013 Thu, Oct 28, 2010" },
+ { "en", "yMMMEd", CAP_NONE, tzUSPacific, Date201009270800, Date201009270800 + 410.0*_DAY, "Mon, Sep 27, 2010 \\u2013 Fri, Nov 11, 2011" },
+ { "de", "Hm", CAP_NONE, tzUSPacific, Date201009270800, Date201009270800 + 12.0*_HOUR, "08:00\\u201320:00 Uhr" },
+ { "de", "Hm", CAP_NONE, tzUSPacific, Date201009270800, Date201009270800 + 31.0*_DAY, "27.9.2010, 08:00 \\u2013 28.10.2010, 08:00" },
+ { "ja", "MMMd", CAP_NONE, tzUSPacific, Date201009270800, Date201009270800 + 1.0*_DAY, "9\\u670827\\u65E5\\uFF5E28\\u65E5" },
+ { "cs", "MMMEd", CAP_NONE, tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY, "po 27. 9. \\u2013 p\\u00E1 26. 11." },
+ { "cs", "yMMMM", CAP_NONE, tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY, "z\\u00E1\\u0159\\u00ED\\u2013listopad 2010" },
+ { "cs", "yMMMM", CAP_NONE, tzUSPacific, Date201009270800, Date201009270800 + 1.0*_DAY, "z\\u00E1\\u0159\\u00ED 2010" },
+ { "cs", "MMMEd", CAP_BEGIN, tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY, "Po 27. 9. \\u2013 p\\u00E1 26. 11." },
+ { "cs", "yMMMM", CAP_BEGIN, tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY, "Z\\u00E1\\u0159\\u00ED\\u2013listopad 2010" },
+ { "cs", "yMMMM", CAP_BEGIN, tzUSPacific, Date201009270800, Date201009270800 + 1.0*_DAY, "Z\\u00E1\\u0159\\u00ED 2010" },
+ { "cs", "MMMEd", CAP_LIST, tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY, "Po 27. 9. \\u2013 p\\u00E1 26. 11." },
+ { "cs", "yMMMM", CAP_LIST, tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY, "Z\\u00E1\\u0159\\u00ED\\u2013listopad 2010" },
+ { "cs", "yMMMM", CAP_LIST, tzUSPacific, Date201009270800, Date201009270800 + 1.0*_DAY, "Z\\u00E1\\u0159\\u00ED 2010" },
+ { "cs", "MMMEd", CAP_ALONE, tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY, "po 27. 9. \\u2013 p\\u00E1 26. 11." },
+ { "cs", "yMMMM", CAP_ALONE, tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY, "z\\u00E1\\u0159\\u00ED\\u2013listopad 2010" },
+ { "cs", "yMMMM", CAP_ALONE, tzUSPacific, Date201009270800, Date201009270800 + 1.0*_DAY, "z\\u00E1\\u0159\\u00ED 2010" },
+ { NULL, NULL, CAP_NONE, NULL, 0, 0, NULL }
};
enum {
@@ -98,6 +117,22 @@
if ( U_SUCCESS(status) ) {
UChar result[kFormatBufLen];
UChar resultExpected[kFormatBufLen];
+
+ udtitvfmt_setContext(udtitvfmt, testItemPtr->context, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("FAIL: udtitvfmt_setContext for locale %s, skeleton %s, context %04X - %s\n",
+ testItemPtr->locale, testItemPtr->skeleton, (unsigned)testItemPtr->context, myErrorName(status) );
+ } else {
+ UDisplayContext getContext = udtitvfmt_getContext(udtitvfmt, UDISPCTX_TYPE_CAPITALIZATION, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("FAIL: udtitvfmt_getContext for locale %s, skeleton %s, context %04X - %s\n",
+ testItemPtr->locale, testItemPtr->skeleton, (unsigned)testItemPtr->context, myErrorName(status) );
+ } else if (getContext != testItemPtr->context) {
+ log_err("FAIL: udtitvfmt_getContext for locale %s, skeleton %s, context %04X - got context %04X\n",
+ testItemPtr->locale, testItemPtr->skeleton, (unsigned)testItemPtr->context, (unsigned)getContext );
+ }
+ }
+ status = U_ZERO_ERROR;
int32_t fmtLen = udtitvfmt_format(udtitvfmt, testItemPtr->from, testItemPtr->to, result, kFormatBufLen, NULL, &status);
if (fmtLen >= kFormatBufLen) {
result[kFormatBufLen-1] = 0;
diff --git a/icu4c/source/test/intltest/dtifmtts.cpp b/icu4c/source/test/intltest/dtifmtts.cpp
index 85d74c6..cc91257 100644
--- a/icu4c/source/test/intltest/dtifmtts.cpp
+++ b/icu4c/source/test/intltest/dtifmtts.cpp
@@ -61,6 +61,7 @@
TESTCASE(12, testTicket20707);
TESTCASE(13, testFormatMillisecond);
TESTCASE(14, testHourMetacharacters);
+ TESTCASE(15, testContext);
default: name = ""; break;
}
}
@@ -1275,6 +1276,83 @@
expectUserDII(DATA, UPRV_LENGTHOF(DATA));
}
+/*
+ * Test format using UDisplayContext
+ */
+#define CAP_NONE UDISPCTX_CAPITALIZATION_NONE
+#define CAP_BEGIN UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
+#define CAP_LIST UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
+#define CAP_ALONE UDISPCTX_CAPITALIZATION_FOR_STANDALONE
+#define _DAY (24.0*60.0*60.0*1000.0)
+
+void DateIntervalFormatTest::testContext() {
+ static const UDate startDate = 1285599629000.0; // 2010-Sep-27 0800 in America/Los_Angeles
+ typedef struct {
+ const char * locale;
+ const char * skeleton;
+ UDisplayContext context;
+ const UDate deltaDate;
+ const UChar* expectResult;
+ } DateIntervalContextItem;
+ static const DateIntervalContextItem testItems[] = {
+ { "cs", "MMMEd", CAP_NONE, 60.0*_DAY, u"po 27. 9. – pá 26. 11." },
+ { "cs", "yMMMM", CAP_NONE, 60.0*_DAY, u"září–listopad 2010" },
+ { "cs", "yMMMM", CAP_NONE, 1.0*_DAY, u"září 2010" },
+ { "cs", "MMMEd", CAP_BEGIN, 60.0*_DAY, u"Po 27. 9. – pá 26. 11." },
+ { "cs", "yMMMM", CAP_BEGIN, 60.0*_DAY, u"Září–listopad 2010" },
+ { "cs", "yMMMM", CAP_BEGIN, 1.0*_DAY, u"Září 2010" },
+ { "cs", "MMMEd", CAP_LIST, 60.0*_DAY, u"Po 27. 9. – pá 26. 11." },
+ { "cs", "yMMMM", CAP_LIST, 60.0*_DAY, u"Září–listopad 2010" },
+ { "cs", "yMMMM", CAP_LIST, 1.0*_DAY, u"Září 2010" },
+ { "cs", "MMMEd", CAP_ALONE, 60.0*_DAY, u"po 27. 9. – pá 26. 11." },
+ { "cs", "yMMMM", CAP_ALONE, 60.0*_DAY, u"září–listopad 2010" },
+ { "cs", "yMMMM", CAP_ALONE, 1.0*_DAY, u"září 2010" },
+ { nullptr, nullptr, CAP_NONE, 0, nullptr }
+ };
+ const DateIntervalContextItem* testItemPtr;
+ for ( testItemPtr = testItems; testItemPtr->locale != nullptr; ++testItemPtr ) {
+ UErrorCode status = U_ZERO_ERROR;
+ Locale locale(testItemPtr->locale);
+ UnicodeString skeleton(testItemPtr->skeleton, -1, US_INV);
+ LocalPointer<DateIntervalFormat> fmt(DateIntervalFormat::createInstance(skeleton, locale, status));
+ if (U_FAILURE(status)) {
+ errln("createInstance failed for locale %s skeleton %s: %s",
+ testItemPtr->locale, testItemPtr->skeleton, u_errorName(status));
+ continue;
+ }
+ fmt->adoptTimeZone(TimeZone::createTimeZone("America/Los_Angeles"));
+
+ fmt->setContext(testItemPtr->context, status);
+ if (U_FAILURE(status)) {
+ errln("setContext failed for locale %s skeleton %s context %04X: %s",
+ testItemPtr->locale, testItemPtr->skeleton, (unsigned)testItemPtr->context, u_errorName(status));
+ } else {
+ UDisplayContext getContext = fmt->getContext(UDISPCTX_TYPE_CAPITALIZATION, status);
+ if (U_FAILURE(status)) {
+ errln("getContext failed for locale %s skeleton %s context %04X: %s",
+ testItemPtr->locale, testItemPtr->skeleton, (unsigned)testItemPtr->context, u_errorName(status));
+ } else if (getContext != testItemPtr->context) {
+ errln("getContext failed for locale %s skeleton %s context %04X: got context %04X",
+ testItemPtr->locale, testItemPtr->skeleton, (unsigned)testItemPtr->context, (unsigned)getContext);
+ }
+ }
+
+ status = U_ZERO_ERROR;
+ DateInterval interval(startDate, startDate + testItemPtr->deltaDate);
+ UnicodeString getResult;
+ FieldPosition pos(FieldPosition::DONT_CARE);
+ fmt->format(&interval, getResult, pos, status);
+ if (U_FAILURE(status)) {
+ errln("format failed for locale %s skeleton %s context %04X: %s",
+ testItemPtr->locale, testItemPtr->skeleton, (unsigned)testItemPtr->context, u_errorName(status));
+ continue;
+ }
+ UnicodeString expectResult(true, testItemPtr->expectResult, -1);
+ if (getResult != expectResult) {
+ errln(UnicodeString("format expected ") + expectResult + UnicodeString(" but got ") + getResult);
+ }
+ }
+}
void DateIntervalFormatTest::testSetIntervalPatternNoSideEffect() {
UErrorCode ec = U_ZERO_ERROR;
diff --git a/icu4c/source/test/intltest/dtifmtts.h b/icu4c/source/test/intltest/dtifmtts.h
index 7c96eea..700eb4f 100644
--- a/icu4c/source/test/intltest/dtifmtts.h
+++ b/icu4c/source/test/intltest/dtifmtts.h
@@ -47,6 +47,11 @@
*/
void testFormatUserDII();
+ /*
+ * Test format using UDisplayContext
+ */
+ void testContext();
+
/**
* Test for no unwanted side effects when setting
* interval patterns.
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java
index e682b74..8a847d7 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java
@@ -28,6 +28,7 @@
import com.ibm.icu.impl.SimpleFormatterImpl;
import com.ibm.icu.impl.Utility;
import com.ibm.icu.text.DateIntervalInfo.PatternInfo;
+import com.ibm.icu.text.DisplayContext;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.DateInterval;
import com.ibm.icu.util.Output;
@@ -484,6 +485,10 @@
private String fTimePattern = null;
private String fDateTimeFormat = null;
+ /*
+ * Capitalization context, new in ICU 68
+ */
+ private DisplayContext fCapitalizationSetting = DisplayContext.CAPITALIZATION_NONE;
/*
* default constructor; private because we don't want anyone to use
@@ -722,6 +727,7 @@
other.fDatePattern = fDatePattern;
other.fTimePattern = fTimePattern;
other.fDateTimeFormat = fDateTimeFormat;
+ other.fCapitalizationSetting = fCapitalizationSetting;
return other;
}
@@ -920,6 +926,10 @@
throw new IllegalArgumentException("can not format on two different calendars");
}
+ // Set up fDateFormat to handle the first or only part of the interval
+ // (override later for any second part).
+ fDateFormat.setContext(fCapitalizationSetting);
+
// First, find the largest different calendar field.
int field = -1; //init with an invalid value.
@@ -1008,6 +1018,8 @@
}
if ( intervalPattern.getSecondPart() != null ) {
fDateFormat.applyPattern(intervalPattern.getSecondPart());
+ // No capitalization for second part of interval
+ fDateFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
fDateFormat.format(secondCal, appendTo, pos, attributes);
}
fDateFormat.applyPattern(originalPattern);
@@ -1045,6 +1057,8 @@
if (pos.getEndIndex() > 0) {
pos = new FieldPosition(0);
}
+ // No capitalization for second portion
+ fDateFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
}
}
@@ -1099,6 +1113,8 @@
if (pos.getEndIndex() > 0) {
pos = new FieldPosition(0);
}
+ // No capitalization for second portion
+ fDateFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
}
// restore full pattern
@@ -1245,6 +1261,39 @@
}
/**
+ * {@icu} Set a particular DisplayContext value in the formatter,
+ * such as CAPITALIZATION_FOR_STANDALONE. This causes the formatted
+ * result to be capitalized appropriately for the context in which
+ * it is intended to be used, considering both the locale and the
+ * type of field at the beginning of the formatted result.
+ *
+ * @param context The DisplayContext value to set.
+ * @draft ICU 68
+ * @provisional This API might change or be removed in a future release.
+ */
+ public void setContext(DisplayContext context)
+ {
+ if (context.type() == DisplayContext.Type.CAPITALIZATION) {
+ fCapitalizationSetting = context;
+ }
+ }
+
+ /**
+ * {@icu} Get the formatter's DisplayContext value for the specified DisplayContext.Type,
+ * such as CAPITALIZATION.
+ *
+ * @param type the DisplayContext.Type whose value to return
+ * @return the current DisplayContext setting for the specified type
+ * @draft ICU 68
+ * @provisional This API might change or be removed in a future release.
+ */
+ public DisplayContext getContext(DisplayContext.Type type)
+ {
+ return (type == DisplayContext.Type.CAPITALIZATION && fCapitalizationSetting != null)?
+ fCapitalizationSetting: DisplayContext.CAPITALIZATION_NONE;
+ }
+
+ /**
* Gets the date formatter
* @return a copy of the date formatter associated with
* this date interval formatter.
@@ -2197,6 +2246,10 @@
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
initializePattern(isDateIntervalInfoDefault ? LOCAL_PATTERN_CACHE : null);
+ // if deserialized from a release that didn't have fCapitalizationSetting, set it to default
+ if (fCapitalizationSetting == null) {
+ fCapitalizationSetting = DisplayContext.CAPITALIZATION_NONE;
+ }
}
/**
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java
index cb1572d..d6b1945 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java
@@ -36,6 +36,7 @@
import com.ibm.icu.text.DateIntervalFormat.FormattedDateInterval;
import com.ibm.icu.text.DateIntervalInfo;
import com.ibm.icu.text.DateIntervalInfo.PatternInfo;
+import com.ibm.icu.text.DisplayContext;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.DateInterval;
@@ -943,7 +944,65 @@
}
}
+ /*
+ * Test format using DisplayContext
+ */
+ @Test
+ public void TestContext() {
+ final long startDate = 1285599629000L; // 2010-Sep-27 0800 in America/Los_Angeles
+ final long day = 24*60*60*1000; // milliseconds in a day
+ class DateIntervalContextItem {
+ public String locale;
+ public String skeleton;
+ public DisplayContext context;
+ public long deltaDate;
+ public String expectResult;
+ // Simple constructor
+ public DateIntervalContextItem(String loc, String skel, DisplayContext ctxt, long delta, String expect) {
+ locale = loc;
+ skeleton = skel;
+ context = ctxt;
+ deltaDate = delta;
+ expectResult = expect;
+ }
+ };
+
+ final DateIntervalContextItem[] testItems = {
+ new DateIntervalContextItem( "cs", "MMMEd", DisplayContext.CAPITALIZATION_NONE, 60*day, "po 27. 9. – pá 26. 11." ),
+ new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_NONE, 60*day, "září–listopad 2010" ),
+ new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_NONE, 1*day, "září 2010" ),
+ new DateIntervalContextItem( "cs", "MMMEd", DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, 60*day, "Po 27. 9. – pá 26. 11." ),
+ new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, 60*day, "Září–listopad 2010" ),
+ new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, 1*day, "Září 2010" ),
+ new DateIntervalContextItem( "cs", "MMMEd", DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU, 60*day, "Po 27. 9. – pá 26. 11." ),
+ new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU, 60*day, "Září–listopad 2010" ),
+ new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU, 1*day, "Září 2010" ),
+ new DateIntervalContextItem( "cs", "MMMEd", DisplayContext.CAPITALIZATION_FOR_STANDALONE, 60*day, "po 27. 9. – pá 26. 11." ),
+ new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_FOR_STANDALONE, 60*day, "září–listopad 2010" ),
+ new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_FOR_STANDALONE, 1*day, "září 2010" ),
+ };
+
+ for (DateIntervalContextItem item: testItems) {
+ DateIntervalFormat difmt = DateIntervalFormat.getInstance(item.skeleton, new ULocale(item.locale));
+ difmt.setTimeZone(TimeZone.getFrozenTimeZone("America/Los_Angeles"));
+
+ difmt.setContext(item.context);
+ DisplayContext getContext = difmt.getContext(DisplayContext.Type.CAPITALIZATION);
+ if (getContext != item.context) {
+ errln("For locale " + item.locale + ", skeleton " + item.skeleton + ", context " + item.context +
+ ": getContext returned " + getContext);
+ }
+ DateInterval interval = new DateInterval(startDate, startDate + item.deltaDate);
+ FieldPosition pos = new FieldPosition(0);
+ StringBuffer getResult = new StringBuffer();
+ difmt.format(interval, getResult, pos);
+ if (!getResult.toString().equals(item.expectResult)) {
+ errln("For locale " + item.locale + ", skeleton " + item.skeleton + ", context " + item.context +
+ ": expected " + item.expectResult + ", got " + getResult.toString());
+ }
+ }
+ }
/*
* Test format using user defined DateIntervalInfo