]> granicus.if.org Git - icu/commitdiff
ICU-20651 add setContext/getContext for [U]DateIntervalFormat
authorPeter Edberg <pedberg@unicode.org>
Wed, 9 Sep 2020 07:07:25 +0000 (00:07 -0700)
committerPeter Edberg <42151464+pedberg-icu@users.noreply.github.com>
Sat, 12 Sep 2020 03:12:44 +0000 (20:12 -0700)
icu4c/source/i18n/dtitvfmt.cpp
icu4c/source/i18n/udateintervalformat.cpp
icu4c/source/i18n/unicode/dtitvfmt.h
icu4c/source/i18n/unicode/udateintervalformat.h
icu4c/source/test/cintltst/cdateintervalformattest.c
icu4c/source/test/intltest/dtifmtts.cpp
icu4c/source/test/intltest/dtifmtts.h
icu4j/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java

index 3b65c9461c1d3589efaebf80d84af14aa4662093..2433a80b017d147f3d9cf5b8504b5bf466f6c966 100644 (file)
@@ -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 @@ DateIntervalFormat::DateIntervalFormat()
     fLocale(Locale::getRoot()),
     fDatePattern(nullptr),
     fTimePattern(nullptr),
-    fDateTimeFormat(nullptr)
+    fDateTimeFormat(nullptr),
+    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
 {}
 
 
@@ -156,7 +158,8 @@ DateIntervalFormat::DateIntervalFormat(const DateIntervalFormat& itvfmt)
     fLocale(itvfmt.fLocale),
     fDatePattern(nullptr),
     fTimePattern(nullptr),
-    fDateTimeFormat(nullptr) {
+    fDateTimeFormat(nullptr),
+    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) {
     *this = itvfmt;
 }
 
@@ -203,6 +206,7 @@ DateIntervalFormat::operator=(const DateIntervalFormat& itvfmt) {
         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 @@ DateIntervalFormat::operator==(const Format& other) const {
         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 @@ UnicodeString& DateIntervalFormat::formatIntervalImpl(
 }
 
 
+// The following is only called from within the gFormatterMutex lock
 UnicodeString&
 DateIntervalFormat::formatImpl(Calendar& fromCalendar,
                            Calendar& toCalendar,
@@ -464,6 +470,11 @@ DateIntervalFormat::formatImpl(Calendar& fromCalendar,
     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 @@ DateIntervalFormat::formatImpl(Calendar& fromCalendar,
 
     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 @@ DateIntervalFormat::getTimeZone() const
     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 @@ DateIntervalFormat::DateIntervalFormat(const Locale& locale,
     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 @@ DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern)
     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 @@ void DateIntervalFormat::fallbackFormatRange(
     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 @@ void DateIntervalFormat::fallbackFormatRange(
         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 @@ DateIntervalFormat::fallbackFormat(Calendar& fromCalendar,
         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 @@ DateIntervalFormat::fallbackFormat(Calendar& fromCalendar,
             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 @@ DateIntervalFormat::fallbackFormat(Calendar& fromCalendar,
             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]));
         }
index 4bae622b657416238624aa79da364ba4f60c56b2..355744346a39d729c5b279a6fccd062e22e2d4fe 100644 (file)
@@ -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 @@ udtitvfmt_formatCalendarToResult(
     }
 }
 
+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 */
index 3ff0affbc8a3c489c77df77c13814bdbcbc731ee..9fa1908f0b008b20450037bab4654eadb95c531a 100644 (file)
@@ -31,6 +31,7 @@
 #include "unicode/dtitvinf.h"
 #include "unicode/dtptngen.h"
 #include "unicode/formattedvalue.h"
+#include "unicode/udisplaycontext.h"
 
 U_NAMESPACE_BEGIN
 
@@ -651,6 +652,32 @@ public:
      */
     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:
@@ -1152,6 +1179,11 @@ private:
     UnicodeString* fDatePattern;
     UnicodeString* fTimePattern;
     UnicodeString* fDateTimeFormat;
+
+    /**
+     * Other formatting information
+     */
+    UDisplayContext fCapitalizationContext;
 };
 
 inline UBool
index 03b73610a7ff80f547279ea2a66d723f4b29348b..b100ea6d84f108b4ce686bd306ac5f07a87e1bcb 100644 (file)
@@ -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 @@ udtitvfmt_formatCalendarToResult(
                 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 */
 
index fb5bc73be2888575f8251e73832f3050adc9992f..eb3877e7e0f17317726b1a7a4cee494dd3d2c794 100644 (file)
@@ -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 @@ static const char tzAsiaTokyo[] = "Asia/Tokyo";
 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 @@ static void TestDateIntervalFormat()
         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;
index 85d74c6a7537e221d0d217db6b2282b4c09baaf1..cc91257ce80fff8448527b61a95c42f0bb2dd5d4 100644 (file)
@@ -61,6 +61,7 @@ void DateIntervalFormatTest::runIndexedTest( int32_t index, UBool exec, const ch
         TESTCASE(12, testTicket20707);
         TESTCASE(13, testFormatMillisecond);
         TESTCASE(14, testHourMetacharacters);
+        TESTCASE(15, testContext);
         default: name = ""; break;
     }
 }
@@ -1275,6 +1276,83 @@ void DateIntervalFormatTest::testFormatUserDII() {
     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;
index 7c96eea38b74ec6d726621645c43caae8343dbfe..700eb4fb9e13fa028f74d2ed9a2c1d18c917306e 100644 (file)
@@ -47,6 +47,11 @@ public:
      */
     void testFormatUserDII();
 
+    /*
+     * Test format using UDisplayContext
+     */
+    void testContext();
+
     /**
      * Test for no unwanted side effects when setting
      * interval patterns.
index e682b744b9528df3f66144ad4b7d82ca89b8c8c2..8a847d7d1ab44db0ece1e69ca2af1b8c16352cb2 100644 (file)
@@ -28,6 +28,7 @@ import com.ibm.icu.impl.SimpleCache;
 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 @@ public class DateIntervalFormat extends UFormat {
     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 @@ public class DateIntervalFormat extends UFormat {
         other.fDatePattern = fDatePattern;
         other.fTimePattern = fTimePattern;
         other.fDateTimeFormat = fDateTimeFormat;
+        other.fCapitalizationSetting = fCapitalizationSetting;
         return other;
     }
 
@@ -920,6 +926,10 @@ public class DateIntervalFormat extends UFormat {
             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 @@ public class DateIntervalFormat extends UFormat {
         }
         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 @@ public class DateIntervalFormat extends UFormat {
             if (pos.getEndIndex() > 0) {
                 pos = new FieldPosition(0);
             }
+            // No capitalization for second portion
+            fDateFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
         }
     }
 
@@ -1099,6 +1113,8 @@ public class DateIntervalFormat extends UFormat {
                 if (pos.getEndIndex() > 0) {
                     pos = new FieldPosition(0);
                 }
+                // No capitalization for second portion
+                fDateFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
             }
 
             // restore full pattern
@@ -1244,6 +1260,39 @@ public class DateIntervalFormat extends UFormat {
         }
     }
 
+    /**
+     * {@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
@@ -2197,6 +2246,10 @@ public class DateIntervalFormat extends UFormat {
         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;
+        }
     }
 
     /**
index cb1572d6c36f0bdfa786b367ab21ca37bbf06ef5..d6b19450fc893aa879b30118715b4a9227180d97 100644 (file)
@@ -36,6 +36,7 @@ import com.ibm.icu.text.DateIntervalFormat;
 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 @@ public class DateIntervalFormatTest extends TestFmwk {
         }
     }
 
+    /*
+     * 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