2 ******************************************************************************
3 * Copyright (C) 2014-2016, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ******************************************************************************
8 ******************************************************************************
11 #include "unicode/reldatefmt.h"
13 #if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_BREAK_ITERATION
15 #include "unicode/dtfmtsym.h"
16 #include "unicode/ureldatefmt.h"
17 #include "unicode/udisplaycontext.h"
18 #include "unicode/unum.h"
19 #include "unicode/localpointer.h"
20 #include "unicode/plurrule.h"
21 #include "unicode/simpleformatter.h"
22 #include "unicode/decimfmt.h"
23 #include "unicode/numfmt.h"
24 #include "unicode/brkiter.h"
25 #include "unicode/simpleformatter.h"
27 #include "unicode/ures.h"
33 #include "quantityformatter.h"
35 #include "sharedbreakiterator.h"
36 #include "sharedpluralrules.h"
37 #include "sharednumberformat.h"
38 #include "standardplural.h"
39 #include "unifiedcache.h"
41 // Copied from uscript_props.cpp
43 static UMutex gBrkIterMutex
= U_MUTEX_INITIALIZER
;
47 // RelativeDateTimeFormatter specific data for a single locale
48 class RelativeDateTimeCacheData
: public SharedObject
{
50 RelativeDateTimeCacheData() : combinedDateAndTime(NULL
) {
51 // Initialize the cache arrays
52 for (int32_t style
= 0; style
< UDAT_STYLE_COUNT
; ++style
) {
53 for (int32_t relUnit
= 0; relUnit
< UDAT_RELATIVE_UNIT_COUNT
; ++relUnit
) {
54 for (int32_t pl
= 0; pl
< StandardPlural::COUNT
; ++pl
) {
55 relativeUnitsFormatters
[style
][relUnit
][0][pl
] = NULL
;
56 relativeUnitsFormatters
[style
][relUnit
][1][pl
] = NULL
;
60 for (int32_t i
= 0; i
< UDAT_STYLE_COUNT
; ++i
) {
61 fallBackCache
[i
] = -1;
64 virtual ~RelativeDateTimeCacheData();
66 // no numbers: e.g Next Tuesday; Yesterday; etc.
67 UnicodeString absoluteUnits
[UDAT_STYLE_COUNT
][UDAT_ABSOLUTE_UNIT_COUNT
][UDAT_DIRECTION_COUNT
];
69 // SimpleFormatter pointers for relative unit format,
70 // e.g., Next Tuesday; Yesterday; etc. For third index, 0
71 // means past, e.g., 5 days ago; 1 means future, e.g., in 5 days.
72 SimpleFormatter
*relativeUnitsFormatters
[UDAT_STYLE_COUNT
]
73 [UDAT_RELATIVE_UNIT_COUNT
][2][StandardPlural::COUNT
];
75 const UnicodeString
& getAbsoluteUnitString(int32_t fStyle
,
76 UDateAbsoluteUnit unit
,
77 UDateDirection direction
) const;
78 const SimpleFormatter
* getRelativeUnitFormatter(int32_t fStyle
,
79 UDateRelativeUnit unit
,
80 int32_t pastFutureIndex
,
81 int32_t pluralUnit
) const;
83 const UnicodeString emptyString
;
85 // Mappping from source to target styles for alias fallback.
86 int32_t fallBackCache
[UDAT_STYLE_COUNT
];
88 void adoptCombinedDateAndTime(SimpleFormatter
*fmtToAdopt
) {
89 delete combinedDateAndTime
;
90 combinedDateAndTime
= fmtToAdopt
;
92 const SimpleFormatter
*getCombinedDateAndTime() const {
93 return combinedDateAndTime
;
97 SimpleFormatter
*combinedDateAndTime
;
98 RelativeDateTimeCacheData(const RelativeDateTimeCacheData
&other
);
99 RelativeDateTimeCacheData
& operator=(
100 const RelativeDateTimeCacheData
&other
);
103 RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
104 // clear out the cache arrays
105 for (int32_t style
= 0; style
< UDAT_STYLE_COUNT
; ++style
) {
106 for (int32_t relUnit
= 0; relUnit
< UDAT_RELATIVE_UNIT_COUNT
; ++relUnit
) {
107 for (int32_t pl
= 0; pl
< StandardPlural::COUNT
; ++pl
) {
108 delete relativeUnitsFormatters
[style
][relUnit
][0][pl
];
109 delete relativeUnitsFormatters
[style
][relUnit
][1][pl
];
113 delete combinedDateAndTime
;
117 // Use fallback cache for absolute units.
118 const UnicodeString
& RelativeDateTimeCacheData::getAbsoluteUnitString(
119 int32_t fStyle
, UDateAbsoluteUnit unit
, UDateDirection direction
) const {
120 int32_t style
= fStyle
;
122 if (!absoluteUnits
[style
][unit
][direction
].isEmpty()) {
123 return absoluteUnits
[style
][unit
][direction
];
125 style
= fallBackCache
[style
];
126 } while (style
!= -1);
130 // Use fallback cache for SimpleFormatter relativeUnits.
131 const SimpleFormatter
* RelativeDateTimeCacheData::getRelativeUnitFormatter(
133 UDateRelativeUnit unit
,
134 int32_t pastFutureIndex
,
135 int32_t pluralUnit
) const {
136 int32_t style
= fStyle
;
138 if (relativeUnitsFormatters
[style
][unit
][pastFutureIndex
][pluralUnit
] != NULL
) {
139 return relativeUnitsFormatters
[style
][unit
][pastFutureIndex
][pluralUnit
];
141 style
= fallBackCache
[style
];
142 } while (style
!= -1);
143 return NULL
; // No formatter found.
146 static UBool
getStringWithFallback(
147 const UResourceBundle
*resource
,
149 UnicodeString
&result
,
150 UErrorCode
&status
) {
152 const UChar
*resStr
= ures_getStringByKeyWithFallback(
153 resource
, key
, &len
, &status
);
154 if (U_FAILURE(status
)) {
157 result
.setTo(TRUE
, resStr
, len
);
162 static UBool
getStringByIndex(
163 const UResourceBundle
*resource
,
165 UnicodeString
&result
,
166 UErrorCode
&status
) {
168 const UChar
*resStr
= ures_getStringByIndex(
169 resource
, idx
, &len
, &status
);
170 if (U_FAILURE(status
)) {
173 result
.setTo(TRUE
, resStr
, len
);
180 * Sink for enumerating all of the measurement unit display names.
181 * Contains inner sink classes, each one corresponding to a level of the resource table.
183 * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
184 * Only store a value if it is still missing, that is, it has not been overridden.
186 * C++: Each inner sink class has a reference to the main outer sink.
188 struct RelDateTimeFmtDataSink
: public ResourceTableSink
{
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 // Sinks for additional levels under /fields/*/relative/ and /fields/*/relativeTime/
297 * Make list of simplePatternFmtList, for past and for future.
298 * Set a SimpleFormatter for the <style, relative unit, plurality>
300 * Fill in values for the particular plural given, e.g., ONE, FEW, OTHER, etc.
302 struct RelDateTimeDetailSink
: public ResourceTableSink
{
303 RelDateTimeDetailSink(RelDateTimeFmtDataSink
&sink
) : outer(sink
) {}
304 ~RelDateTimeDetailSink();
306 virtual void put(const char *key
, const ResourceValue
&value
,
307 UErrorCode
&errorCode
) {
308 if (U_FAILURE(errorCode
)) { return; }
310 outer
.relUnitIndex
= relUnitFromGeneric(outer
.genericUnit
);
311 if (outer
.relUnitIndex
< 0) {
315 /* Make two lists of simplePatternFmtList, one for past and one for future.
316 * Set a SimpleFormatter pattern for the <style, relative unit, plurality>
318 * Fill in values for the particular plural given, e.g., ONE, FEW, OTHER, etc.
320 int32_t pluralIndex
= StandardPlural::indexOrNegativeFromString(key
);
321 if (pluralIndex
>= 0) {
322 SimpleFormatter
**patterns
=
323 outer
.outputData
.relativeUnitsFormatters
[outer
.style
][outer
.relUnitIndex
]
324 [outer
.pastFutureIndex
];
325 // Only set if not already established.
326 if (patterns
[pluralIndex
] == NULL
) {
327 patterns
[pluralIndex
] = new SimpleFormatter(
328 value
.getUnicodeString(errorCode
), 0, 1, errorCode
);
329 if (patterns
[pluralIndex
] == NULL
) {
330 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
336 RelDateTimeFmtDataSink
&outer
;
337 } relDateTimeDetailSink
;
340 * Handles "relativeTime" entries, e.g., under "day", "hour", "minute",
341 * "minute-short", etc.
343 struct RelativeTimeSink
: public ResourceTableSink
{
344 RelativeTimeSink(RelDateTimeFmtDataSink
&sink
) : outer(sink
) {}
347 virtual ResourceTableSink
*getOrCreateTableSink(
348 const char *key
, int32_t /* initialSize */, UErrorCode
& errorCode
) {
349 if (U_FAILURE(errorCode
)) { return NULL
; }
350 outer
.relUnitIndex
= relUnitFromGeneric(outer
.genericUnit
);
351 if (outer
.relUnitIndex
< 0) {
355 if (uprv_strcmp(key
, "past") == 0) {
356 outer
.pastFutureIndex
= 0;
357 } else if (uprv_strcmp(key
, "future") == 0) {
358 outer
.pastFutureIndex
= 1;
363 return &outer
.relDateTimeDetailSink
;
366 RelDateTimeFmtDataSink
&outer
;
370 * Handles "relative" entries, e.g., under "day", "day-short", "fri",
371 * "fri-narrow", "fri-short", etc.
373 struct RelativeSink
: public ResourceTableSink
{
374 RelativeSink(RelDateTimeFmtDataSink
&sink
) : outer(sink
) {}
377 virtual void put(const char *key
, const ResourceValue
&value
, UErrorCode
&errorCode
) {
378 if (U_FAILURE(errorCode
)) { return; }
379 int32_t direction
= keyToDirection(key
);
384 int32_t relUnitIndex
= relUnitFromGeneric(outer
.genericUnit
);
385 if (relUnitIndex
== UDAT_RELATIVE_SECONDS
&&
386 direction
== UDAT_DIRECTION_THIS
&&
387 outer
.outputData
.absoluteUnits
[outer
.style
][UDAT_ABSOLUTE_NOW
]
388 [UDAT_DIRECTION_PLAIN
].isEmpty()) {
390 outer
.outputData
.absoluteUnits
[outer
.style
][UDAT_ABSOLUTE_NOW
]
391 [UDAT_DIRECTION_PLAIN
].fastCopyFrom(value
.getUnicodeString(errorCode
));
394 int32_t absUnitIndex
= absUnitFromGeneric(outer
.genericUnit
);
395 if (absUnitIndex
< 0) {
398 // Only reset if slot is empty.
399 if (outer
.outputData
.absoluteUnits
[outer
.style
][absUnitIndex
][direction
].isEmpty()) {
400 outer
.outputData
.absoluteUnits
[outer
.style
][absUnitIndex
]
401 [direction
].fastCopyFrom(value
.getUnicodeString(errorCode
));
405 RelDateTimeFmtDataSink
&outer
;
409 * Handles entries under "fields", recognizing "relative" and "relativeTime" entries.
411 struct UnitSink
: public ResourceTableSink
{
412 UnitSink(RelDateTimeFmtDataSink
&sink
) : outer(sink
) {}
415 virtual void put(const char *key
, const ResourceValue
&value
, UErrorCode
&errorCode
) {
416 if (U_FAILURE(errorCode
)) { return; }
417 if (uprv_strcmp(key
, "dn") != 0) {
421 // Handle Display Name for PLAIN direction for some units.
422 int32_t absUnit
= absUnitFromGeneric(outer
.genericUnit
);
424 return; // Not interesting.
427 // TODO(Travis Keep): This is a hack to get around CLDR bug 6818.
428 UnicodeString displayName
= value
.getUnicodeString(errorCode
);
429 if (U_SUCCESS(errorCode
)) {
430 if (uprv_strcmp("en", outer
.sinkLocaleId
) == 0) {
431 displayName
.toLower();
436 // Store displayname if not set.
437 if (outer
.outputData
.absoluteUnits
[outer
.style
]
438 [absUnit
][UDAT_DIRECTION_PLAIN
].isEmpty()) {
439 outer
.outputData
.absoluteUnits
[outer
.style
]
440 [absUnit
][UDAT_DIRECTION_PLAIN
].fastCopyFrom(displayName
);
445 virtual ResourceTableSink
*getOrCreateTableSink(
446 const char *key
, int32_t /* initialSize */, UErrorCode
&errorCode
) {
447 if (U_FAILURE(errorCode
)) { return NULL
; }
448 if (uprv_strcmp(key
, "relative") == 0) {
449 return &outer
.relativeSink
;
450 } else if (uprv_strcmp(key
, "relativeTime") == 0) {
451 return &outer
.relativeTimeSink
;
456 RelDateTimeFmtDataSink
&outer
;
459 // For hack for locale "en".
460 // TODO(Travis Keep): This is a hack to get around CLDR bug 6818.
461 const char* sinkLocaleId
;
463 // Values kept between levels of parsing the CLDR data.
464 int32_t pastFutureIndex
; // 0 == past or 1 == future
465 UDateRelativeDateTimeFormatterStyle style
; // {LONG, SHORT, NARROW}
466 RelAbsUnit genericUnit
;
467 int32_t relUnitIndex
;
468 int32_t absUnitIndex
;
470 RelativeDateTimeCacheData
&outputData
;
473 RelDateTimeFmtDataSink(RelativeDateTimeCacheData
& cacheData
, const char* localeId
)
474 : relDateTimeDetailSink(*this), relativeTimeSink(*this), relativeSink(*this),
475 unitSink(*this), sinkLocaleId(localeId
), outputData(cacheData
) {
476 // Clear cacheData.fallBackCache
477 cacheData
.fallBackCache
[UDAT_STYLE_LONG
] = -1;
478 cacheData
.fallBackCache
[UDAT_STYLE_SHORT
] = -1;
479 cacheData
.fallBackCache
[UDAT_STYLE_NARROW
] = -1;
482 ~RelDateTimeFmtDataSink();
485 static UDateRelativeDateTimeFormatterStyle
styleFromString(const char *s
) {
486 int32_t len
= uprv_strlen(s
);
487 if (len
>= 7 && uprv_strcmp(s
+ len
- 7, "-narrow") == 0) {
488 return UDAT_STYLE_NARROW
;
490 if (len
>= 6 && uprv_strcmp(s
+ len
- 6, "-short") == 0) {
491 return UDAT_STYLE_SHORT
;
493 return UDAT_STYLE_LONG
;
496 static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style
) {
498 case UDAT_STYLE_NARROW
:
500 case UDAT_STYLE_SHORT
:
508 static UDateRelativeDateTimeFormatterStyle
styleFromAliasUnicodeString(UnicodeString s
) {
509 static const UChar narrow
[7] = {0x002D, 0x006E, 0x0061, 0x0072, 0x0072, 0x006F, 0x0077};
510 static const UChar sshort
[6] = {0x002D, 0x0073, 0x0068, 0x006F, 0x0072, 0x0074,};
511 if (s
.endsWith(narrow
, 7)) {
512 return UDAT_STYLE_NARROW
;
514 if (s
.endsWith(sshort
, 6)) {
515 return UDAT_STYLE_SHORT
;
517 return UDAT_STYLE_LONG
;
520 static RelAbsUnit
unitOrNegativeFromString(const char* keyword
, int32_t length
) {
521 // Quick check from string to enum.
524 if (uprv_strncmp(keyword
, "day", length
) == 0) {
526 } else if (uprv_strncmp(keyword
, "sun", length
) == 0) {
528 } else if (uprv_strncmp(keyword
, "mon", length
) == 0) {
530 } else if (uprv_strncmp(keyword
, "tue", length
) == 0) {
532 } else if (uprv_strncmp(keyword
, "wed", length
) == 0) {
534 } else if (uprv_strncmp(keyword
, "thu", length
) == 0) {
536 } else if (uprv_strncmp(keyword
, "fri", length
) == 0) {
538 } else if (uprv_strncmp(keyword
, "sat", length
) == 0) {
543 if (uprv_strncmp(keyword
, "hour", length
) == 0) {
545 } else if (uprv_strncmp(keyword
, "week", length
) == 0) {
547 } else if (uprv_strncmp(keyword
, "year", length
) == 0) {
552 if (uprv_strncmp(keyword
, "month", length
) == 0) {
557 if (uprv_strncmp(keyword
, "minute", length
) == 0) {
559 } else if (uprv_strncmp(keyword
, "second", length
) == 0) {
564 if (uprv_strncmp(keyword
, "quarter", length
) == 0) {
565 return QUARTER
; // TODO: Check @provisional
574 // Member functions of top level sink.
575 virtual void put(const char *key
, const ResourceValue
&value
, UErrorCode
&errorCode
) {
576 // Only handle aliases, storing information about alias fallback.
578 if (U_SUCCESS(errorCode
)) {
579 if (value
.getType() != URES_ALIAS
) {
582 const UnicodeString valueStr
= value
.getAliasUnicodeString(errorCode
);
583 if (U_SUCCESS(errorCode
)) {
584 UDateRelativeDateTimeFormatterStyle sourceStyle
= styleFromString(key
);
585 UDateRelativeDateTimeFormatterStyle targetStyle
=
586 styleFromAliasUnicodeString(valueStr
);
588 if (sourceStyle
== targetStyle
) {
589 errorCode
= U_INVALID_FORMAT_ERROR
;
592 if (outputData
.fallBackCache
[sourceStyle
] != -1 &&
593 outputData
.fallBackCache
[sourceStyle
] != targetStyle
) {
594 errorCode
= U_INVALID_FORMAT_ERROR
;
597 outputData
.fallBackCache
[sourceStyle
] = targetStyle
;
604 virtual ResourceTableSink
*getOrCreateTableSink(
605 const char *key
, int32_t /* initialSize */, UErrorCode
& /* errorCode */) {
606 style
= styleFromString(key
);
607 int32_t unitSize
= uprv_strlen(key
) - styleSuffixLength(style
);
608 genericUnit
= unitOrNegativeFromString(key
, unitSize
);
609 if (style
< 0 || genericUnit
== INVALID_UNIT
) {
616 // Virtual destructors must be defined out of line.
617 RelDateTimeFmtDataSink::RelDateTimeDetailSink::~RelDateTimeDetailSink() {}
618 RelDateTimeFmtDataSink::RelativeTimeSink::~RelativeTimeSink() {}
619 RelDateTimeFmtDataSink::RelativeSink::~RelativeSink() {}
620 RelDateTimeFmtDataSink::UnitSink::~UnitSink() {}
621 RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
625 DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth
[UDAT_STYLE_COUNT
] = {
626 DateFormatSymbols::WIDE
, DateFormatSymbols::SHORT
, DateFormatSymbols::NARROW
629 // Get days of weeks from the DateFormatSymbols class.
630 static void loadWeekdayNames(UnicodeString absoluteUnits
[UDAT_STYLE_COUNT
]
631 [UDAT_ABSOLUTE_UNIT_COUNT
][UDAT_DIRECTION_COUNT
],
632 const char* localeId
,
633 UErrorCode
& status
) {
634 Locale
locale(localeId
);
635 DateFormatSymbols
dfSym(locale
, status
);
636 for (int32_t style
= 0; style
< UDAT_STYLE_COUNT
; ++style
) {
637 DateFormatSymbols::DtWidthType dtfmtWidth
= styleToDateFormatSymbolWidth
[style
];
639 const UnicodeString
* weekdayNames
=
640 dfSym
.getWeekdays(count
, DateFormatSymbols::STANDALONE
, dtfmtWidth
);
641 for (int32_t dayIndex
= UDAT_ABSOLUTE_SUNDAY
;
642 dayIndex
<= UDAT_ABSOLUTE_SATURDAY
; ++ dayIndex
) {
643 int32_t dateSymbolIndex
= (dayIndex
- UDAT_ABSOLUTE_SUNDAY
) + UCAL_SUNDAY
;
644 absoluteUnits
[style
][dayIndex
][UDAT_DIRECTION_PLAIN
].fastCopyFrom(
645 weekdayNames
[dateSymbolIndex
]);
650 static UBool
loadUnitData(
651 const UResourceBundle
*resource
,
652 RelativeDateTimeCacheData
&cacheData
,
653 const char* localeId
,
654 UErrorCode
&status
) {
655 RelDateTimeFmtDataSink
sink(cacheData
, localeId
);
656 ures_getAllTableItemsWithFallback(resource
, "fields", sink
, status
);
658 // Get the weekday names from DateFormatSymbols.
659 loadWeekdayNames(cacheData
.absoluteUnits
, localeId
, status
);
660 return U_SUCCESS(status
);
663 static UBool
getDateTimePattern(
664 const UResourceBundle
*resource
,
665 UnicodeString
&result
,
666 UErrorCode
&status
) {
667 UnicodeString defaultCalendarName
;
668 if (!getStringWithFallback(
675 CharString pathBuffer
;
676 pathBuffer
.append("calendar/", status
)
677 .appendInvariantChars(defaultCalendarName
, status
)
678 .append("/DateTimePatterns", status
);
679 LocalUResourceBundlePointer
topLevel(
680 ures_getByKeyWithFallback(
681 resource
, pathBuffer
.data(), NULL
, &status
));
682 if (U_FAILURE(status
)) {
685 int32_t size
= ures_getSize(topLevel
.getAlias());
687 // Oops, size is too small to access the index that we want, fallback
688 // to a hard-coded value.
689 result
= UNICODE_STRING_SIMPLE("{1} {0}");
692 return getStringByIndex(topLevel
.getAlias(), 8, result
, status
);
695 template<> U_I18N_API
696 const RelativeDateTimeCacheData
*LocaleCacheKey
<RelativeDateTimeCacheData
>::createObject(const void * /*unused*/, UErrorCode
&status
) const {
697 const char *localeId
= fLoc
.getName();
698 LocalUResourceBundlePointer
topLevel(ures_open(NULL
, localeId
, &status
));
699 if (U_FAILURE(status
)) {
702 LocalPointer
<RelativeDateTimeCacheData
> result(
703 new RelativeDateTimeCacheData());
704 if (result
.isNull()) {
705 status
= U_MEMORY_ALLOCATION_ERROR
;
715 UnicodeString dateTimePattern
;
716 if (!getDateTimePattern(topLevel
.getAlias(), dateTimePattern
, status
)) {
719 result
->adoptCombinedDateAndTime(
720 new SimpleFormatter(dateTimePattern
, 2, 2, status
));
721 if (U_FAILURE(status
)) {
725 return result
.orphan();
728 RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode
& status
) :
732 fStyle(UDAT_STYLE_LONG
),
733 fContext(UDISPCTX_CAPITALIZATION_NONE
),
734 fOptBreakIterator(NULL
) {
735 init(NULL
, NULL
, status
);
738 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
739 const Locale
& locale
, UErrorCode
& status
) :
743 fStyle(UDAT_STYLE_LONG
),
744 fContext(UDISPCTX_CAPITALIZATION_NONE
),
745 fOptBreakIterator(NULL
),
747 init(NULL
, NULL
, status
);
750 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
751 const Locale
& locale
, NumberFormat
*nfToAdopt
, UErrorCode
& status
) :
755 fStyle(UDAT_STYLE_LONG
),
756 fContext(UDISPCTX_CAPITALIZATION_NONE
),
757 fOptBreakIterator(NULL
),
759 init(nfToAdopt
, NULL
, status
);
762 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
763 const Locale
& locale
,
764 NumberFormat
*nfToAdopt
,
765 UDateRelativeDateTimeFormatterStyle styl
,
766 UDisplayContext capitalizationContext
,
767 UErrorCode
& status
) :
772 fContext(capitalizationContext
),
773 fOptBreakIterator(NULL
),
775 if (U_FAILURE(status
)) {
778 if ((capitalizationContext
>> 8) != UDISPCTX_TYPE_CAPITALIZATION
) {
779 status
= U_ILLEGAL_ARGUMENT_ERROR
;
782 if (capitalizationContext
== UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
) {
783 BreakIterator
*bi
= BreakIterator::createSentenceInstance(locale
, status
);
784 if (U_FAILURE(status
)) {
787 init(nfToAdopt
, bi
, status
);
789 init(nfToAdopt
, NULL
, status
);
793 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
794 const RelativeDateTimeFormatter
& other
)
796 fCache(other
.fCache
),
797 fNumberFormat(other
.fNumberFormat
),
798 fPluralRules(other
.fPluralRules
),
799 fStyle(other
.fStyle
),
800 fContext(other
.fContext
),
801 fOptBreakIterator(other
.fOptBreakIterator
),
802 fLocale(other
.fLocale
) {
804 fNumberFormat
->addRef();
805 fPluralRules
->addRef();
806 if (fOptBreakIterator
!= NULL
) {
807 fOptBreakIterator
->addRef();
811 RelativeDateTimeFormatter
& RelativeDateTimeFormatter::operator=(
812 const RelativeDateTimeFormatter
& other
) {
813 if (this != &other
) {
814 SharedObject::copyPtr(other
.fCache
, fCache
);
815 SharedObject::copyPtr(other
.fNumberFormat
, fNumberFormat
);
816 SharedObject::copyPtr(other
.fPluralRules
, fPluralRules
);
817 SharedObject::copyPtr(other
.fOptBreakIterator
, fOptBreakIterator
);
818 fStyle
= other
.fStyle
;
819 fContext
= other
.fContext
;
820 fLocale
= other
.fLocale
;
825 RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
826 if (fCache
!= NULL
) {
829 if (fNumberFormat
!= NULL
) {
830 fNumberFormat
->removeRef();
832 if (fPluralRules
!= NULL
) {
833 fPluralRules
->removeRef();
835 if (fOptBreakIterator
!= NULL
) {
836 fOptBreakIterator
->removeRef();
840 const NumberFormat
& RelativeDateTimeFormatter::getNumberFormat() const {
841 return **fNumberFormat
;
844 UDisplayContext
RelativeDateTimeFormatter::getCapitalizationContext() const {
848 UDateRelativeDateTimeFormatterStyle
RelativeDateTimeFormatter::getFormatStyle() const {
852 UnicodeString
& RelativeDateTimeFormatter::format(
853 double quantity
, UDateDirection direction
, UDateRelativeUnit unit
,
854 UnicodeString
& appendTo
, UErrorCode
& status
) const {
855 if (U_FAILURE(status
)) {
858 if (direction
!= UDAT_DIRECTION_LAST
&& direction
!= UDAT_DIRECTION_NEXT
) {
859 status
= U_ILLEGAL_ARGUMENT_ERROR
;
862 int32_t bFuture
= direction
== UDAT_DIRECTION_NEXT
? 1 : 0;
863 FieldPosition
pos(FieldPosition::DONT_CARE
);
865 UnicodeString result
;
866 UnicodeString formattedNumber
;
868 StandardPlural::Form pluralIndex
= QuantityFormatter::selectPlural(
869 quantity
, **fNumberFormat
, **fPluralRules
, formattedNumber
, pos
,
872 const SimpleFormatter
* formatter
=
873 fCache
->getRelativeUnitFormatter(fStyle
, unit
, bFuture
, pluralIndex
);
874 if (formatter
== NULL
) {
875 // TODO: WARN - look at quantity formatter's action with an error.
876 status
= U_INVALID_FORMAT_ERROR
;
879 formatter
->format(formattedNumber
, result
, status
);
880 adjustForContext(result
);
881 return appendTo
.append(result
);
884 UnicodeString
& RelativeDateTimeFormatter::formatNumeric(
885 double offset
, URelativeDateTimeUnit unit
,
886 UnicodeString
& appendTo
, UErrorCode
& status
) const {
887 if (U_FAILURE(status
)) {
891 // The full implementation of this depends on CLDR data that is not yet available,
892 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
893 // In the meantime do a quick bring-up by calling the old format method; this
894 // leaves some holes (even for data that is currently available, such as quarter).
895 // When the new CLDR data is available, update the data storage accordingly,
896 // rewrite this to use it directly, and rewrite the old format method to call this
897 // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
898 UDateRelativeUnit relunit
= UDAT_RELATIVE_UNIT_COUNT
;
900 case UDAT_REL_UNIT_YEAR
: relunit
= UDAT_RELATIVE_YEARS
; break;
901 case UDAT_REL_UNIT_MONTH
: relunit
= UDAT_RELATIVE_MONTHS
; break;
902 case UDAT_REL_UNIT_WEEK
: relunit
= UDAT_RELATIVE_WEEKS
; break;
903 case UDAT_REL_UNIT_DAY
: relunit
= UDAT_RELATIVE_DAYS
; break;
904 case UDAT_REL_UNIT_HOUR
: relunit
= UDAT_RELATIVE_HOURS
; break;
905 case UDAT_REL_UNIT_MINUTE
: relunit
= UDAT_RELATIVE_MINUTES
; break;
906 case UDAT_REL_UNIT_SECOND
: relunit
= UDAT_RELATIVE_SECONDS
; break;
907 default: // a unit that the above method does not handle
908 status
= U_UNSUPPORTED_ERROR
;
911 UDateDirection direction
= UDAT_DIRECTION_NEXT
;
913 direction
= UDAT_DIRECTION_LAST
;
916 return format(offset
, direction
, relunit
, appendTo
, status
);
919 UnicodeString
& RelativeDateTimeFormatter::format(
920 UDateDirection direction
, UDateAbsoluteUnit unit
,
921 UnicodeString
& appendTo
, UErrorCode
& status
) const {
922 if (U_FAILURE(status
)) {
925 if (unit
== UDAT_ABSOLUTE_NOW
&& direction
!= UDAT_DIRECTION_PLAIN
) {
926 status
= U_ILLEGAL_ARGUMENT_ERROR
;
930 // Get string using fallback.
931 UnicodeString result
;
932 result
.fastCopyFrom(fCache
->getAbsoluteUnitString(fStyle
, unit
, direction
));
933 if (fOptBreakIterator
!= NULL
) {
934 adjustForContext(result
);
936 return appendTo
.append(result
);
939 UnicodeString
& RelativeDateTimeFormatter::format(
940 double offset
, URelativeDateTimeUnit unit
,
941 UnicodeString
& appendTo
, UErrorCode
& status
) const {
942 if (U_FAILURE(status
)) {
946 // The full implementation of this depends on CLDR data that is not yet available,
947 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
948 // In the meantime do a quick bring-up by calling the old format method; this
949 // leaves some holes (even for data that is currently available, such as quarter).
950 // When the new CLDR data is available, update the data storage accordingly,
951 // rewrite this to use it directly, and rewrite the old format method to call this
952 // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
953 UDateDirection direction
= UDAT_DIRECTION_COUNT
;
954 if (offset
> -2.1 && offset
< 2.1) {
955 // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST
956 double offsetx100
= offset
* 100.0;
957 int32_t intoffset
= (offsetx100
< 0)? (int32_t)(offsetx100
-0.5) : (int32_t)(offsetx100
+0.5);
959 case -200/*-2*/: direction
= UDAT_DIRECTION_LAST_2
; break;
960 case -100/*-1*/: direction
= UDAT_DIRECTION_LAST
; break;
961 case 0/* 0*/: direction
= UDAT_DIRECTION_THIS
; break;
962 case 100/* 1*/: direction
= UDAT_DIRECTION_NEXT
; break;
963 case 200/* 2*/: direction
= UDAT_DIRECTION_NEXT_2
; break;
967 UDateAbsoluteUnit absunit
= UDAT_ABSOLUTE_UNIT_COUNT
;
969 case UDAT_REL_UNIT_YEAR
: absunit
= UDAT_ABSOLUTE_YEAR
; break;
970 case UDAT_REL_UNIT_MONTH
: absunit
= UDAT_ABSOLUTE_MONTH
; break;
971 case UDAT_REL_UNIT_WEEK
: absunit
= UDAT_ABSOLUTE_WEEK
; break;
972 case UDAT_REL_UNIT_DAY
: absunit
= UDAT_ABSOLUTE_DAY
; break;
973 case UDAT_REL_UNIT_SECOND
:
974 if (direction
== UDAT_DIRECTION_THIS
) {
975 absunit
= UDAT_ABSOLUTE_NOW
;
976 direction
= UDAT_DIRECTION_PLAIN
;
979 case UDAT_REL_UNIT_SUNDAY
: absunit
= UDAT_ABSOLUTE_SUNDAY
; break;
980 case UDAT_REL_UNIT_MONDAY
: absunit
= UDAT_ABSOLUTE_MONDAY
; break;
981 case UDAT_REL_UNIT_TUESDAY
: absunit
= UDAT_ABSOLUTE_TUESDAY
; break;
982 case UDAT_REL_UNIT_WEDNESDAY
: absunit
= UDAT_ABSOLUTE_WEDNESDAY
; break;
983 case UDAT_REL_UNIT_THURSDAY
: absunit
= UDAT_ABSOLUTE_THURSDAY
; break;
984 case UDAT_REL_UNIT_FRIDAY
: absunit
= UDAT_ABSOLUTE_FRIDAY
; break;
985 case UDAT_REL_UNIT_SATURDAY
: absunit
= UDAT_ABSOLUTE_SATURDAY
; break;
988 if (direction
!= UDAT_DIRECTION_COUNT
&& absunit
!= UDAT_ABSOLUTE_UNIT_COUNT
) {
989 const UnicodeString
&unitFormatString
=
990 fCache
->getAbsoluteUnitString(fStyle
, absunit
, direction
);
991 if (!unitFormatString
.isEmpty()) {
992 if (fOptBreakIterator
!= NULL
) {
993 UnicodeString
result(unitFormatString
);
994 adjustForContext(result
);
995 return appendTo
.append(result
);
997 return appendTo
.append(unitFormatString
);
1001 // otherwise fallback to formatNumeric
1002 return formatNumeric(offset
, unit
, appendTo
, status
);
1005 UnicodeString
& RelativeDateTimeFormatter::combineDateAndTime(
1006 const UnicodeString
& relativeDateString
, const UnicodeString
& timeString
,
1007 UnicodeString
& appendTo
, UErrorCode
& status
) const {
1008 return fCache
->getCombinedDateAndTime()->format(
1009 timeString
, relativeDateString
, appendTo
, status
);
1012 void RelativeDateTimeFormatter::adjustForContext(UnicodeString
&str
) const {
1013 if (fOptBreakIterator
== NULL
1014 || str
.length() == 0 || !u_islower(str
.char32At(0))) {
1018 // Must guarantee that one thread at a time accesses the shared break
1020 Mutex
lock(&gBrkIterMutex
);
1022 fOptBreakIterator
->get(),
1024 U_TITLECASE_NO_LOWERCASE
| U_TITLECASE_NO_BREAK_ADJUSTMENT
);
1027 void RelativeDateTimeFormatter::init(
1028 NumberFormat
*nfToAdopt
,
1029 BreakIterator
*biToAdopt
,
1030 UErrorCode
&status
) {
1031 LocalPointer
<NumberFormat
> nf(nfToAdopt
);
1032 LocalPointer
<BreakIterator
> bi(biToAdopt
);
1033 UnifiedCache::getByLocale(fLocale
, fCache
, status
);
1034 if (U_FAILURE(status
)) {
1037 const SharedPluralRules
*pr
= PluralRules::createSharedInstance(
1038 fLocale
, UPLURAL_TYPE_CARDINAL
, status
);
1039 if (U_FAILURE(status
)) {
1042 SharedObject::copyPtr(pr
, fPluralRules
);
1045 const SharedNumberFormat
*shared
= NumberFormat::createSharedInstance(
1046 fLocale
, UNUM_DECIMAL
, status
);
1047 if (U_FAILURE(status
)) {
1050 SharedObject::copyPtr(shared
, fNumberFormat
);
1051 shared
->removeRef();
1053 SharedNumberFormat
*shared
= new SharedNumberFormat(nf
.getAlias());
1054 if (shared
== NULL
) {
1055 status
= U_MEMORY_ALLOCATION_ERROR
;
1059 SharedObject::copyPtr(shared
, fNumberFormat
);
1062 SharedObject::clearPtr(fOptBreakIterator
);
1064 SharedBreakIterator
*shared
= new SharedBreakIterator(bi
.getAlias());
1065 if (shared
== NULL
) {
1066 status
= U_MEMORY_ALLOCATION_ERROR
;
1070 SharedObject::copyPtr(shared
, fOptBreakIterator
);
1080 U_CAPI URelativeDateTimeFormatter
* U_EXPORT2
1081 ureldatefmt_open( const char* locale
,
1082 UNumberFormat
* nfToAdopt
,
1083 UDateRelativeDateTimeFormatterStyle width
,
1084 UDisplayContext capitalizationContext
,
1085 UErrorCode
* status
)
1087 if (U_FAILURE(*status
)) {
1090 LocalPointer
<RelativeDateTimeFormatter
> formatter(new RelativeDateTimeFormatter(Locale(locale
),
1091 (NumberFormat
*)nfToAdopt
, width
,
1092 capitalizationContext
, *status
), *status
);
1093 if (U_FAILURE(*status
)) {
1096 return (URelativeDateTimeFormatter
*)formatter
.orphan();
1099 U_CAPI
void U_EXPORT2
1100 ureldatefmt_close(URelativeDateTimeFormatter
*reldatefmt
)
1102 delete (RelativeDateTimeFormatter
*)reldatefmt
;
1105 U_CAPI
int32_t U_EXPORT2
1106 ureldatefmt_formatNumeric( const URelativeDateTimeFormatter
* reldatefmt
,
1108 URelativeDateTimeUnit unit
,
1110 int32_t resultCapacity
,
1113 if (U_FAILURE(*status
)) {
1116 if (result
== NULL
? resultCapacity
!= 0 : resultCapacity
< 0) {
1117 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1121 if (result
!= NULL
) {
1122 // NULL destination for pure preflighting: empty dummy string
1123 // otherwise, alias the destination buffer (copied from udat_format)
1124 res
.setTo(result
, 0, resultCapacity
);
1126 ((RelativeDateTimeFormatter
*)reldatefmt
)->formatNumeric(offset
, unit
, res
, *status
);
1127 if (U_FAILURE(*status
)) {
1130 return res
.extract(result
, resultCapacity
, *status
);
1133 U_CAPI
int32_t U_EXPORT2
1134 ureldatefmt_format( const URelativeDateTimeFormatter
* reldatefmt
,
1136 URelativeDateTimeUnit unit
,
1138 int32_t resultCapacity
,
1141 if (U_FAILURE(*status
)) {
1144 if (result
== NULL
? resultCapacity
!= 0 : resultCapacity
< 0) {
1145 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1149 if (result
!= NULL
) {
1150 // NULL destination for pure preflighting: empty dummy string
1151 // otherwise, alias the destination buffer (copied from udat_format)
1152 res
.setTo(result
, 0, resultCapacity
);
1154 ((RelativeDateTimeFormatter
*)reldatefmt
)->format(offset
, unit
, res
, *status
);
1155 if (U_FAILURE(*status
)) {
1158 return res
.extract(result
, resultCapacity
, *status
);
1161 U_CAPI
int32_t U_EXPORT2
1162 ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter
* reldatefmt
,
1163 const UChar
* relativeDateString
,
1164 int32_t relativeDateStringLen
,
1165 const UChar
* timeString
,
1166 int32_t timeStringLen
,
1168 int32_t resultCapacity
,
1169 UErrorCode
* status
)
1171 if (U_FAILURE(*status
)) {
1174 if (result
== NULL
? resultCapacity
!= 0 : resultCapacity
< 0 ||
1175 (relativeDateString
== NULL
? relativeDateStringLen
!= 0 : relativeDateStringLen
< -1) ||
1176 (timeString
== NULL
? timeStringLen
!= 0 : timeStringLen
< -1)) {
1177 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1180 UnicodeString
relDateStr((UBool
)(relativeDateStringLen
== -1), relativeDateString
, relativeDateStringLen
);
1181 UnicodeString
timeStr((UBool
)(timeStringLen
== -1), timeString
, timeStringLen
);
1182 UnicodeString
res(result
, 0, resultCapacity
);
1183 ((RelativeDateTimeFormatter
*)reldatefmt
)->combineDateAndTime(relDateStr
, timeStr
, res
, *status
);
1184 if (U_FAILURE(*status
)) {
1187 return res
.extract(result
, resultCapacity
, *status
);
1190 #endif /* !UCONFIG_NO_FORMATTING */