From 0fa1b5c5ebb4f1dd7db07cefe4212c772ccc2660 Mon Sep 17 00:00:00 2001 From: Frank Tang <41213225+FrankYFTang@users.noreply.github.com> Date: Thu, 6 Sep 2018 16:47:50 -0700 Subject: [PATCH] ICU-20022 support quarter in RelativeDateTimeFormatter (#77) also add UDAT_ABSOLUTE_QUARTER to UDateAbsoluteUnit --- icu4c/source/i18n/reldatefmt.cpp | 234 +++++----- icu4c/source/i18n/unicode/reldatefmt.h | 10 +- icu4c/source/test/intltest/reldatefmttest.cpp | 404 ++++++++++++++++++ 3 files changed, 549 insertions(+), 99 deletions(-) diff --git a/icu4c/source/i18n/reldatefmt.cpp b/icu4c/source/i18n/reldatefmt.cpp index 0a28a743e28..0af9b47a191 100644 --- a/icu4c/source/i18n/reldatefmt.cpp +++ b/icu4c/source/i18n/reldatefmt.cpp @@ -51,13 +51,13 @@ U_NAMESPACE_BEGIN // RelativeDateTimeFormatter specific data for a single locale class RelativeDateTimeCacheData: public SharedObject { public: - RelativeDateTimeCacheData() : combinedDateAndTime(NULL) { + RelativeDateTimeCacheData() : combinedDateAndTime(nullptr) { // Initialize the cache arrays for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) { - for (int32_t relUnit = 0; relUnit < UDAT_RELATIVE_UNIT_COUNT; ++relUnit) { + for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) { for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) { - relativeUnitsFormatters[style][relUnit][0][pl] = NULL; - relativeUnitsFormatters[style][relUnit][1][pl] = NULL; + relativeUnitsFormatters[style][relUnit][0][pl] = nullptr; + relativeUnitsFormatters[style][relUnit][1][pl] = nullptr; } } } @@ -74,7 +74,7 @@ public: // e.g., Next Tuesday; Yesterday; etc. For third index, 0 // means past, e.g., 5 days ago; 1 means future, e.g., in 5 days. SimpleFormatter *relativeUnitsFormatters[UDAT_STYLE_COUNT] - [UDAT_RELATIVE_UNIT_COUNT][2][StandardPlural::COUNT]; + [UDAT_REL_UNIT_COUNT][2][StandardPlural::COUNT]; const UnicodeString& getAbsoluteUnitString(int32_t fStyle, UDateAbsoluteUnit unit, @@ -83,6 +83,10 @@ public: UDateRelativeUnit unit, int32_t pastFutureIndex, int32_t pluralUnit) const; + const SimpleFormatter* getRelativeDateTimeUnitFormatter(int32_t fStyle, + URelativeDateTimeUnit unit, + int32_t pastFutureIndex, + int32_t pluralUnit) const; const UnicodeString emptyString; @@ -107,7 +111,7 @@ private: RelativeDateTimeCacheData::~RelativeDateTimeCacheData() { // clear out the cache arrays for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) { - for (int32_t relUnit = 0; relUnit < UDAT_RELATIVE_UNIT_COUNT; ++relUnit) { + for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) { for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) { delete relativeUnitsFormatters[style][relUnit][0][pl]; delete relativeUnitsFormatters[style][relUnit][1][pl]; @@ -131,20 +135,41 @@ const UnicodeString& RelativeDateTimeCacheData::getAbsoluteUnitString( return emptyString; } - // Use fallback cache for SimpleFormatter relativeUnits. const SimpleFormatter* RelativeDateTimeCacheData::getRelativeUnitFormatter( int32_t fStyle, UDateRelativeUnit unit, int32_t pastFutureIndex, int32_t pluralUnit) const { + URelativeDateTimeUnit rdtunit = UDAT_REL_UNIT_COUNT; + switch (unit) { + case UDAT_RELATIVE_YEARS: rdtunit = UDAT_REL_UNIT_YEAR; break; + case UDAT_RELATIVE_MONTHS: rdtunit = UDAT_REL_UNIT_MONTH; break; + case UDAT_RELATIVE_WEEKS: rdtunit = UDAT_REL_UNIT_WEEK; break; + case UDAT_RELATIVE_DAYS: rdtunit = UDAT_REL_UNIT_DAY; break; + case UDAT_RELATIVE_HOURS: rdtunit = UDAT_REL_UNIT_HOUR; break; + case UDAT_RELATIVE_MINUTES: rdtunit = UDAT_REL_UNIT_MINUTE; break; + case UDAT_RELATIVE_SECONDS: rdtunit = UDAT_REL_UNIT_SECOND; break; + default: // a unit that the above method does not handle + return nullptr; + } + + return getRelativeDateTimeUnitFormatter(fStyle, rdtunit, pastFutureIndex, pluralUnit); + } + + // Use fallback cache for SimpleFormatter relativeUnits. + const SimpleFormatter* RelativeDateTimeCacheData::getRelativeDateTimeUnitFormatter( + int32_t fStyle, + URelativeDateTimeUnit unit, + int32_t pastFutureIndex, + int32_t pluralUnit) const { int32_t style = fStyle; do { - if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != NULL) { + if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != nullptr) { return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit]; } style = fallBackCache[style]; } while (style != -1); - return NULL; // No formatter found. + return nullptr; // No formatter found. } static UBool getStringWithFallback( @@ -217,23 +242,35 @@ struct RelDateTimeFmtDataSink : public ResourceSink { // Converts the generic units to UDAT_RELATIVE version. switch (genUnit) { case SECOND: - return UDAT_RELATIVE_SECONDS; + return UDAT_REL_UNIT_SECOND; case MINUTE: - return UDAT_RELATIVE_MINUTES; + return UDAT_REL_UNIT_MINUTE; case HOUR: - return UDAT_RELATIVE_HOURS; + return UDAT_REL_UNIT_HOUR; case DAY: - return UDAT_RELATIVE_DAYS; + return UDAT_REL_UNIT_DAY; case WEEK: - return UDAT_RELATIVE_WEEKS; + return UDAT_REL_UNIT_WEEK; case MONTH: - return UDAT_RELATIVE_MONTHS; - /* - * case QUARTER: - * return UDATE_RELATIVE_QUARTERS; - */ + return UDAT_REL_UNIT_MONTH; + case QUARTER: + return UDAT_REL_UNIT_QUARTER; case YEAR: - return UDAT_RELATIVE_YEARS; + return UDAT_REL_UNIT_YEAR; + case SUNDAY: + return UDAT_REL_UNIT_SUNDAY; + case MONDAY: + return UDAT_REL_UNIT_MONDAY; + case TUESDAY: + return UDAT_REL_UNIT_TUESDAY; + case WEDNESDAY: + return UDAT_REL_UNIT_WEDNESDAY; + case THURSDAY: + return UDAT_REL_UNIT_THURSDAY; + case FRIDAY: + return UDAT_REL_UNIT_FRIDAY; + case SATURDAY: + return UDAT_REL_UNIT_SATURDAY; default: return -1; } @@ -248,10 +285,8 @@ struct RelDateTimeFmtDataSink : public ResourceSink { return UDAT_ABSOLUTE_WEEK; case MONTH: return UDAT_ABSOLUTE_MONTH; - /* TODO: Add in QUARTER - * case QUARTER: - * return UDAT_ABSOLUTE_QUARTER; - */ + case QUARTER: + return UDAT_ABSOLUTE_QUARTER; case YEAR: return UDAT_ABSOLUTE_YEAR; case SUNDAY: @@ -430,7 +465,7 @@ struct RelDateTimeFmtDataSink : public ResourceSink { } int32_t relUnitIndex = relUnitFromGeneric(genericUnit); - if (relUnitIndex == UDAT_RELATIVE_SECONDS && uprv_strcmp(key, "0") == 0 && + if (relUnitIndex == UDAT_REL_UNIT_SECOND && uprv_strcmp(key, "0") == 0 && outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN].isEmpty()) { // Handle "NOW" outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW] @@ -463,10 +498,10 @@ struct RelDateTimeFmtDataSink : public ResourceSink { outputData.relativeUnitsFormatters[style][relUnitIndex] [pastFutureIndex]; // Only set if not already established. - if (patterns[pluralIndex] == NULL) { + if (patterns[pluralIndex] == nullptr) { patterns[pluralIndex] = new SimpleFormatter( value.getUnicodeString(errorCode), 0, 1, errorCode); - if (patterns[pluralIndex] == NULL) { + if (patterns[pluralIndex] == nullptr) { errorCode = U_MEMORY_ALLOCATION_ERROR; } } @@ -619,7 +654,7 @@ static UBool getDateTimePattern( .append("/DateTimePatterns", status); LocalUResourceBundlePointer topLevel( ures_getByKeyWithFallback( - resource, pathBuffer.data(), NULL, &status)); + resource, pathBuffer.data(), nullptr, &status)); if (U_FAILURE(status)) { return FALSE; } @@ -636,68 +671,68 @@ static UBool getDateTimePattern( template<> U_I18N_API const RelativeDateTimeCacheData *LocaleCacheKey::createObject(const void * /*unused*/, UErrorCode &status) const { const char *localeId = fLoc.getName(); - LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status)); + LocalUResourceBundlePointer topLevel(ures_open(nullptr, localeId, &status)); if (U_FAILURE(status)) { - return NULL; + return nullptr; } LocalPointer result( new RelativeDateTimeCacheData()); if (result.isNull()) { status = U_MEMORY_ALLOCATION_ERROR; - return NULL; + return nullptr; } if (!loadUnitData( topLevel.getAlias(), *result, localeId, status)) { - return NULL; + return nullptr; } UnicodeString dateTimePattern; if (!getDateTimePattern(topLevel.getAlias(), dateTimePattern, status)) { - return NULL; + return nullptr; } result->adoptCombinedDateAndTime( new SimpleFormatter(dateTimePattern, 2, 2, status)); if (U_FAILURE(status)) { - return NULL; + return nullptr; } result->addRef(); return result.orphan(); } RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) : - fCache(NULL), - fNumberFormat(NULL), - fPluralRules(NULL), + fCache(nullptr), + fNumberFormat(nullptr), + fPluralRules(nullptr), fStyle(UDAT_STYLE_LONG), fContext(UDISPCTX_CAPITALIZATION_NONE), - fOptBreakIterator(NULL) { - init(NULL, NULL, status); + fOptBreakIterator(nullptr) { + init(nullptr, nullptr, status); } RelativeDateTimeFormatter::RelativeDateTimeFormatter( const Locale& locale, UErrorCode& status) : - fCache(NULL), - fNumberFormat(NULL), - fPluralRules(NULL), + fCache(nullptr), + fNumberFormat(nullptr), + fPluralRules(nullptr), fStyle(UDAT_STYLE_LONG), fContext(UDISPCTX_CAPITALIZATION_NONE), - fOptBreakIterator(NULL), + fOptBreakIterator(nullptr), fLocale(locale) { - init(NULL, NULL, status); + init(nullptr, nullptr, status); } RelativeDateTimeFormatter::RelativeDateTimeFormatter( const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) : - fCache(NULL), - fNumberFormat(NULL), - fPluralRules(NULL), + fCache(nullptr), + fNumberFormat(nullptr), + fPluralRules(nullptr), fStyle(UDAT_STYLE_LONG), fContext(UDISPCTX_CAPITALIZATION_NONE), - fOptBreakIterator(NULL), + fOptBreakIterator(nullptr), fLocale(locale) { - init(nfToAdopt, NULL, status); + init(nfToAdopt, nullptr, status); } RelativeDateTimeFormatter::RelativeDateTimeFormatter( @@ -706,12 +741,12 @@ RelativeDateTimeFormatter::RelativeDateTimeFormatter( UDateRelativeDateTimeFormatterStyle styl, UDisplayContext capitalizationContext, UErrorCode& status) : - fCache(NULL), - fNumberFormat(NULL), - fPluralRules(NULL), + fCache(nullptr), + fNumberFormat(nullptr), + fPluralRules(nullptr), fStyle(styl), fContext(capitalizationContext), - fOptBreakIterator(NULL), + fOptBreakIterator(nullptr), fLocale(locale) { if (U_FAILURE(status)) { return; @@ -727,7 +762,7 @@ RelativeDateTimeFormatter::RelativeDateTimeFormatter( } init(nfToAdopt, bi, status); } else { - init(nfToAdopt, NULL, status); + init(nfToAdopt, nullptr, status); } } @@ -744,7 +779,7 @@ RelativeDateTimeFormatter::RelativeDateTimeFormatter( fCache->addRef(); fNumberFormat->addRef(); fPluralRules->addRef(); - if (fOptBreakIterator != NULL) { + if (fOptBreakIterator != nullptr) { fOptBreakIterator->addRef(); } } @@ -764,16 +799,16 @@ RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=( } RelativeDateTimeFormatter::~RelativeDateTimeFormatter() { - if (fCache != NULL) { + if (fCache != nullptr) { fCache->removeRef(); } - if (fNumberFormat != NULL) { + if (fNumberFormat != nullptr) { fNumberFormat->removeRef(); } - if (fPluralRules != NULL) { + if (fPluralRules != nullptr) { fPluralRules->removeRef(); } - if (fOptBreakIterator != NULL) { + if (fOptBreakIterator != nullptr) { fOptBreakIterator->removeRef(); } } @@ -812,7 +847,7 @@ UnicodeString& RelativeDateTimeFormatter::format( const SimpleFormatter* formatter = fCache->getRelativeUnitFormatter(fStyle, unit, bFuture, pluralIndex); - if (formatter == NULL) { + if (formatter == nullptr) { // TODO: WARN - look at quantity formatter's action with an error. status = U_INVALID_FORMAT_ERROR; return appendTo; @@ -828,33 +863,35 @@ UnicodeString& RelativeDateTimeFormatter::formatNumeric( if (U_FAILURE(status)) { return appendTo; } - // TODO: - // The full implementation of this depends on CLDR data that is not yet available, - // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data. - // In the meantime do a quick bring-up by calling the old format method; this - // leaves some holes (even for data that is currently available, such as quarter). - // When the new CLDR data is available, update the data storage accordingly, - // rewrite this to use it directly, and rewrite the old format method to call this - // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171. - UDateRelativeUnit relunit = UDAT_RELATIVE_UNIT_COUNT; - switch (unit) { - case UDAT_REL_UNIT_YEAR: relunit = UDAT_RELATIVE_YEARS; break; - case UDAT_REL_UNIT_MONTH: relunit = UDAT_RELATIVE_MONTHS; break; - case UDAT_REL_UNIT_WEEK: relunit = UDAT_RELATIVE_WEEKS; break; - case UDAT_REL_UNIT_DAY: relunit = UDAT_RELATIVE_DAYS; break; - case UDAT_REL_UNIT_HOUR: relunit = UDAT_RELATIVE_HOURS; break; - case UDAT_REL_UNIT_MINUTE: relunit = UDAT_RELATIVE_MINUTES; break; - case UDAT_REL_UNIT_SECOND: relunit = UDAT_RELATIVE_SECONDS; break; - default: // a unit that the above method does not handle - status = U_UNSUPPORTED_ERROR; - return appendTo; - } UDateDirection direction = UDAT_DIRECTION_NEXT; if (std::signbit(offset)) { // needed to handle -0.0 direction = UDAT_DIRECTION_LAST; offset = -offset; } - return format(offset, direction, relunit, appendTo, status); + if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return appendTo; + } + int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0; + FieldPosition pos(FieldPosition::DONT_CARE); + + UnicodeString result; + UnicodeString formattedNumber; + + StandardPlural::Form pluralIndex = QuantityFormatter::selectPlural( + offset, **fNumberFormat, **fPluralRules, formattedNumber, pos, + status); + + const SimpleFormatter* formatter = + fCache->getRelativeDateTimeUnitFormatter(fStyle, unit, bFuture, pluralIndex); + if (formatter == nullptr) { + // TODO: WARN - look at quantity formatter's action with an error. + status = U_INVALID_FORMAT_ERROR; + return appendTo; + } + formatter->format(formattedNumber, result, status); + adjustForContext(result); + return appendTo.append(result); } UnicodeString& RelativeDateTimeFormatter::format( @@ -871,7 +908,7 @@ UnicodeString& RelativeDateTimeFormatter::format( // Get string using fallback. UnicodeString result; result.fastCopyFrom(fCache->getAbsoluteUnitString(fStyle, unit, direction)); - if (fOptBreakIterator != NULL) { + if (fOptBreakIterator != nullptr) { adjustForContext(result); } return appendTo.append(result); @@ -908,6 +945,7 @@ UnicodeString& RelativeDateTimeFormatter::format( UDateAbsoluteUnit absunit = UDAT_ABSOLUTE_UNIT_COUNT; switch (unit) { case UDAT_REL_UNIT_YEAR: absunit = UDAT_ABSOLUTE_YEAR; break; + case UDAT_REL_UNIT_QUARTER: absunit = UDAT_ABSOLUTE_QUARTER; break; case UDAT_REL_UNIT_MONTH: absunit = UDAT_ABSOLUTE_MONTH; break; case UDAT_REL_UNIT_WEEK: absunit = UDAT_ABSOLUTE_WEEK; break; case UDAT_REL_UNIT_DAY: absunit = UDAT_ABSOLUTE_DAY; break; @@ -930,7 +968,7 @@ UnicodeString& RelativeDateTimeFormatter::format( const UnicodeString &unitFormatString = fCache->getAbsoluteUnitString(fStyle, absunit, direction); if (!unitFormatString.isEmpty()) { - if (fOptBreakIterator != NULL) { + if (fOptBreakIterator != nullptr) { UnicodeString result(unitFormatString); adjustForContext(result); return appendTo.append(result); @@ -951,7 +989,7 @@ UnicodeString& RelativeDateTimeFormatter::combineDateAndTime( } void RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const { - if (fOptBreakIterator == NULL + if (fOptBreakIterator == nullptr || str.length() == 0 || !u_islower(str.char32At(0))) { return; } @@ -992,7 +1030,7 @@ void RelativeDateTimeFormatter::init( shared->removeRef(); } else { SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias()); - if (shared == NULL) { + if (shared == nullptr) { status = U_MEMORY_ALLOCATION_ERROR; return; } @@ -1003,7 +1041,7 @@ void RelativeDateTimeFormatter::init( SharedObject::clearPtr(fOptBreakIterator); } else { SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias()); - if (shared == NULL) { + if (shared == nullptr) { status = U_MEMORY_ALLOCATION_ERROR; return; } @@ -1026,13 +1064,13 @@ ureldatefmt_open( const char* locale, UErrorCode* status ) { if (U_FAILURE(*status)) { - return NULL; + return nullptr; } LocalPointer formatter(new RelativeDateTimeFormatter(Locale(locale), (NumberFormat*)nfToAdopt, width, capitalizationContext, *status), *status); if (U_FAILURE(*status)) { - return NULL; + return nullptr; } return (URelativeDateTimeFormatter*)formatter.orphan(); } @@ -1054,13 +1092,13 @@ ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt, if (U_FAILURE(*status)) { return 0; } - if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) { + if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) { *status = U_ILLEGAL_ARGUMENT_ERROR; return 0; } UnicodeString res; - if (result != NULL) { - // NULL destination for pure preflighting: empty dummy string + if (result != nullptr) { + // nullptr destination for pure preflighting: empty dummy string // otherwise, alias the destination buffer (copied from udat_format) res.setTo(result, 0, resultCapacity); } @@ -1082,13 +1120,13 @@ ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt, if (U_FAILURE(*status)) { return 0; } - if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) { + if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) { *status = U_ILLEGAL_ARGUMENT_ERROR; return 0; } UnicodeString res; - if (result != NULL) { - // NULL destination for pure preflighting: empty dummy string + if (result != nullptr) { + // nullptr destination for pure preflighting: empty dummy string // otherwise, alias the destination buffer (copied from udat_format) res.setTo(result, 0, resultCapacity); } @@ -1112,9 +1150,9 @@ ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter* reldatefmt, if (U_FAILURE(*status)) { return 0; } - if (result == NULL ? resultCapacity != 0 : resultCapacity < 0 || - (relativeDateString == NULL ? relativeDateStringLen != 0 : relativeDateStringLen < -1) || - (timeString == NULL ? timeStringLen != 0 : timeStringLen < -1)) { + if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0 || + (relativeDateString == nullptr ? relativeDateStringLen != 0 : relativeDateStringLen < -1) || + (timeString == nullptr ? timeStringLen != 0 : timeStringLen < -1)) { *status = U_ILLEGAL_ARGUMENT_ERROR; return 0; } diff --git a/icu4c/source/i18n/unicode/reldatefmt.h b/icu4c/source/i18n/unicode/reldatefmt.h index 918d43e67a9..be06b1013d5 100644 --- a/icu4c/source/i18n/unicode/reldatefmt.h +++ b/icu4c/source/i18n/unicode/reldatefmt.h @@ -165,12 +165,20 @@ typedef enum UDateAbsoluteUnit { */ UDAT_ABSOLUTE_NOW, +#ifndef U_HIDE_DRAFT_API + /** + * Quarter + * @draft ICU 63 + */ + UDAT_ABSOLUTE_QUARTER, +#endif // U_HIDE_DRAFT_API + #ifndef U_HIDE_DEPRECATED_API /** * One more than the highest normal UDateAbsoluteUnit value. * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. */ - UDAT_ABSOLUTE_UNIT_COUNT + UDAT_ABSOLUTE_UNIT_COUNT = UDAT_ABSOLUTE_NOW + 2 #endif // U_HIDE_DEPRECATED_API } UDateAbsoluteUnit; diff --git a/icu4c/source/test/intltest/reldatefmttest.cpp b/icu4c/source/test/intltest/reldatefmttest.cpp index c0765ddc2e3..65969e48e91 100644 --- a/icu4c/source/test/intltest/reldatefmttest.cpp +++ b/icu4c/source/test/intltest/reldatefmttest.cpp @@ -24,6 +24,7 @@ static const char *DirectionStr(UDateDirection direction); static const char *RelativeUnitStr(UDateRelativeUnit unit); +static const char *RelativeDateTimeUnitStr(URelativeDateTimeUnit unit); static const char *AbsoluteUnitStr(UDateAbsoluteUnit unit); typedef struct WithQuantityExpected { @@ -244,6 +245,7 @@ static WithoutQuantityExpected kEnglishNoQuantity[] = { {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_DAY, "tomorrow"}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_WEEK, "next week"}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_MONTH, "next month"}, + {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_QUARTER, "next quarter"}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_YEAR, "next year"}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_MONDAY, "next Monday"}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_TUESDAY, "next Tuesday"}, @@ -258,6 +260,7 @@ static WithoutQuantityExpected kEnglishNoQuantity[] = { {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_DAY, "yesterday"}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_WEEK, "last week"}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_MONTH, "last month"}, + {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_QUARTER, "last quarter"}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_YEAR, "last year"}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_MONDAY, "last Monday"}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_TUESDAY, "last Tuesday"}, @@ -270,6 +273,7 @@ static WithoutQuantityExpected kEnglishNoQuantity[] = { {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_DAY, "today"}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_WEEK, "this week"}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_MONTH, "this month"}, + {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_QUARTER, "this quarter"}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_YEAR, "this year"}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_MONDAY, "this Monday"}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_TUESDAY, "this Tuesday"}, @@ -282,6 +286,7 @@ static WithoutQuantityExpected kEnglishNoQuantity[] = { {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_DAY, "day"}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_WEEK, "week"}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_MONTH, "month"}, + {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_QUARTER, "quarter"}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_YEAR, "year"}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_MONDAY, "Monday"}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_TUESDAY, "Tuesday"}, @@ -300,6 +305,7 @@ static WithoutQuantityExpected kEnglishNoQuantityCaps[] = { {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_DAY, "Tomorrow"}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_WEEK, "Next week"}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_MONTH, "Next month"}, + {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_QUARTER, "Next quarter"}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_YEAR, "Next year"}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_MONDAY, "Next Monday"}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_TUESDAY, "Next Tuesday"}, @@ -314,6 +320,7 @@ static WithoutQuantityExpected kEnglishNoQuantityCaps[] = { {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_DAY, "Yesterday"}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_WEEK, "Last week"}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_MONTH, "Last month"}, + {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_QUARTER, "Last quarter"}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_YEAR, "Last year"}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_MONDAY, "Last Monday"}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_TUESDAY, "Last Tuesday"}, @@ -326,6 +333,7 @@ static WithoutQuantityExpected kEnglishNoQuantityCaps[] = { {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_DAY, "Today"}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_WEEK, "This week"}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_MONTH, "This month"}, + {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_QUARTER, "This quarter"}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_YEAR, "This year"}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_MONDAY, "This Monday"}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_TUESDAY, "This Tuesday"}, @@ -338,6 +346,7 @@ static WithoutQuantityExpected kEnglishNoQuantityCaps[] = { {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_DAY, "Day"}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_WEEK, "Week"}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_MONTH, "Month"}, + {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_QUARTER, "Quarter"}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_YEAR, "Year"}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_MONDAY, "Monday"}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_TUESDAY, "Tuesday"}, @@ -356,6 +365,7 @@ static WithoutQuantityExpected kEnglishNoQuantityShort[] = { {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_DAY, "tomorrow"}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_WEEK, "next wk."}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_MONTH, "next mo."}, + {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_QUARTER, "next qtr."}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_YEAR, "next yr."}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_MONDAY, "next Mon."}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_TUESDAY, "next Tue."}, @@ -370,6 +380,7 @@ static WithoutQuantityExpected kEnglishNoQuantityShort[] = { {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_DAY, "yesterday"}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_WEEK, "last wk."}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_MONTH, "last mo."}, + {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_QUARTER, "last qtr."}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_YEAR, "last yr."}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_MONDAY, "last Mon."}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_TUESDAY, "last Tue."}, @@ -382,6 +393,7 @@ static WithoutQuantityExpected kEnglishNoQuantityShort[] = { {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_DAY, "today"}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_WEEK, "this wk."}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_MONTH, "this mo."}, + {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_QUARTER, "this qtr."}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_YEAR, "this yr."}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_MONDAY, "this Mon."}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_TUESDAY, "this Tue."}, @@ -394,6 +406,7 @@ static WithoutQuantityExpected kEnglishNoQuantityShort[] = { {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_DAY, "day"}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_WEEK, "wk."}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_MONTH, "mo."}, + {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_QUARTER, "qtr."}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_YEAR, "yr."}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_MONDAY, "Mo"}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_TUESDAY, "Tu"}, @@ -412,6 +425,7 @@ static WithoutQuantityExpected kEnglishNoQuantityNarrow[] = { {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_DAY, "tomorrow"}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_WEEK, "next wk."}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_MONTH, "next mo."}, + {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_QUARTER, "next qtr."}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_YEAR, "next yr."}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_MONDAY, "next M"}, {UDAT_DIRECTION_NEXT, UDAT_ABSOLUTE_TUESDAY, "next Tu"}, @@ -426,6 +440,7 @@ static WithoutQuantityExpected kEnglishNoQuantityNarrow[] = { {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_DAY, "yesterday"}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_WEEK, "last wk."}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_MONTH, "last mo."}, + {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_QUARTER, "last qtr."}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_YEAR, "last yr."}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_MONDAY, "last M"}, {UDAT_DIRECTION_LAST, UDAT_ABSOLUTE_TUESDAY, "last Tu"}, @@ -438,6 +453,7 @@ static WithoutQuantityExpected kEnglishNoQuantityNarrow[] = { {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_DAY, "today"}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_WEEK, "this wk."}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_MONTH, "this mo."}, + {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_QUARTER, "this qtr."}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_YEAR, "this yr."}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_MONDAY, "this M"}, {UDAT_DIRECTION_THIS, UDAT_ABSOLUTE_TUESDAY, "this Tu"}, @@ -450,6 +466,7 @@ static WithoutQuantityExpected kEnglishNoQuantityNarrow[] = { {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_DAY, "day"}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_WEEK, "wk."}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_MONTH, "mo."}, + {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_QUARTER, "qtr."}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_YEAR, "yr."}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_MONDAY, "M"}, {UDAT_DIRECTION_PLAIN, UDAT_ABSOLUTE_TUESDAY, "T"}, @@ -467,6 +484,261 @@ static WithoutQuantityExpected kSpanishNoQuantity[] = { {UDAT_DIRECTION_LAST_2, UDAT_ABSOLUTE_DAY, "anteayer"} }; +typedef struct WithQuantityExpectedRelativeDateTimeUnit { + double value; + URelativeDateTimeUnit unit; + const char *expected; +} WithQuantityExpectedRelativeDateTimeUnit; + +static WithQuantityExpectedRelativeDateTimeUnit kEnglishFormatNumeric[] = { + {0.0, UDAT_REL_UNIT_SECOND, "in 0 seconds"}, + {0.5, UDAT_REL_UNIT_SECOND, "in 0.5 seconds"}, + {1.0, UDAT_REL_UNIT_SECOND, "in 1 second"}, + {2.0, UDAT_REL_UNIT_SECOND, "in 2 seconds"}, + {0.0, UDAT_REL_UNIT_MINUTE, "in 0 minutes"}, + {0.5, UDAT_REL_UNIT_MINUTE, "in 0.5 minutes"}, + {1.0, UDAT_REL_UNIT_MINUTE, "in 1 minute"}, + {2.0, UDAT_REL_UNIT_MINUTE, "in 2 minutes"}, + {0.0, UDAT_REL_UNIT_HOUR, "in 0 hours"}, + {0.5, UDAT_REL_UNIT_HOUR, "in 0.5 hours"}, + {1.0, UDAT_REL_UNIT_HOUR, "in 1 hour"}, + {2.0, UDAT_REL_UNIT_HOUR, "in 2 hours"}, + {0.0, UDAT_REL_UNIT_DAY, "in 0 days"}, + {0.5, UDAT_REL_UNIT_DAY, "in 0.5 days"}, + {1.0, UDAT_REL_UNIT_DAY, "in 1 day"}, + {2.0, UDAT_REL_UNIT_DAY, "in 2 days"}, + {0.0, UDAT_REL_UNIT_WEEK, "in 0 weeks"}, + {0.5, UDAT_REL_UNIT_WEEK, "in 0.5 weeks"}, + {1.0, UDAT_REL_UNIT_WEEK, "in 1 week"}, + {2.0, UDAT_REL_UNIT_WEEK, "in 2 weeks"}, + {0.0, UDAT_REL_UNIT_MONTH, "in 0 months"}, + {0.5, UDAT_REL_UNIT_MONTH, "in 0.5 months"}, + {1.0, UDAT_REL_UNIT_MONTH, "in 1 month"}, + {2.0, UDAT_REL_UNIT_MONTH, "in 2 months"}, + {0.0, UDAT_REL_UNIT_QUARTER, "in 0 quarters"}, + {0.5, UDAT_REL_UNIT_QUARTER, "in 0.5 quarters"}, + {1.0, UDAT_REL_UNIT_QUARTER, "in 1 quarter"}, + {2.0, UDAT_REL_UNIT_QUARTER, "in 2 quarters"}, + {0.0, UDAT_REL_UNIT_YEAR, "in 0 years"}, + {0.5, UDAT_REL_UNIT_YEAR, "in 0.5 years"}, + {1.0, UDAT_REL_UNIT_YEAR, "in 1 year"}, + {2.0, UDAT_REL_UNIT_YEAR, "in 2 years"}, + {0.0, UDAT_REL_UNIT_SUNDAY, "in 0 Sundays"}, + {0.5, UDAT_REL_UNIT_SUNDAY, "in 0.5 Sundays"}, + {1.0, UDAT_REL_UNIT_SUNDAY, "in 1 Sunday"}, + {2.0, UDAT_REL_UNIT_SUNDAY, "in 2 Sundays"}, + {0.0, UDAT_REL_UNIT_MONDAY, "in 0 Mondays"}, + {0.5, UDAT_REL_UNIT_MONDAY, "in 0.5 Mondays"}, + {1.0, UDAT_REL_UNIT_MONDAY, "in 1 Monday"}, + {2.0, UDAT_REL_UNIT_MONDAY, "in 2 Mondays"}, + {0.0, UDAT_REL_UNIT_TUESDAY, "in 0 Tuesdays"}, + {0.5, UDAT_REL_UNIT_TUESDAY, "in 0.5 Tuesdays"}, + {1.0, UDAT_REL_UNIT_TUESDAY, "in 1 Tuesday"}, + {2.0, UDAT_REL_UNIT_TUESDAY, "in 2 Tuesdays"}, + {0.0, UDAT_REL_UNIT_WEDNESDAY, "in 0 Wednesdays"}, + {0.5, UDAT_REL_UNIT_WEDNESDAY, "in 0.5 Wednesdays"}, + {1.0, UDAT_REL_UNIT_WEDNESDAY, "in 1 Wednesday"}, + {2.0, UDAT_REL_UNIT_WEDNESDAY, "in 2 Wednesdays"}, + {0.0, UDAT_REL_UNIT_THURSDAY, "in 0 Thursdays"}, + {0.5, UDAT_REL_UNIT_THURSDAY, "in 0.5 Thursdays"}, + {1.0, UDAT_REL_UNIT_THURSDAY, "in 1 Thursday"}, + {2.0, UDAT_REL_UNIT_THURSDAY, "in 2 Thursdays"}, + {0.0, UDAT_REL_UNIT_FRIDAY, "in 0 Fridays"}, + {0.5, UDAT_REL_UNIT_FRIDAY, "in 0.5 Fridays"}, + {1.0, UDAT_REL_UNIT_FRIDAY, "in 1 Friday"}, + {2.0, UDAT_REL_UNIT_FRIDAY, "in 2 Fridays"}, + {0.0, UDAT_REL_UNIT_SATURDAY, "in 0 Saturdays"}, + {0.5, UDAT_REL_UNIT_SATURDAY, "in 0.5 Saturdays"}, + {1.0, UDAT_REL_UNIT_SATURDAY, "in 1 Saturday"}, + {2.0, UDAT_REL_UNIT_SATURDAY, "in 2 Saturdays"}, + + {-0.0, UDAT_REL_UNIT_SECOND, "0 seconds ago"}, + {-0.5, UDAT_REL_UNIT_SECOND, "0.5 seconds ago"}, + {-1.0, UDAT_REL_UNIT_SECOND, "1 second ago"}, + {-2.0, UDAT_REL_UNIT_SECOND, "2 seconds ago"}, + {-0.0, UDAT_REL_UNIT_MINUTE, "0 minutes ago"}, + {-0.5, UDAT_REL_UNIT_MINUTE, "0.5 minutes ago"}, + {-1.0, UDAT_REL_UNIT_MINUTE, "1 minute ago"}, + {-2.0, UDAT_REL_UNIT_MINUTE, "2 minutes ago"}, + {-0.0, UDAT_REL_UNIT_HOUR, "0 hours ago"}, + {-0.5, UDAT_REL_UNIT_HOUR, "0.5 hours ago"}, + {-1.0, UDAT_REL_UNIT_HOUR, "1 hour ago"}, + {-2.0, UDAT_REL_UNIT_HOUR, "2 hours ago"}, + {-0.0, UDAT_REL_UNIT_DAY, "0 days ago"}, + {-0.5, UDAT_REL_UNIT_DAY, "0.5 days ago"}, + {-1.0, UDAT_REL_UNIT_DAY, "1 day ago"}, + {-2.0, UDAT_REL_UNIT_DAY, "2 days ago"}, + {-0.0, UDAT_REL_UNIT_WEEK, "0 weeks ago"}, + {-0.5, UDAT_REL_UNIT_WEEK, "0.5 weeks ago"}, + {-1.0, UDAT_REL_UNIT_WEEK, "1 week ago"}, + {-2.0, UDAT_REL_UNIT_WEEK, "2 weeks ago"}, + {-0.0, UDAT_REL_UNIT_MONTH, "0 months ago"}, + {-0.5, UDAT_REL_UNIT_MONTH, "0.5 months ago"}, + {-1.0, UDAT_REL_UNIT_MONTH, "1 month ago"}, + {-2.0, UDAT_REL_UNIT_MONTH, "2 months ago"}, + {-0.0, UDAT_REL_UNIT_QUARTER, "0 quarters ago"}, + {-0.5, UDAT_REL_UNIT_QUARTER, "0.5 quarters ago"}, + {-1.0, UDAT_REL_UNIT_QUARTER, "1 quarter ago"}, + {-2.0, UDAT_REL_UNIT_QUARTER, "2 quarters ago"}, + {-0.0, UDAT_REL_UNIT_YEAR, "0 years ago"}, + {-0.5, UDAT_REL_UNIT_YEAR, "0.5 years ago"}, + {-1.0, UDAT_REL_UNIT_YEAR, "1 year ago"}, + {-2.0, UDAT_REL_UNIT_YEAR, "2 years ago"}, + {-0.0, UDAT_REL_UNIT_SUNDAY, "0 Sundays ago"}, + {-0.5, UDAT_REL_UNIT_SUNDAY, "0.5 Sundays ago"}, + {-1.0, UDAT_REL_UNIT_SUNDAY, "1 Sunday ago"}, + {-2.0, UDAT_REL_UNIT_SUNDAY, "2 Sundays ago"}, + {-0.0, UDAT_REL_UNIT_MONDAY, "0 Mondays ago"}, + {-0.5, UDAT_REL_UNIT_MONDAY, "0.5 Mondays ago"}, + {-1.0, UDAT_REL_UNIT_MONDAY, "1 Monday ago"}, + {-2.0, UDAT_REL_UNIT_MONDAY, "2 Mondays ago"}, + {-0.0, UDAT_REL_UNIT_TUESDAY, "0 Tuesdays ago"}, + {-0.5, UDAT_REL_UNIT_TUESDAY, "0.5 Tuesdays ago"}, + {-1.0, UDAT_REL_UNIT_TUESDAY, "1 Tuesday ago"}, + {-2.0, UDAT_REL_UNIT_TUESDAY, "2 Tuesdays ago"}, + {-0.0, UDAT_REL_UNIT_WEDNESDAY, "0 Wednesdays ago"}, + {-0.5, UDAT_REL_UNIT_WEDNESDAY, "0.5 Wednesdays ago"}, + {-1.0, UDAT_REL_UNIT_WEDNESDAY, "1 Wednesday ago"}, + {-2.0, UDAT_REL_UNIT_WEDNESDAY, "2 Wednesdays ago"}, + {-0.0, UDAT_REL_UNIT_THURSDAY, "0 Thursdays ago"}, + {-0.5, UDAT_REL_UNIT_THURSDAY, "0.5 Thursdays ago"}, + {-1.0, UDAT_REL_UNIT_THURSDAY, "1 Thursday ago"}, + {-2.0, UDAT_REL_UNIT_THURSDAY, "2 Thursdays ago"}, + {-0.0, UDAT_REL_UNIT_FRIDAY, "0 Fridays ago"}, + {-0.5, UDAT_REL_UNIT_FRIDAY, "0.5 Fridays ago"}, + {-1.0, UDAT_REL_UNIT_FRIDAY, "1 Friday ago"}, + {-2.0, UDAT_REL_UNIT_FRIDAY, "2 Fridays ago"}, + {-0.0, UDAT_REL_UNIT_SATURDAY, "0 Saturdays ago"}, + {-0.5, UDAT_REL_UNIT_SATURDAY, "0.5 Saturdays ago"}, + {-1.0, UDAT_REL_UNIT_SATURDAY, "1 Saturday ago"}, + {-2.0, UDAT_REL_UNIT_SATURDAY, "2 Saturdays ago"} +}; + +static WithQuantityExpectedRelativeDateTimeUnit kEnglishFormat[] = { + {0.0, UDAT_REL_UNIT_SECOND, "now"}, + {0.5, UDAT_REL_UNIT_SECOND, "in 0.5 seconds"}, + {1.0, UDAT_REL_UNIT_SECOND, "in 1 second"}, + {2.0, UDAT_REL_UNIT_SECOND, "in 2 seconds"}, + {0.0, UDAT_REL_UNIT_MINUTE, "in 0 minutes"}, + {0.5, UDAT_REL_UNIT_MINUTE, "in 0.5 minutes"}, + {1.0, UDAT_REL_UNIT_MINUTE, "in 1 minute"}, + {2.0, UDAT_REL_UNIT_MINUTE, "in 2 minutes"}, + {0.0, UDAT_REL_UNIT_HOUR, "in 0 hours"}, + {0.5, UDAT_REL_UNIT_HOUR, "in 0.5 hours"}, + {1.0, UDAT_REL_UNIT_HOUR, "in 1 hour"}, + {2.0, UDAT_REL_UNIT_HOUR, "in 2 hours"}, + {0.0, UDAT_REL_UNIT_DAY, "today"}, + {0.5, UDAT_REL_UNIT_DAY, "in 0.5 days"}, + {1.0, UDAT_REL_UNIT_DAY, "tomorrow"}, + {2.0, UDAT_REL_UNIT_DAY, "in 2 days"}, + {0.0, UDAT_REL_UNIT_WEEK, "this week"}, + {0.5, UDAT_REL_UNIT_WEEK, "in 0.5 weeks"}, + {1.0, UDAT_REL_UNIT_WEEK, "next week"}, + {2.0, UDAT_REL_UNIT_WEEK, "in 2 weeks"}, + {0.0, UDAT_REL_UNIT_MONTH, "this month"}, + {0.5, UDAT_REL_UNIT_MONTH, "in 0.5 months"}, + {1.0, UDAT_REL_UNIT_MONTH, "next month"}, + {2.0, UDAT_REL_UNIT_MONTH, "in 2 months"}, + {0.0, UDAT_REL_UNIT_QUARTER, "this quarter"}, + {0.5, UDAT_REL_UNIT_QUARTER, "in 0.5 quarters"}, + {1.0, UDAT_REL_UNIT_QUARTER, "next quarter"}, + {2.0, UDAT_REL_UNIT_QUARTER, "in 2 quarters"}, + {0.0, UDAT_REL_UNIT_YEAR, "this year"}, + {0.5, UDAT_REL_UNIT_YEAR, "in 0.5 years"}, + {1.0, UDAT_REL_UNIT_YEAR, "next year"}, + {2.0, UDAT_REL_UNIT_YEAR, "in 2 years"}, + {0.0, UDAT_REL_UNIT_SUNDAY, "this Sunday"}, + {0.5, UDAT_REL_UNIT_SUNDAY, "in 0.5 Sundays"}, + {1.0, UDAT_REL_UNIT_SUNDAY, "next Sunday"}, + {2.0, UDAT_REL_UNIT_SUNDAY, "in 2 Sundays"}, + {0.0, UDAT_REL_UNIT_MONDAY, "this Monday"}, + {0.5, UDAT_REL_UNIT_MONDAY, "in 0.5 Mondays"}, + {1.0, UDAT_REL_UNIT_MONDAY, "next Monday"}, + {2.0, UDAT_REL_UNIT_MONDAY, "in 2 Mondays"}, + {0.0, UDAT_REL_UNIT_TUESDAY, "this Tuesday"}, + {0.5, UDAT_REL_UNIT_TUESDAY, "in 0.5 Tuesdays"}, + {1.0, UDAT_REL_UNIT_TUESDAY, "next Tuesday"}, + {2.0, UDAT_REL_UNIT_TUESDAY, "in 2 Tuesdays"}, + {0.0, UDAT_REL_UNIT_WEDNESDAY, "this Wednesday"}, + {0.5, UDAT_REL_UNIT_WEDNESDAY, "in 0.5 Wednesdays"}, + {1.0, UDAT_REL_UNIT_WEDNESDAY, "next Wednesday"}, + {2.0, UDAT_REL_UNIT_WEDNESDAY, "in 2 Wednesdays"}, + {0.0, UDAT_REL_UNIT_THURSDAY, "this Thursday"}, + {0.5, UDAT_REL_UNIT_THURSDAY, "in 0.5 Thursdays"}, + {1.0, UDAT_REL_UNIT_THURSDAY, "next Thursday"}, + {2.0, UDAT_REL_UNIT_THURSDAY, "in 2 Thursdays"}, + {0.0, UDAT_REL_UNIT_FRIDAY, "this Friday"}, + {0.5, UDAT_REL_UNIT_FRIDAY, "in 0.5 Fridays"}, + {1.0, UDAT_REL_UNIT_FRIDAY, "next Friday"}, + {2.0, UDAT_REL_UNIT_FRIDAY, "in 2 Fridays"}, + {0.0, UDAT_REL_UNIT_SATURDAY, "this Saturday"}, + {0.5, UDAT_REL_UNIT_SATURDAY, "in 0.5 Saturdays"}, + {1.0, UDAT_REL_UNIT_SATURDAY, "next Saturday"}, + {2.0, UDAT_REL_UNIT_SATURDAY, "in 2 Saturdays"}, + + {-0.0, UDAT_REL_UNIT_SECOND, "now"}, + {-0.5, UDAT_REL_UNIT_SECOND, "0.5 seconds ago"}, + {-1.0, UDAT_REL_UNIT_SECOND, "1 second ago"}, + {-2.0, UDAT_REL_UNIT_SECOND, "2 seconds ago"}, + {-0.0, UDAT_REL_UNIT_MINUTE, "0 minutes ago"}, + {-0.5, UDAT_REL_UNIT_MINUTE, "0.5 minutes ago"}, + {-1.0, UDAT_REL_UNIT_MINUTE, "1 minute ago"}, + {-2.0, UDAT_REL_UNIT_MINUTE, "2 minutes ago"}, + {-0.0, UDAT_REL_UNIT_HOUR, "0 hours ago"}, + {-0.5, UDAT_REL_UNIT_HOUR, "0.5 hours ago"}, + {-1.0, UDAT_REL_UNIT_HOUR, "1 hour ago"}, + {-2.0, UDAT_REL_UNIT_HOUR, "2 hours ago"}, + {-0.0, UDAT_REL_UNIT_DAY, "today"}, + {-0.5, UDAT_REL_UNIT_DAY, "0.5 days ago"}, + {-1.0, UDAT_REL_UNIT_DAY, "yesterday"}, + {-2.0, UDAT_REL_UNIT_DAY, "2 days ago"}, + {-0.0, UDAT_REL_UNIT_WEEK, "this week"}, + {-0.5, UDAT_REL_UNIT_WEEK, "0.5 weeks ago"}, + {-1.0, UDAT_REL_UNIT_WEEK, "last week"}, + {-2.0, UDAT_REL_UNIT_WEEK, "2 weeks ago"}, + {-0.0, UDAT_REL_UNIT_MONTH, "this month"}, + {-0.5, UDAT_REL_UNIT_MONTH, "0.5 months ago"}, + {-1.0, UDAT_REL_UNIT_MONTH, "last month"}, + {-2.0, UDAT_REL_UNIT_MONTH, "2 months ago"}, + {-0.0, UDAT_REL_UNIT_QUARTER, "this quarter"}, + {-0.5, UDAT_REL_UNIT_QUARTER, "0.5 quarters ago"}, + {-1.0, UDAT_REL_UNIT_QUARTER, "last quarter"}, + {-2.0, UDAT_REL_UNIT_QUARTER, "2 quarters ago"}, + {-0.0, UDAT_REL_UNIT_YEAR, "this year"}, + {-0.5, UDAT_REL_UNIT_YEAR, "0.5 years ago"}, + {-1.0, UDAT_REL_UNIT_YEAR, "last year"}, + {-2.0, UDAT_REL_UNIT_YEAR, "2 years ago"}, + {-0.0, UDAT_REL_UNIT_SUNDAY, "this Sunday"}, + {-0.5, UDAT_REL_UNIT_SUNDAY, "0.5 Sundays ago"}, + {-1.0, UDAT_REL_UNIT_SUNDAY, "last Sunday"}, + {-2.0, UDAT_REL_UNIT_SUNDAY, "2 Sundays ago"}, + {-0.0, UDAT_REL_UNIT_MONDAY, "this Monday"}, + {-0.5, UDAT_REL_UNIT_MONDAY, "0.5 Mondays ago"}, + {-1.0, UDAT_REL_UNIT_MONDAY, "last Monday"}, + {-2.0, UDAT_REL_UNIT_MONDAY, "2 Mondays ago"}, + {-0.0, UDAT_REL_UNIT_TUESDAY, "this Tuesday"}, + {-0.5, UDAT_REL_UNIT_TUESDAY, "0.5 Tuesdays ago"}, + {-1.0, UDAT_REL_UNIT_TUESDAY, "last Tuesday"}, + {-2.0, UDAT_REL_UNIT_TUESDAY, "2 Tuesdays ago"}, + {-0.0, UDAT_REL_UNIT_WEDNESDAY, "this Wednesday"}, + {-0.5, UDAT_REL_UNIT_WEDNESDAY, "0.5 Wednesdays ago"}, + {-1.0, UDAT_REL_UNIT_WEDNESDAY, "last Wednesday"}, + {-2.0, UDAT_REL_UNIT_WEDNESDAY, "2 Wednesdays ago"}, + {-0.0, UDAT_REL_UNIT_THURSDAY, "this Thursday"}, + {-0.5, UDAT_REL_UNIT_THURSDAY, "0.5 Thursdays ago"}, + {-1.0, UDAT_REL_UNIT_THURSDAY, "last Thursday"}, + {-2.0, UDAT_REL_UNIT_THURSDAY, "2 Thursdays ago"}, + {-0.0, UDAT_REL_UNIT_FRIDAY, "this Friday"}, + {-0.5, UDAT_REL_UNIT_FRIDAY, "0.5 Fridays ago"}, + {-1.0, UDAT_REL_UNIT_FRIDAY, "last Friday"}, + {-2.0, UDAT_REL_UNIT_FRIDAY, "2 Fridays ago"}, + {-0.0, UDAT_REL_UNIT_SATURDAY, "this Saturday"}, + {-0.5, UDAT_REL_UNIT_SATURDAY, "0.5 Saturdays ago"}, + {-1.0, UDAT_REL_UNIT_SATURDAY, "last Saturday"}, + {-2.0, UDAT_REL_UNIT_SATURDAY, "2 Saturdays ago"} +}; + + class RelativeDateTimeFormatterTest : public IntlTest { public: RelativeDateTimeFormatterTest() { @@ -491,10 +763,17 @@ private: void TestGetters(); void TestCombineDateAndTime(); void TestBadDisplayContext(); + void TestFormat(); + void TestFormatNumeric(); void RunTest( const Locale& locale, const WithQuantityExpected* expectedResults, int32_t expectedResultLength); + void RunTest( + const Locale& locale, + const WithQuantityExpectedRelativeDateTimeUnit* expectedResults, + int32_t expectedResultLength, + bool numeric); void RunTest( const Locale& locale, UDateRelativeDateTimeFormatterStyle style, @@ -514,6 +793,12 @@ private: const WithQuantityExpected* expectedResults, int32_t expectedResultLength, const char *description); + void RunTest( + const RelativeDateTimeFormatter& fmt, + const WithQuantityExpectedRelativeDateTimeUnit* expectedResults, + int32_t expectedResultLength, + const char *description, + bool numeric); void RunTest( const RelativeDateTimeFormatter& fmt, const WithoutQuantityExpected* expectedResults, @@ -523,6 +808,11 @@ private: const RelativeDateTimeFormatter& fmt, const WithQuantityExpected& expectedResult, const char* description); + void CheckExpectedResult( + const RelativeDateTimeFormatter& fmt, + const WithQuantityExpectedRelativeDateTimeUnit& expectedResults, + const char* description, + bool numeric); void CheckExpectedResult( const RelativeDateTimeFormatter& fmt, const WithoutQuantityExpected& expectedResult, @@ -562,6 +852,8 @@ void RelativeDateTimeFormatterTest::runIndexedTest( TESTCASE_AUTO(TestCombineDateAndTime); TESTCASE_AUTO(TestBadDisplayContext); TESTCASE_AUTO(TestSidewaysDataLoading); + TESTCASE_AUTO(TestFormat); + TESTCASE_AUTO(TestFormatNumeric); TESTCASE_AUTO_END; } @@ -765,6 +1057,21 @@ void RelativeDateTimeFormatterTest::RunTest( RunTest(fmt, expectedResults, expectedResultLength, locale.getName()); } +void RelativeDateTimeFormatterTest::RunTest( + const Locale& locale, + const WithQuantityExpectedRelativeDateTimeUnit* expectedResults, + int32_t expectedResultLength, + bool numeric) { + UErrorCode status = U_ZERO_ERROR; + RelativeDateTimeFormatter fmt(locale, status); + if (U_FAILURE(status)) { + dataerrln("Unable to create format object - %s", u_errorName(status)); + return; + } + RunTest(fmt, expectedResults, expectedResultLength, locale.getName(), numeric); +} + + void RelativeDateTimeFormatterTest::RunTest( const Locale& locale, UDateRelativeDateTimeFormatterStyle style, @@ -818,6 +1125,17 @@ void RelativeDateTimeFormatterTest::RunTest( } } +void RelativeDateTimeFormatterTest::RunTest( + const RelativeDateTimeFormatter& fmt, + const WithQuantityExpectedRelativeDateTimeUnit* expectedResults, + int32_t expectedResultLength, + const char *description, + bool numeric) { + for (int32_t i = 0; i < expectedResultLength; ++i) { + CheckExpectedResult(fmt, expectedResults[i], description, numeric); + } +} + void RelativeDateTimeFormatterTest::RunTest( const RelativeDateTimeFormatter& fmt, const WithoutQuantityExpected* expectedResults, @@ -852,6 +1170,34 @@ void RelativeDateTimeFormatterTest::CheckExpectedResult( } } +void RelativeDateTimeFormatterTest::CheckExpectedResult( + const RelativeDateTimeFormatter& fmt, + const WithQuantityExpectedRelativeDateTimeUnit& expectedResult, + const char* description, + bool numeric) { + UErrorCode status = U_ZERO_ERROR; + UnicodeString actual; + if (numeric) { + fmt.formatNumeric(expectedResult.value, expectedResult.unit, actual, status); + } else { + fmt.format(expectedResult.value, expectedResult.unit, actual, status); + } + UnicodeString expected(expectedResult.expected, -1, US_INV); + expected = expected.unescape(); + char buffer[256]; + sprintf( + buffer, + "%s, %f, %s", + description, + expectedResult.value, + RelativeDateTimeUnitStr(expectedResult.unit)); + if (actual != expected) { + errln(UnicodeString("Fail: Expected: ") + expected + + ", Got: " + actual + + ", For: " + buffer); + } +} + void RelativeDateTimeFormatterTest::CheckExpectedResult( const RelativeDateTimeFormatter& fmt, const WithoutQuantityExpected& expectedResult, @@ -942,6 +1288,14 @@ void RelativeDateTimeFormatterTest::TestSidewaysDataLoading(void) { assertEquals("next year: ", expected, actual); } +void RelativeDateTimeFormatterTest::TestFormatNumeric() { + RunTest("en", kEnglishFormatNumeric, UPRV_LENGTHOF(kEnglishFormatNumeric), true); +} + +void RelativeDateTimeFormatterTest::TestFormat() { + RunTest("en", kEnglishFormat, UPRV_LENGTHOF(kEnglishFormat), false); +} + static const char *kLast2 = "Last_2"; static const char *kLast = "Last"; static const char *kThis = "This"; @@ -955,7 +1309,15 @@ static const char *kHours = "Hours"; static const char *kDays = "Days"; static const char *kWeeks = "Weeks"; static const char *kMonths = "Months"; +static const char *kQuarters = "Quarters"; static const char *kYears = "Years"; +static const char *kSundays = "Sundays"; +static const char *kMondays = "Mondays"; +static const char *kTuesdays = "Tuesdays"; +static const char *kWednesdays = "Wednesdays"; +static const char *kThursdays = "Thursdays"; +static const char *kFridays = "Fridays"; +static const char *kSaturdays = "Saturdays"; static const char *kSunday = "Sunday"; static const char *kMonday = "Monday"; @@ -967,6 +1329,7 @@ static const char *kSaturday = "Saturday"; static const char *kDay = "Day"; static const char *kWeek = "Week"; static const char *kMonth = "Month"; +static const char *kQuarter = "Quarter"; static const char *kYear = "Year"; static const char *kNow = "Now"; @@ -1016,6 +1379,45 @@ static const char *RelativeUnitStr( return kUndefined; } +static const char *RelativeDateTimeUnitStr( + URelativeDateTimeUnit unit) { + switch (unit) { + case UDAT_REL_UNIT_SECOND: + return kSeconds; + case UDAT_REL_UNIT_MINUTE: + return kMinutes; + case UDAT_REL_UNIT_HOUR: + return kHours; + case UDAT_REL_UNIT_DAY: + return kDays; + case UDAT_REL_UNIT_WEEK: + return kWeeks; + case UDAT_REL_UNIT_MONTH: + return kMonths; + case UDAT_REL_UNIT_QUARTER: + return kQuarters; + case UDAT_REL_UNIT_YEAR: + return kYears; + case UDAT_REL_UNIT_SUNDAY: + return kSundays; + case UDAT_REL_UNIT_MONDAY: + return kMondays; + case UDAT_REL_UNIT_TUESDAY: + return kTuesdays; + case UDAT_REL_UNIT_WEDNESDAY: + return kWednesdays; + case UDAT_REL_UNIT_THURSDAY: + return kThursdays; + case UDAT_REL_UNIT_FRIDAY: + return kFridays; + case UDAT_REL_UNIT_SATURDAY: + return kSaturdays; + default: + return kUndefined; + } + return kUndefined; +} + static const char *AbsoluteUnitStr( UDateAbsoluteUnit unit) { switch (unit) { @@ -1039,6 +1441,8 @@ static const char *AbsoluteUnitStr( return kWeek; case UDAT_ABSOLUTE_MONTH: return kMonth; + case UDAT_ABSOLUTE_QUARTER: + return kQuarter; case UDAT_ABSOLUTE_YEAR: return kYear; case UDAT_ABSOLUTE_NOW: