From: Travis Keep Date: Thu, 25 Jul 2013 16:36:25 +0000 (+0000) Subject: ICU-10274 Add compound duration formatting for C++ X-Git-Tag: milestone-59-0-1~2735 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ecd7ea193b6c8740c06ebaa59a771afee993029a;p=icu ICU-10274 Add compound duration formatting for C++ X-SVN-Rev: 33986 --- diff --git a/.gitattributes b/.gitattributes index 3e83e8aa65b..0f1411de1b5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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 diff --git a/icu4c/source/common/listformatter.cpp b/icu4c/source/common/listformatter.cpp index 30971431f6f..f4350d25ccc 100644 --- a/icu4c/source/common/listformatter.cpp +++ b/icu4c/source/common/listformatter.cpp @@ -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; diff --git a/icu4c/source/common/unicode/listformatter.h b/icu4c/source/common/unicode/listformatter.h index 30d99e97717..0f90e5cbdd4 100644 --- a/icu4c/source/common/unicode/listformatter.h +++ b/icu4c/source/common/unicode/listformatter.h @@ -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 diff --git a/icu4c/source/i18n/Makefile.in b/icu4c/source/i18n/Makefile.in index b1b8b7e086c..953da41f3e7 100644 --- a/icu4c/source/i18n/Makefile.in +++ b/icu4c/source/i18n/Makefile.in @@ -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 diff --git a/icu4c/source/i18n/i18n.vcxproj b/icu4c/source/i18n/i18n.vcxproj index fc33d41529b..e6da521490f 100644 --- a/icu4c/source/i18n/i18n.vcxproj +++ b/icu4c/source/i18n/i18n.vcxproj @@ -330,6 +330,7 @@ + @@ -1179,6 +1180,20 @@ + + copy "%(FullPath)" ..\..\include\unicode + + ..\..\include\unicode\%(Filename)%(Extension);%(Outputs) + copy "%(FullPath)" ..\..\include\unicode + + ..\..\include\unicode\%(Filename)%(Extension);%(Outputs) + copy "%(FullPath)" ..\..\include\unicode + + ..\..\include\unicode\%(Filename)%(Extension);%(Outputs) + copy "%(FullPath)" ..\..\include\unicode + + ..\..\include\unicode\%(Filename)%(Extension);%(Outputs) + copy "%(FullPath)" ..\..\include\unicode diff --git a/icu4c/source/i18n/i18n.vcxproj.filters b/icu4c/source/i18n/i18n.vcxproj.filters index 3ae44ec9b50..1239511d722 100644 --- a/icu4c/source/i18n/i18n.vcxproj.filters +++ b/icu4c/source/i18n/i18n.vcxproj.filters @@ -249,6 +249,9 @@ formatting + + formatting + formatting @@ -942,6 +945,9 @@ formatting + + formatting + formatting diff --git a/icu4c/source/i18n/timeperiod.cpp b/icu4c/source/i18n/timeperiod.cpp new file mode 100644 index 00000000000..94702919dbe --- /dev/null +++ b/icu4c/source/i18n/timeperiod.cpp @@ -0,0 +1,119 @@ +/* + ******************************************************************************* + * Copyright (C) 2013-2013, Google, International Business Machines Corporation + * and others. All Rights Reserved. + ******************************************************************************* + */ + +#include +#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 diff --git a/icu4c/source/i18n/tmutfmt.cpp b/icu4c/source/i18n/tmutfmt.cpp index 5ce65314d7d..98fe89a74c6 100644 --- a/icu4c/source/i18n/tmutfmt.cpp +++ b/icu4c/source/i18n/tmutfmt.cpp @@ -17,7 +17,12 @@ #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(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 index 00000000000..6cfa8779191 --- /dev/null +++ b/icu4c/source/i18n/unicode/timeperiod.h @@ -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 diff --git a/icu4c/source/i18n/unicode/tmutfmt.h b/icu4c/source/i18n/unicode/tmutfmt.h index 8c3979f5cf6..81604f44295 100644 --- a/icu4c/source/i18n/unicode/tmutfmt.h +++ b/icu4c/source/i18n/unicode/tmutfmt.h @@ -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; + }; diff --git a/icu4c/source/test/intltest/listformattertest.cpp b/icu4c/source/test/intltest/listformattertest.cpp index 07c1c5609cf..a4b9e18df92 100644 --- a/icu4c/source/test/intltest/listformattertest.cpp +++ b/icu4c/source/test/intltest/listformattertest.cpp @@ -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]); diff --git a/icu4c/source/test/intltest/tufmtts.cpp b/icu4c/source/test/intltest/tufmtts.cpp index abf0a1134cb..c92a9504d6e 100644 --- a/icu4c/source/test/intltest/tufmtts.cpp +++ b/icu4c/source/test/intltest/tufmtts.cpp @@ -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 @@ -20,6 +21,45 @@ #include #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 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 diff --git a/icu4c/source/test/intltest/tufmtts.h b/icu4c/source/test/intltest/tufmtts.h index 45e841cb806..475ca895572 100644 --- a/icu4c/source/test/intltest/tufmtts.h +++ b/icu4c/source/test/intltest/tufmtts.h @@ -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. ********************************************************************/ @@ -14,6 +14,12 @@ #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 */