1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 ******************************************************************************
5 * Copyright (C) 2014-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ******************************************************************************
10 ******************************************************************************
13 #include "unicode/reldatefmt.h"
15 #if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_BREAK_ITERATION
17 #include "unicode/dtfmtsym.h"
18 #include "unicode/ureldatefmt.h"
19 #include "unicode/udisplaycontext.h"
20 #include "unicode/unum.h"
21 #include "unicode/localpointer.h"
22 #include "unicode/plurrule.h"
23 #include "unicode/simpleformatter.h"
24 #include "unicode/decimfmt.h"
25 #include "unicode/numfmt.h"
26 #include "unicode/brkiter.h"
27 #include "unicode/simpleformatter.h"
29 #include "unicode/ures.h"
35 #include "quantityformatter.h"
37 #include "sharedbreakiterator.h"
38 #include "sharedpluralrules.h"
39 #include "sharednumberformat.h"
40 #include "standardplural.h"
41 #include "unifiedcache.h"
43 // Copied from uscript_props.cpp
45 static UMutex gBrkIterMutex = U_MUTEX_INITIALIZER;
49 // RelativeDateTimeFormatter specific data for a single locale
50 class RelativeDateTimeCacheData: public SharedObject {
52 RelativeDateTimeCacheData() : combinedDateAndTime(NULL) {
53 // Initialize the cache arrays
54 for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
55 for (int32_t relUnit = 0; relUnit < UDAT_RELATIVE_UNIT_COUNT; ++relUnit) {
56 for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
57 relativeUnitsFormatters[style][relUnit][0][pl] = NULL;
58 relativeUnitsFormatters[style][relUnit][1][pl] = NULL;
62 for (int32_t i = 0; i < UDAT_STYLE_COUNT; ++i) {
63 fallBackCache[i] = -1;
66 virtual ~RelativeDateTimeCacheData();
68 // no numbers: e.g Next Tuesday; Yesterday; etc.
69 UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT];
71 // SimpleFormatter pointers for relative unit format,
72 // e.g., Next Tuesday; Yesterday; etc. For third index, 0
73 // means past, e.g., 5 days ago; 1 means future, e.g., in 5 days.
74 SimpleFormatter *relativeUnitsFormatters[UDAT_STYLE_COUNT]
75 [UDAT_RELATIVE_UNIT_COUNT][2][StandardPlural::COUNT];
77 const UnicodeString& getAbsoluteUnitString(int32_t fStyle,
78 UDateAbsoluteUnit unit,
79 UDateDirection direction) const;
80 const SimpleFormatter* getRelativeUnitFormatter(int32_t fStyle,
81 UDateRelativeUnit unit,
82 int32_t pastFutureIndex,
83 int32_t pluralUnit) const;
85 const UnicodeString emptyString;
87 // Mappping from source to target styles for alias fallback.
88 int32_t fallBackCache[UDAT_STYLE_COUNT];
90 void adoptCombinedDateAndTime(SimpleFormatter *fmtToAdopt) {
91 delete combinedDateAndTime;
92 combinedDateAndTime = fmtToAdopt;
94 const SimpleFormatter *getCombinedDateAndTime() const {
95 return combinedDateAndTime;
99 SimpleFormatter *combinedDateAndTime;
100 RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other);
101 RelativeDateTimeCacheData& operator=(
102 const RelativeDateTimeCacheData &other);
105 RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
106 // clear out the cache arrays
107 for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
108 for (int32_t relUnit = 0; relUnit < UDAT_RELATIVE_UNIT_COUNT; ++relUnit) {
109 for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
110 delete relativeUnitsFormatters[style][relUnit][0][pl];
111 delete relativeUnitsFormatters[style][relUnit][1][pl];
115 delete combinedDateAndTime;
119 // Use fallback cache for absolute units.
120 const UnicodeString& RelativeDateTimeCacheData::getAbsoluteUnitString(
121 int32_t fStyle, UDateAbsoluteUnit unit, UDateDirection direction) const {
122 int32_t style = fStyle;
124 if (!absoluteUnits[style][unit][direction].isEmpty()) {
125 return absoluteUnits[style][unit][direction];
127 style = fallBackCache[style];
128 } while (style != -1);
132 // Use fallback cache for SimpleFormatter relativeUnits.
133 const SimpleFormatter* RelativeDateTimeCacheData::getRelativeUnitFormatter(
135 UDateRelativeUnit unit,
136 int32_t pastFutureIndex,
137 int32_t pluralUnit) const {
138 int32_t style = fStyle;
140 if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != NULL) {
141 return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit];
143 style = fallBackCache[style];
144 } while (style != -1);
145 return NULL; // No formatter found.
148 static UBool getStringWithFallback(
149 const UResourceBundle *resource,
151 UnicodeString &result,
152 UErrorCode &status) {
154 const UChar *resStr = ures_getStringByKeyWithFallback(
155 resource, key, &len, &status);
156 if (U_FAILURE(status)) {
159 result.setTo(TRUE, resStr, len);
164 static UBool getStringByIndex(
165 const UResourceBundle *resource,
167 UnicodeString &result,
168 UErrorCode &status) {
170 const UChar *resStr = ures_getStringByIndex(
171 resource, idx, &len, &status);
172 if (U_FAILURE(status)) {
175 result.setTo(TRUE, resStr, len);
182 * Sink for enumerating all of the measurement unit display names.
184 * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
185 * Only store a value if it is still missing, that is, it has not been overridden.
187 struct RelDateTimeFmtDataSink : public ResourceSink {
190 * Sink for patterns for relative dates and times. For example,
191 * fields/relative/...
194 // Generic unit enum for storing Unit info.
195 typedef enum RelAbsUnit {
214 static int32_t relUnitFromGeneric(RelAbsUnit genUnit) {
215 // Converts the generic units to UDAT_RELATIVE version.
218 return UDAT_RELATIVE_SECONDS;
220 return UDAT_RELATIVE_MINUTES;
222 return UDAT_RELATIVE_HOURS;
224 return UDAT_RELATIVE_DAYS;
226 return UDAT_RELATIVE_WEEKS;
228 return UDAT_RELATIVE_MONTHS;
231 * return UDATE_RELATIVE_QUARTERS;
234 return UDAT_RELATIVE_YEARS;
240 static int32_t absUnitFromGeneric(RelAbsUnit genUnit) {
241 // Converts the generic units to UDAT_RELATIVE version.
244 return UDAT_ABSOLUTE_DAY;
246 return UDAT_ABSOLUTE_WEEK;
248 return UDAT_ABSOLUTE_MONTH;
249 /* TODO: Add in QUARTER
251 * return UDAT_ABSOLUTE_QUARTER;
254 return UDAT_ABSOLUTE_YEAR;
256 return UDAT_ABSOLUTE_SUNDAY;
258 return UDAT_ABSOLUTE_MONDAY;
260 return UDAT_ABSOLUTE_TUESDAY;
262 return UDAT_ABSOLUTE_WEDNESDAY;
264 return UDAT_ABSOLUTE_THURSDAY;
266 return UDAT_ABSOLUTE_FRIDAY;
268 return UDAT_ABSOLUTE_SATURDAY;
274 static int32_t keyToDirection(const char* key) {
275 if (uprv_strcmp(key, "-2") == 0) {
276 return UDAT_DIRECTION_LAST_2;
278 if (uprv_strcmp(key, "-1") == 0) {
279 return UDAT_DIRECTION_LAST;
281 if (uprv_strcmp(key, "0") == 0) {
282 return UDAT_DIRECTION_THIS;
284 if (uprv_strcmp(key, "1") == 0) {
285 return UDAT_DIRECTION_NEXT;
287 if (uprv_strcmp(key, "2") == 0) {
288 return UDAT_DIRECTION_NEXT_2;
293 // Values kept between levels of parsing the CLDR data.
294 int32_t pastFutureIndex; // 0 == past or 1 == future
295 UDateRelativeDateTimeFormatterStyle style; // {LONG, SHORT, NARROW}
296 RelAbsUnit genericUnit;
298 RelativeDateTimeCacheData &outputData;
301 RelDateTimeFmtDataSink(RelativeDateTimeCacheData& cacheData)
302 : outputData(cacheData) {
303 // Clear cacheData.fallBackCache
304 cacheData.fallBackCache[UDAT_STYLE_LONG] = -1;
305 cacheData.fallBackCache[UDAT_STYLE_SHORT] = -1;
306 cacheData.fallBackCache[UDAT_STYLE_NARROW] = -1;
309 ~RelDateTimeFmtDataSink();
312 static UDateRelativeDateTimeFormatterStyle styleFromString(const char *s) {
313 int32_t len = uprv_strlen(s);
314 if (len >= 7 && uprv_strcmp(s + len - 7, "-narrow") == 0) {
315 return UDAT_STYLE_NARROW;
317 if (len >= 6 && uprv_strcmp(s + len - 6, "-short") == 0) {
318 return UDAT_STYLE_SHORT;
320 return UDAT_STYLE_LONG;
323 static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style) {
325 case UDAT_STYLE_NARROW:
327 case UDAT_STYLE_SHORT:
335 static UDateRelativeDateTimeFormatterStyle styleFromAliasUnicodeString(UnicodeString s) {
336 static const UChar narrow[7] = {0x002D, 0x006E, 0x0061, 0x0072, 0x0072, 0x006F, 0x0077};
337 static const UChar sshort[6] = {0x002D, 0x0073, 0x0068, 0x006F, 0x0072, 0x0074,};
338 if (s.endsWith(narrow, 7)) {
339 return UDAT_STYLE_NARROW;
341 if (s.endsWith(sshort, 6)) {
342 return UDAT_STYLE_SHORT;
344 return UDAT_STYLE_LONG;
347 static RelAbsUnit unitOrNegativeFromString(const char* keyword, int32_t length) {
348 // Quick check from string to enum.
351 if (uprv_strncmp(keyword, "day", length) == 0) {
353 } else if (uprv_strncmp(keyword, "sun", length) == 0) {
355 } else if (uprv_strncmp(keyword, "mon", length) == 0) {
357 } else if (uprv_strncmp(keyword, "tue", length) == 0) {
359 } else if (uprv_strncmp(keyword, "wed", length) == 0) {
361 } else if (uprv_strncmp(keyword, "thu", length) == 0) {
363 } else if (uprv_strncmp(keyword, "fri", length) == 0) {
365 } else if (uprv_strncmp(keyword, "sat", length) == 0) {
370 if (uprv_strncmp(keyword, "hour", length) == 0) {
372 } else if (uprv_strncmp(keyword, "week", length) == 0) {
374 } else if (uprv_strncmp(keyword, "year", length) == 0) {
379 if (uprv_strncmp(keyword, "month", length) == 0) {
384 if (uprv_strncmp(keyword, "minute", length) == 0) {
386 } else if (uprv_strncmp(keyword, "second", length) == 0) {
391 if (uprv_strncmp(keyword, "quarter", length) == 0) {
392 return QUARTER; // TODO: Check @provisional
401 void handlePlainDirection(ResourceValue &value, UErrorCode &errorCode) {
402 // Handle Display Name for PLAIN direction for some units.
403 if (U_FAILURE(errorCode)) { return; }
405 int32_t absUnit = absUnitFromGeneric(genericUnit);
407 return; // Not interesting.
410 // Store displayname if not set.
411 if (outputData.absoluteUnits[style]
412 [absUnit][UDAT_DIRECTION_PLAIN].isEmpty()) {
413 outputData.absoluteUnits[style]
414 [absUnit][UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
419 void consumeTableRelative(const char *key, ResourceValue &value, UErrorCode &errorCode) {
420 ResourceTable unitTypesTable = value.getTable(errorCode);
421 if (U_FAILURE(errorCode)) { return; }
423 for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
424 if (value.getType() == URES_STRING) {
425 int32_t direction = keyToDirection(key);
430 int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
431 if (relUnitIndex == UDAT_RELATIVE_SECONDS && uprv_strcmp(key, "0") == 0 &&
432 outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN].isEmpty()) {
434 outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW]
435 [UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
438 int32_t absUnitIndex = absUnitFromGeneric(genericUnit);
439 if (absUnitIndex < 0) {
442 // Only reset if slot is empty.
443 if (outputData.absoluteUnits[style][absUnitIndex][direction].isEmpty()) {
444 outputData.absoluteUnits[style][absUnitIndex]
445 [direction].fastCopyFrom(value.getUnicodeString(errorCode));
451 void consumeTimeDetail(int32_t relUnitIndex,
452 const char *key, ResourceValue &value, UErrorCode &errorCode) {
453 ResourceTable unitTypesTable = value.getTable(errorCode);
454 if (U_FAILURE(errorCode)) { return; }
456 for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
457 if (value.getType() == URES_STRING) {
458 int32_t pluralIndex = StandardPlural::indexOrNegativeFromString(key);
459 if (pluralIndex >= 0) {
460 SimpleFormatter **patterns =
461 outputData.relativeUnitsFormatters[style][relUnitIndex]
463 // Only set if not already established.
464 if (patterns[pluralIndex] == NULL) {
465 patterns[pluralIndex] = new SimpleFormatter(
466 value.getUnicodeString(errorCode), 0, 1, errorCode);
467 if (patterns[pluralIndex] == NULL) {
468 errorCode = U_MEMORY_ALLOCATION_ERROR;
476 void consumeTableRelativeTime(const char *key, ResourceValue &value, UErrorCode &errorCode) {
477 ResourceTable relativeTimeTable = value.getTable(errorCode);
478 if (U_FAILURE(errorCode)) { return; }
480 int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
481 if (relUnitIndex < 0) {
484 for (int32_t i = 0; relativeTimeTable.getKeyAndValue(i, key, value); ++i) {
485 if (uprv_strcmp(key, "past") == 0) {
487 } else if (uprv_strcmp(key, "future") == 0) {
493 consumeTimeDetail(relUnitIndex, key, value, errorCode);
497 void consumeAlias(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
499 UDateRelativeDateTimeFormatterStyle sourceStyle = styleFromString(key);
500 const UnicodeString valueStr = value.getAliasUnicodeString(errorCode);
501 if (U_FAILURE(errorCode)) { return; }
503 UDateRelativeDateTimeFormatterStyle targetStyle =
504 styleFromAliasUnicodeString(valueStr);
506 if (sourceStyle == targetStyle) {
507 errorCode = U_INVALID_FORMAT_ERROR;
510 if (outputData.fallBackCache[sourceStyle] != -1 &&
511 outputData.fallBackCache[sourceStyle] != targetStyle) {
512 errorCode = U_INVALID_FORMAT_ERROR;
515 outputData.fallBackCache[sourceStyle] = targetStyle;
518 void consumeTimeUnit(const char *key, ResourceValue &value, UErrorCode &errorCode) {
519 ResourceTable unitTypesTable = value.getTable(errorCode);
520 if (U_FAILURE(errorCode)) { return; }
522 for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
523 // Handle display name.
524 if (uprv_strcmp(key, "dn") == 0 && value.getType() == URES_STRING) {
525 handlePlainDirection(value, errorCode);
527 if (value.getType() == URES_TABLE) {
528 if (uprv_strcmp(key, "relative") == 0) {
529 consumeTableRelative(key, value, errorCode);
530 } else if (uprv_strcmp(key, "relativeTime") == 0) {
531 consumeTableRelativeTime(key, value, errorCode);
537 virtual void put(const char *key, ResourceValue &value,
538 UBool /*noFallback*/, UErrorCode &errorCode) {
539 // Main entry point to sink
540 ResourceTable table = value.getTable(errorCode);
541 if (U_FAILURE(errorCode)) { return; }
542 for (int32_t i = 0; table.getKeyAndValue(i, key, value); ++i) {
543 if (value.getType() == URES_ALIAS) {
544 consumeAlias(key, value, errorCode);
546 style = styleFromString(key);
547 int32_t unitSize = uprv_strlen(key) - styleSuffixLength(style);
548 genericUnit = unitOrNegativeFromString(key, unitSize);
549 if (style >= 0 && genericUnit != INVALID_UNIT) {
550 consumeTimeUnit(key, value, errorCode);
558 // Virtual destructors must be defined out of line.
559 RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
562 DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth[UDAT_STYLE_COUNT] = {
563 DateFormatSymbols::WIDE, DateFormatSymbols::SHORT, DateFormatSymbols::NARROW
566 // Get days of weeks from the DateFormatSymbols class.
567 static void loadWeekdayNames(UnicodeString absoluteUnits[UDAT_STYLE_COUNT]
568 [UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT],
569 const char* localeId,
570 UErrorCode& status) {
571 Locale locale(localeId);
572 DateFormatSymbols dfSym(locale, status);
573 for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
574 DateFormatSymbols::DtWidthType dtfmtWidth = styleToDateFormatSymbolWidth[style];
576 const UnicodeString* weekdayNames =
577 dfSym.getWeekdays(count, DateFormatSymbols::STANDALONE, dtfmtWidth);
578 for (int32_t dayIndex = UDAT_ABSOLUTE_SUNDAY;
579 dayIndex <= UDAT_ABSOLUTE_SATURDAY; ++ dayIndex) {
580 int32_t dateSymbolIndex = (dayIndex - UDAT_ABSOLUTE_SUNDAY) + UCAL_SUNDAY;
581 absoluteUnits[style][dayIndex][UDAT_DIRECTION_PLAIN].fastCopyFrom(
582 weekdayNames[dateSymbolIndex]);
587 static UBool loadUnitData(
588 const UResourceBundle *resource,
589 RelativeDateTimeCacheData &cacheData,
590 const char* localeId,
591 UErrorCode &status) {
593 RelDateTimeFmtDataSink sink(cacheData);
595 ures_getAllItemsWithFallback(resource, "fields", sink, status);
597 // Get the weekday names from DateFormatSymbols.
598 loadWeekdayNames(cacheData.absoluteUnits, localeId, status);
599 return U_SUCCESS(status);
602 static UBool getDateTimePattern(
603 const UResourceBundle *resource,
604 UnicodeString &result,
605 UErrorCode &status) {
606 UnicodeString defaultCalendarName;
607 if (!getStringWithFallback(
614 CharString pathBuffer;
615 pathBuffer.append("calendar/", status)
616 .appendInvariantChars(defaultCalendarName, status)
617 .append("/DateTimePatterns", status);
618 LocalUResourceBundlePointer topLevel(
619 ures_getByKeyWithFallback(
620 resource, pathBuffer.data(), NULL, &status));
621 if (U_FAILURE(status)) {
624 int32_t size = ures_getSize(topLevel.getAlias());
626 // Oops, size is too small to access the index that we want, fallback
627 // to a hard-coded value.
628 result = UNICODE_STRING_SIMPLE("{1} {0}");
631 return getStringByIndex(topLevel.getAlias(), 8, result, status);
634 template<> U_I18N_API
635 const RelativeDateTimeCacheData *LocaleCacheKey<RelativeDateTimeCacheData>::createObject(const void * /*unused*/, UErrorCode &status) const {
636 const char *localeId = fLoc.getName();
637 LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status));
638 if (U_FAILURE(status)) {
641 LocalPointer<RelativeDateTimeCacheData> result(
642 new RelativeDateTimeCacheData());
643 if (result.isNull()) {
644 status = U_MEMORY_ALLOCATION_ERROR;
654 UnicodeString dateTimePattern;
655 if (!getDateTimePattern(topLevel.getAlias(), dateTimePattern, status)) {
658 result->adoptCombinedDateAndTime(
659 new SimpleFormatter(dateTimePattern, 2, 2, status));
660 if (U_FAILURE(status)) {
664 return result.orphan();
667 RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) :
671 fStyle(UDAT_STYLE_LONG),
672 fContext(UDISPCTX_CAPITALIZATION_NONE),
673 fOptBreakIterator(NULL) {
674 init(NULL, NULL, status);
677 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
678 const Locale& locale, UErrorCode& status) :
682 fStyle(UDAT_STYLE_LONG),
683 fContext(UDISPCTX_CAPITALIZATION_NONE),
684 fOptBreakIterator(NULL),
686 init(NULL, NULL, status);
689 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
690 const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) :
694 fStyle(UDAT_STYLE_LONG),
695 fContext(UDISPCTX_CAPITALIZATION_NONE),
696 fOptBreakIterator(NULL),
698 init(nfToAdopt, NULL, status);
701 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
702 const Locale& locale,
703 NumberFormat *nfToAdopt,
704 UDateRelativeDateTimeFormatterStyle styl,
705 UDisplayContext capitalizationContext,
706 UErrorCode& status) :
711 fContext(capitalizationContext),
712 fOptBreakIterator(NULL),
714 if (U_FAILURE(status)) {
717 if ((capitalizationContext >> 8) != UDISPCTX_TYPE_CAPITALIZATION) {
718 status = U_ILLEGAL_ARGUMENT_ERROR;
721 if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) {
722 BreakIterator *bi = BreakIterator::createSentenceInstance(locale, status);
723 if (U_FAILURE(status)) {
726 init(nfToAdopt, bi, status);
728 init(nfToAdopt, NULL, status);
732 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
733 const RelativeDateTimeFormatter& other)
735 fCache(other.fCache),
736 fNumberFormat(other.fNumberFormat),
737 fPluralRules(other.fPluralRules),
738 fStyle(other.fStyle),
739 fContext(other.fContext),
740 fOptBreakIterator(other.fOptBreakIterator),
741 fLocale(other.fLocale) {
743 fNumberFormat->addRef();
744 fPluralRules->addRef();
745 if (fOptBreakIterator != NULL) {
746 fOptBreakIterator->addRef();
750 RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=(
751 const RelativeDateTimeFormatter& other) {
752 if (this != &other) {
753 SharedObject::copyPtr(other.fCache, fCache);
754 SharedObject::copyPtr(other.fNumberFormat, fNumberFormat);
755 SharedObject::copyPtr(other.fPluralRules, fPluralRules);
756 SharedObject::copyPtr(other.fOptBreakIterator, fOptBreakIterator);
757 fStyle = other.fStyle;
758 fContext = other.fContext;
759 fLocale = other.fLocale;
764 RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
765 if (fCache != NULL) {
768 if (fNumberFormat != NULL) {
769 fNumberFormat->removeRef();
771 if (fPluralRules != NULL) {
772 fPluralRules->removeRef();
774 if (fOptBreakIterator != NULL) {
775 fOptBreakIterator->removeRef();
779 const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const {
780 return **fNumberFormat;
783 UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const {
787 UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const {
791 UnicodeString& RelativeDateTimeFormatter::format(
792 double quantity, UDateDirection direction, UDateRelativeUnit unit,
793 UnicodeString& appendTo, UErrorCode& status) const {
794 if (U_FAILURE(status)) {
797 if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
798 status = U_ILLEGAL_ARGUMENT_ERROR;
801 int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
802 FieldPosition pos(FieldPosition::DONT_CARE);
804 UnicodeString result;
805 UnicodeString formattedNumber;
807 StandardPlural::Form pluralIndex = QuantityFormatter::selectPlural(
808 quantity, **fNumberFormat, **fPluralRules, formattedNumber, pos,
811 const SimpleFormatter* formatter =
812 fCache->getRelativeUnitFormatter(fStyle, unit, bFuture, pluralIndex);
813 if (formatter == NULL) {
814 // TODO: WARN - look at quantity formatter's action with an error.
815 status = U_INVALID_FORMAT_ERROR;
818 formatter->format(formattedNumber, result, status);
819 adjustForContext(result);
820 return appendTo.append(result);
823 UnicodeString& RelativeDateTimeFormatter::formatNumeric(
824 double offset, URelativeDateTimeUnit unit,
825 UnicodeString& appendTo, UErrorCode& status) const {
826 if (U_FAILURE(status)) {
830 // The full implementation of this depends on CLDR data that is not yet available,
831 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
832 // In the meantime do a quick bring-up by calling the old format method; this
833 // leaves some holes (even for data that is currently available, such as quarter).
834 // When the new CLDR data is available, update the data storage accordingly,
835 // rewrite this to use it directly, and rewrite the old format method to call this
836 // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
837 UDateRelativeUnit relunit = UDAT_RELATIVE_UNIT_COUNT;
839 case UDAT_REL_UNIT_YEAR: relunit = UDAT_RELATIVE_YEARS; break;
840 case UDAT_REL_UNIT_MONTH: relunit = UDAT_RELATIVE_MONTHS; break;
841 case UDAT_REL_UNIT_WEEK: relunit = UDAT_RELATIVE_WEEKS; break;
842 case UDAT_REL_UNIT_DAY: relunit = UDAT_RELATIVE_DAYS; break;
843 case UDAT_REL_UNIT_HOUR: relunit = UDAT_RELATIVE_HOURS; break;
844 case UDAT_REL_UNIT_MINUTE: relunit = UDAT_RELATIVE_MINUTES; break;
845 case UDAT_REL_UNIT_SECOND: relunit = UDAT_RELATIVE_SECONDS; break;
846 default: // a unit that the above method does not handle
847 status = U_UNSUPPORTED_ERROR;
850 UDateDirection direction = UDAT_DIRECTION_NEXT;
852 direction = UDAT_DIRECTION_LAST;
855 return format(offset, direction, relunit, appendTo, status);
858 UnicodeString& RelativeDateTimeFormatter::format(
859 UDateDirection direction, UDateAbsoluteUnit unit,
860 UnicodeString& appendTo, UErrorCode& status) const {
861 if (U_FAILURE(status)) {
864 if (unit == UDAT_ABSOLUTE_NOW && direction != UDAT_DIRECTION_PLAIN) {
865 status = U_ILLEGAL_ARGUMENT_ERROR;
869 // Get string using fallback.
870 UnicodeString result;
871 result.fastCopyFrom(fCache->getAbsoluteUnitString(fStyle, unit, direction));
872 if (fOptBreakIterator != NULL) {
873 adjustForContext(result);
875 return appendTo.append(result);
878 UnicodeString& RelativeDateTimeFormatter::format(
879 double offset, URelativeDateTimeUnit unit,
880 UnicodeString& appendTo, UErrorCode& status) const {
881 if (U_FAILURE(status)) {
885 // The full implementation of this depends on CLDR data that is not yet available,
886 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
887 // In the meantime do a quick bring-up by calling the old format method; this
888 // leaves some holes (even for data that is currently available, such as quarter).
889 // When the new CLDR data is available, update the data storage accordingly,
890 // rewrite this to use it directly, and rewrite the old format method to call this
891 // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
892 UDateDirection direction = UDAT_DIRECTION_COUNT;
893 if (offset > -2.1 && offset < 2.1) {
894 // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST
895 double offsetx100 = offset * 100.0;
896 int32_t intoffset = (offsetx100 < 0)? (int32_t)(offsetx100-0.5) : (int32_t)(offsetx100+0.5);
898 case -200/*-2*/: direction = UDAT_DIRECTION_LAST_2; break;
899 case -100/*-1*/: direction = UDAT_DIRECTION_LAST; break;
900 case 0/* 0*/: direction = UDAT_DIRECTION_THIS; break;
901 case 100/* 1*/: direction = UDAT_DIRECTION_NEXT; break;
902 case 200/* 2*/: direction = UDAT_DIRECTION_NEXT_2; break;
906 UDateAbsoluteUnit absunit = UDAT_ABSOLUTE_UNIT_COUNT;
908 case UDAT_REL_UNIT_YEAR: absunit = UDAT_ABSOLUTE_YEAR; break;
909 case UDAT_REL_UNIT_MONTH: absunit = UDAT_ABSOLUTE_MONTH; break;
910 case UDAT_REL_UNIT_WEEK: absunit = UDAT_ABSOLUTE_WEEK; break;
911 case UDAT_REL_UNIT_DAY: absunit = UDAT_ABSOLUTE_DAY; break;
912 case UDAT_REL_UNIT_SECOND:
913 if (direction == UDAT_DIRECTION_THIS) {
914 absunit = UDAT_ABSOLUTE_NOW;
915 direction = UDAT_DIRECTION_PLAIN;
918 case UDAT_REL_UNIT_SUNDAY: absunit = UDAT_ABSOLUTE_SUNDAY; break;
919 case UDAT_REL_UNIT_MONDAY: absunit = UDAT_ABSOLUTE_MONDAY; break;
920 case UDAT_REL_UNIT_TUESDAY: absunit = UDAT_ABSOLUTE_TUESDAY; break;
921 case UDAT_REL_UNIT_WEDNESDAY: absunit = UDAT_ABSOLUTE_WEDNESDAY; break;
922 case UDAT_REL_UNIT_THURSDAY: absunit = UDAT_ABSOLUTE_THURSDAY; break;
923 case UDAT_REL_UNIT_FRIDAY: absunit = UDAT_ABSOLUTE_FRIDAY; break;
924 case UDAT_REL_UNIT_SATURDAY: absunit = UDAT_ABSOLUTE_SATURDAY; break;
927 if (direction != UDAT_DIRECTION_COUNT && absunit != UDAT_ABSOLUTE_UNIT_COUNT) {
928 const UnicodeString &unitFormatString =
929 fCache->getAbsoluteUnitString(fStyle, absunit, direction);
930 if (!unitFormatString.isEmpty()) {
931 if (fOptBreakIterator != NULL) {
932 UnicodeString result(unitFormatString);
933 adjustForContext(result);
934 return appendTo.append(result);
936 return appendTo.append(unitFormatString);
940 // otherwise fallback to formatNumeric
941 return formatNumeric(offset, unit, appendTo, status);
944 UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
945 const UnicodeString& relativeDateString, const UnicodeString& timeString,
946 UnicodeString& appendTo, UErrorCode& status) const {
947 return fCache->getCombinedDateAndTime()->format(
948 timeString, relativeDateString, appendTo, status);
951 void RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const {
952 if (fOptBreakIterator == NULL
953 || str.length() == 0 || !u_islower(str.char32At(0))) {
957 // Must guarantee that one thread at a time accesses the shared break
959 Mutex lock(&gBrkIterMutex);
961 fOptBreakIterator->get(),
963 U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
966 void RelativeDateTimeFormatter::init(
967 NumberFormat *nfToAdopt,
968 BreakIterator *biToAdopt,
969 UErrorCode &status) {
970 LocalPointer<NumberFormat> nf(nfToAdopt);
971 LocalPointer<BreakIterator> bi(biToAdopt);
972 UnifiedCache::getByLocale(fLocale, fCache, status);
973 if (U_FAILURE(status)) {
976 const SharedPluralRules *pr = PluralRules::createSharedInstance(
977 fLocale, UPLURAL_TYPE_CARDINAL, status);
978 if (U_FAILURE(status)) {
981 SharedObject::copyPtr(pr, fPluralRules);
984 const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
985 fLocale, UNUM_DECIMAL, status);
986 if (U_FAILURE(status)) {
989 SharedObject::copyPtr(shared, fNumberFormat);
992 SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
993 if (shared == NULL) {
994 status = U_MEMORY_ALLOCATION_ERROR;
998 SharedObject::copyPtr(shared, fNumberFormat);
1001 SharedObject::clearPtr(fOptBreakIterator);
1003 SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias());
1004 if (shared == NULL) {
1005 status = U_MEMORY_ALLOCATION_ERROR;
1009 SharedObject::copyPtr(shared, fOptBreakIterator);
1019 U_CAPI URelativeDateTimeFormatter* U_EXPORT2
1020 ureldatefmt_open( const char* locale,
1021 UNumberFormat* nfToAdopt,
1022 UDateRelativeDateTimeFormatterStyle width,
1023 UDisplayContext capitalizationContext,
1024 UErrorCode* status )
1026 if (U_FAILURE(*status)) {
1029 LocalPointer<RelativeDateTimeFormatter> formatter(new RelativeDateTimeFormatter(Locale(locale),
1030 (NumberFormat*)nfToAdopt, width,
1031 capitalizationContext, *status), *status);
1032 if (U_FAILURE(*status)) {
1035 return (URelativeDateTimeFormatter*)formatter.orphan();
1038 U_CAPI void U_EXPORT2
1039 ureldatefmt_close(URelativeDateTimeFormatter *reldatefmt)
1041 delete (RelativeDateTimeFormatter*)reldatefmt;
1044 U_CAPI int32_t U_EXPORT2
1045 ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt,
1047 URelativeDateTimeUnit unit,
1049 int32_t resultCapacity,
1052 if (U_FAILURE(*status)) {
1055 if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) {
1056 *status = U_ILLEGAL_ARGUMENT_ERROR;
1060 if (result != NULL) {
1061 // NULL destination for pure preflighting: empty dummy string
1062 // otherwise, alias the destination buffer (copied from udat_format)
1063 res.setTo(result, 0, resultCapacity);
1065 ((RelativeDateTimeFormatter*)reldatefmt)->formatNumeric(offset, unit, res, *status);
1066 if (U_FAILURE(*status)) {
1069 return res.extract(result, resultCapacity, *status);
1072 U_CAPI int32_t U_EXPORT2
1073 ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt,
1075 URelativeDateTimeUnit unit,
1077 int32_t resultCapacity,
1080 if (U_FAILURE(*status)) {
1083 if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) {
1084 *status = U_ILLEGAL_ARGUMENT_ERROR;
1088 if (result != NULL) {
1089 // NULL destination for pure preflighting: empty dummy string
1090 // otherwise, alias the destination buffer (copied from udat_format)
1091 res.setTo(result, 0, resultCapacity);
1093 ((RelativeDateTimeFormatter*)reldatefmt)->format(offset, unit, res, *status);
1094 if (U_FAILURE(*status)) {
1097 return res.extract(result, resultCapacity, *status);
1100 U_CAPI int32_t U_EXPORT2
1101 ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter* reldatefmt,
1102 const UChar * relativeDateString,
1103 int32_t relativeDateStringLen,
1104 const UChar * timeString,
1105 int32_t timeStringLen,
1107 int32_t resultCapacity,
1108 UErrorCode* status )
1110 if (U_FAILURE(*status)) {
1113 if (result == NULL ? resultCapacity != 0 : resultCapacity < 0 ||
1114 (relativeDateString == NULL ? relativeDateStringLen != 0 : relativeDateStringLen < -1) ||
1115 (timeString == NULL ? timeStringLen != 0 : timeStringLen < -1)) {
1116 *status = U_ILLEGAL_ARGUMENT_ERROR;
1119 UnicodeString relDateStr((UBool)(relativeDateStringLen == -1), relativeDateString, relativeDateStringLen);
1120 UnicodeString timeStr((UBool)(timeStringLen == -1), timeString, timeStringLen);
1121 UnicodeString res(result, 0, resultCapacity);
1122 ((RelativeDateTimeFormatter*)reldatefmt)->combineDateAndTime(relDateStr, timeStr, res, *status);
1123 if (U_FAILURE(*status)) {
1126 return res.extract(result, resultCapacity, *status);
1129 #endif /* !UCONFIG_NO_FORMATTING */