]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/reldatefmt.cpp
ICU-62141.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / reldatefmt.cpp
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 ******************************************************************************
5 * Copyright (C) 2014-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ******************************************************************************
8 *
9 * File reldatefmt.cpp
10 ******************************************************************************
11 */
12
13 #include "unicode/reldatefmt.h"
14
15 #if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_BREAK_ITERATION
16
17 #include <cmath>
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"
30 #include "uresimp.h"
31 #include "unicode/ures.h"
32 #include "cstring.h"
33 #include "ucln_in.h"
34 #include "mutex.h"
35 #include "charstr.h"
36 #include "uassert.h"
37 #include "quantityformatter.h"
38 #include "resource.h"
39 #include "sharedbreakiterator.h"
40 #include "sharedpluralrules.h"
41 #include "sharednumberformat.h"
42 #include "standardplural.h"
43 #include "unifiedcache.h"
44
45 // Copied from uscript_props.cpp
46
47 static UMutex gBrkIterMutex = U_MUTEX_INITIALIZER;
48
49 U_NAMESPACE_BEGIN
50
51 // RelativeDateTimeFormatter specific data for a single locale
52 class RelativeDateTimeCacheData: public SharedObject {
53 public:
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;
61 }
62 }
63 }
64 for (int32_t i = 0; i < UDAT_STYLE_COUNT; ++i) {
65 fallBackCache[i] = -1;
66 }
67 }
68 virtual ~RelativeDateTimeCacheData();
69
70 // no numbers: e.g Next Tuesday; Yesterday; etc.
71 UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT];
72
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];
78
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;
86
87 const UnicodeString emptyString;
88
89 // Mappping from source to target styles for alias fallback.
90 int32_t fallBackCache[UDAT_STYLE_COUNT];
91
92 void adoptCombinedDateAndTime(SimpleFormatter *fmtToAdopt) {
93 delete combinedDateAndTime;
94 combinedDateAndTime = fmtToAdopt;
95 }
96 const SimpleFormatter *getCombinedDateAndTime() const {
97 return combinedDateAndTime;
98 }
99
100 private:
101 SimpleFormatter *combinedDateAndTime;
102 RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other);
103 RelativeDateTimeCacheData& operator=(
104 const RelativeDateTimeCacheData &other);
105 };
106
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];
114 }
115 }
116 }
117 delete combinedDateAndTime;
118 }
119
120
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;
125 do {
126 if (!absoluteUnits[style][unit][direction].isEmpty()) {
127 return absoluteUnits[style][unit][direction];
128 }
129 style = fallBackCache[style];
130 } while (style != -1);
131 return emptyString;
132 }
133
134 // Use fallback cache for SimpleFormatter relativeUnits.
135 const SimpleFormatter* RelativeDateTimeCacheData::getRelativeUnitFormatter(
136 int32_t fStyle,
137 UDateRelativeUnit unit,
138 int32_t pastFutureIndex,
139 int32_t pluralUnit) const {
140 int32_t style = fStyle;
141 do {
142 if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != NULL) {
143 return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit];
144 }
145 style = fallBackCache[style];
146 } while (style != -1);
147 return NULL; // No formatter found.
148 }
149
150 static UBool getStringWithFallback(
151 const UResourceBundle *resource,
152 const char *key,
153 UnicodeString &result,
154 UErrorCode &status) {
155 int32_t len = 0;
156 const UChar *resStr = ures_getStringByKeyWithFallback(
157 resource, key, &len, &status);
158 if (U_FAILURE(status)) {
159 return FALSE;
160 }
161 result.setTo(TRUE, resStr, len);
162 return TRUE;
163 }
164
165
166 static UBool getStringByIndex(
167 const UResourceBundle *resource,
168 int32_t idx,
169 UnicodeString &result,
170 UErrorCode &status) {
171 int32_t len = 0;
172 const UChar *resStr = ures_getStringByIndex(
173 resource, idx, &len, &status);
174 if (U_FAILURE(status)) {
175 return FALSE;
176 }
177 result.setTo(TRUE, resStr, len);
178 return TRUE;
179 }
180
181 namespace {
182
183 /**
184 * Sink for enumerating all of the measurement unit display names.
185 *
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.
188 */
189 struct RelDateTimeFmtDataSink : public ResourceSink {
190
191 /**
192 * Sink for patterns for relative dates and times. For example,
193 * fields/relative/...
194 */
195
196 // Generic unit enum for storing Unit info.
197 typedef enum RelAbsUnit {
198 INVALID_UNIT = -1,
199 SECOND,
200 MINUTE,
201 HOUR,
202 DAY,
203 WEEK,
204 MONTH,
205 QUARTER,
206 YEAR,
207 SUNDAY,
208 MONDAY,
209 TUESDAY,
210 WEDNESDAY,
211 THURSDAY,
212 FRIDAY,
213 SATURDAY
214 } RelAbsUnit;
215
216 static int32_t relUnitFromGeneric(RelAbsUnit genUnit) {
217 // Converts the generic units to UDAT_RELATIVE version.
218 switch (genUnit) {
219 case SECOND:
220 return UDAT_RELATIVE_SECONDS;
221 case MINUTE:
222 return UDAT_RELATIVE_MINUTES;
223 case HOUR:
224 return UDAT_RELATIVE_HOURS;
225 case DAY:
226 return UDAT_RELATIVE_DAYS;
227 case WEEK:
228 return UDAT_RELATIVE_WEEKS;
229 case MONTH:
230 return UDAT_RELATIVE_MONTHS;
231 /*
232 * case QUARTER:
233 * return UDATE_RELATIVE_QUARTERS;
234 */
235 case YEAR:
236 return UDAT_RELATIVE_YEARS;
237 default:
238 return -1;
239 }
240 }
241
242 static int32_t absUnitFromGeneric(RelAbsUnit genUnit) {
243 // Converts the generic units to UDAT_RELATIVE version.
244 switch (genUnit) {
245 case DAY:
246 return UDAT_ABSOLUTE_DAY;
247 case WEEK:
248 return UDAT_ABSOLUTE_WEEK;
249 case MONTH:
250 return UDAT_ABSOLUTE_MONTH;
251 /* TODO: Add in QUARTER
252 * case QUARTER:
253 * return UDAT_ABSOLUTE_QUARTER;
254 */
255 case YEAR:
256 return UDAT_ABSOLUTE_YEAR;
257 case SUNDAY:
258 return UDAT_ABSOLUTE_SUNDAY;
259 case MONDAY:
260 return UDAT_ABSOLUTE_MONDAY;
261 case TUESDAY:
262 return UDAT_ABSOLUTE_TUESDAY;
263 case WEDNESDAY:
264 return UDAT_ABSOLUTE_WEDNESDAY;
265 case THURSDAY:
266 return UDAT_ABSOLUTE_THURSDAY;
267 case FRIDAY:
268 return UDAT_ABSOLUTE_FRIDAY;
269 case SATURDAY:
270 return UDAT_ABSOLUTE_SATURDAY;
271 default:
272 return -1;
273 }
274 }
275
276 static int32_t keyToDirection(const char* key) {
277 if (uprv_strcmp(key, "-2") == 0) {
278 return UDAT_DIRECTION_LAST_2;
279 }
280 if (uprv_strcmp(key, "-1") == 0) {
281 return UDAT_DIRECTION_LAST;
282 }
283 if (uprv_strcmp(key, "0") == 0) {
284 return UDAT_DIRECTION_THIS;
285 }
286 if (uprv_strcmp(key, "1") == 0) {
287 return UDAT_DIRECTION_NEXT;
288 }
289 if (uprv_strcmp(key, "2") == 0) {
290 return UDAT_DIRECTION_NEXT_2;
291 }
292 return -1;
293 }
294
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;
299
300 RelativeDateTimeCacheData &outputData;
301
302 // Constructor
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;
309 }
310
311 ~RelDateTimeFmtDataSink();
312
313 // Utility functions
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;
318 }
319 if (len >= 6 && uprv_strcmp(s + len - 6, "-short") == 0) {
320 return UDAT_STYLE_SHORT;
321 }
322 return UDAT_STYLE_LONG;
323 }
324
325 static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style) {
326 switch (style) {
327 case UDAT_STYLE_NARROW:
328 return 7;
329 case UDAT_STYLE_SHORT:
330 return 6;
331 default:
332 return 0;
333 }
334 }
335
336 // Utility functions
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;
342 }
343 if (s.endsWith(sshort, 6)) {
344 return UDAT_STYLE_SHORT;
345 }
346 return UDAT_STYLE_LONG;
347 }
348
349 static RelAbsUnit unitOrNegativeFromString(const char* keyword, int32_t length) {
350 // Quick check from string to enum.
351 switch (length) {
352 case 3:
353 if (uprv_strncmp(keyword, "day", length) == 0) {
354 return DAY;
355 } else if (uprv_strncmp(keyword, "sun", length) == 0) {
356 return SUNDAY;
357 } else if (uprv_strncmp(keyword, "mon", length) == 0) {
358 return MONDAY;
359 } else if (uprv_strncmp(keyword, "tue", length) == 0) {
360 return TUESDAY;
361 } else if (uprv_strncmp(keyword, "wed", length) == 0) {
362 return WEDNESDAY;
363 } else if (uprv_strncmp(keyword, "thu", length) == 0) {
364 return THURSDAY;
365 } else if (uprv_strncmp(keyword, "fri", length) == 0) {
366 return FRIDAY;
367 } else if (uprv_strncmp(keyword, "sat", length) == 0) {
368 return SATURDAY;
369 }
370 break;
371 case 4:
372 if (uprv_strncmp(keyword, "hour", length) == 0) {
373 return HOUR;
374 } else if (uprv_strncmp(keyword, "week", length) == 0) {
375 return WEEK;
376 } else if (uprv_strncmp(keyword, "year", length) == 0) {
377 return YEAR;
378 }
379 break;
380 case 5:
381 if (uprv_strncmp(keyword, "month", length) == 0) {
382 return MONTH;
383 }
384 break;
385 case 6:
386 if (uprv_strncmp(keyword, "minute", length) == 0) {
387 return MINUTE;
388 } else if (uprv_strncmp(keyword, "second", length) == 0) {
389 return SECOND;
390 }
391 break;
392 case 7:
393 if (uprv_strncmp(keyword, "quarter", length) == 0) {
394 return QUARTER; // TODO: Check @provisional
395 }
396 break;
397 default:
398 break;
399 }
400 return INVALID_UNIT;
401 }
402
403 void handlePlainDirection(ResourceValue &value, UErrorCode &errorCode) {
404 // Handle Display Name for PLAIN direction for some units.
405 if (U_FAILURE(errorCode)) { return; }
406
407 int32_t absUnit = absUnitFromGeneric(genericUnit);
408 if (absUnit < 0) {
409 return; // Not interesting.
410 }
411
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));
417 return;
418 }
419 }
420
421 void consumeTableRelative(const char *key, ResourceValue &value, UErrorCode &errorCode) {
422 ResourceTable unitTypesTable = value.getTable(errorCode);
423 if (U_FAILURE(errorCode)) { return; }
424
425 for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
426 if (value.getType() == URES_STRING) {
427 int32_t direction = keyToDirection(key);
428 if (direction < 0) {
429 continue;
430 }
431
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()) {
435 // Handle "NOW"
436 outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW]
437 [UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
438 }
439
440 int32_t absUnitIndex = absUnitFromGeneric(genericUnit);
441 if (absUnitIndex < 0) {
442 continue;
443 }
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));
448 }
449 }
450 }
451 }
452
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; }
457
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]
464 [pastFutureIndex];
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;
471 }
472 }
473 }
474 }
475 }
476 }
477
478 void consumeTableRelativeTime(const char *key, ResourceValue &value, UErrorCode &errorCode) {
479 ResourceTable relativeTimeTable = value.getTable(errorCode);
480 if (U_FAILURE(errorCode)) { return; }
481
482 int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
483 if (relUnitIndex < 0) {
484 return;
485 }
486 for (int32_t i = 0; relativeTimeTable.getKeyAndValue(i, key, value); ++i) {
487 if (uprv_strcmp(key, "past") == 0) {
488 pastFutureIndex = 0;
489 } else if (uprv_strcmp(key, "future") == 0) {
490 pastFutureIndex = 1;
491 } else {
492 // Unknown key.
493 continue;
494 }
495 consumeTimeDetail(relUnitIndex, key, value, errorCode);
496 }
497 }
498
499 void consumeAlias(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
500
501 UDateRelativeDateTimeFormatterStyle sourceStyle = styleFromString(key);
502 const UnicodeString valueStr = value.getAliasUnicodeString(errorCode);
503 if (U_FAILURE(errorCode)) { return; }
504
505 UDateRelativeDateTimeFormatterStyle targetStyle =
506 styleFromAliasUnicodeString(valueStr);
507
508 if (sourceStyle == targetStyle) {
509 errorCode = U_INVALID_FORMAT_ERROR;
510 return;
511 }
512 if (outputData.fallBackCache[sourceStyle] != -1 &&
513 outputData.fallBackCache[sourceStyle] != targetStyle) {
514 errorCode = U_INVALID_FORMAT_ERROR;
515 return;
516 }
517 outputData.fallBackCache[sourceStyle] = targetStyle;
518 }
519
520 void consumeTimeUnit(const char *key, ResourceValue &value, UErrorCode &errorCode) {
521 ResourceTable unitTypesTable = value.getTable(errorCode);
522 if (U_FAILURE(errorCode)) { return; }
523
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);
528 }
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);
534 }
535 }
536 }
537 }
538
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);
547 } else {
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);
553 }
554 }
555 }
556 }
557
558 };
559
560 // Virtual destructors must be defined out of line.
561 RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
562 } // namespace
563
564 static const DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth[UDAT_STYLE_COUNT] = {
565 DateFormatSymbols::WIDE, DateFormatSymbols::SHORT, DateFormatSymbols::NARROW
566 };
567
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];
577 int32_t count;
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]);
585 }
586 }
587 }
588
589 static UBool loadUnitData(
590 const UResourceBundle *resource,
591 RelativeDateTimeCacheData &cacheData,
592 const char* localeId,
593 UErrorCode &status) {
594
595 RelDateTimeFmtDataSink sink(cacheData);
596
597 ures_getAllItemsWithFallback(resource, "fields", sink, status);
598
599 // Get the weekday names from DateFormatSymbols.
600 loadWeekdayNames(cacheData.absoluteUnits, localeId, status);
601 return U_SUCCESS(status);
602 }
603
604 static UBool getDateTimePattern(
605 const UResourceBundle *resource,
606 UnicodeString &result,
607 UErrorCode &status) {
608 UnicodeString defaultCalendarName;
609 if (!getStringWithFallback(
610 resource,
611 "calendar/default",
612 defaultCalendarName,
613 status)) {
614 return FALSE;
615 }
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)) {
624 return FALSE;
625 }
626 int32_t size = ures_getSize(topLevel.getAlias());
627 if (size <= 8) {
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}");
631 return TRUE;
632 }
633 return getStringByIndex(topLevel.getAlias(), 8, result, status);
634 }
635
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)) {
641 return NULL;
642 }
643 LocalPointer<RelativeDateTimeCacheData> result(
644 new RelativeDateTimeCacheData());
645 if (result.isNull()) {
646 status = U_MEMORY_ALLOCATION_ERROR;
647 return NULL;
648 }
649 if (!loadUnitData(
650 topLevel.getAlias(),
651 *result,
652 localeId,
653 status)) {
654 return NULL;
655 }
656 UnicodeString dateTimePattern;
657 if (!getDateTimePattern(topLevel.getAlias(), dateTimePattern, status)) {
658 return NULL;
659 }
660 result->adoptCombinedDateAndTime(
661 new SimpleFormatter(dateTimePattern, 2, 2, status));
662 if (U_FAILURE(status)) {
663 return NULL;
664 }
665 result->addRef();
666 return result.orphan();
667 }
668
669 RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) :
670 fCache(NULL),
671 fNumberFormat(NULL),
672 fPluralRules(NULL),
673 fStyle(UDAT_STYLE_LONG),
674 fContext(UDISPCTX_CAPITALIZATION_NONE),
675 fOptBreakIterator(NULL) {
676 init(NULL, NULL, status);
677 }
678
679 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
680 const Locale& locale, UErrorCode& status) :
681 fCache(NULL),
682 fNumberFormat(NULL),
683 fPluralRules(NULL),
684 fStyle(UDAT_STYLE_LONG),
685 fContext(UDISPCTX_CAPITALIZATION_NONE),
686 fOptBreakIterator(NULL),
687 fLocale(locale) {
688 init(NULL, NULL, status);
689 }
690
691 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
692 const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) :
693 fCache(NULL),
694 fNumberFormat(NULL),
695 fPluralRules(NULL),
696 fStyle(UDAT_STYLE_LONG),
697 fContext(UDISPCTX_CAPITALIZATION_NONE),
698 fOptBreakIterator(NULL),
699 fLocale(locale) {
700 init(nfToAdopt, NULL, status);
701 }
702
703 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
704 const Locale& locale,
705 NumberFormat *nfToAdopt,
706 UDateRelativeDateTimeFormatterStyle styl,
707 UDisplayContext capitalizationContext,
708 UErrorCode& status) :
709 fCache(NULL),
710 fNumberFormat(NULL),
711 fPluralRules(NULL),
712 fStyle(styl),
713 fContext(capitalizationContext),
714 fOptBreakIterator(NULL),
715 fLocale(locale) {
716 if (U_FAILURE(status)) {
717 return;
718 }
719 if ((capitalizationContext >> 8) != UDISPCTX_TYPE_CAPITALIZATION) {
720 status = U_ILLEGAL_ARGUMENT_ERROR;
721 return;
722 }
723 if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) {
724 BreakIterator *bi = BreakIterator::createSentenceInstance(locale, status);
725 if (U_FAILURE(status)) {
726 return;
727 }
728 init(nfToAdopt, bi, status);
729 } else {
730 init(nfToAdopt, NULL, status);
731 }
732 }
733
734 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
735 const RelativeDateTimeFormatter& other)
736 : UObject(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) {
744 fCache->addRef();
745 fNumberFormat->addRef();
746 fPluralRules->addRef();
747 if (fOptBreakIterator != NULL) {
748 fOptBreakIterator->addRef();
749 }
750 }
751
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;
762 }
763 return *this;
764 }
765
766 RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
767 if (fCache != NULL) {
768 fCache->removeRef();
769 }
770 if (fNumberFormat != NULL) {
771 fNumberFormat->removeRef();
772 }
773 if (fPluralRules != NULL) {
774 fPluralRules->removeRef();
775 }
776 if (fOptBreakIterator != NULL) {
777 fOptBreakIterator->removeRef();
778 }
779 }
780
781 const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const {
782 return **fNumberFormat;
783 }
784
785 UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const {
786 return fContext;
787 }
788
789 UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const {
790 return fStyle;
791 }
792
793 UnicodeString& RelativeDateTimeFormatter::format(
794 double quantity, UDateDirection direction, UDateRelativeUnit unit,
795 UnicodeString& appendTo, UErrorCode& status) const {
796 if (U_FAILURE(status)) {
797 return appendTo;
798 }
799 if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
800 status = U_ILLEGAL_ARGUMENT_ERROR;
801 return appendTo;
802 }
803 int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
804 FieldPosition pos(FieldPosition::DONT_CARE);
805
806 UnicodeString result;
807 UnicodeString formattedNumber;
808
809 StandardPlural::Form pluralIndex = QuantityFormatter::selectPlural(
810 quantity, **fNumberFormat, **fPluralRules, formattedNumber, pos,
811 status);
812
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;
818 return appendTo;
819 }
820 formatter->format(formattedNumber, result, status);
821 adjustForContext(result);
822 return appendTo.append(result);
823 }
824
825 UnicodeString& RelativeDateTimeFormatter::formatNumeric(
826 double offset, URelativeDateTimeUnit unit,
827 UnicodeString& appendTo, UErrorCode& status) const {
828 if (U_FAILURE(status)) {
829 return appendTo;
830 }
831 // TODO:
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;
840 switch (unit) {
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;
850 return appendTo;
851 }
852 UDateDirection direction = UDAT_DIRECTION_NEXT;
853 if (std::signbit(offset)) { // needed to handle -0.0
854 direction = UDAT_DIRECTION_LAST;
855 offset = -offset;
856 }
857 return format(offset, direction, relunit, appendTo, status);
858 }
859
860 UnicodeString& RelativeDateTimeFormatter::format(
861 UDateDirection direction, UDateAbsoluteUnit unit,
862 UnicodeString& appendTo, UErrorCode& status) const {
863 if (U_FAILURE(status)) {
864 return appendTo;
865 }
866 if (unit == UDAT_ABSOLUTE_NOW && direction != UDAT_DIRECTION_PLAIN) {
867 status = U_ILLEGAL_ARGUMENT_ERROR;
868 return appendTo;
869 }
870
871 // Get string using fallback.
872 UnicodeString result;
873 result.fastCopyFrom(fCache->getAbsoluteUnitString(fStyle, unit, direction));
874 if (fOptBreakIterator != NULL) {
875 adjustForContext(result);
876 }
877 return appendTo.append(result);
878 }
879
880 UnicodeString& RelativeDateTimeFormatter::format(
881 double offset, URelativeDateTimeUnit unit,
882 UnicodeString& appendTo, UErrorCode& status) const {
883 if (U_FAILURE(status)) {
884 return appendTo;
885 }
886 // TODO:
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);
899 switch (intoffset) {
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;
905 default: break;
906 }
907 }
908 UDateAbsoluteUnit absunit = UDAT_ABSOLUTE_UNIT_COUNT;
909 switch (unit) {
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;
918 }
919 break;
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;
927 default: break;
928 }
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);
937 } else {
938 return appendTo.append(unitFormatString);
939 }
940 }
941 }
942 // otherwise fallback to formatNumeric
943 return formatNumeric(offset, unit, appendTo, status);
944 }
945
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);
951 }
952
953 void RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const {
954 if (fOptBreakIterator == NULL
955 || str.length() == 0 || !u_islower(str.char32At(0))) {
956 return;
957 }
958
959 // Must guarantee that one thread at a time accesses the shared break
960 // iterator.
961 Mutex lock(&gBrkIterMutex);
962 str.toTitle(
963 fOptBreakIterator->get(),
964 fLocale,
965 U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
966 }
967
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)) {
976 return;
977 }
978 const SharedPluralRules *pr = PluralRules::createSharedInstance(
979 fLocale, UPLURAL_TYPE_CARDINAL, status);
980 if (U_FAILURE(status)) {
981 return;
982 }
983 SharedObject::copyPtr(pr, fPluralRules);
984 pr->removeRef();
985 if (nf.isNull()) {
986 const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
987 fLocale, UNUM_DECIMAL, status);
988 if (U_FAILURE(status)) {
989 return;
990 }
991 SharedObject::copyPtr(shared, fNumberFormat);
992 shared->removeRef();
993 } else {
994 SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
995 if (shared == NULL) {
996 status = U_MEMORY_ALLOCATION_ERROR;
997 return;
998 }
999 nf.orphan();
1000 SharedObject::copyPtr(shared, fNumberFormat);
1001 }
1002 if (bi.isNull()) {
1003 SharedObject::clearPtr(fOptBreakIterator);
1004 } else {
1005 SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias());
1006 if (shared == NULL) {
1007 status = U_MEMORY_ALLOCATION_ERROR;
1008 return;
1009 }
1010 bi.orphan();
1011 SharedObject::copyPtr(shared, fOptBreakIterator);
1012 }
1013 }
1014
1015 U_NAMESPACE_END
1016
1017 // Plain C API
1018
1019 U_NAMESPACE_USE
1020
1021 U_CAPI URelativeDateTimeFormatter* U_EXPORT2
1022 ureldatefmt_open( const char* locale,
1023 UNumberFormat* nfToAdopt,
1024 UDateRelativeDateTimeFormatterStyle width,
1025 UDisplayContext capitalizationContext,
1026 UErrorCode* status )
1027 {
1028 if (U_FAILURE(*status)) {
1029 return NULL;
1030 }
1031 LocalPointer<RelativeDateTimeFormatter> formatter(new RelativeDateTimeFormatter(Locale(locale),
1032 (NumberFormat*)nfToAdopt, width,
1033 capitalizationContext, *status), *status);
1034 if (U_FAILURE(*status)) {
1035 return NULL;
1036 }
1037 return (URelativeDateTimeFormatter*)formatter.orphan();
1038 }
1039
1040 U_CAPI void U_EXPORT2
1041 ureldatefmt_close(URelativeDateTimeFormatter *reldatefmt)
1042 {
1043 delete (RelativeDateTimeFormatter*)reldatefmt;
1044 }
1045
1046 U_CAPI int32_t U_EXPORT2
1047 ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt,
1048 double offset,
1049 URelativeDateTimeUnit unit,
1050 UChar* result,
1051 int32_t resultCapacity,
1052 UErrorCode* status)
1053 {
1054 if (U_FAILURE(*status)) {
1055 return 0;
1056 }
1057 if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) {
1058 *status = U_ILLEGAL_ARGUMENT_ERROR;
1059 return 0;
1060 }
1061 UnicodeString res;
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);
1066 }
1067 ((RelativeDateTimeFormatter*)reldatefmt)->formatNumeric(offset, unit, res, *status);
1068 if (U_FAILURE(*status)) {
1069 return 0;
1070 }
1071 return res.extract(result, resultCapacity, *status);
1072 }
1073
1074 U_CAPI int32_t U_EXPORT2
1075 ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt,
1076 double offset,
1077 URelativeDateTimeUnit unit,
1078 UChar* result,
1079 int32_t resultCapacity,
1080 UErrorCode* status)
1081 {
1082 if (U_FAILURE(*status)) {
1083 return 0;
1084 }
1085 if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) {
1086 *status = U_ILLEGAL_ARGUMENT_ERROR;
1087 return 0;
1088 }
1089 UnicodeString res;
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);
1094 }
1095 ((RelativeDateTimeFormatter*)reldatefmt)->format(offset, unit, res, *status);
1096 if (U_FAILURE(*status)) {
1097 return 0;
1098 }
1099 return res.extract(result, resultCapacity, *status);
1100 }
1101
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,
1108 UChar* result,
1109 int32_t resultCapacity,
1110 UErrorCode* status )
1111 {
1112 if (U_FAILURE(*status)) {
1113 return 0;
1114 }
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;
1119 return 0;
1120 }
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)) {
1126 return 0;
1127 }
1128 return res.extract(result, resultCapacity, *status);
1129 }
1130
1131 #endif /* !UCONFIG_NO_FORMATTING */