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
18 #include "unicode/dtfmtsym.h"
19 #include "unicode/ucasemap.h"
20 #include "unicode/ureldatefmt.h"
21 #include "unicode/udisplaycontext.h"
22 #include "unicode/unum.h"
23 #include "unicode/localpointer.h"
24 #include "unicode/plurrule.h"
25 #include "unicode/simpleformatter.h"
26 #include "unicode/decimfmt.h"
27 #include "unicode/numfmt.h"
28 #include "unicode/brkiter.h"
29 #include "unicode/simpleformatter.h"
31 #include "unicode/ures.h"
37 #include "quantityformatter.h"
39 #include "sharedbreakiterator.h"
40 #include "sharedpluralrules.h"
41 #include "sharednumberformat.h"
42 #include "standardplural.h"
43 #include "unifiedcache.h"
45 // Copied from uscript_props.cpp
47 static UMutex gBrkIterMutex
= U_MUTEX_INITIALIZER
;
51 // RelativeDateTimeFormatter specific data for a single locale
52 class RelativeDateTimeCacheData
: public SharedObject
{
54 RelativeDateTimeCacheData() : combinedDateAndTime(NULL
) {
55 // Initialize the cache arrays
56 for (int32_t style
= 0; style
< UDAT_STYLE_COUNT
; ++style
) {
57 for (int32_t relUnit
= 0; relUnit
< UDAT_RELATIVE_UNIT_COUNT
; ++relUnit
) {
58 for (int32_t pl
= 0; pl
< StandardPlural::COUNT
; ++pl
) {
59 relativeUnitsFormatters
[style
][relUnit
][0][pl
] = NULL
;
60 relativeUnitsFormatters
[style
][relUnit
][1][pl
] = NULL
;
64 for (int32_t i
= 0; i
< UDAT_STYLE_COUNT
; ++i
) {
65 fallBackCache
[i
] = -1;
68 virtual ~RelativeDateTimeCacheData();
70 // no numbers: e.g Next Tuesday; Yesterday; etc.
71 UnicodeString absoluteUnits
[UDAT_STYLE_COUNT
][UDAT_ABSOLUTE_UNIT_COUNT
][UDAT_DIRECTION_COUNT
];
73 // SimpleFormatter pointers for relative unit format,
74 // e.g., Next Tuesday; Yesterday; etc. For third index, 0
75 // means past, e.g., 5 days ago; 1 means future, e.g., in 5 days.
76 SimpleFormatter
*relativeUnitsFormatters
[UDAT_STYLE_COUNT
]
77 [UDAT_RELATIVE_UNIT_COUNT
][2][StandardPlural::COUNT
];
79 const UnicodeString
& getAbsoluteUnitString(int32_t fStyle
,
80 UDateAbsoluteUnit unit
,
81 UDateDirection direction
) const;
82 const SimpleFormatter
* getRelativeUnitFormatter(int32_t fStyle
,
83 UDateRelativeUnit unit
,
84 int32_t pastFutureIndex
,
85 int32_t pluralUnit
) const;
87 const UnicodeString emptyString
;
89 // Mappping from source to target styles for alias fallback.
90 int32_t fallBackCache
[UDAT_STYLE_COUNT
];
92 void adoptCombinedDateAndTime(SimpleFormatter
*fmtToAdopt
) {
93 delete combinedDateAndTime
;
94 combinedDateAndTime
= fmtToAdopt
;
96 const SimpleFormatter
*getCombinedDateAndTime() const {
97 return combinedDateAndTime
;
101 SimpleFormatter
*combinedDateAndTime
;
102 RelativeDateTimeCacheData(const RelativeDateTimeCacheData
&other
);
103 RelativeDateTimeCacheData
& operator=(
104 const RelativeDateTimeCacheData
&other
);
107 RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
108 // clear out the cache arrays
109 for (int32_t style
= 0; style
< UDAT_STYLE_COUNT
; ++style
) {
110 for (int32_t relUnit
= 0; relUnit
< UDAT_RELATIVE_UNIT_COUNT
; ++relUnit
) {
111 for (int32_t pl
= 0; pl
< StandardPlural::COUNT
; ++pl
) {
112 delete relativeUnitsFormatters
[style
][relUnit
][0][pl
];
113 delete relativeUnitsFormatters
[style
][relUnit
][1][pl
];
117 delete combinedDateAndTime
;
121 // Use fallback cache for absolute units.
122 const UnicodeString
& RelativeDateTimeCacheData::getAbsoluteUnitString(
123 int32_t fStyle
, UDateAbsoluteUnit unit
, UDateDirection direction
) const {
124 int32_t style
= fStyle
;
126 if (!absoluteUnits
[style
][unit
][direction
].isEmpty()) {
127 return absoluteUnits
[style
][unit
][direction
];
129 style
= fallBackCache
[style
];
130 } while (style
!= -1);
134 // Use fallback cache for SimpleFormatter relativeUnits.
135 const SimpleFormatter
* RelativeDateTimeCacheData::getRelativeUnitFormatter(
137 UDateRelativeUnit unit
,
138 int32_t pastFutureIndex
,
139 int32_t pluralUnit
) const {
140 int32_t style
= fStyle
;
142 if (relativeUnitsFormatters
[style
][unit
][pastFutureIndex
][pluralUnit
] != NULL
) {
143 return relativeUnitsFormatters
[style
][unit
][pastFutureIndex
][pluralUnit
];
145 style
= fallBackCache
[style
];
146 } while (style
!= -1);
147 return NULL
; // No formatter found.
150 static UBool
getStringWithFallback(
151 const UResourceBundle
*resource
,
153 UnicodeString
&result
,
154 UErrorCode
&status
) {
156 const UChar
*resStr
= ures_getStringByKeyWithFallback(
157 resource
, key
, &len
, &status
);
158 if (U_FAILURE(status
)) {
161 result
.setTo(TRUE
, resStr
, len
);
166 static UBool
getStringByIndex(
167 const UResourceBundle
*resource
,
169 UnicodeString
&result
,
170 UErrorCode
&status
) {
172 const UChar
*resStr
= ures_getStringByIndex(
173 resource
, idx
, &len
, &status
);
174 if (U_FAILURE(status
)) {
177 result
.setTo(TRUE
, resStr
, len
);
184 * Sink for enumerating all of the measurement unit display names.
186 * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
187 * Only store a value if it is still missing, that is, it has not been overridden.
189 struct RelDateTimeFmtDataSink
: public ResourceSink
{
192 * Sink for patterns for relative dates and times. For example,
193 * fields/relative/...
196 // Generic unit enum for storing Unit info.
197 typedef enum RelAbsUnit
{
216 static int32_t relUnitFromGeneric(RelAbsUnit genUnit
) {
217 // Converts the generic units to UDAT_RELATIVE version.
220 return UDAT_RELATIVE_SECONDS
;
222 return UDAT_RELATIVE_MINUTES
;
224 return UDAT_RELATIVE_HOURS
;
226 return UDAT_RELATIVE_DAYS
;
228 return UDAT_RELATIVE_WEEKS
;
230 return UDAT_RELATIVE_MONTHS
;
233 * return UDATE_RELATIVE_QUARTERS;
236 return UDAT_RELATIVE_YEARS
;
242 static int32_t absUnitFromGeneric(RelAbsUnit genUnit
) {
243 // Converts the generic units to UDAT_RELATIVE version.
246 return UDAT_ABSOLUTE_DAY
;
248 return UDAT_ABSOLUTE_WEEK
;
250 return UDAT_ABSOLUTE_MONTH
;
251 /* TODO: Add in QUARTER
253 * return UDAT_ABSOLUTE_QUARTER;
256 return UDAT_ABSOLUTE_YEAR
;
258 return UDAT_ABSOLUTE_SUNDAY
;
260 return UDAT_ABSOLUTE_MONDAY
;
262 return UDAT_ABSOLUTE_TUESDAY
;
264 return UDAT_ABSOLUTE_WEDNESDAY
;
266 return UDAT_ABSOLUTE_THURSDAY
;
268 return UDAT_ABSOLUTE_FRIDAY
;
270 return UDAT_ABSOLUTE_SATURDAY
;
276 static int32_t keyToDirection(const char* key
) {
277 if (uprv_strcmp(key
, "-2") == 0) {
278 return UDAT_DIRECTION_LAST_2
;
280 if (uprv_strcmp(key
, "-1") == 0) {
281 return UDAT_DIRECTION_LAST
;
283 if (uprv_strcmp(key
, "0") == 0) {
284 return UDAT_DIRECTION_THIS
;
286 if (uprv_strcmp(key
, "1") == 0) {
287 return UDAT_DIRECTION_NEXT
;
289 if (uprv_strcmp(key
, "2") == 0) {
290 return UDAT_DIRECTION_NEXT_2
;
295 // Values kept between levels of parsing the CLDR data.
296 int32_t pastFutureIndex
; // 0 == past or 1 == future
297 UDateRelativeDateTimeFormatterStyle style
; // {LONG, SHORT, NARROW}
298 RelAbsUnit genericUnit
;
300 RelativeDateTimeCacheData
&outputData
;
303 RelDateTimeFmtDataSink(RelativeDateTimeCacheData
& cacheData
)
304 : outputData(cacheData
) {
305 // Clear cacheData.fallBackCache
306 cacheData
.fallBackCache
[UDAT_STYLE_LONG
] = -1;
307 cacheData
.fallBackCache
[UDAT_STYLE_SHORT
] = -1;
308 cacheData
.fallBackCache
[UDAT_STYLE_NARROW
] = -1;
311 ~RelDateTimeFmtDataSink();
314 static UDateRelativeDateTimeFormatterStyle
styleFromString(const char *s
) {
315 int32_t len
= uprv_strlen(s
);
316 if (len
>= 7 && uprv_strcmp(s
+ len
- 7, "-narrow") == 0) {
317 return UDAT_STYLE_NARROW
;
319 if (len
>= 6 && uprv_strcmp(s
+ len
- 6, "-short") == 0) {
320 return UDAT_STYLE_SHORT
;
322 return UDAT_STYLE_LONG
;
325 static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style
) {
327 case UDAT_STYLE_NARROW
:
329 case UDAT_STYLE_SHORT
:
337 static UDateRelativeDateTimeFormatterStyle
styleFromAliasUnicodeString(UnicodeString s
) {
338 static const UChar narrow
[7] = {0x002D, 0x006E, 0x0061, 0x0072, 0x0072, 0x006F, 0x0077};
339 static const UChar sshort
[6] = {0x002D, 0x0073, 0x0068, 0x006F, 0x0072, 0x0074,};
340 if (s
.endsWith(narrow
, 7)) {
341 return UDAT_STYLE_NARROW
;
343 if (s
.endsWith(sshort
, 6)) {
344 return UDAT_STYLE_SHORT
;
346 return UDAT_STYLE_LONG
;
349 static RelAbsUnit
unitOrNegativeFromString(const char* keyword
, int32_t length
) {
350 // Quick check from string to enum.
353 if (uprv_strncmp(keyword
, "day", length
) == 0) {
355 } else if (uprv_strncmp(keyword
, "sun", length
) == 0) {
357 } else if (uprv_strncmp(keyword
, "mon", length
) == 0) {
359 } else if (uprv_strncmp(keyword
, "tue", length
) == 0) {
361 } else if (uprv_strncmp(keyword
, "wed", length
) == 0) {
363 } else if (uprv_strncmp(keyword
, "thu", length
) == 0) {
365 } else if (uprv_strncmp(keyword
, "fri", length
) == 0) {
367 } else if (uprv_strncmp(keyword
, "sat", length
) == 0) {
372 if (uprv_strncmp(keyword
, "hour", length
) == 0) {
374 } else if (uprv_strncmp(keyword
, "week", length
) == 0) {
376 } else if (uprv_strncmp(keyword
, "year", length
) == 0) {
381 if (uprv_strncmp(keyword
, "month", length
) == 0) {
386 if (uprv_strncmp(keyword
, "minute", length
) == 0) {
388 } else if (uprv_strncmp(keyword
, "second", length
) == 0) {
393 if (uprv_strncmp(keyword
, "quarter", length
) == 0) {
394 return QUARTER
; // TODO: Check @provisional
403 void handlePlainDirection(ResourceValue
&value
, UErrorCode
&errorCode
) {
404 // Handle Display Name for PLAIN direction for some units.
405 if (U_FAILURE(errorCode
)) { return; }
407 int32_t absUnit
= absUnitFromGeneric(genericUnit
);
409 return; // Not interesting.
412 // Store displayname if not set.
413 if (outputData
.absoluteUnits
[style
]
414 [absUnit
][UDAT_DIRECTION_PLAIN
].isEmpty()) {
415 outputData
.absoluteUnits
[style
]
416 [absUnit
][UDAT_DIRECTION_PLAIN
].fastCopyFrom(value
.getUnicodeString(errorCode
));
421 void consumeTableRelative(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
422 ResourceTable unitTypesTable
= value
.getTable(errorCode
);
423 if (U_FAILURE(errorCode
)) { return; }
425 for (int32_t i
= 0; unitTypesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
426 if (value
.getType() == URES_STRING
) {
427 int32_t direction
= keyToDirection(key
);
432 int32_t relUnitIndex
= relUnitFromGeneric(genericUnit
);
433 if (relUnitIndex
== UDAT_RELATIVE_SECONDS
&& uprv_strcmp(key
, "0") == 0 &&
434 outputData
.absoluteUnits
[style
][UDAT_ABSOLUTE_NOW
][UDAT_DIRECTION_PLAIN
].isEmpty()) {
436 outputData
.absoluteUnits
[style
][UDAT_ABSOLUTE_NOW
]
437 [UDAT_DIRECTION_PLAIN
].fastCopyFrom(value
.getUnicodeString(errorCode
));
440 int32_t absUnitIndex
= absUnitFromGeneric(genericUnit
);
441 if (absUnitIndex
< 0) {
444 // Only reset if slot is empty.
445 if (outputData
.absoluteUnits
[style
][absUnitIndex
][direction
].isEmpty()) {
446 outputData
.absoluteUnits
[style
][absUnitIndex
]
447 [direction
].fastCopyFrom(value
.getUnicodeString(errorCode
));
453 void consumeTimeDetail(int32_t relUnitIndex
,
454 const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
455 ResourceTable unitTypesTable
= value
.getTable(errorCode
);
456 if (U_FAILURE(errorCode
)) { return; }
458 for (int32_t i
= 0; unitTypesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
459 if (value
.getType() == URES_STRING
) {
460 int32_t pluralIndex
= StandardPlural::indexOrNegativeFromString(key
);
461 if (pluralIndex
>= 0) {
462 SimpleFormatter
**patterns
=
463 outputData
.relativeUnitsFormatters
[style
][relUnitIndex
]
465 // Only set if not already established.
466 if (patterns
[pluralIndex
] == NULL
) {
467 patterns
[pluralIndex
] = new SimpleFormatter(
468 value
.getUnicodeString(errorCode
), 0, 1, errorCode
);
469 if (patterns
[pluralIndex
] == NULL
) {
470 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
478 void consumeTableRelativeTime(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
479 ResourceTable relativeTimeTable
= value
.getTable(errorCode
);
480 if (U_FAILURE(errorCode
)) { return; }
482 int32_t relUnitIndex
= relUnitFromGeneric(genericUnit
);
483 if (relUnitIndex
< 0) {
486 for (int32_t i
= 0; relativeTimeTable
.getKeyAndValue(i
, key
, value
); ++i
) {
487 if (uprv_strcmp(key
, "past") == 0) {
489 } else if (uprv_strcmp(key
, "future") == 0) {
495 consumeTimeDetail(relUnitIndex
, key
, value
, errorCode
);
499 void consumeAlias(const char *key
, const ResourceValue
&value
, UErrorCode
&errorCode
) {
501 UDateRelativeDateTimeFormatterStyle sourceStyle
= styleFromString(key
);
502 const UnicodeString valueStr
= value
.getAliasUnicodeString(errorCode
);
503 if (U_FAILURE(errorCode
)) { return; }
505 UDateRelativeDateTimeFormatterStyle targetStyle
=
506 styleFromAliasUnicodeString(valueStr
);
508 if (sourceStyle
== targetStyle
) {
509 errorCode
= U_INVALID_FORMAT_ERROR
;
512 if (outputData
.fallBackCache
[sourceStyle
] != -1 &&
513 outputData
.fallBackCache
[sourceStyle
] != targetStyle
) {
514 errorCode
= U_INVALID_FORMAT_ERROR
;
517 outputData
.fallBackCache
[sourceStyle
] = targetStyle
;
520 void consumeTimeUnit(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
521 ResourceTable unitTypesTable
= value
.getTable(errorCode
);
522 if (U_FAILURE(errorCode
)) { return; }
524 for (int32_t i
= 0; unitTypesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
525 // Handle display name.
526 if (uprv_strcmp(key
, "dn") == 0 && value
.getType() == URES_STRING
) {
527 handlePlainDirection(value
, errorCode
);
529 if (value
.getType() == URES_TABLE
) {
530 if (uprv_strcmp(key
, "relative") == 0) {
531 consumeTableRelative(key
, value
, errorCode
);
532 } else if (uprv_strcmp(key
, "relativeTime") == 0) {
533 consumeTableRelativeTime(key
, value
, errorCode
);
539 virtual void put(const char *key
, ResourceValue
&value
,
540 UBool
/*noFallback*/, UErrorCode
&errorCode
) {
541 // Main entry point to sink
542 ResourceTable table
= value
.getTable(errorCode
);
543 if (U_FAILURE(errorCode
)) { return; }
544 for (int32_t i
= 0; table
.getKeyAndValue(i
, key
, value
); ++i
) {
545 if (value
.getType() == URES_ALIAS
) {
546 consumeAlias(key
, value
, errorCode
);
548 style
= styleFromString(key
);
549 int32_t unitSize
= uprv_strlen(key
) - styleSuffixLength(style
);
550 genericUnit
= unitOrNegativeFromString(key
, unitSize
);
551 if (style
>= 0 && genericUnit
!= INVALID_UNIT
) {
552 consumeTimeUnit(key
, value
, errorCode
);
560 // Virtual destructors must be defined out of line.
561 RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
564 static const DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth
[UDAT_STYLE_COUNT
] = {
565 DateFormatSymbols::WIDE
, DateFormatSymbols::SHORT
, DateFormatSymbols::NARROW
568 // Get days of weeks from the DateFormatSymbols class.
569 static void loadWeekdayNames(UnicodeString absoluteUnits
[UDAT_STYLE_COUNT
]
570 [UDAT_ABSOLUTE_UNIT_COUNT
][UDAT_DIRECTION_COUNT
],
571 const char* localeId
,
572 UErrorCode
& status
) {
573 Locale
locale(localeId
);
574 DateFormatSymbols
dfSym(locale
, status
);
575 for (int32_t style
= 0; style
< UDAT_STYLE_COUNT
; ++style
) {
576 DateFormatSymbols::DtWidthType dtfmtWidth
= styleToDateFormatSymbolWidth
[style
];
578 const UnicodeString
* weekdayNames
=
579 dfSym
.getWeekdays(count
, DateFormatSymbols::STANDALONE
, dtfmtWidth
);
580 for (int32_t dayIndex
= UDAT_ABSOLUTE_SUNDAY
;
581 dayIndex
<= UDAT_ABSOLUTE_SATURDAY
; ++ dayIndex
) {
582 int32_t dateSymbolIndex
= (dayIndex
- UDAT_ABSOLUTE_SUNDAY
) + UCAL_SUNDAY
;
583 absoluteUnits
[style
][dayIndex
][UDAT_DIRECTION_PLAIN
].fastCopyFrom(
584 weekdayNames
[dateSymbolIndex
]);
589 static UBool
loadUnitData(
590 const UResourceBundle
*resource
,
591 RelativeDateTimeCacheData
&cacheData
,
592 const char* localeId
,
593 UErrorCode
&status
) {
595 RelDateTimeFmtDataSink
sink(cacheData
);
597 ures_getAllItemsWithFallback(resource
, "fields", sink
, status
);
599 // Get the weekday names from DateFormatSymbols.
600 loadWeekdayNames(cacheData
.absoluteUnits
, localeId
, status
);
601 return U_SUCCESS(status
);
604 static UBool
getDateTimePattern(
605 const UResourceBundle
*resource
,
606 UnicodeString
&result
,
607 UErrorCode
&status
) {
608 UnicodeString defaultCalendarName
;
609 if (!getStringWithFallback(
616 CharString pathBuffer
;
617 pathBuffer
.append("calendar/", status
)
618 .appendInvariantChars(defaultCalendarName
, status
)
619 .append("/DateTimePatterns", status
);
620 LocalUResourceBundlePointer
topLevel(
621 ures_getByKeyWithFallback(
622 resource
, pathBuffer
.data(), NULL
, &status
));
623 if (U_FAILURE(status
)) {
626 int32_t size
= ures_getSize(topLevel
.getAlias());
628 // Oops, size is too small to access the index that we want, fallback
629 // to a hard-coded value.
630 result
= UNICODE_STRING_SIMPLE("{1} {0}");
633 return getStringByIndex(topLevel
.getAlias(), 8, result
, status
);
636 template<> U_I18N_API
637 const RelativeDateTimeCacheData
*LocaleCacheKey
<RelativeDateTimeCacheData
>::createObject(const void * /*unused*/, UErrorCode
&status
) const {
638 const char *localeId
= fLoc
.getName();
639 LocalUResourceBundlePointer
topLevel(ures_open(NULL
, localeId
, &status
));
640 if (U_FAILURE(status
)) {
643 LocalPointer
<RelativeDateTimeCacheData
> result(
644 new RelativeDateTimeCacheData());
645 if (result
.isNull()) {
646 status
= U_MEMORY_ALLOCATION_ERROR
;
656 UnicodeString dateTimePattern
;
657 if (!getDateTimePattern(topLevel
.getAlias(), dateTimePattern
, status
)) {
660 result
->adoptCombinedDateAndTime(
661 new SimpleFormatter(dateTimePattern
, 2, 2, status
));
662 if (U_FAILURE(status
)) {
666 return result
.orphan();
669 RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode
& status
) :
673 fStyle(UDAT_STYLE_LONG
),
674 fContext(UDISPCTX_CAPITALIZATION_NONE
),
675 fOptBreakIterator(NULL
) {
676 init(NULL
, NULL
, status
);
679 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
680 const Locale
& locale
, UErrorCode
& status
) :
684 fStyle(UDAT_STYLE_LONG
),
685 fContext(UDISPCTX_CAPITALIZATION_NONE
),
686 fOptBreakIterator(NULL
),
688 init(NULL
, NULL
, status
);
691 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
692 const Locale
& locale
, NumberFormat
*nfToAdopt
, UErrorCode
& status
) :
696 fStyle(UDAT_STYLE_LONG
),
697 fContext(UDISPCTX_CAPITALIZATION_NONE
),
698 fOptBreakIterator(NULL
),
700 init(nfToAdopt
, NULL
, status
);
703 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
704 const Locale
& locale
,
705 NumberFormat
*nfToAdopt
,
706 UDateRelativeDateTimeFormatterStyle styl
,
707 UDisplayContext capitalizationContext
,
708 UErrorCode
& status
) :
713 fContext(capitalizationContext
),
714 fOptBreakIterator(NULL
),
716 if (U_FAILURE(status
)) {
719 if ((capitalizationContext
>> 8) != UDISPCTX_TYPE_CAPITALIZATION
) {
720 status
= U_ILLEGAL_ARGUMENT_ERROR
;
723 if (capitalizationContext
== UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
) {
724 BreakIterator
*bi
= BreakIterator::createSentenceInstance(locale
, status
);
725 if (U_FAILURE(status
)) {
728 init(nfToAdopt
, bi
, status
);
730 init(nfToAdopt
, NULL
, status
);
734 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
735 const RelativeDateTimeFormatter
& other
)
737 fCache(other
.fCache
),
738 fNumberFormat(other
.fNumberFormat
),
739 fPluralRules(other
.fPluralRules
),
740 fStyle(other
.fStyle
),
741 fContext(other
.fContext
),
742 fOptBreakIterator(other
.fOptBreakIterator
),
743 fLocale(other
.fLocale
) {
745 fNumberFormat
->addRef();
746 fPluralRules
->addRef();
747 if (fOptBreakIterator
!= NULL
) {
748 fOptBreakIterator
->addRef();
752 RelativeDateTimeFormatter
& RelativeDateTimeFormatter::operator=(
753 const RelativeDateTimeFormatter
& other
) {
754 if (this != &other
) {
755 SharedObject::copyPtr(other
.fCache
, fCache
);
756 SharedObject::copyPtr(other
.fNumberFormat
, fNumberFormat
);
757 SharedObject::copyPtr(other
.fPluralRules
, fPluralRules
);
758 SharedObject::copyPtr(other
.fOptBreakIterator
, fOptBreakIterator
);
759 fStyle
= other
.fStyle
;
760 fContext
= other
.fContext
;
761 fLocale
= other
.fLocale
;
766 RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
767 if (fCache
!= NULL
) {
770 if (fNumberFormat
!= NULL
) {
771 fNumberFormat
->removeRef();
773 if (fPluralRules
!= NULL
) {
774 fPluralRules
->removeRef();
776 if (fOptBreakIterator
!= NULL
) {
777 fOptBreakIterator
->removeRef();
781 const NumberFormat
& RelativeDateTimeFormatter::getNumberFormat() const {
782 return **fNumberFormat
;
785 UDisplayContext
RelativeDateTimeFormatter::getCapitalizationContext() const {
789 UDateRelativeDateTimeFormatterStyle
RelativeDateTimeFormatter::getFormatStyle() const {
793 UnicodeString
& RelativeDateTimeFormatter::format(
794 double quantity
, UDateDirection direction
, UDateRelativeUnit unit
,
795 UnicodeString
& appendTo
, UErrorCode
& status
) const {
796 if (U_FAILURE(status
)) {
799 if (direction
!= UDAT_DIRECTION_LAST
&& direction
!= UDAT_DIRECTION_NEXT
) {
800 status
= U_ILLEGAL_ARGUMENT_ERROR
;
803 int32_t bFuture
= direction
== UDAT_DIRECTION_NEXT
? 1 : 0;
804 FieldPosition
pos(FieldPosition::DONT_CARE
);
806 UnicodeString result
;
807 UnicodeString formattedNumber
;
809 StandardPlural::Form pluralIndex
= QuantityFormatter::selectPlural(
810 quantity
, **fNumberFormat
, **fPluralRules
, formattedNumber
, pos
,
813 const SimpleFormatter
* formatter
=
814 fCache
->getRelativeUnitFormatter(fStyle
, unit
, bFuture
, pluralIndex
);
815 if (formatter
== NULL
) {
816 // TODO: WARN - look at quantity formatter's action with an error.
817 status
= U_INVALID_FORMAT_ERROR
;
820 formatter
->format(formattedNumber
, result
, status
);
821 adjustForContext(result
);
822 return appendTo
.append(result
);
825 UnicodeString
& RelativeDateTimeFormatter::formatNumeric(
826 double offset
, URelativeDateTimeUnit unit
,
827 UnicodeString
& appendTo
, UErrorCode
& status
) const {
828 if (U_FAILURE(status
)) {
832 // The full implementation of this depends on CLDR data that is not yet available,
833 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
834 // In the meantime do a quick bring-up by calling the old format method; this
835 // leaves some holes (even for data that is currently available, such as quarter).
836 // When the new CLDR data is available, update the data storage accordingly,
837 // rewrite this to use it directly, and rewrite the old format method to call this
838 // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
839 UDateRelativeUnit relunit
= UDAT_RELATIVE_UNIT_COUNT
;
841 case UDAT_REL_UNIT_YEAR
: relunit
= UDAT_RELATIVE_YEARS
; break;
842 case UDAT_REL_UNIT_MONTH
: relunit
= UDAT_RELATIVE_MONTHS
; break;
843 case UDAT_REL_UNIT_WEEK
: relunit
= UDAT_RELATIVE_WEEKS
; break;
844 case UDAT_REL_UNIT_DAY
: relunit
= UDAT_RELATIVE_DAYS
; break;
845 case UDAT_REL_UNIT_HOUR
: relunit
= UDAT_RELATIVE_HOURS
; break;
846 case UDAT_REL_UNIT_MINUTE
: relunit
= UDAT_RELATIVE_MINUTES
; break;
847 case UDAT_REL_UNIT_SECOND
: relunit
= UDAT_RELATIVE_SECONDS
; break;
848 default: // a unit that the above method does not handle
849 status
= U_UNSUPPORTED_ERROR
;
852 UDateDirection direction
= UDAT_DIRECTION_NEXT
;
853 if (std::signbit(offset
)) { // needed to handle -0.0
854 direction
= UDAT_DIRECTION_LAST
;
857 return format(offset
, direction
, relunit
, appendTo
, status
);
860 UnicodeString
& RelativeDateTimeFormatter::format(
861 UDateDirection direction
, UDateAbsoluteUnit unit
,
862 UnicodeString
& appendTo
, UErrorCode
& status
) const {
863 if (U_FAILURE(status
)) {
866 if (unit
== UDAT_ABSOLUTE_NOW
&& direction
!= UDAT_DIRECTION_PLAIN
) {
867 status
= U_ILLEGAL_ARGUMENT_ERROR
;
871 // Get string using fallback.
872 UnicodeString result
;
873 result
.fastCopyFrom(fCache
->getAbsoluteUnitString(fStyle
, unit
, direction
));
874 if (fOptBreakIterator
!= NULL
) {
875 adjustForContext(result
);
877 return appendTo
.append(result
);
880 UnicodeString
& RelativeDateTimeFormatter::format(
881 double offset
, URelativeDateTimeUnit unit
,
882 UnicodeString
& appendTo
, UErrorCode
& status
) const {
883 if (U_FAILURE(status
)) {
887 // The full implementation of this depends on CLDR data that is not yet available,
888 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
889 // In the meantime do a quick bring-up by calling the old format method; this
890 // leaves some holes (even for data that is currently available, such as quarter).
891 // When the new CLDR data is available, update the data storage accordingly,
892 // rewrite this to use it directly, and rewrite the old format method to call this
893 // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
894 UDateDirection direction
= UDAT_DIRECTION_COUNT
;
895 if (offset
> -2.1 && offset
< 2.1) {
896 // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST
897 double offsetx100
= offset
* 100.0;
898 int32_t intoffset
= (offsetx100
< 0)? (int32_t)(offsetx100
-0.5) : (int32_t)(offsetx100
+0.5);
900 case -200/*-2*/: direction
= UDAT_DIRECTION_LAST_2
; break;
901 case -100/*-1*/: direction
= UDAT_DIRECTION_LAST
; break;
902 case 0/* 0*/: direction
= UDAT_DIRECTION_THIS
; break;
903 case 100/* 1*/: direction
= UDAT_DIRECTION_NEXT
; break;
904 case 200/* 2*/: direction
= UDAT_DIRECTION_NEXT_2
; break;
908 UDateAbsoluteUnit absunit
= UDAT_ABSOLUTE_UNIT_COUNT
;
910 case UDAT_REL_UNIT_YEAR
: absunit
= UDAT_ABSOLUTE_YEAR
; break;
911 case UDAT_REL_UNIT_MONTH
: absunit
= UDAT_ABSOLUTE_MONTH
; break;
912 case UDAT_REL_UNIT_WEEK
: absunit
= UDAT_ABSOLUTE_WEEK
; break;
913 case UDAT_REL_UNIT_DAY
: absunit
= UDAT_ABSOLUTE_DAY
; break;
914 case UDAT_REL_UNIT_SECOND
:
915 if (direction
== UDAT_DIRECTION_THIS
) {
916 absunit
= UDAT_ABSOLUTE_NOW
;
917 direction
= UDAT_DIRECTION_PLAIN
;
920 case UDAT_REL_UNIT_SUNDAY
: absunit
= UDAT_ABSOLUTE_SUNDAY
; break;
921 case UDAT_REL_UNIT_MONDAY
: absunit
= UDAT_ABSOLUTE_MONDAY
; break;
922 case UDAT_REL_UNIT_TUESDAY
: absunit
= UDAT_ABSOLUTE_TUESDAY
; break;
923 case UDAT_REL_UNIT_WEDNESDAY
: absunit
= UDAT_ABSOLUTE_WEDNESDAY
; break;
924 case UDAT_REL_UNIT_THURSDAY
: absunit
= UDAT_ABSOLUTE_THURSDAY
; break;
925 case UDAT_REL_UNIT_FRIDAY
: absunit
= UDAT_ABSOLUTE_FRIDAY
; break;
926 case UDAT_REL_UNIT_SATURDAY
: absunit
= UDAT_ABSOLUTE_SATURDAY
; break;
929 if (direction
!= UDAT_DIRECTION_COUNT
&& absunit
!= UDAT_ABSOLUTE_UNIT_COUNT
) {
930 const UnicodeString
&unitFormatString
=
931 fCache
->getAbsoluteUnitString(fStyle
, absunit
, direction
);
932 if (!unitFormatString
.isEmpty()) {
933 if (fOptBreakIterator
!= NULL
) {
934 UnicodeString
result(unitFormatString
);
935 adjustForContext(result
);
936 return appendTo
.append(result
);
938 return appendTo
.append(unitFormatString
);
942 // otherwise fallback to formatNumeric
943 return formatNumeric(offset
, unit
, appendTo
, status
);
946 UnicodeString
& RelativeDateTimeFormatter::combineDateAndTime(
947 const UnicodeString
& relativeDateString
, const UnicodeString
& timeString
,
948 UnicodeString
& appendTo
, UErrorCode
& status
) const {
949 return fCache
->getCombinedDateAndTime()->format(
950 timeString
, relativeDateString
, appendTo
, status
);
953 void RelativeDateTimeFormatter::adjustForContext(UnicodeString
&str
) const {
954 if (fOptBreakIterator
== NULL
955 || str
.length() == 0 || !u_islower(str
.char32At(0))) {
959 // Must guarantee that one thread at a time accesses the shared break
961 Mutex
lock(&gBrkIterMutex
);
963 fOptBreakIterator
->get(),
965 U_TITLECASE_NO_LOWERCASE
| U_TITLECASE_NO_BREAK_ADJUSTMENT
);
968 void RelativeDateTimeFormatter::init(
969 NumberFormat
*nfToAdopt
,
970 BreakIterator
*biToAdopt
,
971 UErrorCode
&status
) {
972 LocalPointer
<NumberFormat
> nf(nfToAdopt
);
973 LocalPointer
<BreakIterator
> bi(biToAdopt
);
974 UnifiedCache::getByLocale(fLocale
, fCache
, status
);
975 if (U_FAILURE(status
)) {
978 const SharedPluralRules
*pr
= PluralRules::createSharedInstance(
979 fLocale
, UPLURAL_TYPE_CARDINAL
, status
);
980 if (U_FAILURE(status
)) {
983 SharedObject::copyPtr(pr
, fPluralRules
);
986 const SharedNumberFormat
*shared
= NumberFormat::createSharedInstance(
987 fLocale
, UNUM_DECIMAL
, status
);
988 if (U_FAILURE(status
)) {
991 SharedObject::copyPtr(shared
, fNumberFormat
);
994 SharedNumberFormat
*shared
= new SharedNumberFormat(nf
.getAlias());
995 if (shared
== NULL
) {
996 status
= U_MEMORY_ALLOCATION_ERROR
;
1000 SharedObject::copyPtr(shared
, fNumberFormat
);
1003 SharedObject::clearPtr(fOptBreakIterator
);
1005 SharedBreakIterator
*shared
= new SharedBreakIterator(bi
.getAlias());
1006 if (shared
== NULL
) {
1007 status
= U_MEMORY_ALLOCATION_ERROR
;
1011 SharedObject::copyPtr(shared
, fOptBreakIterator
);
1021 U_CAPI URelativeDateTimeFormatter
* U_EXPORT2
1022 ureldatefmt_open( const char* locale
,
1023 UNumberFormat
* nfToAdopt
,
1024 UDateRelativeDateTimeFormatterStyle width
,
1025 UDisplayContext capitalizationContext
,
1026 UErrorCode
* status
)
1028 if (U_FAILURE(*status
)) {
1031 LocalPointer
<RelativeDateTimeFormatter
> formatter(new RelativeDateTimeFormatter(Locale(locale
),
1032 (NumberFormat
*)nfToAdopt
, width
,
1033 capitalizationContext
, *status
), *status
);
1034 if (U_FAILURE(*status
)) {
1037 return (URelativeDateTimeFormatter
*)formatter
.orphan();
1040 U_CAPI
void U_EXPORT2
1041 ureldatefmt_close(URelativeDateTimeFormatter
*reldatefmt
)
1043 delete (RelativeDateTimeFormatter
*)reldatefmt
;
1046 U_CAPI
int32_t U_EXPORT2
1047 ureldatefmt_formatNumeric( const URelativeDateTimeFormatter
* reldatefmt
,
1049 URelativeDateTimeUnit unit
,
1051 int32_t resultCapacity
,
1054 if (U_FAILURE(*status
)) {
1057 if (result
== NULL
? resultCapacity
!= 0 : resultCapacity
< 0) {
1058 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1062 if (result
!= NULL
) {
1063 // NULL destination for pure preflighting: empty dummy string
1064 // otherwise, alias the destination buffer (copied from udat_format)
1065 res
.setTo(result
, 0, resultCapacity
);
1067 ((RelativeDateTimeFormatter
*)reldatefmt
)->formatNumeric(offset
, unit
, res
, *status
);
1068 if (U_FAILURE(*status
)) {
1071 return res
.extract(result
, resultCapacity
, *status
);
1074 U_CAPI
int32_t U_EXPORT2
1075 ureldatefmt_format( const URelativeDateTimeFormatter
* reldatefmt
,
1077 URelativeDateTimeUnit unit
,
1079 int32_t resultCapacity
,
1082 if (U_FAILURE(*status
)) {
1085 if (result
== NULL
? resultCapacity
!= 0 : resultCapacity
< 0) {
1086 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1090 if (result
!= NULL
) {
1091 // NULL destination for pure preflighting: empty dummy string
1092 // otherwise, alias the destination buffer (copied from udat_format)
1093 res
.setTo(result
, 0, resultCapacity
);
1095 ((RelativeDateTimeFormatter
*)reldatefmt
)->format(offset
, unit
, res
, *status
);
1096 if (U_FAILURE(*status
)) {
1099 return res
.extract(result
, resultCapacity
, *status
);
1102 U_CAPI
int32_t U_EXPORT2
1103 ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter
* reldatefmt
,
1104 const UChar
* relativeDateString
,
1105 int32_t relativeDateStringLen
,
1106 const UChar
* timeString
,
1107 int32_t timeStringLen
,
1109 int32_t resultCapacity
,
1110 UErrorCode
* status
)
1112 if (U_FAILURE(*status
)) {
1115 if (result
== NULL
? resultCapacity
!= 0 : resultCapacity
< 0 ||
1116 (relativeDateString
== NULL
? relativeDateStringLen
!= 0 : relativeDateStringLen
< -1) ||
1117 (timeString
== NULL
? timeStringLen
!= 0 : timeStringLen
< -1)) {
1118 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1121 UnicodeString
relDateStr((UBool
)(relativeDateStringLen
== -1), relativeDateString
, relativeDateStringLen
);
1122 UnicodeString
timeStr((UBool
)(timeStringLen
== -1), timeString
, timeStringLen
);
1123 UnicodeString
res(result
, 0, resultCapacity
);
1124 ((RelativeDateTimeFormatter
*)reldatefmt
)->combineDateAndTime(relDateStr
, timeStr
, res
, *status
);
1125 if (U_FAILURE(*status
)) {
1128 return res
.extract(result
, resultCapacity
, *status
);
1131 #endif /* !UCONFIG_NO_FORMATTING */