]> granicus.if.org Git - icu/commitdiff
ICU-10274 Add compound duration formatting for C++
authorTravis Keep <keep94@gmail.com>
Thu, 25 Jul 2013 16:36:25 +0000 (16:36 +0000)
committerTravis Keep <keep94@gmail.com>
Thu, 25 Jul 2013 16:36:25 +0000 (16:36 +0000)
X-SVN-Rev: 33986

13 files changed:
.gitattributes
icu4c/source/common/listformatter.cpp
icu4c/source/common/unicode/listformatter.h
icu4c/source/i18n/Makefile.in
icu4c/source/i18n/i18n.vcxproj
icu4c/source/i18n/i18n.vcxproj.filters
icu4c/source/i18n/timeperiod.cpp [new file with mode: 0644]
icu4c/source/i18n/tmutfmt.cpp
icu4c/source/i18n/unicode/timeperiod.h [new file with mode: 0644]
icu4c/source/i18n/unicode/tmutfmt.h
icu4c/source/test/intltest/listformattertest.cpp
icu4c/source/test/intltest/tufmtts.cpp
icu4c/source/test/intltest/tufmtts.h

index 3e83e8aa65b3c13abb1735b06f9edf66e1a4a693..0f1411de1b57d564ce6a3ea634ca78814d20e48e 100644 (file)
@@ -82,6 +82,8 @@ icu4c/source/extra/uconv/uconv.vcxproj -text
 icu4c/source/extra/uconv/uconv.vcxproj.filters -text
 icu4c/source/i18n/i18n.vcxproj -text
 icu4c/source/i18n/i18n.vcxproj.filters -text
+icu4c/source/i18n/timeperiod.cpp -text
+icu4c/source/i18n/unicode/timeperiod.h -text
 icu4c/source/i18n/unicode/uregion.h -text
 icu4c/source/i18n/uregion.cpp -text
 icu4c/source/io/io.vcxproj -text
index 30971431f6fa3c77f21ab7a583f1d94b7ab1627c..f4350d25cccc24493b9d68971df3799c51d48718 100644 (file)
@@ -1,7 +1,7 @@
 /*
 *******************************************************************************
 *
-*   Copyright (C) 2012, International Business Machines
+*   Copyright (C) 2013, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *
 *******************************************************************************
@@ -29,6 +29,7 @@ static Hashtable* listPatternHash = NULL;
 static UMutex listFormatterMutex = U_MUTEX_INITIALIZER;
 static UChar FIRST_PARAMETER[] = { 0x7b, 0x30, 0x7d };  // "{0}"
 static UChar SECOND_PARAMETER[] = { 0x7b, 0x31, 0x7d };  // "{0}"
+static const char *STANDARD_STYLE = "standard";
 
 U_CDECL_BEGIN
 static UBool U_CALLCONV uprv_listformatter_cleanup() {
@@ -44,9 +45,17 @@ uprv_deleteListFormatData(void *obj) {
 
 U_CDECL_END
 
-static ListFormatData* loadListFormatData(const Locale& locale, UErrorCode& errorCode);
+static ListFormatData* loadListFormatData(const Locale& locale, const char* style, UErrorCode& errorCode);
 static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode);
 
+ListFormatter::ListFormatter(const ListFormatter& other) : data(other.data) {
+}
+
+ListFormatter& ListFormatter::operator=(const ListFormatter& other) {
+    data = other.data;
+    return *this;
+}
+
 void ListFormatter::initializeHash(UErrorCode& errorCode) {
     if (U_FAILURE(errorCode)) {
         return;
@@ -64,11 +73,13 @@ void ListFormatter::initializeHash(UErrorCode& errorCode) {
 }
 
 const ListFormatData* ListFormatter::getListFormatData(
-        const Locale& locale, UErrorCode& errorCode) {
+        const Locale& locale, const char *style, UErrorCode& errorCode) {
     if (U_FAILURE(errorCode)) {
         return NULL;
     }
-    UnicodeString key(locale.getName(), -1, US_INV);
+    CharString keyBuffer(locale.getName(), errorCode);
+    keyBuffer.append(':', errorCode).append(style, errorCode);
+    UnicodeString key(keyBuffer.data(), -1, US_INV);
     ListFormatData* result = NULL;
     {
         Mutex m(&listFormatterMutex);
@@ -83,7 +94,7 @@ const ListFormatData* ListFormatter::getListFormatData(
     if (result != NULL) {
         return result;
     }
-    result = loadListFormatData(locale, errorCode);
+    result = loadListFormatData(locale, style, errorCode);
     if (U_FAILURE(errorCode)) {
         return NULL;
     }
@@ -104,14 +115,22 @@ const ListFormatData* ListFormatter::getListFormatData(
     return result;
 }
 
-static ListFormatData* loadListFormatData(const Locale& locale, UErrorCode& errorCode) {
+static ListFormatData* loadListFormatData(
+        const Locale& locale, const char * style, UErrorCode& errorCode) {
     UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode);
     if (U_FAILURE(errorCode)) {
         ures_close(rb);
         return NULL;
     }
     rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
-    rb = ures_getByKeyWithFallback(rb, "standard", rb, &errorCode);
+    rb = ures_getByKeyWithFallback(rb, style, rb, &errorCode);
+
+    // TODO(Travis Keep): This is a hack until fallbacks can be added for
+    // listPattern/duration and listPattern/duration-narrow in CLDR.
+    if (errorCode == U_MISSING_RESOURCE_ERROR) {
+        errorCode = U_ZERO_ERROR;
+        rb = ures_getByKeyWithFallback(rb, "standard", rb, &errorCode);
+    }
     if (U_FAILURE(errorCode)) {
         ures_close(rb);
         return NULL;
@@ -148,12 +167,16 @@ ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
 }
 
 ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
+    return createInstance(locale, STANDARD_STYLE, errorCode);
+}
+
+ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) {
     Locale tempLocale = locale;
-    const ListFormatData* listFormatData = getListFormatData(tempLocale, errorCode);
+    const ListFormatData* listFormatData = getListFormatData(tempLocale, style, errorCode);
     if (U_FAILURE(errorCode)) {
         return NULL;
     }
-    ListFormatter* p = new ListFormatter(*listFormatData);
+    ListFormatter* p = new ListFormatter(listFormatData);
     if (p == NULL) {
         errorCode = U_MEMORY_ALLOCATION_ERROR;
         return NULL;
@@ -161,7 +184,8 @@ ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& e
     return p;
 }
 
-ListFormatter::ListFormatter(const ListFormatData& listFormatterData) : data(listFormatterData) {
+
+ListFormatter::ListFormatter(const ListFormatData* listFormatterData) : data(listFormatterData) {
 }
 
 ListFormatter::~ListFormatter() {}
@@ -171,18 +195,22 @@ UnicodeString& ListFormatter::format(const UnicodeString items[], int32_t nItems
     if (U_FAILURE(errorCode)) {
         return appendTo;
     }
+    if (data == NULL) {
+        errorCode = U_INVALID_STATE_ERROR;
+        return appendTo;
+    }
 
     if (nItems > 0) {
         UnicodeString newString = items[0];
         if (nItems == 2) {
-            addNewString(data.twoPattern, newString, items[1], errorCode);
+            addNewString(data->twoPattern, newString, items[1], errorCode);
         } else if (nItems > 2) {
-            addNewString(data.startPattern, newString, items[1], errorCode);
+            addNewString(data->startPattern, newString, items[1], errorCode);
             int32_t i;
             for (i = 2; i < nItems - 1; ++i) {
-                addNewString(data.middlePattern, newString, items[i], errorCode);
+                addNewString(data->middlePattern, newString, items[i], errorCode);
             }
-            addNewString(data.endPattern, newString, items[nItems - 1], errorCode);
+            addNewString(data->endPattern, newString, items[nItems - 1], errorCode);
         }
         if (U_SUCCESS(errorCode)) {
             appendTo += newString;
index 30d99e9771773582d7142ffd5bbe8e8c51f06110..0f90e5cbdd48320dadb02efd358f49d226acea78 100644 (file)
@@ -60,12 +60,26 @@ struct ListFormatData : public UMemory {
 class U_COMMON_API ListFormatter : public UObject{
 
   public:
+
+    /**
+     * Copy constructor.
+     * @draft ICU 52
+     */
+    ListFormatter(const ListFormatter&);
+
+    /**
+     * Assignment operator.
+     * @draft ICU 52
+     */
+    ListFormatter& operator=(const ListFormatter& other);
+
     /**
      * Creates a ListFormatter appropriate for the default locale.
      *
      * @param errorCode ICU error code, set if no data available for default locale.
      * @return Pointer to a ListFormatter object for the default locale,
      *     created from internal data derived from CLDR data.
+     * @deprecated
      * @draft ICU 50
      */
     static ListFormatter* createInstance(UErrorCode& errorCode);
@@ -77,10 +91,22 @@ class U_COMMON_API ListFormatter : public UObject{
      * @param errorCode ICU error code, set if no data available for the given locale.
      * @return A ListFormatter object created from internal data derived from
      *     CLDR data.
+     * @deprecated
      * @draft ICU 50
      */
     static ListFormatter* createInstance(const Locale& locale, UErrorCode& errorCode);
 
+    /**
+     * Creates a ListFormatter appropriate for a locale and style.
+     *
+     * @param locale The locale.
+     * @param style the style, either "standard", "duration", or "duration-short"
+     * @param errorCode ICU error code, set if no data available for the given locale.
+     * @return A ListFormatter object created from internal data derived from
+     *     CLDR data.
+     * @internal
+     */
+    static ListFormatter* createInstance(const Locale& locale, const char* style, UErrorCode& errorCode);
 
     /**
      * Destructor.
@@ -106,20 +132,17 @@ class U_COMMON_API ListFormatter : public UObject{
     /**
      * @internal constructor made public for testing.
      */
-    ListFormatter(const ListFormatData& listFormatterData);
+    ListFormatter(const ListFormatData* listFormatterData);
 
   private:
     static void initializeHash(UErrorCode& errorCode);
-    static const ListFormatData* getListFormatData(const Locale& locale, UErrorCode& errorCode);
+    static const ListFormatData* getListFormatData(const Locale& locale, const char *style, UErrorCode& errorCode);
 
     ListFormatter();
-    ListFormatter(const ListFormatter&);
-
-    ListFormatter& operator = (const ListFormatter&);
     void addNewString(const UnicodeString& pattern, UnicodeString& originalString,
                       const UnicodeString& newString, UErrorCode& errorCode) const;
 
-    const ListFormatData& data;
+    const ListFormatData* data;
 };
 
 U_NAMESPACE_END
index b1b8b7e086cf9f6f5ee45bf9b84b34ac06c05e58..953da41f3e7480883923322ccaf191d6948ebf0e 100644 (file)
@@ -86,7 +86,9 @@ tmunit.o tmutamt.o tmutfmt.o currpinf.o \
 uspoof.o uspoof_impl.o uspoof_build.o uspoof_conf.o uspoof_wsconf.o decfmtst.o smpdtfst.o \
 ztrans.o zrule.o vzone.o fphdlimp.o fpositer.o locdspnm.o \
 decNumber.o decContext.o alphaindex.o tznames.o tznames_impl.o tzgnames.o \
-tzfmt.o compactdecimalformat.o gender.o region.o uregion.o scriptset.o identifier_info.o
+tzfmt.o compactdecimalformat.o gender.o region.o scriptset.o identifier_info.o \
+tzfmt.o compactdecimalformat.o gender.o region.o uregion.o scriptset.o \
+identifier_info.o timeperiod.o
 
 ## Header files to install
 HEADERS = $(srcdir)/unicode/*.h
index fc33d41529b013b290dde94cbb568e5729c11c06..e6da521490fbda585e231d5ce75598e5b4a2fac5 100644 (file)
     <ClCompile Include="smpdtfmt.cpp" />\r
     <ClCompile Include="smpdtfst.cpp" />\r
     <ClCompile Include="taiwncal.cpp" />\r
+    <ClCompile Include="timeperiod.cpp" />\r
     <ClCompile Include="timezone.cpp" />\r
     <ClCompile Include="tmunit.cpp" />\r
     <ClCompile Include="tmutamt.cpp" />\r
     </CustomBuild>\r
     <ClInclude Include="smpdtfst.h" />\r
     <ClInclude Include="taiwncal.h" />\r
+    <CustomBuild Include="unicode\timeperiod.h">\r
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">copy "%(FullPath)" ..\..\include\unicode\r
+</Command>\r
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>\r
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">copy "%(FullPath)" ..\..\include\unicode\r
+</Command>\r
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>\r
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">copy "%(FullPath)" ..\..\include\unicode\r
+</Command>\r
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>\r
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">copy "%(FullPath)" ..\..\include\unicode\r
+</Command>\r
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>\r
+    </CustomBuild>\r
     <CustomBuild Include="unicode\timezone.h">\r
       <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">copy "%(FullPath)" ..\..\include\unicode\r
 </Command>\r
index 3ae44ec9b502102dccd7b60093a24389ca809804..1239511d7228aa6904deaeceeb1a35c150efbecf 100644 (file)
     <ClCompile Include="taiwncal.cpp">\r
       <Filter>formatting</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="timeperiod.cpp">\r
+      <Filter>formatting</Filter>\r
+    </ClCompile>\r
     <ClCompile Include="timezone.cpp">\r
       <Filter>formatting</Filter>\r
     </ClCompile>\r
     <CustomBuild Include="unicode\smpdtfmt.h">\r
       <Filter>formatting</Filter>\r
     </CustomBuild>\r
+ <CustomBuild Include="unicode\timeperiod.h">\r
+      <Filter>formatting</Filter>\r
+    </CustomBuild>\r
     <CustomBuild Include="unicode\timezone.h">\r
       <Filter>formatting</Filter>\r
     </CustomBuild>\r
diff --git a/icu4c/source/i18n/timeperiod.cpp b/icu4c/source/i18n/timeperiod.cpp
new file mode 100644 (file)
index 0000000..9470291
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2013-2013, Google, International Business Machines Corporation
+ * and others. All Rights Reserved.
+ *******************************************************************************
+ */
+
+#include <math.h>
+#include "unicode/timeperiod.h"
+#include "putilimp.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+U_NAMESPACE_BEGIN
+
+TimePeriod::TimePeriod(const TimePeriod& other) : fLength(other.fLength) {
+    for (int32_t i = 0; i < sizeof(fFields) / sizeof(fFields[0]); ++i) {
+        if (other.fFields[i] == NULL) {
+            fFields[i] = NULL;
+        } else {
+            fFields[i] = (TimeUnitAmount *) other.fFields[i]->clone();
+        }
+    }
+}
+
+
+TimePeriod::TimePeriod(
+        const TimeUnitAmount * const *timeUnitAmounts,
+        int32_t length,
+        UErrorCode& status) : fLength(0) {
+    int32_t fieldCount = sizeof(fFields) / sizeof(fFields[0]);
+    for (int32_t i = 0; i < fieldCount; ++i) {
+        fFields[i] = NULL;
+    }
+    if (U_FAILURE(status)) {
+        return;
+    }
+    for (int32_t i = 0; i < length; ++i) {
+        const TimeUnitAmount *tua = timeUnitAmounts[i];
+        int32_t idx = tua->getTimeUnitField();
+        if (fFields[idx] != NULL) {
+            status = U_ILLEGAL_ARGUMENT_ERROR;
+            return;
+        }
+        fFields[idx] = (TimeUnitAmount *) tua->clone();
+        ++fLength;
+    }
+    validate(status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+}
+
+void TimePeriod::validate(UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    if (fLength == 0) {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+        return;
+    }  
+    int32_t fieldCount = sizeof(fFields) / sizeof(fFields[0]);
+    UBool fractionalFieldEncountered = FALSE;
+    for (int32_t i = 0; i < fieldCount; ++i) {
+        if (fFields[i] == NULL) {
+             continue;
+        }
+        if (fractionalFieldEncountered) {
+            status = U_ILLEGAL_ARGUMENT_ERROR;
+            return;
+        }
+        UErrorCode doubleStatus = U_ZERO_ERROR;
+        double value = fFields[i]->getNumber().getDouble(doubleStatus);
+        if (value != uprv_floor(value)) {
+            fractionalFieldEncountered = TRUE;
+        }
+    }
+}
+
+TimePeriod::~TimePeriod() {
+    int32_t fieldCount = sizeof(fFields) / sizeof(fFields[0]);
+    for (int32_t i = 0; i < fieldCount; ++i) {
+        delete fFields[i];
+    }
+}
+
+UBool TimePeriod::operator==(const TimePeriod& other) const {
+    // If same object, they are equal
+    if (this == &other) {
+        return TRUE;
+    }
+    int32_t fieldCount = sizeof(fFields) / sizeof(fFields[0]);
+    for (int32_t i = 0; i < fieldCount; ++i) {
+        if (fFields[i] == other.fFields[i]) {
+            continue;
+        }    
+        // One is NULL, the other isn't.
+        if (fFields[i] == NULL || other.fFields[i] == NULL) {
+            return FALSE;
+        }
+        if (*fFields[i] != *other.fFields[i]) {
+            return FALSE;
+        }
+    }
+    return TRUE;
+}
+    
+UBool TimePeriod::operator!=(const TimePeriod& other) const {
+    return !(*this == other);
+}
+
+const TimeUnitAmount *TimePeriod::getAmount(
+        TimeUnit::UTimeUnitFields field) const {
+    return fFields[field];
+}
+
+U_NAMESPACE_END
+
+#endif
index 5ce65314d7d3c1fc2dc27d496859c844fc5615cf..98fe89a74c671356b5aa18c47e99fff60631b8f5 100644 (file)
 #include "cstring.h"
 #include "hash.h"
 #include "uresimp.h"
+#include "unicode/datefmt.h"
+#include "unicode/listformatter.h"
 #include "unicode/msgfmt.h"
+#include "unicode/smpdtfmt.h"
+#include "unicode/timeperiod.h"
+#include "unicode/udat.h"
 #include "uassert.h"
 
 #define LEFT_CURLY_BRACKET  ((UChar)0x007B)
@@ -76,24 +81,48 @@ static const UChar PLURAL_COUNT_ZERO[] = {LOW_Z, LOW_E, LOW_R, LOW_O, 0};
 static const UChar PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0};
 static const UChar PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0};
 
+static const TimeUnit::UTimeUnitFields FIELDS_BY_DURATION[] = {
+        TimeUnit::UTIMEUNIT_YEAR,
+        TimeUnit::UTIMEUNIT_MONTH,
+        TimeUnit::UTIMEUNIT_WEEK,
+        TimeUnit::UTIMEUNIT_DAY,
+        TimeUnit::UTIMEUNIT_HOUR,
+        TimeUnit::UTIMEUNIT_MINUTE,
+        TimeUnit::UTIMEUNIT_SECOND,
+        TimeUnit::UTIMEUNIT_FIELD_COUNT};
+
+static void cloneDateFormat(DateFormat *&, const DateFormat *);
+static double getAmountOrZero(const TimePeriod& timePeriod, TimeUnit::UTimeUnitFields field);
 
 TimeUnitFormat::TimeUnitFormat(UErrorCode& status)
 :   fNumberFormat(NULL),
-    fPluralRules(NULL) {
+    fPluralRules(NULL),
+    fListFormatter(NULL),
+    fHourMinute(NULL),
+    fHourMinuteSecond(NULL),
+    fMinuteSecond(NULL) {
     create(Locale::getDefault(), UTMUTFMT_FULL_STYLE, status);
 }
 
 
 TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status)
 :   fNumberFormat(NULL),
-    fPluralRules(NULL) {
+    fPluralRules(NULL),
+    fListFormatter(NULL),
+    fHourMinute(NULL),
+    fHourMinuteSecond(NULL),
+    fMinuteSecond(NULL) {
     create(locale, UTMUTFMT_FULL_STYLE, status);
 }
 
 
 TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status)
 :   fNumberFormat(NULL),
-    fPluralRules(NULL) {
+    fPluralRules(NULL),
+    fListFormatter(NULL),
+    fHourMinute(NULL),
+    fHourMinuteSecond(NULL),
+    fMinuteSecond(NULL) {
     create(locale, style, status);
 }
 
@@ -102,8 +131,11 @@ TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other)
 :   MeasureFormat(other),
     fNumberFormat(NULL),
     fPluralRules(NULL),
-    fStyle(UTMUTFMT_FULL_STYLE)
-{
+    fListFormatter(NULL),
+    fHourMinute(NULL),
+    fHourMinuteSecond(NULL),
+    fMinuteSecond(NULL),
+    fStyle(UTMUTFMT_FULL_STYLE) {
     for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
          i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
          i = (TimeUnit::UTimeUnitFields)(i+1)) {
@@ -124,6 +156,14 @@ TimeUnitFormat::~TimeUnitFormat() {
     }
     delete fPluralRules;
     fPluralRules = NULL;
+    delete fListFormatter;
+    fListFormatter = NULL;
+    delete fHourMinute;
+    fHourMinute = NULL;
+    delete fHourMinuteSecond;
+    fHourMinuteSecond = NULL;
+    delete fMinuteSecond;
+    fMinuteSecond = NULL;
 }
 
 
@@ -151,6 +191,16 @@ TimeUnitFormat::operator=(const TimeUnitFormat& other) {
     } else {
         fNumberFormat = NULL;
     }
+    delete fListFormatter;
+    if (other.fListFormatter) {
+        fListFormatter = new ListFormatter(*other.fListFormatter);
+    } else {
+        fListFormatter = NULL;
+    }
+    cloneDateFormat(fHourMinute, other.fHourMinute);
+    cloneDateFormat(fHourMinuteSecond, other.fHourMinuteSecond);
+    cloneDateFormat(fMinuteSecond, other.fMinuteSecond);
+
     fLocale = other.fLocale;
     for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
          i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
@@ -224,11 +274,17 @@ TimeUnitFormat::format(const Formattable& obj, UnicodeString& toAppendTo,
             count.extract(0, count.length(), result, "UTF-8");
             std::cout << "number: " << number << "; format plural count: " << result << "\n";           
 #endif
-            MessageFormat* pattern = ((MessageFormat**)countToPattern->get(count))[fStyle];
+            UTimeUnitFormatStyle effectiveStyle = (fStyle == UTMUTFMT_NUMERIC_STYLE) ?
+                UTMUTFMT_ABBREVIATED_STYLE : fStyle;
+            MessageFormat* pattern = ((MessageFormat**)countToPattern->get(count))[effectiveStyle];
             Formattable formattable[1];
             formattable[0].setDouble(number);
             return pattern->format(formattable, 1, toAppendTo, pos, status);
         }
+        const TimePeriod* period = dynamic_cast<const TimePeriod*>(formatObj);
+        if (period != NULL) {
+            return formatTimePeriod(*period, toAppendTo, status);
+        }
     }
     status = U_ILLEGAL_ARGUMENT_ERROR;
     return toAppendTo;
@@ -271,6 +327,10 @@ TimeUnitFormat::parseObject(const UnicodeString& source,
             MessageFormat** patterns = (MessageFormat**)valueTok.pointer;
             for (UTimeUnitFormatStyle style = UTMUTFMT_FULL_STYLE; style < UTMUTFMT_FORMAT_STYLE_COUNT;
                  style = (UTimeUnitFormatStyle)(style + 1)) {
+                // We don't support parsing of numeric styles, so skip it.
+                if (style == UTMUTFMT_NUMERIC_STYLE) {
+                    continue;
+                }
                 MessageFormat* pattern = patterns[style];
                 pos.setErrorIndex(-1);
                 pos.setIndex(oldPos);
@@ -356,13 +416,129 @@ TimeUnitFormat::parseObject(const UnicodeString& source,
     }
 }
 
+UnicodeString& 
+TimeUnitFormat::formatTimePeriod(
+        const TimePeriod& timePeriod, UnicodeString& toAppendTo, UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return toAppendTo;
+    }
+    if (fStyle == UTMUTFMT_NUMERIC_STYLE && formatTimePeriodAsNumeric(timePeriod, toAppendTo, status)) {
+        return toAppendTo;
+    }
+    UnicodeString *items = new UnicodeString[timePeriod.length()];
+    if (items == NULL) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return toAppendTo;
+    }
+    int32_t idx = 0;
+    for (int32_t fieldIndex = 0; FIELDS_BY_DURATION[fieldIndex] != TimeUnit::UTIMEUNIT_FIELD_COUNT; ++fieldIndex) {
+        const TimeUnitAmount *amount = timePeriod.getAmount(FIELDS_BY_DURATION[fieldIndex]);
+        if (amount == NULL) {
+            continue;
+        }
+        TimeUnitAmount *timeUnitAmountCopy = new TimeUnitAmount(*amount);
+        if (timeUnitAmountCopy == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            delete [] items;
+            return toAppendTo;
+        }
+        format(Formattable(timeUnitAmountCopy), items[idx], status);
+        if (U_FAILURE(status)) {
+            delete [] items;
+            return toAppendTo;
+        }
+        ++idx;
+    }
+    UnicodeString& result = fListFormatter->format(items, timePeriod.length(), toAppendTo, status);
+    delete [] items;
+    return result;
+}
+
+UBool
+TimeUnitFormat::formatTimePeriodAsNumeric(
+        const TimePeriod& timePeriod, UnicodeString& toAppendTo, UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return TRUE;
+    }
+    TimeUnit::UTimeUnitFields biggestUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT;
+    TimeUnit::UTimeUnitFields smallestUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT;
+    const TimeUnitAmount *smallestAmount = NULL;
+    for (int32_t fieldIndex = 0; FIELDS_BY_DURATION[fieldIndex] != TimeUnit::UTIMEUNIT_FIELD_COUNT; ++fieldIndex) {
+        const TimeUnitAmount *amount = timePeriod.getAmount(FIELDS_BY_DURATION[fieldIndex]);
+        if (amount == NULL) {
+            continue;
+        }
+        if (biggestUnit == TimeUnit::UTIMEUNIT_FIELD_COUNT) {
+            biggestUnit = FIELDS_BY_DURATION[fieldIndex];
+        }
+        smallestUnit = FIELDS_BY_DURATION[fieldIndex];
+        smallestAmount = amount;
+    }
+    U_ASSERT(biggestUnit != TimeUnit::UTIMEUNIT_FIELD_COUNT);
+    U_ASSERT(smallestUnit != TimeUnit::UTIMEUNIT_FIELD_COUNT);
+    U_ASSERT(smallestAmount != NULL);
+    
+    double millis = ((getAmountOrZero(timePeriod, TimeUnit::UTIMEUNIT_HOUR) * 60.0
+            + getAmountOrZero(timePeriod, TimeUnit::UTIMEUNIT_MINUTE)) * 60.0
+            + getAmountOrZero(timePeriod, TimeUnit::UTIMEUNIT_SECOND)) * 1000.0;
+    if (biggestUnit == TimeUnit::UTIMEUNIT_HOUR && smallestUnit == TimeUnit::UTIMEUNIT_SECOND) {
+        numericFormat(millis, *fHourMinuteSecond, UDAT_SECOND_FIELD, smallestAmount->getNumber(), toAppendTo, status);
+        return TRUE;
+    }
+    if (biggestUnit == TimeUnit::UTIMEUNIT_MINUTE && smallestUnit == TimeUnit::UTIMEUNIT_SECOND) {
+        numericFormat(millis, *fMinuteSecond, UDAT_SECOND_FIELD, smallestAmount->getNumber(), toAppendTo, status);
+        return TRUE;
+    }
+    if (biggestUnit == TimeUnit::UTIMEUNIT_HOUR && smallestUnit == TimeUnit::UTIMEUNIT_MINUTE) {
+        numericFormat(millis, *fHourMinute, UDAT_MINUTE_FIELD, smallestAmount->getNumber(), toAppendTo, status);
+        return TRUE;
+    }
+    return FALSE;
+}
+
+void
+TimeUnitFormat::numericFormat(
+        double date,
+        const DateFormat &dateFormat,
+        int32_t smallestField,
+        const Formattable& smallestAmount,
+        UnicodeString& toAppendTo,
+        UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    UnicodeString smallestAmountFormatted;
+    fNumberFormat->format(smallestAmount, smallestAmountFormatted, status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+    FieldPositionIterator fpi;
+    int32_t prevLength = toAppendTo.length();
+    dateFormat.format(Formattable(date, Formattable::kIsDate), toAppendTo, &fpi, status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+    FieldPosition fp;
+    UBool substituted = FALSE;
+    while (fpi.next(fp)) {
+        if (fp.getField() == smallestField) {
+            toAppendTo.replace(
+                prevLength + fp.getBeginIndex(),
+                fp.getEndIndex() - fp.getBeginIndex(),
+                smallestAmountFormatted);
+            substituted = TRUE;
+            break;
+        }
+    }
+    U_ASSERT(substituted);
+}
 
 void
 TimeUnitFormat::create(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) {
     if (U_FAILURE(status)) {
         return;
     }
-    if (style < UTMUTFMT_FULL_STYLE || style > UTMUTFMT_ABBREVIATED_STYLE) {
+    if (style < UTMUTFMT_FULL_STYLE || style >= UTMUTFMT_FORMAT_STYLE_COUNT) {
         status = U_ILLEGAL_ARGUMENT_ERROR;
         return;
     }
@@ -415,6 +591,18 @@ TimeUnitFormat::initDataMembers(UErrorCode& err){
     }
     delete fPluralRules;
     fPluralRules = PluralRules::forLocale(fLocale, err);
+    delete fListFormatter;
+    if (fStyle == UTMUTFMT_FULL_STYLE) {
+        fListFormatter = ListFormatter::createInstance(fLocale, "duration", err);
+    } else {
+        fListFormatter = ListFormatter::createInstance(fLocale, "duration-short", err);
+    }
+    delete fHourMinute;
+    fHourMinute = loadNumericDurationFormat("hm", err);
+    delete fHourMinuteSecond;
+    fHourMinuteSecond = loadNumericDurationFormat("hms", err);
+    delete fMinuteSecond;
+    fMinuteSecond = loadNumericDurationFormat("ms", err);
     for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
          i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
          i = (TimeUnit::UTimeUnitFields)(i+1)) {
@@ -423,8 +611,40 @@ TimeUnitFormat::initDataMembers(UErrorCode& err){
     }
 }
 
+DateFormat *
+TimeUnitFormat::loadNumericDurationFormat(const char *pattern, UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+    UResourceBundle *rb, *unitsRes;
+    rb = ures_open(NULL, fLocale.getName(), &status);
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+    CharString path("durationUnits", status);
+    path.append('/', status).append(pattern, status);
 
-
+    int32_t fullPatternSize;
+    const UChar *fullPatternChars = ures_getStringByKeyWithFallback(rb, path.data(), &fullPatternSize, &status); 
+    if (U_FAILURE(status)) {
+        ures_close(rb);
+        return NULL;
+    }
+    UnicodeString fullPattern(FALSE, fullPatternChars, fullPatternSize);
+    fullPattern.findAndReplace(UnicodeString("h"), UnicodeString("H"));
+    DateFormat *result = new SimpleDateFormat(fullPattern, status);
+    if (result == NULL) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        ures_close(rb);
+        return NULL;
+    }
+    if (U_FAILURE(status)) {
+        delete result;
+        return NULL;
+    }
+    result->setTimeZone(*TimeZone::getGMT());
+    return result;
+}
 
 void
 TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* key,
@@ -900,6 +1120,25 @@ TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField,
     }
 }
 
+void cloneDateFormat(DateFormat *&dest, const DateFormat *src) {
+    delete dest;
+    if (src) {
+        dest = (DateFormat *) src->clone();
+    } else {
+        dest = NULL;
+    }
+}
+
+double getAmountOrZero(const TimePeriod& timePeriod, TimeUnit::UTimeUnitFields field) {
+    const TimeUnitAmount *amount = timePeriod.getAmount(field);
+    if (amount == NULL) {
+        return 0.0;
+    }
+    UErrorCode status = U_ZERO_ERROR;
+    return amount->getNumber().getDouble(status);
+}
+
+
 U_NAMESPACE_END
 
 #endif
diff --git a/icu4c/source/i18n/unicode/timeperiod.h b/icu4c/source/i18n/unicode/timeperiod.h
new file mode 100644 (file)
index 0000000..6cfa877
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2013-2013, Google, International Business Machines Corporation
+ * and others. All Rights Reserved.
+ *******************************************************************************
+ */
+
+#ifndef __TIMEPERIOD_H__
+#define __TIMEPERIOD_H__
+
+// TODO:
+// 1. Add size() method and test.
+// 2. testEquals() -> testTimePeriodEquals()
+
+#include "unicode/utypes.h"
+
+/**
+ * \file
+ * \brief C++ API: Format and parse duration in single time unit
+ */
+
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/tmunit.h"
+#include "unicode/tmutamt.h"
+
+U_NAMESPACE_BEGIN
+
+class U_I18N_API TimePeriod: public UObject {
+public:
+    // copy constructor.
+    TimePeriod(const TimePeriod& other);
+
+    /** 
+     * Constructor.
+     * @param timeUnitAmounts an array of TimeUnitAmounts pointers. TimePeriod copies the
+     * data in this array. The caller is responsible for freeing the TimeUnitAmount objects
+     * and the array.
+     * @param length the number of timeUnitAmount pointers in timeUnitAmounts array.
+     * @param status error returned here if timeUnitAmounts is empty;
+     *    timeUnitAmounts has duplicate time units; or any timeUnitAmount except the 
+     *    smallest has a non-integer value.
+     * @draft ICU 52
+     */  
+    TimePeriod(
+        const TimeUnitAmount * const *timeUnitAmounts, int32_t length, UErrorCode& status);
+
+    /** 
+     * Destructor
+     * @draft ICU 52
+     */  
+    virtual ~TimePeriod();
+
+    /** 
+     * Returns true if the given TimePeriod objects are semantically equal.
+     * For two TimePeriod objects to be equal, they must contain the same
+     * units, and the amount for each unit much be equal.  For example,
+     * 5 hours, 37 minutes == 37 minutes, 5 hours,
+     * but 0 days, 5 hours != 5 hours.
+     * @param that the TimePeriod to compare with. 
+     * @return true if the given TimePeriod objects are semantically equal.
+     * @draft ICU 52
+     */  
+    UBool operator==(const TimePeriod& that) const;
+
+    /** 
+     * Returns true if the given TimePeriod objects are semantically
+     * unequal.
+     * @param that the TimePeriod to compare with. 
+     * @return true if the given TimePeriod objects are semantically
+     * unequal.
+     * @draft ICU 52
+     */  
+    UBool operator!=(const TimePeriod& that) const;
+
+    /** 
+     * Gets a specific field out of a time period.
+     * @param field is the field to fetch
+     * @return The desired field or NULL if it does not exist.
+     */  
+    const TimeUnitAmount* getAmount(TimeUnit::UTimeUnitFields field) const;
+
+    /**
+     * Returns the number of time units in this object.
+     * @draft ICU 52
+     */
+    inline int32_t length() const {
+        return fLength;
+    }
+
+private:
+    int32_t fLength;
+    TimeUnitAmount *fFields[TimeUnit::UTIMEUNIT_FIELD_COUNT];
+
+    // Clients use forAmount and never use ctor directly.
+    TimePeriod() {
+    }   
+
+    /** 
+     * No assignment operator needed because class is immutable.
+     */  
+    TimePeriod& operator=(const TimePeriod& other);
+
+    void validate(UErrorCode& status) const;
+};
+
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+
+#endif // __TMUTFMT_H__
+//eof
index 8c3979f5cf61e326b6286996a8bf865489c95107..81604f442958b53d33a43b6f33f0613a0cef15b2 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2008-2012, Google, International Business Machines Corporation
+ * Copyright (C) 2008-2013, Google, International Business Machines Corporation
  * and others. All Rights Reserved.
  *******************************************************************************
  */
@@ -37,6 +37,8 @@ enum UTimeUnitFormatStyle {
     UTMUTFMT_FULL_STYLE,
     /** @stable ICU 4.8 */
     UTMUTFMT_ABBREVIATED_STYLE,
+    /** @draft ICU 52 */
+    UTMUTFMT_NUMERIC_STYLE,
     /** @stable ICU 4.8 */
     UTMUTFMT_FORMAT_STYLE_COUNT
 };
@@ -44,8 +46,11 @@ typedef enum UTimeUnitFormatStyle UTimeUnitFormatStyle; /**< @stable ICU 4.8 */
 
 U_NAMESPACE_BEGIN
 
+class DateFormat;
 class Hashtable;
 class UVector;
+class TimePeriod;
+class ListFormatter;
 
 /**
  * Format or parse a TimeUnitAmount, using plural rules for the units where available.
@@ -186,6 +191,18 @@ public:
                              Formattable& result,
                              ParsePosition& pos) const;
 
+    /**
+      * Format a time period.
+      * @param timePeriod the time period to format.
+      * @param toAppendTo where the formatted string is stored.
+      * @param status any error is stored here
+      * @return a reference to toAppendto
+      * @draft ICU 52
+      */
+    UnicodeString& formatTimePeriod(const TimePeriod &timePeriod,
+                                          UnicodeString& toAppendTo,
+                                          UErrorCode& status) const;
+
     /**
      * Return the class ID for this class. This is useful only for comparing to
      * a return value from getDynamicClassID(). For example:
@@ -217,8 +234,15 @@ private:
     Locale        fLocale;
     Hashtable*    fTimeUnitToCountToPatterns[TimeUnit::UTIMEUNIT_FIELD_COUNT];
     PluralRules*  fPluralRules;
+    ListFormatter *fListFormatter;
+    DateFormat *fHourMinute;
+    DateFormat *fHourMinuteSecond;
+    DateFormat *fMinuteSecond;
     UTimeUnitFormatStyle fStyle;
 
+    UBool formatTimePeriodAsNumeric(
+            const TimePeriod& timePeriod, UnicodeString& toAppendTo, UErrorCode& status) const;
+
     void create(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status);
 
     // it might actually be simpler to make them Decimal Formats later.
@@ -252,6 +276,19 @@ private:
     // get time unit name, such as "year", from time unit field enum, such as
     // UTIMEUNIT_YEAR.
     static const char* getTimeUnitName(TimeUnit::UTimeUnitFields field, UErrorCode& status);
+
+    void numericFormat(
+            double date,
+            const DateFormat &dateFormat,
+            int32_t smallestField,
+            const Formattable& smallestAmount,
+            UnicodeString& toAppendto,
+            UErrorCode& status) const;
+
+    DateFormat *loadNumericDurationFormat(
+            const char *pattern,
+            UErrorCode& status) const;
+
 };
 
 
index 07c1c5609cfd7a8ad9dd0a931bbd78ecd1c0887b..a4b9e18df924c4c166a10ddc78ab2d1ca4af76e8 100644 (file)
@@ -175,7 +175,7 @@ void ListFormatterTest::TestOutOfOrderPatterns() {
 
     ListFormatData data("{1} after {0}", "{1} after the first {0}",
                         "{1} after {0}", "{1} in the last after {0}");
-    ListFormatter formatter(data);
+    ListFormatter formatter(&data);
 
     UnicodeString input1[] = {one};
     CheckFormatting(&formatter, input1, 1, results[0]);
index abf0a1134cb046fe07564a67fc56701e523edbdf..c92a9504d6eeda6f1c447e2a9ac072d14d7de65b 100644 (file)
@@ -1,5 +1,5 @@
 /********************************************************************
- * Copyright (c) 2008-2012, International Business Machines Corporation and
+ * Copyright (c) 2008-2013, International Business Machines Corporation and
  * others. All Rights Reserved.
  ********************************************************************/
 
@@ -11,6 +11,7 @@
 #include "unicode/tmutamt.h"
 #include "unicode/tmutfmt.h"
 #include "tufmtts.h"
+#include "unicode/timeperiod.h"
 #include "unicode/ustring.h"
 
 //TODO: put as compilation flag
 #include <iostream>
 #endif
 
+struct TimePeriodResult {
+    TimePeriod timePeriod;
+    const char* result;
+};
+
+class TimeUnitAmountSubClass : public TimeUnitAmount {
+  public:
+    TimeUnitAmountSubClass(double amount, TimeUnit::UTimeUnitFields timeUnitField, int ex, UErrorCode &status) : TimeUnitAmount(amount, timeUnitField, status), extra(ex) { }
+
+    TimeUnitAmountSubClass(const TimeUnitAmountSubClass &that)
+    : TimeUnitAmount(that), extra(that.extra) { }
+
+    TimeUnitAmountSubClass &operator=(const TimeUnitAmountSubClass &that) {
+      TimeUnitAmount::operator=(that);
+      extra = that.extra;
+      return *this;
+    }
+
+    virtual UObject* clone() const {
+      return new TimeUnitAmountSubClass(*this);
+    }
+
+    virtual ~TimeUnitAmountSubClass() { }
+    int extra;
+};
+
+static TimePeriod create1m59_9996s(UErrorCode &status);
+static TimePeriod create19m(UErrorCode &status);
+static TimePeriod create19m28s(UErrorCode &status);
+static TimePeriod create19m29s(UErrorCode &status);
+static TimePeriod create1h23_5s(UErrorCode &status);
+static TimePeriod create1h23s(UErrorCode &status);
+static TimePeriod create1h23_5m(UErrorCode &status);
+static TimePeriod create1h0m23s(UErrorCode &status);
+static TimePeriod create5h17m(UErrorCode &status);
+static TimePeriod create2y5M3w4d(UErrorCode &status);
+static TimePeriod create0h0m17s(UErrorCode &status);
+static TimePeriod create6h56_92m(UErrorCode &status);
+
 void TimeUnitTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) {
     if (exec) logln("TestSuite TimeUnitTest");
     switch (index) {
@@ -27,6 +67,11 @@ void TimeUnitTest::runIndexedTest( int32_t index, UBool exec, const char* &name,
         TESTCASE(1, testAPI);
         TESTCASE(2, testGreekWithFallback);
         TESTCASE(3, testGreekWithSanitization);
+        TESTCASE(4, testFormatPeriodEn);
+        TESTCASE(5, testTimePeriodForAmounts);
+        TESTCASE(6, testTimeUnitAmountSubClass);
+        TESTCASE(7, testTimePeriodEquals);
+        TESTCASE(8, testTimePeriodLength);
         default: name = ""; break;
     }
 }
@@ -342,5 +387,315 @@ void TimeUnitTest::testGreekWithSanitization() {
     delete timeUnitFormat;
 }
 
+void TimeUnitTest::testFormatPeriodEn() {
+    UErrorCode status = U_ZERO_ERROR;
+
+    TimePeriodResult fullResults[] = {
+        {create1m59_9996s(status), "1 minute, 59.9996 seconds"},
+        {create19m(status), "19 minutes"},
+        {create1h23_5s(status), "1 hour, 23.5 seconds"},
+        {create1h23_5m(status), "1 hour, 23.5 minutes"},
+        {create1h0m23s(status), "1 hour, 0 minutes, 23 seconds"},
+        {create2y5M3w4d(status), "2 years, 5 months, 3 weeks, 4 days"}};
+  
+    TimePeriodResult abbrevResults[] = {
+        {create1m59_9996s(status), "1 min, 59.9996 secs"},
+        {create19m(status), "19 mins"},
+        {create1h23_5s(status), "1 hr, 23.5 secs"},
+        {create1h23_5m(status), "1 hr, 23.5 mins"},
+        {create1h0m23s(status), "1 hr, 0 mins, 23 secs"},
+        {create2y5M3w4d(status), "2 yrs, 5 mths, 3 wks, 4 days"}};
+  
+    TimePeriodResult numericResults[] = {
+        {create1m59_9996s(status), "1:59.9996"},
+        {create19m(status), "19 mins"},
+        {create1h23_5s(status), "1:00:23.5"},
+        {create1h0m23s(status), "1:00:23"},
+        {create5h17m(status), "5:17"},
+        {create19m28s(status), "19:28"},
+        {create2y5M3w4d(status), "2 yrs, 5 mths, 3 wks, 4 days"},
+        {create0h0m17s(status), "0:00:17"},
+        {create6h56_92m(status), "6:56.92"}};
+
+   if (U_FAILURE(status)) {
+      dataerrln("Unable to create time periods - %s", u_errorName(status));
+      return;
+    }
+
+    LocalPointer<NumberFormat> nf(NumberFormat::createInstance(Locale::getEnglish(), status));
+    if (U_FAILURE(status)) {
+        dataerrln("Unable to create NumberFormat object - %s", u_errorName(status));
+        return;
+    }
+    nf->setMaximumFractionDigits(4);
+    {
+        TimeUnitFormat tuf(Locale::getEnglish(), UTMUTFMT_FULL_STYLE, status);
+        tuf.setNumberFormat(*nf, status);
+        if (U_FAILURE(status)) {
+            dataerrln("Unable to create TimeUnitFormat object - %s", u_errorName(status));
+            return;
+        }
+        verifyFormatTimePeriod(
+            tuf,
+            fullResults,
+            sizeof(fullResults) / sizeof(TimePeriodResult));
+    }
+    {
+        TimeUnitFormat tuf(Locale::getEnglish(), UTMUTFMT_ABBREVIATED_STYLE, status);
+        tuf.setNumberFormat(*nf, status);
+        if (U_FAILURE(status)) {
+            dataerrln("Unable to create TimeUnitFormat object - %s", u_errorName(status));
+            return;
+        }
+        verifyFormatTimePeriod(
+            tuf,
+            abbrevResults,
+            sizeof(abbrevResults) / sizeof(TimePeriodResult));
+    }
+    {
+        TimeUnitFormat tuf(Locale::getEnglish(), UTMUTFMT_NUMERIC_STYLE, status);
+        tuf.setNumberFormat(*nf, status);
+        if (U_FAILURE(status)) {
+            dataerrln("Unable to create TimeUnitFormat object - %s", u_errorName(status));
+            return;
+        }
+        verifyFormatTimePeriod(
+            tuf,
+            numericResults,
+            sizeof(numericResults) / sizeof(TimePeriodResult));
+    }
+}
+
+void TimeUnitTest::testTimePeriodLength() {
+   UErrorCode status = U_ZERO_ERROR;
+   int32_t actual = create1h23_5m(status).length();
+    if (U_FAILURE(status)) {
+      dataerrln("Unable to create time period object - %s", u_errorName(status));
+      return;
+    }
+   if (actual != 2) {
+       errln("Expected 2, got %d", actual);
+    }
+}
+
+void TimeUnitTest::testTimePeriodForAmounts() {
+    UErrorCode status = U_ZERO_ERROR;
+    TimeUnitAmount _5h(5.0, TimeUnit::UTIMEUNIT_HOUR, status);
+    TimeUnitAmount _3_5h(3.5, TimeUnit::UTIMEUNIT_HOUR, status);
+    TimeUnitAmount _3h(3.0, TimeUnit::UTIMEUNIT_HOUR, status);
+    TimeUnitAmount _5m(5.0, TimeUnit::UTIMEUNIT_MINUTE, status);
+    if (U_FAILURE(status)) {
+      dataerrln("Unable to alocate time unit amounts - %s", u_errorName(status));
+      return;
+    }
+    {
+        UErrorCode status = U_ZERO_ERROR;
+        TimeUnitAmount *amounts[] = {&_3h, &_5h};
+        int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
+        TimePeriod(amounts, len, status);
+        if (status != U_ILLEGAL_ARGUMENT_ERROR) {
+            errln("Expected U_ILLEGAL_ARGUMENT_ERROR for 3h + 5h, got %s", u_errorName(status));
+        }
+    } 
+    {
+        UErrorCode status = U_ZERO_ERROR;
+        TimePeriod(NULL, 0, status);
+        if (status != U_ILLEGAL_ARGUMENT_ERROR) {
+            errln("Expected U_ILLEGAL_ARGUMENT_ERROR for empty time period, got %s", u_errorName(status));
+        }
+    } 
+    {
+        UErrorCode status = U_ZERO_ERROR;
+        TimeUnitAmount *amounts[] = {&_3_5h, &_5m};
+        int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
+        TimePeriod(amounts, len, status);
+        if (status != U_ILLEGAL_ARGUMENT_ERROR) {
+            errln("Expected U_ILLEGAL_ARGUMENT_ERROR for 3.5h + 5m, got %s", u_errorName(status));
+        }
+    } 
+}
+
+void TimeUnitTest::testTimePeriodEquals() {
+    UErrorCode status = U_ZERO_ERROR;
+
+    TimePeriod _1h23s = create1h23s(status);
+
+    // Same variable
+    verifyEquals(_1h23s, _1h23s);
+
+    // Different variables same value
+    verifyEquals(_1h23s, TimePeriod(_1h23s));
+
+    // Different fields
+    verifyNotEqual(_1h23s, create1h0m23s(status));
+
+    // Same fields different values
+    verifyNotEqual(create19m28s(status), create19m29s(status));
+
+    if (U_FAILURE(status)) {
+        errln("Failure creating TimePeriods, got %s", u_errorName(status));
+    }
+}
+
+void TimeUnitTest::testTimeUnitAmountSubClass() {
+    UErrorCode status = U_ZERO_ERROR;
+    TimeUnitAmountSubClass _6h(6.0, TimeUnit::UTIMEUNIT_HOUR, 1, status);
+    TimeUnitAmountSubClass _5m(5.0, TimeUnit::UTIMEUNIT_MINUTE, 2, status);
+    if (U_FAILURE(status)) {
+      dataerrln("Unable to alocate time unit amounts - %s", u_errorName(status));
+      return;
+    }
+    {
+        UErrorCode status = U_ZERO_ERROR;
+        TimeUnitAmount *amounts[] = {&_6h, &_5m};
+        int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
+        TimePeriod period(amounts, len, status);
+        if (2 != ((const TimeUnitAmountSubClass *) period.getAmount(TimeUnit::UTIMEUNIT_MINUTE))->extra) {
+            errln("Expected polymorphic behavior.");
+        }
+    } 
+}
+
+void TimeUnitTest::verifyFormatTimePeriod(
+        const TimeUnitFormat& tuf,
+        const TimePeriodResult* timePeriodResults,
+        int32_t numResults) {
+    for (int32_t i = 0; i < numResults; i++) {
+        UnicodeString expected(timePeriodResults[i].result, -1, US_INV);
+        expected = expected.unescape();
+        UErrorCode status = U_ZERO_ERROR;
+        Formattable formattable(new TimePeriod(timePeriodResults[i].timePeriod));
+        UnicodeString actual;
+        FieldPosition pos(0);
+        tuf.format(formattable, actual, pos, status);
+        if (U_FAILURE(status)) {
+            dataerrln("Unable to format time period - %s", u_errorName(status));
+            return;
+        }
+        if (actual != expected) {
+            errln(UnicodeString("Fail: Expected: ") + expected
+                + UnicodeString(" Got: ") + actual);
+        }
+    }
+}
+
+void TimeUnitTest::verifyEquals(const TimePeriod& lhs, const TimePeriod& rhs) {
+  if (lhs != rhs) {
+    errln("Expected equal.");
+    return;
+  }
+  if (!(lhs == rhs)) {
+    errln("Expected not not equal.");
+  }
+}
+
+void TimeUnitTest::verifyNotEqual(const TimePeriod& lhs, const TimePeriod& rhs) {
+  if (lhs == rhs) {
+    errln("Expected not equal.");
+    return;
+  }
+  if (!(lhs != rhs)) {
+    errln("Expected not not not equal.");
+  }
+}
+
+static TimePeriod create1m59_9996s(UErrorCode &status) {
+  TimeUnitAmount minutes(1.0, TimeUnit::UTIMEUNIT_MINUTE, status);
+  TimeUnitAmount seconds(59.9996, TimeUnit::UTIMEUNIT_SECOND, status);
+  TimeUnitAmount *amounts[] = {&minutes, &seconds};
+  int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
+  return TimePeriod(amounts, len, status); 
+}
+
+static TimePeriod create19m(UErrorCode &status) {
+  TimeUnitAmount minutes(19.0, TimeUnit::UTIMEUNIT_MINUTE, status);
+  TimeUnitAmount *amounts[] = {&minutes};
+  int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
+  return TimePeriod(amounts, len, status); 
+}
+
+static TimePeriod create19m28s(UErrorCode &status) {
+  TimeUnitAmount minutes(19.0, TimeUnit::UTIMEUNIT_MINUTE, status);
+  TimeUnitAmount seconds(28.0, TimeUnit::UTIMEUNIT_SECOND, status);
+  TimeUnitAmount *amounts[] = {&minutes, &seconds};
+  int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
+  return TimePeriod(amounts, len, status); 
+}
+
+static TimePeriod create19m29s(UErrorCode &status) {
+  TimeUnitAmount minutes(19.0, TimeUnit::UTIMEUNIT_MINUTE, status);
+  TimeUnitAmount seconds(29.0, TimeUnit::UTIMEUNIT_SECOND, status);
+  TimeUnitAmount *amounts[] = {&minutes, &seconds};
+  int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
+  return TimePeriod(amounts, len, status); 
+}
+
+static TimePeriod create1h23_5s(UErrorCode &status) {
+  TimeUnitAmount hours(1.0, TimeUnit::UTIMEUNIT_HOUR, status);
+  TimeUnitAmount seconds(23.5, TimeUnit::UTIMEUNIT_SECOND, status);
+  TimeUnitAmount *amounts[] = {&hours, &seconds};
+  int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
+  return TimePeriod(amounts, len, status); 
+}
+
+static TimePeriod create1h23_5m(UErrorCode &status) {
+  TimeUnitAmount hours(1.0, TimeUnit::UTIMEUNIT_HOUR, status);
+  TimeUnitAmount seconds(23.5, TimeUnit::UTIMEUNIT_MINUTE, status);
+  TimeUnitAmount *amounts[] = {&hours, &seconds};
+  int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
+  return TimePeriod(amounts, len, status); 
+}
+
+static TimePeriod create1h0m23s(UErrorCode &status) {
+  TimeUnitAmount hours(1.0, TimeUnit::UTIMEUNIT_HOUR, status);
+  TimeUnitAmount minutes(0.0, TimeUnit::UTIMEUNIT_MINUTE, status);
+  TimeUnitAmount seconds(23.0, TimeUnit::UTIMEUNIT_SECOND, status);
+  TimeUnitAmount *amounts[] = {&hours, &minutes, &seconds};
+  int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
+  return TimePeriod(amounts, len, status); 
+}
+
+static TimePeriod create1h23s(UErrorCode &status) {
+  TimeUnitAmount hours(1.0, TimeUnit::UTIMEUNIT_HOUR, status);
+  TimeUnitAmount seconds(23.0, TimeUnit::UTIMEUNIT_SECOND, status);
+  TimeUnitAmount *amounts[] = {&hours, &seconds};
+  int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
+  return TimePeriod(amounts, len, status); 
+}
+
+static TimePeriod create5h17m(UErrorCode &status) {
+  TimeUnitAmount hours(5.0, TimeUnit::UTIMEUNIT_HOUR, status);
+  TimeUnitAmount minutes(17.0, TimeUnit::UTIMEUNIT_MINUTE, status);
+  TimeUnitAmount *amounts[] = {&hours, &minutes};
+  int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
+  return TimePeriod(amounts, len, status); 
+}
+
+static TimePeriod create2y5M3w4d(UErrorCode &status) {
+  TimeUnitAmount years(2.0, TimeUnit::UTIMEUNIT_YEAR, status);
+  TimeUnitAmount months(5.0, TimeUnit::UTIMEUNIT_MONTH, status);
+  TimeUnitAmount weeks(3.0, TimeUnit::UTIMEUNIT_WEEK, status);
+  TimeUnitAmount days(4.0, TimeUnit::UTIMEUNIT_DAY, status);
+  TimeUnitAmount *amounts[] = {&years, &months, &weeks, &days};
+  int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
+  return TimePeriod(amounts, len, status); 
+}
+
+static TimePeriod create0h0m17s(UErrorCode &status) {
+  TimeUnitAmount hours(0.0, TimeUnit::UTIMEUNIT_HOUR, status);
+  TimeUnitAmount minutes(0.0, TimeUnit::UTIMEUNIT_MINUTE, status);
+  TimeUnitAmount seconds(17.0, TimeUnit::UTIMEUNIT_SECOND, status);
+  TimeUnitAmount *amounts[] = {&hours, &minutes, &seconds};
+  int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
+  return TimePeriod(amounts, len, status); 
+}
+
+static TimePeriod create6h56_92m(UErrorCode &status) {
+  TimeUnitAmount hours(6.0, TimeUnit::UTIMEUNIT_HOUR, status);
+  TimeUnitAmount minutes(56.92, TimeUnit::UTIMEUNIT_MINUTE, status);
+  TimeUnitAmount *amounts[] = {&hours, &minutes};
+  int32_t len = sizeof(amounts) / sizeof(TimeUnitAmount*);
+  return TimePeriod(amounts, len, status); 
+}
 
 #endif
index 45e841cb8063b203aed94c5231da00ce173b3bdd..475ca895572b76f6ba3ebf9d1ce3657e52022456 100644 (file)
@@ -1,6 +1,6 @@
 /********************************************************************
  * COPYRIGHT: 
- * Copyright (c) 2008-2012, International Business Machines Corporation
+ * Copyright (c) 2008-2013, International Business Machines Corporation
  * and others. All Rights Reserved.
  ********************************************************************/
 
 #include "unicode/locid.h"
 #include "intltest.h"
 
+U_NAMESPACE_BEGIN
+class TimeUnitFormat;
+class TimePeriod;
+U_NAMESPACE_END
+struct TimePeriodResult;
+
 /**
  * Test basic functionality of various API functions
  **/
@@ -49,6 +55,27 @@ public:
      * As of CLDR shiped in ICU4.8, Greek is one such language.
      */
     void testGreekWithSanitization();
+
+    void testFormatPeriodEn();
+
+    void testTimePeriodLength();
+
+    void testTimePeriodEquals();
+
+    void testTimePeriodForAmounts();
+
+    void testTimeUnitAmountSubClass();
+
+    void verifyEquals(const TimePeriod&, const TimePeriod&);
+
+    void verifyNotEqual(const TimePeriod&, const TimePeriod&);
+
+    void verifyFormatTimePeriod(
+        const TimeUnitFormat& tuf,
+        const TimePeriodResult* timePeriodResults,
+        int32_t numResults);
+
+
 };
 
 #endif /* #if !UCONFIG_NO_FORMATTING */