]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/reldatefmt.cpp
ICU-59180.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 "unicode/dtfmtsym.h"
18 #include "unicode/ucasemap.h"
19 #include "unicode/ureldatefmt.h"
20 #include "unicode/udisplaycontext.h"
21 #include "unicode/unum.h"
22 #include "unicode/localpointer.h"
23 #include "unicode/plurrule.h"
24 #include "unicode/simpleformatter.h"
25 #include "unicode/decimfmt.h"
26 #include "unicode/numfmt.h"
27 #include "unicode/brkiter.h"
28 #include "unicode/simpleformatter.h"
29 #include "uresimp.h"
30 #include "unicode/ures.h"
31 #include "cstring.h"
32 #include "ucln_in.h"
33 #include "mutex.h"
34 #include "charstr.h"
35 #include "uassert.h"
36 #include "quantityformatter.h"
37 #include "resource.h"
38 #include "sharedbreakiterator.h"
39 #include "sharedpluralrules.h"
40 #include "sharednumberformat.h"
41 #include "standardplural.h"
42 #include "unifiedcache.h"
43
44 // Copied from uscript_props.cpp
45
46 static UMutex gBrkIterMutex = U_MUTEX_INITIALIZER;
47
48 U_NAMESPACE_BEGIN
49
50 // RelativeDateTimeFormatter specific data for a single locale
51 class RelativeDateTimeCacheData: public SharedObject {
52 public:
53 RelativeDateTimeCacheData() : combinedDateAndTime(NULL) {
54 // Initialize the cache arrays
55 for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
56 for (int32_t relUnit = 0; relUnit < UDAT_RELATIVE_UNIT_COUNT; ++relUnit) {
57 for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
58 relativeUnitsFormatters[style][relUnit][0][pl] = NULL;
59 relativeUnitsFormatters[style][relUnit][1][pl] = NULL;
60 }
61 }
62 }
63 for (int32_t i = 0; i < UDAT_STYLE_COUNT; ++i) {
64 fallBackCache[i] = -1;
65 }
66 }
67 virtual ~RelativeDateTimeCacheData();
68
69 // no numbers: e.g Next Tuesday; Yesterday; etc.
70 UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT];
71
72 // SimpleFormatter pointers for relative unit format,
73 // e.g., Next Tuesday; Yesterday; etc. For third index, 0
74 // means past, e.g., 5 days ago; 1 means future, e.g., in 5 days.
75 SimpleFormatter *relativeUnitsFormatters[UDAT_STYLE_COUNT]
76 [UDAT_RELATIVE_UNIT_COUNT][2][StandardPlural::COUNT];
77
78 const UnicodeString& getAbsoluteUnitString(int32_t fStyle,
79 UDateAbsoluteUnit unit,
80 UDateDirection direction) const;
81 const SimpleFormatter* getRelativeUnitFormatter(int32_t fStyle,
82 UDateRelativeUnit unit,
83 int32_t pastFutureIndex,
84 int32_t pluralUnit) const;
85
86 const UnicodeString emptyString;
87
88 // Mappping from source to target styles for alias fallback.
89 int32_t fallBackCache[UDAT_STYLE_COUNT];
90
91 void adoptCombinedDateAndTime(SimpleFormatter *fmtToAdopt) {
92 delete combinedDateAndTime;
93 combinedDateAndTime = fmtToAdopt;
94 }
95 const SimpleFormatter *getCombinedDateAndTime() const {
96 return combinedDateAndTime;
97 }
98
99 private:
100 SimpleFormatter *combinedDateAndTime;
101 RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other);
102 RelativeDateTimeCacheData& operator=(
103 const RelativeDateTimeCacheData &other);
104 };
105
106 RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
107 // clear out the cache arrays
108 for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
109 for (int32_t relUnit = 0; relUnit < UDAT_RELATIVE_UNIT_COUNT; ++relUnit) {
110 for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
111 delete relativeUnitsFormatters[style][relUnit][0][pl];
112 delete relativeUnitsFormatters[style][relUnit][1][pl];
113 }
114 }
115 }
116 delete combinedDateAndTime;
117 }
118
119
120 // Use fallback cache for absolute units.
121 const UnicodeString& RelativeDateTimeCacheData::getAbsoluteUnitString(
122 int32_t fStyle, UDateAbsoluteUnit unit, UDateDirection direction) const {
123 int32_t style = fStyle;
124 do {
125 if (!absoluteUnits[style][unit][direction].isEmpty()) {
126 return absoluteUnits[style][unit][direction];
127 }
128 style = fallBackCache[style];
129 } while (style != -1);
130 return emptyString;
131 }
132
133 // Use fallback cache for SimpleFormatter relativeUnits.
134 const SimpleFormatter* RelativeDateTimeCacheData::getRelativeUnitFormatter(
135 int32_t fStyle,
136 UDateRelativeUnit unit,
137 int32_t pastFutureIndex,
138 int32_t pluralUnit) const {
139 int32_t style = fStyle;
140 do {
141 if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != NULL) {
142 return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit];
143 }
144 style = fallBackCache[style];
145 } while (style != -1);
146 return NULL; // No formatter found.
147 }
148
149 static UBool getStringWithFallback(
150 const UResourceBundle *resource,
151 const char *key,
152 UnicodeString &result,
153 UErrorCode &status) {
154 int32_t len = 0;
155 const UChar *resStr = ures_getStringByKeyWithFallback(
156 resource, key, &len, &status);
157 if (U_FAILURE(status)) {
158 return FALSE;
159 }
160 result.setTo(TRUE, resStr, len);
161 return TRUE;
162 }
163
164
165 static UBool getStringByIndex(
166 const UResourceBundle *resource,
167 int32_t idx,
168 UnicodeString &result,
169 UErrorCode &status) {
170 int32_t len = 0;
171 const UChar *resStr = ures_getStringByIndex(
172 resource, idx, &len, &status);
173 if (U_FAILURE(status)) {
174 return FALSE;
175 }
176 result.setTo(TRUE, resStr, len);
177 return TRUE;
178 }
179
180 namespace {
181
182 /**
183 * Sink for enumerating all of the measurement unit display names.
184 *
185 * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
186 * Only store a value if it is still missing, that is, it has not been overridden.
187 */
188 struct RelDateTimeFmtDataSink : public ResourceSink {
189
190 /**
191 * Sink for patterns for relative dates and times. For example,
192 * fields/relative/...
193 */
194
195 // Generic unit enum for storing Unit info.
196 typedef enum RelAbsUnit {
197 INVALID_UNIT = -1,
198 SECOND,
199 MINUTE,
200 HOUR,
201 DAY,
202 WEEK,
203 MONTH,
204 QUARTER,
205 YEAR,
206 SUNDAY,
207 MONDAY,
208 TUESDAY,
209 WEDNESDAY,
210 THURSDAY,
211 FRIDAY,
212 SATURDAY
213 } RelAbsUnit;
214
215 static int32_t relUnitFromGeneric(RelAbsUnit genUnit) {
216 // Converts the generic units to UDAT_RELATIVE version.
217 switch (genUnit) {
218 case SECOND:
219 return UDAT_RELATIVE_SECONDS;
220 case MINUTE:
221 return UDAT_RELATIVE_MINUTES;
222 case HOUR:
223 return UDAT_RELATIVE_HOURS;
224 case DAY:
225 return UDAT_RELATIVE_DAYS;
226 case WEEK:
227 return UDAT_RELATIVE_WEEKS;
228 case MONTH:
229 return UDAT_RELATIVE_MONTHS;
230 /*
231 * case QUARTER:
232 * return UDATE_RELATIVE_QUARTERS;
233 */
234 case YEAR:
235 return UDAT_RELATIVE_YEARS;
236 default:
237 return -1;
238 }
239 }
240
241 static int32_t absUnitFromGeneric(RelAbsUnit genUnit) {
242 // Converts the generic units to UDAT_RELATIVE version.
243 switch (genUnit) {
244 case DAY:
245 return UDAT_ABSOLUTE_DAY;
246 case WEEK:
247 return UDAT_ABSOLUTE_WEEK;
248 case MONTH:
249 return UDAT_ABSOLUTE_MONTH;
250 /* TODO: Add in QUARTER
251 * case QUARTER:
252 * return UDAT_ABSOLUTE_QUARTER;
253 */
254 case YEAR:
255 return UDAT_ABSOLUTE_YEAR;
256 case SUNDAY:
257 return UDAT_ABSOLUTE_SUNDAY;
258 case MONDAY:
259 return UDAT_ABSOLUTE_MONDAY;
260 case TUESDAY:
261 return UDAT_ABSOLUTE_TUESDAY;
262 case WEDNESDAY:
263 return UDAT_ABSOLUTE_WEDNESDAY;
264 case THURSDAY:
265 return UDAT_ABSOLUTE_THURSDAY;
266 case FRIDAY:
267 return UDAT_ABSOLUTE_FRIDAY;
268 case SATURDAY:
269 return UDAT_ABSOLUTE_SATURDAY;
270 default:
271 return -1;
272 }
273 }
274
275 static int32_t keyToDirection(const char* key) {
276 if (uprv_strcmp(key, "-2") == 0) {
277 return UDAT_DIRECTION_LAST_2;
278 }
279 if (uprv_strcmp(key, "-1") == 0) {
280 return UDAT_DIRECTION_LAST;
281 }
282 if (uprv_strcmp(key, "0") == 0) {
283 return UDAT_DIRECTION_THIS;
284 }
285 if (uprv_strcmp(key, "1") == 0) {
286 return UDAT_DIRECTION_NEXT;
287 }
288 if (uprv_strcmp(key, "2") == 0) {
289 return UDAT_DIRECTION_NEXT_2;
290 }
291 return -1;
292 }
293
294 // Values kept between levels of parsing the CLDR data.
295 int32_t pastFutureIndex; // 0 == past or 1 == future
296 UDateRelativeDateTimeFormatterStyle style; // {LONG, SHORT, NARROW}
297 RelAbsUnit genericUnit;
298
299 RelativeDateTimeCacheData &outputData;
300
301 // Constructor
302 RelDateTimeFmtDataSink(RelativeDateTimeCacheData& cacheData)
303 : outputData(cacheData) {
304 // Clear cacheData.fallBackCache
305 cacheData.fallBackCache[UDAT_STYLE_LONG] = -1;
306 cacheData.fallBackCache[UDAT_STYLE_SHORT] = -1;
307 cacheData.fallBackCache[UDAT_STYLE_NARROW] = -1;
308 }
309
310 ~RelDateTimeFmtDataSink();
311
312 // Utility functions
313 static UDateRelativeDateTimeFormatterStyle styleFromString(const char *s) {
314 int32_t len = uprv_strlen(s);
315 if (len >= 7 && uprv_strcmp(s + len - 7, "-narrow") == 0) {
316 return UDAT_STYLE_NARROW;
317 }
318 if (len >= 6 && uprv_strcmp(s + len - 6, "-short") == 0) {
319 return UDAT_STYLE_SHORT;
320 }
321 return UDAT_STYLE_LONG;
322 }
323
324 static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style) {
325 switch (style) {
326 case UDAT_STYLE_NARROW:
327 return 7;
328 case UDAT_STYLE_SHORT:
329 return 6;
330 default:
331 return 0;
332 }
333 }
334
335 // Utility functions
336 static UDateRelativeDateTimeFormatterStyle styleFromAliasUnicodeString(UnicodeString s) {
337 static const UChar narrow[7] = {0x002D, 0x006E, 0x0061, 0x0072, 0x0072, 0x006F, 0x0077};
338 static const UChar sshort[6] = {0x002D, 0x0073, 0x0068, 0x006F, 0x0072, 0x0074,};
339 if (s.endsWith(narrow, 7)) {
340 return UDAT_STYLE_NARROW;
341 }
342 if (s.endsWith(sshort, 6)) {
343 return UDAT_STYLE_SHORT;
344 }
345 return UDAT_STYLE_LONG;
346 }
347
348 static RelAbsUnit unitOrNegativeFromString(const char* keyword, int32_t length) {
349 // Quick check from string to enum.
350 switch (length) {
351 case 3:
352 if (uprv_strncmp(keyword, "day", length) == 0) {
353 return DAY;
354 } else if (uprv_strncmp(keyword, "sun", length) == 0) {
355 return SUNDAY;
356 } else if (uprv_strncmp(keyword, "mon", length) == 0) {
357 return MONDAY;
358 } else if (uprv_strncmp(keyword, "tue", length) == 0) {
359 return TUESDAY;
360 } else if (uprv_strncmp(keyword, "wed", length) == 0) {
361 return WEDNESDAY;
362 } else if (uprv_strncmp(keyword, "thu", length) == 0) {
363 return THURSDAY;
364 } else if (uprv_strncmp(keyword, "fri", length) == 0) {
365 return FRIDAY;
366 } else if (uprv_strncmp(keyword, "sat", length) == 0) {
367 return SATURDAY;
368 }
369 break;
370 case 4:
371 if (uprv_strncmp(keyword, "hour", length) == 0) {
372 return HOUR;
373 } else if (uprv_strncmp(keyword, "week", length) == 0) {
374 return WEEK;
375 } else if (uprv_strncmp(keyword, "year", length) == 0) {
376 return YEAR;
377 }
378 break;
379 case 5:
380 if (uprv_strncmp(keyword, "month", length) == 0) {
381 return MONTH;
382 }
383 break;
384 case 6:
385 if (uprv_strncmp(keyword, "minute", length) == 0) {
386 return MINUTE;
387 } else if (uprv_strncmp(keyword, "second", length) == 0) {
388 return SECOND;
389 }
390 break;
391 case 7:
392 if (uprv_strncmp(keyword, "quarter", length) == 0) {
393 return QUARTER; // TODO: Check @provisional
394 }
395 break;
396 default:
397 break;
398 }
399 return INVALID_UNIT;
400 }
401
402 void handlePlainDirection(ResourceValue &value, UErrorCode &errorCode) {
403 // Handle Display Name for PLAIN direction for some units.
404 if (U_FAILURE(errorCode)) { return; }
405
406 int32_t absUnit = absUnitFromGeneric(genericUnit);
407 if (absUnit < 0) {
408 return; // Not interesting.
409 }
410
411 // Store displayname if not set.
412 if (outputData.absoluteUnits[style]
413 [absUnit][UDAT_DIRECTION_PLAIN].isEmpty()) {
414 outputData.absoluteUnits[style]
415 [absUnit][UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
416 return;
417 }
418 }
419
420 void consumeTableRelative(const char *key, ResourceValue &value, UErrorCode &errorCode) {
421 ResourceTable unitTypesTable = value.getTable(errorCode);
422 if (U_FAILURE(errorCode)) { return; }
423
424 for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
425 if (value.getType() == URES_STRING) {
426 int32_t direction = keyToDirection(key);
427 if (direction < 0) {
428 continue;
429 }
430
431 int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
432 if (relUnitIndex == UDAT_RELATIVE_SECONDS && uprv_strcmp(key, "0") == 0 &&
433 outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN].isEmpty()) {
434 // Handle "NOW"
435 outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW]
436 [UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
437 }
438
439 int32_t absUnitIndex = absUnitFromGeneric(genericUnit);
440 if (absUnitIndex < 0) {
441 continue;
442 }
443 // Only reset if slot is empty.
444 if (outputData.absoluteUnits[style][absUnitIndex][direction].isEmpty()) {
445 outputData.absoluteUnits[style][absUnitIndex]
446 [direction].fastCopyFrom(value.getUnicodeString(errorCode));
447 }
448 }
449 }
450 }
451
452 void consumeTimeDetail(int32_t relUnitIndex,
453 const char *key, ResourceValue &value, UErrorCode &errorCode) {
454 ResourceTable unitTypesTable = value.getTable(errorCode);
455 if (U_FAILURE(errorCode)) { return; }
456
457 for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
458 if (value.getType() == URES_STRING) {
459 int32_t pluralIndex = StandardPlural::indexOrNegativeFromString(key);
460 if (pluralIndex >= 0) {
461 SimpleFormatter **patterns =
462 outputData.relativeUnitsFormatters[style][relUnitIndex]
463 [pastFutureIndex];
464 // Only set if not already established.
465 if (patterns[pluralIndex] == NULL) {
466 patterns[pluralIndex] = new SimpleFormatter(
467 value.getUnicodeString(errorCode), 0, 1, errorCode);
468 if (patterns[pluralIndex] == NULL) {
469 errorCode = U_MEMORY_ALLOCATION_ERROR;
470 }
471 }
472 }
473 }
474 }
475 }
476
477 void consumeTableRelativeTime(const char *key, ResourceValue &value, UErrorCode &errorCode) {
478 ResourceTable relativeTimeTable = value.getTable(errorCode);
479 if (U_FAILURE(errorCode)) { return; }
480
481 int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
482 if (relUnitIndex < 0) {
483 return;
484 }
485 for (int32_t i = 0; relativeTimeTable.getKeyAndValue(i, key, value); ++i) {
486 if (uprv_strcmp(key, "past") == 0) {
487 pastFutureIndex = 0;
488 } else if (uprv_strcmp(key, "future") == 0) {
489 pastFutureIndex = 1;
490 } else {
491 // Unknown key.
492 continue;
493 }
494 consumeTimeDetail(relUnitIndex, key, value, errorCode);
495 }
496 }
497
498 void consumeAlias(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
499
500 UDateRelativeDateTimeFormatterStyle sourceStyle = styleFromString(key);
501 const UnicodeString valueStr = value.getAliasUnicodeString(errorCode);
502 if (U_FAILURE(errorCode)) { return; }
503
504 UDateRelativeDateTimeFormatterStyle targetStyle =
505 styleFromAliasUnicodeString(valueStr);
506
507 if (sourceStyle == targetStyle) {
508 errorCode = U_INVALID_FORMAT_ERROR;
509 return;
510 }
511 if (outputData.fallBackCache[sourceStyle] != -1 &&
512 outputData.fallBackCache[sourceStyle] != targetStyle) {
513 errorCode = U_INVALID_FORMAT_ERROR;
514 return;
515 }
516 outputData.fallBackCache[sourceStyle] = targetStyle;
517 }
518
519 void consumeTimeUnit(const char *key, ResourceValue &value, UErrorCode &errorCode) {
520 ResourceTable unitTypesTable = value.getTable(errorCode);
521 if (U_FAILURE(errorCode)) { return; }
522
523 for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
524 // Handle display name.
525 if (uprv_strcmp(key, "dn") == 0 && value.getType() == URES_STRING) {
526 handlePlainDirection(value, errorCode);
527 }
528 if (value.getType() == URES_TABLE) {
529 if (uprv_strcmp(key, "relative") == 0) {
530 consumeTableRelative(key, value, errorCode);
531 } else if (uprv_strcmp(key, "relativeTime") == 0) {
532 consumeTableRelativeTime(key, value, errorCode);
533 }
534 }
535 }
536 }
537
538 virtual void put(const char *key, ResourceValue &value,
539 UBool /*noFallback*/, UErrorCode &errorCode) {
540 // Main entry point to sink
541 ResourceTable table = value.getTable(errorCode);
542 if (U_FAILURE(errorCode)) { return; }
543 for (int32_t i = 0; table.getKeyAndValue(i, key, value); ++i) {
544 if (value.getType() == URES_ALIAS) {
545 consumeAlias(key, value, errorCode);
546 } else {
547 style = styleFromString(key);
548 int32_t unitSize = uprv_strlen(key) - styleSuffixLength(style);
549 genericUnit = unitOrNegativeFromString(key, unitSize);
550 if (style >= 0 && genericUnit != INVALID_UNIT) {
551 consumeTimeUnit(key, value, errorCode);
552 }
553 }
554 }
555 }
556
557 };
558
559 // Virtual destructors must be defined out of line.
560 RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
561 } // namespace
562
563 DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth[UDAT_STYLE_COUNT] = {
564 DateFormatSymbols::WIDE, DateFormatSymbols::SHORT, DateFormatSymbols::NARROW
565 };
566
567 // Get days of weeks from the DateFormatSymbols class.
568 static void loadWeekdayNames(UnicodeString absoluteUnits[UDAT_STYLE_COUNT]
569 [UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT],
570 const char* localeId,
571 UErrorCode& status) {
572 Locale locale(localeId);
573 DateFormatSymbols dfSym(locale, status);
574 for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
575 DateFormatSymbols::DtWidthType dtfmtWidth = styleToDateFormatSymbolWidth[style];
576 int32_t count;
577 const UnicodeString* weekdayNames =
578 dfSym.getWeekdays(count, DateFormatSymbols::STANDALONE, dtfmtWidth);
579 for (int32_t dayIndex = UDAT_ABSOLUTE_SUNDAY;
580 dayIndex <= UDAT_ABSOLUTE_SATURDAY; ++ dayIndex) {
581 int32_t dateSymbolIndex = (dayIndex - UDAT_ABSOLUTE_SUNDAY) + UCAL_SUNDAY;
582 absoluteUnits[style][dayIndex][UDAT_DIRECTION_PLAIN].fastCopyFrom(
583 weekdayNames[dateSymbolIndex]);
584 }
585 }
586 }
587
588 static UBool loadUnitData(
589 const UResourceBundle *resource,
590 RelativeDateTimeCacheData &cacheData,
591 const char* localeId,
592 UErrorCode &status) {
593
594 RelDateTimeFmtDataSink sink(cacheData);
595
596 ures_getAllItemsWithFallback(resource, "fields", sink, status);
597
598 // Get the weekday names from DateFormatSymbols.
599 loadWeekdayNames(cacheData.absoluteUnits, localeId, status);
600 return U_SUCCESS(status);
601 }
602
603 static UBool getDateTimePattern(
604 const UResourceBundle *resource,
605 UnicodeString &result,
606 UErrorCode &status) {
607 UnicodeString defaultCalendarName;
608 if (!getStringWithFallback(
609 resource,
610 "calendar/default",
611 defaultCalendarName,
612 status)) {
613 return FALSE;
614 }
615 CharString pathBuffer;
616 pathBuffer.append("calendar/", status)
617 .appendInvariantChars(defaultCalendarName, status)
618 .append("/DateTimePatterns", status);
619 LocalUResourceBundlePointer topLevel(
620 ures_getByKeyWithFallback(
621 resource, pathBuffer.data(), NULL, &status));
622 if (U_FAILURE(status)) {
623 return FALSE;
624 }
625 int32_t size = ures_getSize(topLevel.getAlias());
626 if (size <= 8) {
627 // Oops, size is too small to access the index that we want, fallback
628 // to a hard-coded value.
629 result = UNICODE_STRING_SIMPLE("{1} {0}");
630 return TRUE;
631 }
632 return getStringByIndex(topLevel.getAlias(), 8, result, status);
633 }
634
635 template<> U_I18N_API
636 const RelativeDateTimeCacheData *LocaleCacheKey<RelativeDateTimeCacheData>::createObject(const void * /*unused*/, UErrorCode &status) const {
637 const char *localeId = fLoc.getName();
638 LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status));
639 if (U_FAILURE(status)) {
640 return NULL;
641 }
642 LocalPointer<RelativeDateTimeCacheData> result(
643 new RelativeDateTimeCacheData());
644 if (result.isNull()) {
645 status = U_MEMORY_ALLOCATION_ERROR;
646 return NULL;
647 }
648 if (!loadUnitData(
649 topLevel.getAlias(),
650 *result,
651 localeId,
652 status)) {
653 return NULL;
654 }
655 UnicodeString dateTimePattern;
656 if (!getDateTimePattern(topLevel.getAlias(), dateTimePattern, status)) {
657 return NULL;
658 }
659 result->adoptCombinedDateAndTime(
660 new SimpleFormatter(dateTimePattern, 2, 2, status));
661 if (U_FAILURE(status)) {
662 return NULL;
663 }
664 result->addRef();
665 return result.orphan();
666 }
667
668 RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) :
669 fCache(NULL),
670 fNumberFormat(NULL),
671 fPluralRules(NULL),
672 fStyle(UDAT_STYLE_LONG),
673 fContext(UDISPCTX_CAPITALIZATION_NONE),
674 fOptBreakIterator(NULL) {
675 init(NULL, NULL, status);
676 }
677
678 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
679 const Locale& locale, UErrorCode& status) :
680 fCache(NULL),
681 fNumberFormat(NULL),
682 fPluralRules(NULL),
683 fStyle(UDAT_STYLE_LONG),
684 fContext(UDISPCTX_CAPITALIZATION_NONE),
685 fOptBreakIterator(NULL),
686 fLocale(locale) {
687 init(NULL, NULL, status);
688 }
689
690 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
691 const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) :
692 fCache(NULL),
693 fNumberFormat(NULL),
694 fPluralRules(NULL),
695 fStyle(UDAT_STYLE_LONG),
696 fContext(UDISPCTX_CAPITALIZATION_NONE),
697 fOptBreakIterator(NULL),
698 fLocale(locale) {
699 init(nfToAdopt, NULL, status);
700 }
701
702 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
703 const Locale& locale,
704 NumberFormat *nfToAdopt,
705 UDateRelativeDateTimeFormatterStyle styl,
706 UDisplayContext capitalizationContext,
707 UErrorCode& status) :
708 fCache(NULL),
709 fNumberFormat(NULL),
710 fPluralRules(NULL),
711 fStyle(styl),
712 fContext(capitalizationContext),
713 fOptBreakIterator(NULL),
714 fLocale(locale) {
715 if (U_FAILURE(status)) {
716 return;
717 }
718 if ((capitalizationContext >> 8) != UDISPCTX_TYPE_CAPITALIZATION) {
719 status = U_ILLEGAL_ARGUMENT_ERROR;
720 return;
721 }
722 if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) {
723 BreakIterator *bi = BreakIterator::createSentenceInstance(locale, status);
724 if (U_FAILURE(status)) {
725 return;
726 }
727 init(nfToAdopt, bi, status);
728 } else {
729 init(nfToAdopt, NULL, status);
730 }
731 }
732
733 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
734 const RelativeDateTimeFormatter& other)
735 : UObject(other),
736 fCache(other.fCache),
737 fNumberFormat(other.fNumberFormat),
738 fPluralRules(other.fPluralRules),
739 fStyle(other.fStyle),
740 fContext(other.fContext),
741 fOptBreakIterator(other.fOptBreakIterator),
742 fLocale(other.fLocale) {
743 fCache->addRef();
744 fNumberFormat->addRef();
745 fPluralRules->addRef();
746 if (fOptBreakIterator != NULL) {
747 fOptBreakIterator->addRef();
748 }
749 }
750
751 RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=(
752 const RelativeDateTimeFormatter& other) {
753 if (this != &other) {
754 SharedObject::copyPtr(other.fCache, fCache);
755 SharedObject::copyPtr(other.fNumberFormat, fNumberFormat);
756 SharedObject::copyPtr(other.fPluralRules, fPluralRules);
757 SharedObject::copyPtr(other.fOptBreakIterator, fOptBreakIterator);
758 fStyle = other.fStyle;
759 fContext = other.fContext;
760 fLocale = other.fLocale;
761 }
762 return *this;
763 }
764
765 RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
766 if (fCache != NULL) {
767 fCache->removeRef();
768 }
769 if (fNumberFormat != NULL) {
770 fNumberFormat->removeRef();
771 }
772 if (fPluralRules != NULL) {
773 fPluralRules->removeRef();
774 }
775 if (fOptBreakIterator != NULL) {
776 fOptBreakIterator->removeRef();
777 }
778 }
779
780 const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const {
781 return **fNumberFormat;
782 }
783
784 UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const {
785 return fContext;
786 }
787
788 UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const {
789 return fStyle;
790 }
791
792 UnicodeString& RelativeDateTimeFormatter::format(
793 double quantity, UDateDirection direction, UDateRelativeUnit unit,
794 UnicodeString& appendTo, UErrorCode& status) const {
795 if (U_FAILURE(status)) {
796 return appendTo;
797 }
798 if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
799 status = U_ILLEGAL_ARGUMENT_ERROR;
800 return appendTo;
801 }
802 int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
803 FieldPosition pos(FieldPosition::DONT_CARE);
804
805 UnicodeString result;
806 UnicodeString formattedNumber;
807
808 StandardPlural::Form pluralIndex = QuantityFormatter::selectPlural(
809 quantity, **fNumberFormat, **fPluralRules, formattedNumber, pos,
810 status);
811
812 const SimpleFormatter* formatter =
813 fCache->getRelativeUnitFormatter(fStyle, unit, bFuture, pluralIndex);
814 if (formatter == NULL) {
815 // TODO: WARN - look at quantity formatter's action with an error.
816 status = U_INVALID_FORMAT_ERROR;
817 return appendTo;
818 }
819 formatter->format(formattedNumber, result, status);
820 adjustForContext(result);
821 return appendTo.append(result);
822 }
823
824 UnicodeString& RelativeDateTimeFormatter::formatNumeric(
825 double offset, URelativeDateTimeUnit unit,
826 UnicodeString& appendTo, UErrorCode& status) const {
827 if (U_FAILURE(status)) {
828 return appendTo;
829 }
830 // TODO:
831 // The full implementation of this depends on CLDR data that is not yet available,
832 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
833 // In the meantime do a quick bring-up by calling the old format method; this
834 // leaves some holes (even for data that is currently available, such as quarter).
835 // When the new CLDR data is available, update the data storage accordingly,
836 // rewrite this to use it directly, and rewrite the old format method to call this
837 // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
838 UDateRelativeUnit relunit = UDAT_RELATIVE_UNIT_COUNT;
839 switch (unit) {
840 case UDAT_REL_UNIT_YEAR: relunit = UDAT_RELATIVE_YEARS; break;
841 case UDAT_REL_UNIT_MONTH: relunit = UDAT_RELATIVE_MONTHS; break;
842 case UDAT_REL_UNIT_WEEK: relunit = UDAT_RELATIVE_WEEKS; break;
843 case UDAT_REL_UNIT_DAY: relunit = UDAT_RELATIVE_DAYS; break;
844 case UDAT_REL_UNIT_HOUR: relunit = UDAT_RELATIVE_HOURS; break;
845 case UDAT_REL_UNIT_MINUTE: relunit = UDAT_RELATIVE_MINUTES; break;
846 case UDAT_REL_UNIT_SECOND: relunit = UDAT_RELATIVE_SECONDS; break;
847 default: // a unit that the above method does not handle
848 status = U_UNSUPPORTED_ERROR;
849 return appendTo;
850 }
851 UDateDirection direction = UDAT_DIRECTION_NEXT;
852 if (offset < 0) {
853 direction = UDAT_DIRECTION_LAST;
854 offset = -offset;
855 }
856 return format(offset, direction, relunit, appendTo, status);
857 }
858
859 UnicodeString& RelativeDateTimeFormatter::format(
860 UDateDirection direction, UDateAbsoluteUnit unit,
861 UnicodeString& appendTo, UErrorCode& status) const {
862 if (U_FAILURE(status)) {
863 return appendTo;
864 }
865 if (unit == UDAT_ABSOLUTE_NOW && direction != UDAT_DIRECTION_PLAIN) {
866 status = U_ILLEGAL_ARGUMENT_ERROR;
867 return appendTo;
868 }
869
870 // Get string using fallback.
871 UnicodeString result;
872 result.fastCopyFrom(fCache->getAbsoluteUnitString(fStyle, unit, direction));
873 if (fOptBreakIterator != NULL) {
874 adjustForContext(result);
875 }
876 return appendTo.append(result);
877 }
878
879 UnicodeString& RelativeDateTimeFormatter::format(
880 double offset, URelativeDateTimeUnit unit,
881 UnicodeString& appendTo, UErrorCode& status) const {
882 if (U_FAILURE(status)) {
883 return appendTo;
884 }
885 // TODO:
886 // The full implementation of this depends on CLDR data that is not yet available,
887 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
888 // In the meantime do a quick bring-up by calling the old format method; this
889 // leaves some holes (even for data that is currently available, such as quarter).
890 // When the new CLDR data is available, update the data storage accordingly,
891 // rewrite this to use it directly, and rewrite the old format method to call this
892 // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
893 UDateDirection direction = UDAT_DIRECTION_COUNT;
894 if (offset > -2.1 && offset < 2.1) {
895 // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST
896 double offsetx100 = offset * 100.0;
897 int32_t intoffset = (offsetx100 < 0)? (int32_t)(offsetx100-0.5) : (int32_t)(offsetx100+0.5);
898 switch (intoffset) {
899 case -200/*-2*/: direction = UDAT_DIRECTION_LAST_2; break;
900 case -100/*-1*/: direction = UDAT_DIRECTION_LAST; break;
901 case 0/* 0*/: direction = UDAT_DIRECTION_THIS; break;
902 case 100/* 1*/: direction = UDAT_DIRECTION_NEXT; break;
903 case 200/* 2*/: direction = UDAT_DIRECTION_NEXT_2; break;
904 default: break;
905 }
906 }
907 UDateAbsoluteUnit absunit = UDAT_ABSOLUTE_UNIT_COUNT;
908 switch (unit) {
909 case UDAT_REL_UNIT_YEAR: absunit = UDAT_ABSOLUTE_YEAR; break;
910 case UDAT_REL_UNIT_MONTH: absunit = UDAT_ABSOLUTE_MONTH; break;
911 case UDAT_REL_UNIT_WEEK: absunit = UDAT_ABSOLUTE_WEEK; break;
912 case UDAT_REL_UNIT_DAY: absunit = UDAT_ABSOLUTE_DAY; break;
913 case UDAT_REL_UNIT_SECOND:
914 if (direction == UDAT_DIRECTION_THIS) {
915 absunit = UDAT_ABSOLUTE_NOW;
916 direction = UDAT_DIRECTION_PLAIN;
917 }
918 break;
919 case UDAT_REL_UNIT_SUNDAY: absunit = UDAT_ABSOLUTE_SUNDAY; break;
920 case UDAT_REL_UNIT_MONDAY: absunit = UDAT_ABSOLUTE_MONDAY; break;
921 case UDAT_REL_UNIT_TUESDAY: absunit = UDAT_ABSOLUTE_TUESDAY; break;
922 case UDAT_REL_UNIT_WEDNESDAY: absunit = UDAT_ABSOLUTE_WEDNESDAY; break;
923 case UDAT_REL_UNIT_THURSDAY: absunit = UDAT_ABSOLUTE_THURSDAY; break;
924 case UDAT_REL_UNIT_FRIDAY: absunit = UDAT_ABSOLUTE_FRIDAY; break;
925 case UDAT_REL_UNIT_SATURDAY: absunit = UDAT_ABSOLUTE_SATURDAY; break;
926 default: break;
927 }
928 if (direction != UDAT_DIRECTION_COUNT && absunit != UDAT_ABSOLUTE_UNIT_COUNT) {
929 const UnicodeString &unitFormatString =
930 fCache->getAbsoluteUnitString(fStyle, absunit, direction);
931 if (!unitFormatString.isEmpty()) {
932 if (fOptBreakIterator != NULL) {
933 UnicodeString result(unitFormatString);
934 adjustForContext(result);
935 return appendTo.append(result);
936 } else {
937 return appendTo.append(unitFormatString);
938 }
939 }
940 }
941 // otherwise fallback to formatNumeric
942 return formatNumeric(offset, unit, appendTo, status);
943 }
944
945 UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
946 const UnicodeString& relativeDateString, const UnicodeString& timeString,
947 UnicodeString& appendTo, UErrorCode& status) const {
948 return fCache->getCombinedDateAndTime()->format(
949 timeString, relativeDateString, appendTo, status);
950 }
951
952 void RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const {
953 if (fOptBreakIterator == NULL
954 || str.length() == 0 || !u_islower(str.char32At(0))) {
955 return;
956 }
957
958 // Must guarantee that one thread at a time accesses the shared break
959 // iterator.
960 Mutex lock(&gBrkIterMutex);
961 str.toTitle(
962 fOptBreakIterator->get(),
963 fLocale,
964 U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
965 }
966
967 void RelativeDateTimeFormatter::init(
968 NumberFormat *nfToAdopt,
969 BreakIterator *biToAdopt,
970 UErrorCode &status) {
971 LocalPointer<NumberFormat> nf(nfToAdopt);
972 LocalPointer<BreakIterator> bi(biToAdopt);
973 UnifiedCache::getByLocale(fLocale, fCache, status);
974 if (U_FAILURE(status)) {
975 return;
976 }
977 const SharedPluralRules *pr = PluralRules::createSharedInstance(
978 fLocale, UPLURAL_TYPE_CARDINAL, status);
979 if (U_FAILURE(status)) {
980 return;
981 }
982 SharedObject::copyPtr(pr, fPluralRules);
983 pr->removeRef();
984 if (nf.isNull()) {
985 const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
986 fLocale, UNUM_DECIMAL, status);
987 if (U_FAILURE(status)) {
988 return;
989 }
990 SharedObject::copyPtr(shared, fNumberFormat);
991 shared->removeRef();
992 } else {
993 SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
994 if (shared == NULL) {
995 status = U_MEMORY_ALLOCATION_ERROR;
996 return;
997 }
998 nf.orphan();
999 SharedObject::copyPtr(shared, fNumberFormat);
1000 }
1001 if (bi.isNull()) {
1002 SharedObject::clearPtr(fOptBreakIterator);
1003 } else {
1004 SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias());
1005 if (shared == NULL) {
1006 status = U_MEMORY_ALLOCATION_ERROR;
1007 return;
1008 }
1009 bi.orphan();
1010 SharedObject::copyPtr(shared, fOptBreakIterator);
1011 }
1012 }
1013
1014 U_NAMESPACE_END
1015
1016 // Plain C API
1017
1018 U_NAMESPACE_USE
1019
1020 U_CAPI URelativeDateTimeFormatter* U_EXPORT2
1021 ureldatefmt_open( const char* locale,
1022 UNumberFormat* nfToAdopt,
1023 UDateRelativeDateTimeFormatterStyle width,
1024 UDisplayContext capitalizationContext,
1025 UErrorCode* status )
1026 {
1027 if (U_FAILURE(*status)) {
1028 return NULL;
1029 }
1030 LocalPointer<RelativeDateTimeFormatter> formatter(new RelativeDateTimeFormatter(Locale(locale),
1031 (NumberFormat*)nfToAdopt, width,
1032 capitalizationContext, *status), *status);
1033 if (U_FAILURE(*status)) {
1034 return NULL;
1035 }
1036 return (URelativeDateTimeFormatter*)formatter.orphan();
1037 }
1038
1039 U_CAPI void U_EXPORT2
1040 ureldatefmt_close(URelativeDateTimeFormatter *reldatefmt)
1041 {
1042 delete (RelativeDateTimeFormatter*)reldatefmt;
1043 }
1044
1045 U_CAPI int32_t U_EXPORT2
1046 ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt,
1047 double offset,
1048 URelativeDateTimeUnit unit,
1049 UChar* result,
1050 int32_t resultCapacity,
1051 UErrorCode* status)
1052 {
1053 if (U_FAILURE(*status)) {
1054 return 0;
1055 }
1056 if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) {
1057 *status = U_ILLEGAL_ARGUMENT_ERROR;
1058 return 0;
1059 }
1060 UnicodeString res;
1061 if (result != NULL) {
1062 // NULL destination for pure preflighting: empty dummy string
1063 // otherwise, alias the destination buffer (copied from udat_format)
1064 res.setTo(result, 0, resultCapacity);
1065 }
1066 ((RelativeDateTimeFormatter*)reldatefmt)->formatNumeric(offset, unit, res, *status);
1067 if (U_FAILURE(*status)) {
1068 return 0;
1069 }
1070 return res.extract(result, resultCapacity, *status);
1071 }
1072
1073 U_CAPI int32_t U_EXPORT2
1074 ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt,
1075 double offset,
1076 URelativeDateTimeUnit unit,
1077 UChar* result,
1078 int32_t resultCapacity,
1079 UErrorCode* status)
1080 {
1081 if (U_FAILURE(*status)) {
1082 return 0;
1083 }
1084 if (result == NULL ? resultCapacity != 0 : resultCapacity < 0) {
1085 *status = U_ILLEGAL_ARGUMENT_ERROR;
1086 return 0;
1087 }
1088 UnicodeString res;
1089 if (result != NULL) {
1090 // NULL destination for pure preflighting: empty dummy string
1091 // otherwise, alias the destination buffer (copied from udat_format)
1092 res.setTo(result, 0, resultCapacity);
1093 }
1094 ((RelativeDateTimeFormatter*)reldatefmt)->format(offset, unit, res, *status);
1095 if (U_FAILURE(*status)) {
1096 return 0;
1097 }
1098 return res.extract(result, resultCapacity, *status);
1099 }
1100
1101 U_CAPI int32_t U_EXPORT2
1102 ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter* reldatefmt,
1103 const UChar * relativeDateString,
1104 int32_t relativeDateStringLen,
1105 const UChar * timeString,
1106 int32_t timeStringLen,
1107 UChar* result,
1108 int32_t resultCapacity,
1109 UErrorCode* status )
1110 {
1111 if (U_FAILURE(*status)) {
1112 return 0;
1113 }
1114 if (result == NULL ? resultCapacity != 0 : resultCapacity < 0 ||
1115 (relativeDateString == NULL ? relativeDateStringLen != 0 : relativeDateStringLen < -1) ||
1116 (timeString == NULL ? timeStringLen != 0 : timeStringLen < -1)) {
1117 *status = U_ILLEGAL_ARGUMENT_ERROR;
1118 return 0;
1119 }
1120 UnicodeString relDateStr((UBool)(relativeDateStringLen == -1), relativeDateString, relativeDateStringLen);
1121 UnicodeString timeStr((UBool)(timeStringLen == -1), timeString, timeStringLen);
1122 UnicodeString res(result, 0, resultCapacity);
1123 ((RelativeDateTimeFormatter*)reldatefmt)->combineDateAndTime(relDateStr, timeStr, res, *status);
1124 if (U_FAILURE(*status)) {
1125 return 0;
1126 }
1127 return res.extract(result, resultCapacity, *status);
1128 }
1129
1130 #endif /* !UCONFIG_NO_FORMATTING */