1 // © 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/ucasemap.h"
19 #include "unicode/ureldatefmt.h"
20 #include "unicode/udisplaycontext.h"
21 #include "unicode/unum.h"
22 #include "unicode/localpointer.h"
23 #include "unicode/plurrule.h"
24 #include "unicode/simpleformatter.h"
25 #include "unicode/decimfmt.h"
26 #include "unicode/numfmt.h"
27 #include "unicode/brkiter.h"
28 #include "unicode/simpleformatter.h"
30 #include "unicode/ures.h"
36 #include "quantityformatter.h"
38 #include "sharedbreakiterator.h"
39 #include "sharedpluralrules.h"
40 #include "sharednumberformat.h"
41 #include "standardplural.h"
42 #include "unifiedcache.h"
44 // Copied from uscript_props.cpp
46 static UMutex gBrkIterMutex
= U_MUTEX_INITIALIZER
;
50 // RelativeDateTimeFormatter specific data for a single locale
51 class RelativeDateTimeCacheData
: public SharedObject
{
53 RelativeDateTimeCacheData() : combinedDateAndTime(NULL
) {
54 // Initialize the cache arrays
55 for (int32_t style
= 0; style
< UDAT_STYLE_COUNT
; ++style
) {
56 for (int32_t relUnit
= 0; relUnit
< UDAT_RELATIVE_UNIT_COUNT
; ++relUnit
) {
57 for (int32_t pl
= 0; pl
< StandardPlural::COUNT
; ++pl
) {
58 relativeUnitsFormatters
[style
][relUnit
][0][pl
] = NULL
;
59 relativeUnitsFormatters
[style
][relUnit
][1][pl
] = NULL
;
63 for (int32_t i
= 0; i
< UDAT_STYLE_COUNT
; ++i
) {
64 fallBackCache
[i
] = -1;
67 virtual ~RelativeDateTimeCacheData();
69 // no numbers: e.g Next Tuesday; Yesterday; etc.
70 UnicodeString absoluteUnits
[UDAT_STYLE_COUNT
][UDAT_ABSOLUTE_UNIT_COUNT
][UDAT_DIRECTION_COUNT
];
72 // SimpleFormatter pointers for relative unit format,
73 // e.g., Next Tuesday; Yesterday; etc. For third index, 0
74 // means past, e.g., 5 days ago; 1 means future, e.g., in 5 days.
75 SimpleFormatter
*relativeUnitsFormatters
[UDAT_STYLE_COUNT
]
76 [UDAT_RELATIVE_UNIT_COUNT
][2][StandardPlural::COUNT
];
78 const UnicodeString
& getAbsoluteUnitString(int32_t fStyle
,
79 UDateAbsoluteUnit unit
,
80 UDateDirection direction
) const;
81 const SimpleFormatter
* getRelativeUnitFormatter(int32_t fStyle
,
82 UDateRelativeUnit unit
,
83 int32_t pastFutureIndex
,
84 int32_t pluralUnit
) const;
86 const UnicodeString emptyString
;
88 // Mappping from source to target styles for alias fallback.
89 int32_t fallBackCache
[UDAT_STYLE_COUNT
];
91 void adoptCombinedDateAndTime(SimpleFormatter
*fmtToAdopt
) {
92 delete combinedDateAndTime
;
93 combinedDateAndTime
= fmtToAdopt
;
95 const SimpleFormatter
*getCombinedDateAndTime() const {
96 return combinedDateAndTime
;
100 SimpleFormatter
*combinedDateAndTime
;
101 RelativeDateTimeCacheData(const RelativeDateTimeCacheData
&other
);
102 RelativeDateTimeCacheData
& operator=(
103 const RelativeDateTimeCacheData
&other
);
106 RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
107 // clear out the cache arrays
108 for (int32_t style
= 0; style
< UDAT_STYLE_COUNT
; ++style
) {
109 for (int32_t relUnit
= 0; relUnit
< UDAT_RELATIVE_UNIT_COUNT
; ++relUnit
) {
110 for (int32_t pl
= 0; pl
< StandardPlural::COUNT
; ++pl
) {
111 delete relativeUnitsFormatters
[style
][relUnit
][0][pl
];
112 delete relativeUnitsFormatters
[style
][relUnit
][1][pl
];
116 delete combinedDateAndTime
;
120 // Use fallback cache for absolute units.
121 const UnicodeString
& RelativeDateTimeCacheData::getAbsoluteUnitString(
122 int32_t fStyle
, UDateAbsoluteUnit unit
, UDateDirection direction
) const {
123 int32_t style
= fStyle
;
125 if (!absoluteUnits
[style
][unit
][direction
].isEmpty()) {
126 return absoluteUnits
[style
][unit
][direction
];
128 style
= fallBackCache
[style
];
129 } while (style
!= -1);
133 // Use fallback cache for SimpleFormatter relativeUnits.
134 const SimpleFormatter
* RelativeDateTimeCacheData::getRelativeUnitFormatter(
136 UDateRelativeUnit unit
,
137 int32_t pastFutureIndex
,
138 int32_t pluralUnit
) const {
139 int32_t style
= fStyle
;
141 if (relativeUnitsFormatters
[style
][unit
][pastFutureIndex
][pluralUnit
] != NULL
) {
142 return relativeUnitsFormatters
[style
][unit
][pastFutureIndex
][pluralUnit
];
144 style
= fallBackCache
[style
];
145 } while (style
!= -1);
146 return NULL
; // No formatter found.
149 static UBool
getStringWithFallback(
150 const UResourceBundle
*resource
,
152 UnicodeString
&result
,
153 UErrorCode
&status
) {
155 const UChar
*resStr
= ures_getStringByKeyWithFallback(
156 resource
, key
, &len
, &status
);
157 if (U_FAILURE(status
)) {
160 result
.setTo(TRUE
, resStr
, len
);
165 static UBool
getStringByIndex(
166 const UResourceBundle
*resource
,
168 UnicodeString
&result
,
169 UErrorCode
&status
) {
171 const UChar
*resStr
= ures_getStringByIndex(
172 resource
, idx
, &len
, &status
);
173 if (U_FAILURE(status
)) {
176 result
.setTo(TRUE
, resStr
, len
);
183 * Sink for enumerating all of the measurement unit display names.
185 * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
186 * Only store a value if it is still missing, that is, it has not been overridden.
188 struct RelDateTimeFmtDataSink
: public ResourceSink
{
191 * Sink for patterns for relative dates and times. For example,
192 * fields/relative/...
195 // Generic unit enum for storing Unit info.
196 typedef enum RelAbsUnit
{
215 static int32_t relUnitFromGeneric(RelAbsUnit genUnit
) {
216 // Converts the generic units to UDAT_RELATIVE version.
219 return UDAT_RELATIVE_SECONDS
;
221 return UDAT_RELATIVE_MINUTES
;
223 return UDAT_RELATIVE_HOURS
;
225 return UDAT_RELATIVE_DAYS
;
227 return UDAT_RELATIVE_WEEKS
;
229 return UDAT_RELATIVE_MONTHS
;
232 * return UDATE_RELATIVE_QUARTERS;
235 return UDAT_RELATIVE_YEARS
;
241 static int32_t absUnitFromGeneric(RelAbsUnit genUnit
) {
242 // Converts the generic units to UDAT_RELATIVE version.
245 return UDAT_ABSOLUTE_DAY
;
247 return UDAT_ABSOLUTE_WEEK
;
249 return UDAT_ABSOLUTE_MONTH
;
250 /* TODO: Add in QUARTER
252 * return UDAT_ABSOLUTE_QUARTER;
255 return UDAT_ABSOLUTE_YEAR
;
257 return UDAT_ABSOLUTE_SUNDAY
;
259 return UDAT_ABSOLUTE_MONDAY
;
261 return UDAT_ABSOLUTE_TUESDAY
;
263 return UDAT_ABSOLUTE_WEDNESDAY
;
265 return UDAT_ABSOLUTE_THURSDAY
;
267 return UDAT_ABSOLUTE_FRIDAY
;
269 return UDAT_ABSOLUTE_SATURDAY
;
275 static int32_t keyToDirection(const char* key
) {
276 if (uprv_strcmp(key
, "-2") == 0) {
277 return UDAT_DIRECTION_LAST_2
;
279 if (uprv_strcmp(key
, "-1") == 0) {
280 return UDAT_DIRECTION_LAST
;
282 if (uprv_strcmp(key
, "0") == 0) {
283 return UDAT_DIRECTION_THIS
;
285 if (uprv_strcmp(key
, "1") == 0) {
286 return UDAT_DIRECTION_NEXT
;
288 if (uprv_strcmp(key
, "2") == 0) {
289 return UDAT_DIRECTION_NEXT_2
;
294 // Values kept between levels of parsing the CLDR data.
295 int32_t pastFutureIndex
; // 0 == past or 1 == future
296 UDateRelativeDateTimeFormatterStyle style
; // {LONG, SHORT, NARROW}
297 RelAbsUnit genericUnit
;
299 RelativeDateTimeCacheData
&outputData
;
302 RelDateTimeFmtDataSink(RelativeDateTimeCacheData
& cacheData
)
303 : outputData(cacheData
) {
304 // Clear cacheData.fallBackCache
305 cacheData
.fallBackCache
[UDAT_STYLE_LONG
] = -1;
306 cacheData
.fallBackCache
[UDAT_STYLE_SHORT
] = -1;
307 cacheData
.fallBackCache
[UDAT_STYLE_NARROW
] = -1;
310 ~RelDateTimeFmtDataSink();
313 static UDateRelativeDateTimeFormatterStyle
styleFromString(const char *s
) {
314 int32_t len
= uprv_strlen(s
);
315 if (len
>= 7 && uprv_strcmp(s
+ len
- 7, "-narrow") == 0) {
316 return UDAT_STYLE_NARROW
;
318 if (len
>= 6 && uprv_strcmp(s
+ len
- 6, "-short") == 0) {
319 return UDAT_STYLE_SHORT
;
321 return UDAT_STYLE_LONG
;
324 static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style
) {
326 case UDAT_STYLE_NARROW
:
328 case UDAT_STYLE_SHORT
:
336 static UDateRelativeDateTimeFormatterStyle
styleFromAliasUnicodeString(UnicodeString s
) {
337 static const UChar narrow
[7] = {0x002D, 0x006E, 0x0061, 0x0072, 0x0072, 0x006F, 0x0077};
338 static const UChar sshort
[6] = {0x002D, 0x0073, 0x0068, 0x006F, 0x0072, 0x0074,};
339 if (s
.endsWith(narrow
, 7)) {
340 return UDAT_STYLE_NARROW
;
342 if (s
.endsWith(sshort
, 6)) {
343 return UDAT_STYLE_SHORT
;
345 return UDAT_STYLE_LONG
;
348 static RelAbsUnit
unitOrNegativeFromString(const char* keyword
, int32_t length
) {
349 // Quick check from string to enum.
352 if (uprv_strncmp(keyword
, "day", length
) == 0) {
354 } else if (uprv_strncmp(keyword
, "sun", length
) == 0) {
356 } else if (uprv_strncmp(keyword
, "mon", length
) == 0) {
358 } else if (uprv_strncmp(keyword
, "tue", length
) == 0) {
360 } else if (uprv_strncmp(keyword
, "wed", length
) == 0) {
362 } else if (uprv_strncmp(keyword
, "thu", length
) == 0) {
364 } else if (uprv_strncmp(keyword
, "fri", length
) == 0) {
366 } else if (uprv_strncmp(keyword
, "sat", length
) == 0) {
371 if (uprv_strncmp(keyword
, "hour", length
) == 0) {
373 } else if (uprv_strncmp(keyword
, "week", length
) == 0) {
375 } else if (uprv_strncmp(keyword
, "year", length
) == 0) {
380 if (uprv_strncmp(keyword
, "month", length
) == 0) {
385 if (uprv_strncmp(keyword
, "minute", length
) == 0) {
387 } else if (uprv_strncmp(keyword
, "second", length
) == 0) {
392 if (uprv_strncmp(keyword
, "quarter", length
) == 0) {
393 return QUARTER
; // TODO: Check @provisional
402 void handlePlainDirection(ResourceValue
&value
, UErrorCode
&errorCode
) {
403 // Handle Display Name for PLAIN direction for some units.
404 if (U_FAILURE(errorCode
)) { return; }
406 int32_t absUnit
= absUnitFromGeneric(genericUnit
);
408 return; // Not interesting.
411 // Store displayname if not set.
412 if (outputData
.absoluteUnits
[style
]
413 [absUnit
][UDAT_DIRECTION_PLAIN
].isEmpty()) {
414 outputData
.absoluteUnits
[style
]
415 [absUnit
][UDAT_DIRECTION_PLAIN
].fastCopyFrom(value
.getUnicodeString(errorCode
));
420 void consumeTableRelative(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
421 ResourceTable unitTypesTable
= value
.getTable(errorCode
);
422 if (U_FAILURE(errorCode
)) { return; }
424 for (int32_t i
= 0; unitTypesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
425 if (value
.getType() == URES_STRING
) {
426 int32_t direction
= keyToDirection(key
);
431 int32_t relUnitIndex
= relUnitFromGeneric(genericUnit
);
432 if (relUnitIndex
== UDAT_RELATIVE_SECONDS
&& uprv_strcmp(key
, "0") == 0 &&
433 outputData
.absoluteUnits
[style
][UDAT_ABSOLUTE_NOW
][UDAT_DIRECTION_PLAIN
].isEmpty()) {
435 outputData
.absoluteUnits
[style
][UDAT_ABSOLUTE_NOW
]
436 [UDAT_DIRECTION_PLAIN
].fastCopyFrom(value
.getUnicodeString(errorCode
));
439 int32_t absUnitIndex
= absUnitFromGeneric(genericUnit
);
440 if (absUnitIndex
< 0) {
443 // Only reset if slot is empty.
444 if (outputData
.absoluteUnits
[style
][absUnitIndex
][direction
].isEmpty()) {
445 outputData
.absoluteUnits
[style
][absUnitIndex
]
446 [direction
].fastCopyFrom(value
.getUnicodeString(errorCode
));
452 void consumeTimeDetail(int32_t relUnitIndex
,
453 const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
454 ResourceTable unitTypesTable
= value
.getTable(errorCode
);
455 if (U_FAILURE(errorCode
)) { return; }
457 for (int32_t i
= 0; unitTypesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
458 if (value
.getType() == URES_STRING
) {
459 int32_t pluralIndex
= StandardPlural::indexOrNegativeFromString(key
);
460 if (pluralIndex
>= 0) {
461 SimpleFormatter
**patterns
=
462 outputData
.relativeUnitsFormatters
[style
][relUnitIndex
]
464 // Only set if not already established.
465 if (patterns
[pluralIndex
] == NULL
) {
466 patterns
[pluralIndex
] = new SimpleFormatter(
467 value
.getUnicodeString(errorCode
), 0, 1, errorCode
);
468 if (patterns
[pluralIndex
] == NULL
) {
469 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
477 void consumeTableRelativeTime(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
478 ResourceTable relativeTimeTable
= value
.getTable(errorCode
);
479 if (U_FAILURE(errorCode
)) { return; }
481 int32_t relUnitIndex
= relUnitFromGeneric(genericUnit
);
482 if (relUnitIndex
< 0) {
485 for (int32_t i
= 0; relativeTimeTable
.getKeyAndValue(i
, key
, value
); ++i
) {
486 if (uprv_strcmp(key
, "past") == 0) {
488 } else if (uprv_strcmp(key
, "future") == 0) {
494 consumeTimeDetail(relUnitIndex
, key
, value
, errorCode
);
498 void consumeAlias(const char *key
, const ResourceValue
&value
, UErrorCode
&errorCode
) {
500 UDateRelativeDateTimeFormatterStyle sourceStyle
= styleFromString(key
);
501 const UnicodeString valueStr
= value
.getAliasUnicodeString(errorCode
);
502 if (U_FAILURE(errorCode
)) { return; }
504 UDateRelativeDateTimeFormatterStyle targetStyle
=
505 styleFromAliasUnicodeString(valueStr
);
507 if (sourceStyle
== targetStyle
) {
508 errorCode
= U_INVALID_FORMAT_ERROR
;
511 if (outputData
.fallBackCache
[sourceStyle
] != -1 &&
512 outputData
.fallBackCache
[sourceStyle
] != targetStyle
) {
513 errorCode
= U_INVALID_FORMAT_ERROR
;
516 outputData
.fallBackCache
[sourceStyle
] = targetStyle
;
519 void consumeTimeUnit(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
520 ResourceTable unitTypesTable
= value
.getTable(errorCode
);
521 if (U_FAILURE(errorCode
)) { return; }
523 for (int32_t i
= 0; unitTypesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
524 // Handle display name.
525 if (uprv_strcmp(key
, "dn") == 0 && value
.getType() == URES_STRING
) {
526 handlePlainDirection(value
, errorCode
);
528 if (value
.getType() == URES_TABLE
) {
529 if (uprv_strcmp(key
, "relative") == 0) {
530 consumeTableRelative(key
, value
, errorCode
);
531 } else if (uprv_strcmp(key
, "relativeTime") == 0) {
532 consumeTableRelativeTime(key
, value
, errorCode
);
538 virtual void put(const char *key
, ResourceValue
&value
,
539 UBool
/*noFallback*/, UErrorCode
&errorCode
) {
540 // Main entry point to sink
541 ResourceTable table
= value
.getTable(errorCode
);
542 if (U_FAILURE(errorCode
)) { return; }
543 for (int32_t i
= 0; table
.getKeyAndValue(i
, key
, value
); ++i
) {
544 if (value
.getType() == URES_ALIAS
) {
545 consumeAlias(key
, value
, errorCode
);
547 style
= styleFromString(key
);
548 int32_t unitSize
= uprv_strlen(key
) - styleSuffixLength(style
);
549 genericUnit
= unitOrNegativeFromString(key
, unitSize
);
550 if (style
>= 0 && genericUnit
!= INVALID_UNIT
) {
551 consumeTimeUnit(key
, value
, errorCode
);
559 // Virtual destructors must be defined out of line.
560 RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
563 DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth
[UDAT_STYLE_COUNT
] = {
564 DateFormatSymbols::WIDE
, DateFormatSymbols::SHORT
, DateFormatSymbols::NARROW
567 // Get days of weeks from the DateFormatSymbols class.
568 static void loadWeekdayNames(UnicodeString absoluteUnits
[UDAT_STYLE_COUNT
]
569 [UDAT_ABSOLUTE_UNIT_COUNT
][UDAT_DIRECTION_COUNT
],
570 const char* localeId
,
571 UErrorCode
& status
) {
572 Locale
locale(localeId
);
573 DateFormatSymbols
dfSym(locale
, status
);
574 for (int32_t style
= 0; style
< UDAT_STYLE_COUNT
; ++style
) {
575 DateFormatSymbols::DtWidthType dtfmtWidth
= styleToDateFormatSymbolWidth
[style
];
577 const UnicodeString
* weekdayNames
=
578 dfSym
.getWeekdays(count
, DateFormatSymbols::STANDALONE
, dtfmtWidth
);
579 for (int32_t dayIndex
= UDAT_ABSOLUTE_SUNDAY
;
580 dayIndex
<= UDAT_ABSOLUTE_SATURDAY
; ++ dayIndex
) {
581 int32_t dateSymbolIndex
= (dayIndex
- UDAT_ABSOLUTE_SUNDAY
) + UCAL_SUNDAY
;
582 absoluteUnits
[style
][dayIndex
][UDAT_DIRECTION_PLAIN
].fastCopyFrom(
583 weekdayNames
[dateSymbolIndex
]);
588 static UBool
loadUnitData(
589 const UResourceBundle
*resource
,
590 RelativeDateTimeCacheData
&cacheData
,
591 const char* localeId
,
592 UErrorCode
&status
) {
594 RelDateTimeFmtDataSink
sink(cacheData
);
596 ures_getAllItemsWithFallback(resource
, "fields", sink
, status
);
598 // Get the weekday names from DateFormatSymbols.
599 loadWeekdayNames(cacheData
.absoluteUnits
, localeId
, status
);
600 return U_SUCCESS(status
);
603 static UBool
getDateTimePattern(
604 const UResourceBundle
*resource
,
605 UnicodeString
&result
,
606 UErrorCode
&status
) {
607 UnicodeString defaultCalendarName
;
608 if (!getStringWithFallback(
615 CharString pathBuffer
;
616 pathBuffer
.append("calendar/", status
)
617 .appendInvariantChars(defaultCalendarName
, status
)
618 .append("/DateTimePatterns", status
);
619 LocalUResourceBundlePointer
topLevel(
620 ures_getByKeyWithFallback(
621 resource
, pathBuffer
.data(), NULL
, &status
));
622 if (U_FAILURE(status
)) {
625 int32_t size
= ures_getSize(topLevel
.getAlias());
627 // Oops, size is too small to access the index that we want, fallback
628 // to a hard-coded value.
629 result
= UNICODE_STRING_SIMPLE("{1} {0}");
632 return getStringByIndex(topLevel
.getAlias(), 8, result
, status
);
635 template<> U_I18N_API
636 const RelativeDateTimeCacheData
*LocaleCacheKey
<RelativeDateTimeCacheData
>::createObject(const void * /*unused*/, UErrorCode
&status
) const {
637 const char *localeId
= fLoc
.getName();
638 LocalUResourceBundlePointer
topLevel(ures_open(NULL
, localeId
, &status
));
639 if (U_FAILURE(status
)) {
642 LocalPointer
<RelativeDateTimeCacheData
> result(
643 new RelativeDateTimeCacheData());
644 if (result
.isNull()) {
645 status
= U_MEMORY_ALLOCATION_ERROR
;
655 UnicodeString dateTimePattern
;
656 if (!getDateTimePattern(topLevel
.getAlias(), dateTimePattern
, status
)) {
659 result
->adoptCombinedDateAndTime(
660 new SimpleFormatter(dateTimePattern
, 2, 2, status
));
661 if (U_FAILURE(status
)) {
665 return result
.orphan();
668 RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode
& status
) :
672 fStyle(UDAT_STYLE_LONG
),
673 fContext(UDISPCTX_CAPITALIZATION_NONE
),
674 fOptBreakIterator(NULL
) {
675 init(NULL
, NULL
, status
);
678 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
679 const Locale
& locale
, UErrorCode
& status
) :
683 fStyle(UDAT_STYLE_LONG
),
684 fContext(UDISPCTX_CAPITALIZATION_NONE
),
685 fOptBreakIterator(NULL
),
687 init(NULL
, NULL
, status
);
690 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
691 const Locale
& locale
, NumberFormat
*nfToAdopt
, UErrorCode
& status
) :
695 fStyle(UDAT_STYLE_LONG
),
696 fContext(UDISPCTX_CAPITALIZATION_NONE
),
697 fOptBreakIterator(NULL
),
699 init(nfToAdopt
, NULL
, status
);
702 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
703 const Locale
& locale
,
704 NumberFormat
*nfToAdopt
,
705 UDateRelativeDateTimeFormatterStyle styl
,
706 UDisplayContext capitalizationContext
,
707 UErrorCode
& status
) :
712 fContext(capitalizationContext
),
713 fOptBreakIterator(NULL
),
715 if (U_FAILURE(status
)) {
718 if ((capitalizationContext
>> 8) != UDISPCTX_TYPE_CAPITALIZATION
) {
719 status
= U_ILLEGAL_ARGUMENT_ERROR
;
722 if (capitalizationContext
== UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
) {
723 BreakIterator
*bi
= BreakIterator::createSentenceInstance(locale
, status
);
724 if (U_FAILURE(status
)) {
727 init(nfToAdopt
, bi
, status
);
729 init(nfToAdopt
, NULL
, status
);
733 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
734 const RelativeDateTimeFormatter
& other
)
736 fCache(other
.fCache
),
737 fNumberFormat(other
.fNumberFormat
),
738 fPluralRules(other
.fPluralRules
),
739 fStyle(other
.fStyle
),
740 fContext(other
.fContext
),
741 fOptBreakIterator(other
.fOptBreakIterator
),
742 fLocale(other
.fLocale
) {
744 fNumberFormat
->addRef();
745 fPluralRules
->addRef();
746 if (fOptBreakIterator
!= NULL
) {
747 fOptBreakIterator
->addRef();
751 RelativeDateTimeFormatter
& RelativeDateTimeFormatter::operator=(
752 const RelativeDateTimeFormatter
& other
) {
753 if (this != &other
) {
754 SharedObject::copyPtr(other
.fCache
, fCache
);
755 SharedObject::copyPtr(other
.fNumberFormat
, fNumberFormat
);
756 SharedObject::copyPtr(other
.fPluralRules
, fPluralRules
);
757 SharedObject::copyPtr(other
.fOptBreakIterator
, fOptBreakIterator
);
758 fStyle
= other
.fStyle
;
759 fContext
= other
.fContext
;
760 fLocale
= other
.fLocale
;
765 RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
766 if (fCache
!= NULL
) {
769 if (fNumberFormat
!= NULL
) {
770 fNumberFormat
->removeRef();
772 if (fPluralRules
!= NULL
) {
773 fPluralRules
->removeRef();
775 if (fOptBreakIterator
!= NULL
) {
776 fOptBreakIterator
->removeRef();
780 const NumberFormat
& RelativeDateTimeFormatter::getNumberFormat() const {
781 return **fNumberFormat
;
784 UDisplayContext
RelativeDateTimeFormatter::getCapitalizationContext() const {
788 UDateRelativeDateTimeFormatterStyle
RelativeDateTimeFormatter::getFormatStyle() const {
792 UnicodeString
& RelativeDateTimeFormatter::format(
793 double quantity
, UDateDirection direction
, UDateRelativeUnit unit
,
794 UnicodeString
& appendTo
, UErrorCode
& status
) const {
795 if (U_FAILURE(status
)) {
798 if (direction
!= UDAT_DIRECTION_LAST
&& direction
!= UDAT_DIRECTION_NEXT
) {
799 status
= U_ILLEGAL_ARGUMENT_ERROR
;
802 int32_t bFuture
= direction
== UDAT_DIRECTION_NEXT
? 1 : 0;
803 FieldPosition
pos(FieldPosition::DONT_CARE
);
805 UnicodeString result
;
806 UnicodeString formattedNumber
;
808 StandardPlural::Form pluralIndex
= QuantityFormatter::selectPlural(
809 quantity
, **fNumberFormat
, **fPluralRules
, formattedNumber
, pos
,
812 const SimpleFormatter
* formatter
=
813 fCache
->getRelativeUnitFormatter(fStyle
, unit
, bFuture
, pluralIndex
);
814 if (formatter
== NULL
) {
815 // TODO: WARN - look at quantity formatter's action with an error.
816 status
= U_INVALID_FORMAT_ERROR
;
819 formatter
->format(formattedNumber
, result
, status
);
820 adjustForContext(result
);
821 return appendTo
.append(result
);
824 UnicodeString
& RelativeDateTimeFormatter::formatNumeric(
825 double offset
, URelativeDateTimeUnit unit
,
826 UnicodeString
& appendTo
, UErrorCode
& status
) const {
827 if (U_FAILURE(status
)) {
831 // The full implementation of this depends on CLDR data that is not yet available,
832 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
833 // In the meantime do a quick bring-up by calling the old format method; this
834 // leaves some holes (even for data that is currently available, such as quarter).
835 // When the new CLDR data is available, update the data storage accordingly,
836 // rewrite this to use it directly, and rewrite the old format method to call this
837 // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
838 UDateRelativeUnit relunit
= UDAT_RELATIVE_UNIT_COUNT
;
840 case UDAT_REL_UNIT_YEAR
: relunit
= UDAT_RELATIVE_YEARS
; break;
841 case UDAT_REL_UNIT_MONTH
: relunit
= UDAT_RELATIVE_MONTHS
; break;
842 case UDAT_REL_UNIT_WEEK
: relunit
= UDAT_RELATIVE_WEEKS
; break;
843 case UDAT_REL_UNIT_DAY
: relunit
= UDAT_RELATIVE_DAYS
; break;
844 case UDAT_REL_UNIT_HOUR
: relunit
= UDAT_RELATIVE_HOURS
; break;
845 case UDAT_REL_UNIT_MINUTE
: relunit
= UDAT_RELATIVE_MINUTES
; break;
846 case UDAT_REL_UNIT_SECOND
: relunit
= UDAT_RELATIVE_SECONDS
; break;
847 default: // a unit that the above method does not handle
848 status
= U_UNSUPPORTED_ERROR
;
851 UDateDirection direction
= UDAT_DIRECTION_NEXT
;
853 direction
= UDAT_DIRECTION_LAST
;
856 return format(offset
, direction
, relunit
, appendTo
, status
);
859 UnicodeString
& RelativeDateTimeFormatter::format(
860 UDateDirection direction
, UDateAbsoluteUnit unit
,
861 UnicodeString
& appendTo
, UErrorCode
& status
) const {
862 if (U_FAILURE(status
)) {
865 if (unit
== UDAT_ABSOLUTE_NOW
&& direction
!= UDAT_DIRECTION_PLAIN
) {
866 status
= U_ILLEGAL_ARGUMENT_ERROR
;
870 // Get string using fallback.
871 UnicodeString result
;
872 result
.fastCopyFrom(fCache
->getAbsoluteUnitString(fStyle
, unit
, direction
));
873 if (fOptBreakIterator
!= NULL
) {
874 adjustForContext(result
);
876 return appendTo
.append(result
);
879 UnicodeString
& RelativeDateTimeFormatter::format(
880 double offset
, URelativeDateTimeUnit unit
,
881 UnicodeString
& appendTo
, UErrorCode
& status
) const {
882 if (U_FAILURE(status
)) {
886 // The full implementation of this depends on CLDR data that is not yet available,
887 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
888 // In the meantime do a quick bring-up by calling the old format method; this
889 // leaves some holes (even for data that is currently available, such as quarter).
890 // When the new CLDR data is available, update the data storage accordingly,
891 // rewrite this to use it directly, and rewrite the old format method to call this
892 // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
893 UDateDirection direction
= UDAT_DIRECTION_COUNT
;
894 if (offset
> -2.1 && offset
< 2.1) {
895 // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST
896 double offsetx100
= offset
* 100.0;
897 int32_t intoffset
= (offsetx100
< 0)? (int32_t)(offsetx100
-0.5) : (int32_t)(offsetx100
+0.5);
899 case -200/*-2*/: direction
= UDAT_DIRECTION_LAST_2
; break;
900 case -100/*-1*/: direction
= UDAT_DIRECTION_LAST
; break;
901 case 0/* 0*/: direction
= UDAT_DIRECTION_THIS
; break;
902 case 100/* 1*/: direction
= UDAT_DIRECTION_NEXT
; break;
903 case 200/* 2*/: direction
= UDAT_DIRECTION_NEXT_2
; break;
907 UDateAbsoluteUnit absunit
= UDAT_ABSOLUTE_UNIT_COUNT
;
909 case UDAT_REL_UNIT_YEAR
: absunit
= UDAT_ABSOLUTE_YEAR
; break;
910 case UDAT_REL_UNIT_MONTH
: absunit
= UDAT_ABSOLUTE_MONTH
; break;
911 case UDAT_REL_UNIT_WEEK
: absunit
= UDAT_ABSOLUTE_WEEK
; break;
912 case UDAT_REL_UNIT_DAY
: absunit
= UDAT_ABSOLUTE_DAY
; break;
913 case UDAT_REL_UNIT_SECOND
:
914 if (direction
== UDAT_DIRECTION_THIS
) {
915 absunit
= UDAT_ABSOLUTE_NOW
;
916 direction
= UDAT_DIRECTION_PLAIN
;
919 case UDAT_REL_UNIT_SUNDAY
: absunit
= UDAT_ABSOLUTE_SUNDAY
; break;
920 case UDAT_REL_UNIT_MONDAY
: absunit
= UDAT_ABSOLUTE_MONDAY
; break;
921 case UDAT_REL_UNIT_TUESDAY
: absunit
= UDAT_ABSOLUTE_TUESDAY
; break;
922 case UDAT_REL_UNIT_WEDNESDAY
: absunit
= UDAT_ABSOLUTE_WEDNESDAY
; break;
923 case UDAT_REL_UNIT_THURSDAY
: absunit
= UDAT_ABSOLUTE_THURSDAY
; break;
924 case UDAT_REL_UNIT_FRIDAY
: absunit
= UDAT_ABSOLUTE_FRIDAY
; break;
925 case UDAT_REL_UNIT_SATURDAY
: absunit
= UDAT_ABSOLUTE_SATURDAY
; break;
928 if (direction
!= UDAT_DIRECTION_COUNT
&& absunit
!= UDAT_ABSOLUTE_UNIT_COUNT
) {
929 const UnicodeString
&unitFormatString
=
930 fCache
->getAbsoluteUnitString(fStyle
, absunit
, direction
);
931 if (!unitFormatString
.isEmpty()) {
932 if (fOptBreakIterator
!= NULL
) {
933 UnicodeString
result(unitFormatString
);
934 adjustForContext(result
);
935 return appendTo
.append(result
);
937 return appendTo
.append(unitFormatString
);
941 // otherwise fallback to formatNumeric
942 return formatNumeric(offset
, unit
, appendTo
, status
);
945 UnicodeString
& RelativeDateTimeFormatter::combineDateAndTime(
946 const UnicodeString
& relativeDateString
, const UnicodeString
& timeString
,
947 UnicodeString
& appendTo
, UErrorCode
& status
) const {
948 return fCache
->getCombinedDateAndTime()->format(
949 timeString
, relativeDateString
, appendTo
, status
);
952 void RelativeDateTimeFormatter::adjustForContext(UnicodeString
&str
) const {
953 if (fOptBreakIterator
== NULL
954 || str
.length() == 0 || !u_islower(str
.char32At(0))) {
958 // Must guarantee that one thread at a time accesses the shared break
960 Mutex
lock(&gBrkIterMutex
);
962 fOptBreakIterator
->get(),
964 U_TITLECASE_NO_LOWERCASE
| U_TITLECASE_NO_BREAK_ADJUSTMENT
);
967 void RelativeDateTimeFormatter::init(
968 NumberFormat
*nfToAdopt
,
969 BreakIterator
*biToAdopt
,
970 UErrorCode
&status
) {
971 LocalPointer
<NumberFormat
> nf(nfToAdopt
);
972 LocalPointer
<BreakIterator
> bi(biToAdopt
);
973 UnifiedCache::getByLocale(fLocale
, fCache
, status
);
974 if (U_FAILURE(status
)) {
977 const SharedPluralRules
*pr
= PluralRules::createSharedInstance(
978 fLocale
, UPLURAL_TYPE_CARDINAL
, status
);
979 if (U_FAILURE(status
)) {
982 SharedObject::copyPtr(pr
, fPluralRules
);
985 const SharedNumberFormat
*shared
= NumberFormat::createSharedInstance(
986 fLocale
, UNUM_DECIMAL
, status
);
987 if (U_FAILURE(status
)) {
990 SharedObject::copyPtr(shared
, fNumberFormat
);
993 SharedNumberFormat
*shared
= new SharedNumberFormat(nf
.getAlias());
994 if (shared
== NULL
) {
995 status
= U_MEMORY_ALLOCATION_ERROR
;
999 SharedObject::copyPtr(shared
, fNumberFormat
);
1002 SharedObject::clearPtr(fOptBreakIterator
);
1004 SharedBreakIterator
*shared
= new SharedBreakIterator(bi
.getAlias());
1005 if (shared
== NULL
) {
1006 status
= U_MEMORY_ALLOCATION_ERROR
;
1010 SharedObject::copyPtr(shared
, fOptBreakIterator
);
1020 U_CAPI URelativeDateTimeFormatter
* U_EXPORT2
1021 ureldatefmt_open( const char* locale
,
1022 UNumberFormat
* nfToAdopt
,
1023 UDateRelativeDateTimeFormatterStyle width
,
1024 UDisplayContext capitalizationContext
,
1025 UErrorCode
* status
)
1027 if (U_FAILURE(*status
)) {
1030 LocalPointer
<RelativeDateTimeFormatter
> formatter(new RelativeDateTimeFormatter(Locale(locale
),
1031 (NumberFormat
*)nfToAdopt
, width
,
1032 capitalizationContext
, *status
), *status
);
1033 if (U_FAILURE(*status
)) {
1036 return (URelativeDateTimeFormatter
*)formatter
.orphan();
1039 U_CAPI
void U_EXPORT2
1040 ureldatefmt_close(URelativeDateTimeFormatter
*reldatefmt
)
1042 delete (RelativeDateTimeFormatter
*)reldatefmt
;
1045 U_CAPI
int32_t U_EXPORT2
1046 ureldatefmt_formatNumeric( const URelativeDateTimeFormatter
* reldatefmt
,
1048 URelativeDateTimeUnit unit
,
1050 int32_t resultCapacity
,
1053 if (U_FAILURE(*status
)) {
1056 if (result
== NULL
? resultCapacity
!= 0 : resultCapacity
< 0) {
1057 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1061 if (result
!= NULL
) {
1062 // NULL destination for pure preflighting: empty dummy string
1063 // otherwise, alias the destination buffer (copied from udat_format)
1064 res
.setTo(result
, 0, resultCapacity
);
1066 ((RelativeDateTimeFormatter
*)reldatefmt
)->formatNumeric(offset
, unit
, res
, *status
);
1067 if (U_FAILURE(*status
)) {
1070 return res
.extract(result
, resultCapacity
, *status
);
1073 U_CAPI
int32_t U_EXPORT2
1074 ureldatefmt_format( const URelativeDateTimeFormatter
* reldatefmt
,
1076 URelativeDateTimeUnit unit
,
1078 int32_t resultCapacity
,
1081 if (U_FAILURE(*status
)) {
1084 if (result
== NULL
? resultCapacity
!= 0 : resultCapacity
< 0) {
1085 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1089 if (result
!= NULL
) {
1090 // NULL destination for pure preflighting: empty dummy string
1091 // otherwise, alias the destination buffer (copied from udat_format)
1092 res
.setTo(result
, 0, resultCapacity
);
1094 ((RelativeDateTimeFormatter
*)reldatefmt
)->format(offset
, unit
, res
, *status
);
1095 if (U_FAILURE(*status
)) {
1098 return res
.extract(result
, resultCapacity
, *status
);
1101 U_CAPI
int32_t U_EXPORT2
1102 ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter
* reldatefmt
,
1103 const UChar
* relativeDateString
,
1104 int32_t relativeDateStringLen
,
1105 const UChar
* timeString
,
1106 int32_t timeStringLen
,
1108 int32_t resultCapacity
,
1109 UErrorCode
* status
)
1111 if (U_FAILURE(*status
)) {
1114 if (result
== NULL
? resultCapacity
!= 0 : resultCapacity
< 0 ||
1115 (relativeDateString
== NULL
? relativeDateStringLen
!= 0 : relativeDateStringLen
< -1) ||
1116 (timeString
== NULL
? timeStringLen
!= 0 : timeStringLen
< -1)) {
1117 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1120 UnicodeString
relDateStr((UBool
)(relativeDateStringLen
== -1), relativeDateString
, relativeDateStringLen
);
1121 UnicodeString
timeStr((UBool
)(timeStringLen
== -1), timeString
, timeStringLen
);
1122 UnicodeString
res(result
, 0, resultCapacity
);
1123 ((RelativeDateTimeFormatter
*)reldatefmt
)->combineDateAndTime(relDateStr
, timeStr
, res
, *status
);
1124 if (U_FAILURE(*status
)) {
1127 return res
.extract(result
, resultCapacity
, *status
);
1130 #endif /* !UCONFIG_NO_FORMATTING */