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
16
17 #include <cmath>
18 #include <functional>
19 #include "unicode/calendar.h"
20 #include "unicode/datefmt.h"
21 #include "unicode/dtfmtsym.h"
22 #include "unicode/ucasemap.h"
23 #include "unicode/ureldatefmt.h"
24 #include "unicode/udisplaycontext.h"
25 #include "unicode/unum.h"
26 #include "unicode/localpointer.h"
27 #include "unicode/plurrule.h"
28 #include "unicode/simpleformatter.h"
29 #include "unicode/decimfmt.h"
30 #include "unicode/numfmt.h"
31 #include "unicode/brkiter.h"
32 #include "unicode/simpleformatter.h"
33 #include "uresimp.h"
34 #include "unicode/ures.h"
35 #include "cstring.h"
36 #include "ucln_in.h"
37 #include "mutex.h"
38 #include "charstr.h"
39 #include "uassert.h"
40 #include "quantityformatter.h"
41 #include "resource.h"
42 #include "sharedbreakiterator.h"
43 #include "sharedpluralrules.h"
44 #include "sharednumberformat.h"
45 #include "standardplural.h"
46 #include "unifiedcache.h"
47 #include "util.h"
48 #include "formatted_string_builder.h"
49 #include "number_utypes.h"
50 #include "number_modifiers.h"
51 #include "formattedval_impl.h"
52 #include "number_utils.h"
53
54 // Copied from uscript_props.cpp
55
56 U_NAMESPACE_BEGIN
57
58 // RelativeDateTimeFormatter specific data for a single locale
59 class RelativeDateTimeCacheData: public SharedObject {
60 public:
RelativeDateTimeCacheData()61 RelativeDateTimeCacheData() : combinedDateAndTime(nullptr) {
62 // Initialize the cache arrays
63 for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
64 for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) {
65 for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
66 relativeUnitsFormatters[style][relUnit][0][pl] = nullptr;
67 relativeUnitsFormatters[style][relUnit][1][pl] = nullptr;
68 }
69 }
70 }
71 for (int32_t i = 0; i < UDAT_STYLE_COUNT; ++i) {
72 fallBackCache[i] = -1;
73 }
74 }
75 virtual ~RelativeDateTimeCacheData();
76
77 // no numbers: e.g Next Tuesday; Yesterday; etc.
78 UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT];
79
80 // SimpleFormatter pointers for relative unit format,
81 // e.g., Next Tuesday; Yesterday; etc. For third index, 0
82 // means past, e.g., 5 days ago; 1 means future, e.g., in 5 days.
83 SimpleFormatter *relativeUnitsFormatters[UDAT_STYLE_COUNT]
84 [UDAT_REL_UNIT_COUNT][2][StandardPlural::COUNT];
85
86 const UnicodeString& getAbsoluteUnitString(int32_t fStyle,
87 UDateAbsoluteUnit unit,
88 UDateDirection direction) const;
89 const SimpleFormatter* getRelativeUnitFormatter(int32_t fStyle,
90 UDateRelativeUnit unit,
91 int32_t pastFutureIndex,
92 int32_t pluralUnit) const;
93 const SimpleFormatter* getRelativeDateTimeUnitFormatter(int32_t fStyle,
94 URelativeDateTimeUnit unit,
95 int32_t pastFutureIndex,
96 int32_t pluralUnit) const;
97
98 const UnicodeString emptyString;
99
100 // Mapping from source to target styles for alias fallback.
101 int32_t fallBackCache[UDAT_STYLE_COUNT];
102
adoptCombinedDateAndTime(SimpleFormatter * fmtToAdopt)103 void adoptCombinedDateAndTime(SimpleFormatter *fmtToAdopt) {
104 delete combinedDateAndTime;
105 combinedDateAndTime = fmtToAdopt;
106 }
getCombinedDateAndTime() const107 const SimpleFormatter *getCombinedDateAndTime() const {
108 return combinedDateAndTime;
109 }
110
111 private:
112 SimpleFormatter *combinedDateAndTime;
113 RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other);
114 RelativeDateTimeCacheData& operator=(
115 const RelativeDateTimeCacheData &other);
116 };
117
~RelativeDateTimeCacheData()118 RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
119 // clear out the cache arrays
120 for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
121 for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) {
122 for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
123 delete relativeUnitsFormatters[style][relUnit][0][pl];
124 delete relativeUnitsFormatters[style][relUnit][1][pl];
125 }
126 }
127 }
128 delete combinedDateAndTime;
129 }
130
131
132 // Use fallback cache for absolute units.
getAbsoluteUnitString(int32_t fStyle,UDateAbsoluteUnit unit,UDateDirection direction) const133 const UnicodeString& RelativeDateTimeCacheData::getAbsoluteUnitString(
134 int32_t fStyle, UDateAbsoluteUnit unit, UDateDirection direction) const {
135 int32_t style = fStyle;
136 do {
137 if (!absoluteUnits[style][unit][direction].isEmpty()) {
138 return absoluteUnits[style][unit][direction];
139 }
140 style = fallBackCache[style];
141 } while (style != -1);
142 return emptyString;
143 }
144
getRelativeUnitFormatter(int32_t fStyle,UDateRelativeUnit unit,int32_t pastFutureIndex,int32_t pluralUnit) const145 const SimpleFormatter* RelativeDateTimeCacheData::getRelativeUnitFormatter(
146 int32_t fStyle,
147 UDateRelativeUnit unit,
148 int32_t pastFutureIndex,
149 int32_t pluralUnit) const {
150 URelativeDateTimeUnit rdtunit = UDAT_REL_UNIT_COUNT;
151 switch (unit) {
152 case UDAT_RELATIVE_YEARS: rdtunit = UDAT_REL_UNIT_YEAR; break;
153 case UDAT_RELATIVE_MONTHS: rdtunit = UDAT_REL_UNIT_MONTH; break;
154 case UDAT_RELATIVE_WEEKS: rdtunit = UDAT_REL_UNIT_WEEK; break;
155 case UDAT_RELATIVE_DAYS: rdtunit = UDAT_REL_UNIT_DAY; break;
156 case UDAT_RELATIVE_HOURS: rdtunit = UDAT_REL_UNIT_HOUR; break;
157 case UDAT_RELATIVE_MINUTES: rdtunit = UDAT_REL_UNIT_MINUTE; break;
158 case UDAT_RELATIVE_SECONDS: rdtunit = UDAT_REL_UNIT_SECOND; break;
159 default: // a unit that the above method does not handle
160 return nullptr;
161 }
162
163 return getRelativeDateTimeUnitFormatter(fStyle, rdtunit, pastFutureIndex, pluralUnit);
164 }
165
166 // Use fallback cache for SimpleFormatter relativeUnits.
getRelativeDateTimeUnitFormatter(int32_t fStyle,URelativeDateTimeUnit unit,int32_t pastFutureIndex,int32_t pluralUnit) const167 const SimpleFormatter* RelativeDateTimeCacheData::getRelativeDateTimeUnitFormatter(
168 int32_t fStyle,
169 URelativeDateTimeUnit unit,
170 int32_t pastFutureIndex,
171 int32_t pluralUnit) const {
172 while (true) {
173 int32_t style = fStyle;
174 do {
175 if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != nullptr) {
176 return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit];
177 }
178 style = fallBackCache[style];
179 } while (style != -1);
180
181 if (pluralUnit == StandardPlural::OTHER) {
182 break;
183 }
184 pluralUnit = StandardPlural::OTHER;
185 }
186 return nullptr; // No formatter found.
187 }
188
getStringByIndex(const UResourceBundle * resource,int32_t idx,UnicodeString & result,UErrorCode & status)189 static UBool getStringByIndex(
190 const UResourceBundle *resource,
191 int32_t idx,
192 UnicodeString &result,
193 UErrorCode &status) {
194 int32_t len = 0;
195 const char16_t *resStr = ures_getStringByIndex(
196 resource, idx, &len, &status);
197 if (U_FAILURE(status)) {
198 return false;
199 }
200 result.setTo(true, resStr, len);
201 return true;
202 }
203
204 namespace {
205
206 /**
207 * Sink for enumerating all of the measurement unit display names.
208 *
209 * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
210 * Only store a value if it is still missing, that is, it has not been overridden.
211 */
212 struct RelDateTimeFmtDataSink : public ResourceSink {
213
214 /**
215 * Sink for patterns for relative dates and times. For example,
216 * fields/relative/...
217 */
218
219 // Generic unit enum for storing Unit info.
220 typedef enum RelAbsUnit {
221 INVALID_UNIT = -1,
222 SECOND,
223 MINUTE,
224 HOUR,
225 DAY,
226 WEEK,
227 MONTH,
228 QUARTER,
229 YEAR,
230 SUNDAY,
231 MONDAY,
232 TUESDAY,
233 WEDNESDAY,
234 THURSDAY,
235 FRIDAY,
236 SATURDAY
237 } RelAbsUnit;
238
relUnitFromGeneric__anonf6b318b20111::RelDateTimeFmtDataSink239 static int32_t relUnitFromGeneric(RelAbsUnit genUnit) {
240 // Converts the generic units to UDAT_RELATIVE version.
241 switch (genUnit) {
242 case SECOND:
243 return UDAT_REL_UNIT_SECOND;
244 case MINUTE:
245 return UDAT_REL_UNIT_MINUTE;
246 case HOUR:
247 return UDAT_REL_UNIT_HOUR;
248 case DAY:
249 return UDAT_REL_UNIT_DAY;
250 case WEEK:
251 return UDAT_REL_UNIT_WEEK;
252 case MONTH:
253 return UDAT_REL_UNIT_MONTH;
254 case QUARTER:
255 return UDAT_REL_UNIT_QUARTER;
256 case YEAR:
257 return UDAT_REL_UNIT_YEAR;
258 case SUNDAY:
259 return UDAT_REL_UNIT_SUNDAY;
260 case MONDAY:
261 return UDAT_REL_UNIT_MONDAY;
262 case TUESDAY:
263 return UDAT_REL_UNIT_TUESDAY;
264 case WEDNESDAY:
265 return UDAT_REL_UNIT_WEDNESDAY;
266 case THURSDAY:
267 return UDAT_REL_UNIT_THURSDAY;
268 case FRIDAY:
269 return UDAT_REL_UNIT_FRIDAY;
270 case SATURDAY:
271 return UDAT_REL_UNIT_SATURDAY;
272 default:
273 return -1;
274 }
275 }
276
absUnitFromGeneric__anonf6b318b20111::RelDateTimeFmtDataSink277 static int32_t absUnitFromGeneric(RelAbsUnit genUnit) {
278 // Converts the generic units to UDAT_RELATIVE version.
279 switch (genUnit) {
280 case DAY:
281 return UDAT_ABSOLUTE_DAY;
282 case WEEK:
283 return UDAT_ABSOLUTE_WEEK;
284 case MONTH:
285 return UDAT_ABSOLUTE_MONTH;
286 case QUARTER:
287 return UDAT_ABSOLUTE_QUARTER;
288 case YEAR:
289 return UDAT_ABSOLUTE_YEAR;
290 case SUNDAY:
291 return UDAT_ABSOLUTE_SUNDAY;
292 case MONDAY:
293 return UDAT_ABSOLUTE_MONDAY;
294 case TUESDAY:
295 return UDAT_ABSOLUTE_TUESDAY;
296 case WEDNESDAY:
297 return UDAT_ABSOLUTE_WEDNESDAY;
298 case THURSDAY:
299 return UDAT_ABSOLUTE_THURSDAY;
300 case FRIDAY:
301 return UDAT_ABSOLUTE_FRIDAY;
302 case SATURDAY:
303 return UDAT_ABSOLUTE_SATURDAY;
304 case HOUR:
305 return UDAT_ABSOLUTE_HOUR;
306 case MINUTE:
307 return UDAT_ABSOLUTE_MINUTE;
308 default:
309 return -1;
310 }
311 }
312
keyToDirection__anonf6b318b20111::RelDateTimeFmtDataSink313 static int32_t keyToDirection(const char* key) {
314 if (uprv_strcmp(key, "-2") == 0) {
315 return UDAT_DIRECTION_LAST_2;
316 }
317 if (uprv_strcmp(key, "-1") == 0) {
318 return UDAT_DIRECTION_LAST;
319 }
320 if (uprv_strcmp(key, "0") == 0) {
321 return UDAT_DIRECTION_THIS;
322 }
323 if (uprv_strcmp(key, "1") == 0) {
324 return UDAT_DIRECTION_NEXT;
325 }
326 if (uprv_strcmp(key, "2") == 0) {
327 return UDAT_DIRECTION_NEXT_2;
328 }
329 return -1;
330 }
331
332 // Values kept between levels of parsing the CLDR data.
333 int32_t pastFutureIndex; // 0 == past or 1 == future
334 UDateRelativeDateTimeFormatterStyle style; // {LONG, SHORT, NARROW}
335 RelAbsUnit genericUnit;
336
337 RelativeDateTimeCacheData &outputData;
338
339 // Constructor
RelDateTimeFmtDataSink__anonf6b318b20111::RelDateTimeFmtDataSink340 RelDateTimeFmtDataSink(RelativeDateTimeCacheData& cacheData)
341 : outputData(cacheData) {
342 // Clear cacheData.fallBackCache
343 cacheData.fallBackCache[UDAT_STYLE_LONG] = -1;
344 cacheData.fallBackCache[UDAT_STYLE_SHORT] = -1;
345 cacheData.fallBackCache[UDAT_STYLE_NARROW] = -1;
346 }
347
348 ~RelDateTimeFmtDataSink();
349
350 // Utility functions
styleFromString__anonf6b318b20111::RelDateTimeFmtDataSink351 static UDateRelativeDateTimeFormatterStyle styleFromString(const char *s) {
352 int32_t len = static_cast<int32_t>(uprv_strlen(s));
353 if (len >= 7 && uprv_strcmp(s + len - 7, "-narrow") == 0) {
354 return UDAT_STYLE_NARROW;
355 }
356 if (len >= 6 && uprv_strcmp(s + len - 6, "-short") == 0) {
357 return UDAT_STYLE_SHORT;
358 }
359 return UDAT_STYLE_LONG;
360 }
361
styleSuffixLength__anonf6b318b20111::RelDateTimeFmtDataSink362 static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style) {
363 switch (style) {
364 case UDAT_STYLE_NARROW:
365 return 7;
366 case UDAT_STYLE_SHORT:
367 return 6;
368 default:
369 return 0;
370 }
371 }
372
373 // Utility functions
styleFromAliasUnicodeString__anonf6b318b20111::RelDateTimeFmtDataSink374 static UDateRelativeDateTimeFormatterStyle styleFromAliasUnicodeString(UnicodeString s) {
375 static const char16_t narrow[7] = {0x002D, 0x006E, 0x0061, 0x0072, 0x0072, 0x006F, 0x0077};
376 static const char16_t sshort[6] = {0x002D, 0x0073, 0x0068, 0x006F, 0x0072, 0x0074,};
377 if (s.endsWith(narrow, 7)) {
378 return UDAT_STYLE_NARROW;
379 }
380 if (s.endsWith(sshort, 6)) {
381 return UDAT_STYLE_SHORT;
382 }
383 return UDAT_STYLE_LONG;
384 }
385
unitOrNegativeFromString__anonf6b318b20111::RelDateTimeFmtDataSink386 static RelAbsUnit unitOrNegativeFromString(const char* keyword, int32_t length) {
387 // Quick check from string to enum.
388 switch (length) {
389 case 3:
390 if (uprv_strncmp(keyword, "day", length) == 0) {
391 return DAY;
392 } else if (uprv_strncmp(keyword, "sun", length) == 0) {
393 return SUNDAY;
394 } else if (uprv_strncmp(keyword, "mon", length) == 0) {
395 return MONDAY;
396 } else if (uprv_strncmp(keyword, "tue", length) == 0) {
397 return TUESDAY;
398 } else if (uprv_strncmp(keyword, "wed", length) == 0) {
399 return WEDNESDAY;
400 } else if (uprv_strncmp(keyword, "thu", length) == 0) {
401 return THURSDAY;
402 } else if (uprv_strncmp(keyword, "fri", length) == 0) {
403 return FRIDAY;
404 } else if (uprv_strncmp(keyword, "sat", length) == 0) {
405 return SATURDAY;
406 }
407 break;
408 case 4:
409 if (uprv_strncmp(keyword, "hour", length) == 0) {
410 return HOUR;
411 } else if (uprv_strncmp(keyword, "week", length) == 0) {
412 return WEEK;
413 } else if (uprv_strncmp(keyword, "year", length) == 0) {
414 return YEAR;
415 }
416 break;
417 case 5:
418 if (uprv_strncmp(keyword, "month", length) == 0) {
419 return MONTH;
420 }
421 break;
422 case 6:
423 if (uprv_strncmp(keyword, "minute", length) == 0) {
424 return MINUTE;
425 } else if (uprv_strncmp(keyword, "second", length) == 0) {
426 return SECOND;
427 }
428 break;
429 case 7:
430 if (uprv_strncmp(keyword, "quarter", length) == 0) {
431 return QUARTER; // TODO: Check @provisional
432 }
433 break;
434 default:
435 break;
436 }
437 return INVALID_UNIT;
438 }
439
handlePlainDirection__anonf6b318b20111::RelDateTimeFmtDataSink440 void handlePlainDirection(ResourceValue &value, UErrorCode &errorCode) {
441 // Handle Display Name for PLAIN direction for some units.
442 if (U_FAILURE(errorCode)) { return; }
443
444 int32_t absUnit = absUnitFromGeneric(genericUnit);
445 if (absUnit < 0) {
446 return; // Not interesting.
447 }
448
449 // Store displayname if not set.
450 if (outputData.absoluteUnits[style]
451 [absUnit][UDAT_DIRECTION_PLAIN].isEmpty()) {
452 outputData.absoluteUnits[style]
453 [absUnit][UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
454 return;
455 }
456 }
457
consumeTableRelative__anonf6b318b20111::RelDateTimeFmtDataSink458 void consumeTableRelative(const char *key, ResourceValue &value, UErrorCode &errorCode) {
459 ResourceTable unitTypesTable = value.getTable(errorCode);
460 if (U_FAILURE(errorCode)) { return; }
461
462 for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
463 if (value.getType() == URES_STRING) {
464 int32_t direction = keyToDirection(key);
465 if (direction < 0) {
466 continue;
467 }
468
469 int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
470 if (relUnitIndex == UDAT_REL_UNIT_SECOND && uprv_strcmp(key, "0") == 0 &&
471 outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN].isEmpty()) {
472 // Handle "NOW"
473 outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW]
474 [UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
475 }
476
477 int32_t absUnitIndex = absUnitFromGeneric(genericUnit);
478 if (absUnitIndex < 0) {
479 continue;
480 }
481 // Only reset if slot is empty.
482 if (outputData.absoluteUnits[style][absUnitIndex][direction].isEmpty()) {
483 outputData.absoluteUnits[style][absUnitIndex]
484 [direction].fastCopyFrom(value.getUnicodeString(errorCode));
485 }
486 }
487 }
488 }
489
consumeTimeDetail__anonf6b318b20111::RelDateTimeFmtDataSink490 void consumeTimeDetail(int32_t relUnitIndex,
491 const char *key, ResourceValue &value, UErrorCode &errorCode) {
492 ResourceTable unitTypesTable = value.getTable(errorCode);
493 if (U_FAILURE(errorCode)) { return; }
494
495 for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
496 if (value.getType() == URES_STRING) {
497 int32_t pluralIndex = StandardPlural::indexOrNegativeFromString(key);
498 if (pluralIndex >= 0) {
499 SimpleFormatter **patterns =
500 outputData.relativeUnitsFormatters[style][relUnitIndex]
501 [pastFutureIndex];
502 // Only set if not already established.
503 if (patterns[pluralIndex] == nullptr) {
504 patterns[pluralIndex] = new SimpleFormatter(
505 value.getUnicodeString(errorCode), 0, 1, errorCode);
506 if (patterns[pluralIndex] == nullptr) {
507 errorCode = U_MEMORY_ALLOCATION_ERROR;
508 }
509 }
510 }
511 }
512 }
513 }
514
consumeTableRelativeTime__anonf6b318b20111::RelDateTimeFmtDataSink515 void consumeTableRelativeTime(const char *key, ResourceValue &value, UErrorCode &errorCode) {
516 ResourceTable relativeTimeTable = value.getTable(errorCode);
517 if (U_FAILURE(errorCode)) { return; }
518
519 int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
520 if (relUnitIndex < 0) {
521 return;
522 }
523 for (int32_t i = 0; relativeTimeTable.getKeyAndValue(i, key, value); ++i) {
524 if (uprv_strcmp(key, "past") == 0) {
525 pastFutureIndex = 0;
526 } else if (uprv_strcmp(key, "future") == 0) {
527 pastFutureIndex = 1;
528 } else {
529 // Unknown key.
530 continue;
531 }
532 consumeTimeDetail(relUnitIndex, key, value, errorCode);
533 }
534 }
535
consumeAlias__anonf6b318b20111::RelDateTimeFmtDataSink536 void consumeAlias(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
537
538 UDateRelativeDateTimeFormatterStyle sourceStyle = styleFromString(key);
539 const UnicodeString valueStr = value.getAliasUnicodeString(errorCode);
540 if (U_FAILURE(errorCode)) { return; }
541
542 UDateRelativeDateTimeFormatterStyle targetStyle =
543 styleFromAliasUnicodeString(valueStr);
544
545 if (sourceStyle == targetStyle) {
546 errorCode = U_INVALID_FORMAT_ERROR;
547 return;
548 }
549 if (outputData.fallBackCache[sourceStyle] != -1 &&
550 outputData.fallBackCache[sourceStyle] != targetStyle) {
551 errorCode = U_INVALID_FORMAT_ERROR;
552 return;
553 }
554 outputData.fallBackCache[sourceStyle] = targetStyle;
555 }
556
consumeTimeUnit__anonf6b318b20111::RelDateTimeFmtDataSink557 void consumeTimeUnit(const char *key, ResourceValue &value, UErrorCode &errorCode) {
558 ResourceTable unitTypesTable = value.getTable(errorCode);
559 if (U_FAILURE(errorCode)) { return; }
560
561 for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
562 // Handle display name.
563 if (uprv_strcmp(key, "dn") == 0 && value.getType() == URES_STRING) {
564 handlePlainDirection(value, errorCode);
565 }
566 if (value.getType() == URES_TABLE) {
567 if (uprv_strcmp(key, "relative") == 0) {
568 consumeTableRelative(key, value, errorCode);
569 } else if (uprv_strcmp(key, "relativeTime") == 0) {
570 consumeTableRelativeTime(key, value, errorCode);
571 }
572 }
573 }
574 }
575
put__anonf6b318b20111::RelDateTimeFmtDataSink576 virtual void put(const char *key, ResourceValue &value,
577 UBool /*noFallback*/, UErrorCode &errorCode) override {
578 // Main entry point to sink
579 ResourceTable table = value.getTable(errorCode);
580 if (U_FAILURE(errorCode)) { return; }
581 for (int32_t i = 0; table.getKeyAndValue(i, key, value); ++i) {
582 if (value.getType() == URES_ALIAS) {
583 consumeAlias(key, value, errorCode);
584 } else {
585 style = styleFromString(key);
586 int32_t unitSize = static_cast<int32_t>(uprv_strlen(key)) - styleSuffixLength(style);
587 genericUnit = unitOrNegativeFromString(key, unitSize);
588 if (style >= 0 && genericUnit != INVALID_UNIT) {
589 consumeTimeUnit(key, value, errorCode);
590 }
591 }
592 }
593 }
594
595 };
596
597 // Virtual destructors must be defined out of line.
~RelDateTimeFmtDataSink()598 RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
599 } // namespace
600
601 static const DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth[UDAT_STYLE_COUNT] = {
602 DateFormatSymbols::WIDE, DateFormatSymbols::SHORT, DateFormatSymbols::NARROW
603 };
604
605 // Get days of weeks from the DateFormatSymbols class.
loadWeekdayNames(UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT],const char * localeId,UErrorCode & status)606 static void loadWeekdayNames(UnicodeString absoluteUnits[UDAT_STYLE_COUNT]
607 [UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT],
608 const char* localeId,
609 UErrorCode& status) {
610 if (U_FAILURE(status)) {
611 return;
612 }
613 Locale locale(localeId);
614 DateFormatSymbols dfSym(locale, status);
615 if (U_FAILURE(status)) {
616 return;
617 }
618 for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
619 DateFormatSymbols::DtWidthType dtfmtWidth = styleToDateFormatSymbolWidth[style];
620 int32_t count;
621 const UnicodeString* weekdayNames =
622 dfSym.getWeekdays(count, DateFormatSymbols::STANDALONE, dtfmtWidth);
623 for (int32_t dayIndex = UDAT_ABSOLUTE_SUNDAY;
624 dayIndex <= UDAT_ABSOLUTE_SATURDAY; ++ dayIndex) {
625 int32_t dateSymbolIndex = (dayIndex - UDAT_ABSOLUTE_SUNDAY) + UCAL_SUNDAY;
626 absoluteUnits[style][dayIndex][UDAT_DIRECTION_PLAIN].fastCopyFrom(
627 weekdayNames[dateSymbolIndex]);
628 }
629 }
630 }
631
loadUnitData(const UResourceBundle * resource,RelativeDateTimeCacheData & cacheData,const char * localeId,UErrorCode & status)632 static UBool loadUnitData(
633 const UResourceBundle *resource,
634 RelativeDateTimeCacheData &cacheData,
635 const char* localeId,
636 UErrorCode &status) {
637
638 RelDateTimeFmtDataSink sink(cacheData);
639
640 ures_getAllItemsWithFallback(resource, "fields", sink, status);
641 if (U_FAILURE(status)) {
642 return false;
643 }
644
645 // Get the weekday names from DateFormatSymbols.
646 loadWeekdayNames(cacheData.absoluteUnits, localeId, status);
647 return U_SUCCESS(status);
648 }
649
650 static const int32_t cTypeBufMax = 32;
651
getDateTimePattern(Locale locale,const UResourceBundle * resource,UnicodeString & result,UErrorCode & status)652 static UBool getDateTimePattern(
653 Locale locale,
654 const UResourceBundle *resource,
655 UnicodeString &result,
656 UErrorCode &status) {
657 if (U_FAILURE(status)) {
658 return false;
659 }
660 char cType[cTypeBufMax + 1];
661 Calendar::getCalendarTypeFromLocale(locale, cType, cTypeBufMax, status);
662 cType[cTypeBufMax] = 0;
663 if (U_FAILURE(status) || cType[0] == 0) {
664 status = U_ZERO_ERROR;
665 uprv_strcpy(cType, "gregorian");
666 }
667
668 LocalUResourceBundlePointer topLevel;
669 int32_t dateTimeFormatOffset = DateFormat::kMedium;
670 CharString pathBuffer;
671 // Currently, for compatibility with pre-CLDR-42 data, we default to the "atTime"
672 // combining patterns. Depending on guidance in CLDR 42 spec and on DisplayOptions,
673 // we may change this.
674 pathBuffer.append("calendar/", status)
675 .append(cType, status)
676 .append("/DateTimePatterns%atTime", status);
677 topLevel.adoptInstead(
678 ures_getByKeyWithFallback(
679 resource, pathBuffer.data(), nullptr, &status));
680 if (U_FAILURE(status) || ures_getSize(topLevel.getAlias()) < 4) {
681 // Fall back to standard combining patterns
682 status = U_ZERO_ERROR;
683 dateTimeFormatOffset = DateFormat::kDateTime;
684 pathBuffer.clear();
685 pathBuffer.append("calendar/", status)
686 .append(cType, status)
687 .append("/DateTimePatterns", status);
688 topLevel.adoptInstead(
689 ures_getByKeyWithFallback(
690 resource, pathBuffer.data(), nullptr, &status));
691 }
692 if (U_FAILURE(status)) {
693 return false;
694 }
695 if (dateTimeFormatOffset == DateFormat::kDateTime && ures_getSize(topLevel.getAlias()) <= DateFormat::kDateTime) {
696 // Oops, size is too small to access the index that we want, fallback
697 // to a hard-coded value.
698 result = UNICODE_STRING_SIMPLE("{1} {0}");
699 return true;
700 }
701 return getStringByIndex(topLevel.getAlias(), dateTimeFormatOffset, result, status);
702 }
703
704 template<>
createObject(const void *,UErrorCode & status) const705 const RelativeDateTimeCacheData *LocaleCacheKey<RelativeDateTimeCacheData>::createObject(const void * /*unused*/, UErrorCode &status) const {
706 const char *localeId = fLoc.getName();
707 LocalUResourceBundlePointer topLevel(ures_open(nullptr, localeId, &status));
708 if (U_FAILURE(status)) {
709 return nullptr;
710 }
711 LocalPointer<RelativeDateTimeCacheData> result(
712 new RelativeDateTimeCacheData());
713 if (result.isNull()) {
714 status = U_MEMORY_ALLOCATION_ERROR;
715 return nullptr;
716 }
717 if (!loadUnitData(
718 topLevel.getAlias(),
719 *result,
720 localeId,
721 status)) {
722 return nullptr;
723 }
724 UnicodeString dateTimePattern;
725 if (!getDateTimePattern(fLoc, topLevel.getAlias(), dateTimePattern, status)) {
726 return nullptr;
727 }
728 result->adoptCombinedDateAndTime(
729 new SimpleFormatter(dateTimePattern, 2, 2, status));
730 if (U_FAILURE(status)) {
731 return nullptr;
732 }
733 result->addRef();
734 return result.orphan();
735 }
736
737
738
739 static constexpr FormattedStringBuilder::Field kRDTNumericField
740 = {UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_NUMERIC_FIELD};
741
742 static constexpr FormattedStringBuilder::Field kRDTLiteralField
743 = {UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_LITERAL_FIELD};
744
745 class FormattedRelativeDateTimeData : public FormattedValueStringBuilderImpl {
746 public:
FormattedRelativeDateTimeData()747 FormattedRelativeDateTimeData() : FormattedValueStringBuilderImpl(kRDTNumericField) {}
748 virtual ~FormattedRelativeDateTimeData();
749 };
750
751 FormattedRelativeDateTimeData::~FormattedRelativeDateTimeData() = default;
752
753
UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedRelativeDateTime)754 UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedRelativeDateTime)
755
756
757 RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) :
758 fCache(nullptr),
759 fNumberFormat(nullptr),
760 fPluralRules(nullptr),
761 fStyle(UDAT_STYLE_LONG),
762 fContext(UDISPCTX_CAPITALIZATION_NONE),
763 fOptBreakIterator(nullptr) {
764 (void)fOptBreakIterator; // suppress unused field warning
765 init(nullptr, nullptr, status);
766 }
767
RelativeDateTimeFormatter(const Locale & locale,UErrorCode & status)768 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
769 const Locale& locale, UErrorCode& status) :
770 fCache(nullptr),
771 fNumberFormat(nullptr),
772 fPluralRules(nullptr),
773 fStyle(UDAT_STYLE_LONG),
774 fContext(UDISPCTX_CAPITALIZATION_NONE),
775 fOptBreakIterator(nullptr),
776 fLocale(locale) {
777 init(nullptr, nullptr, status);
778 }
779
RelativeDateTimeFormatter(const Locale & locale,NumberFormat * nfToAdopt,UErrorCode & status)780 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
781 const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) :
782 fCache(nullptr),
783 fNumberFormat(nullptr),
784 fPluralRules(nullptr),
785 fStyle(UDAT_STYLE_LONG),
786 fContext(UDISPCTX_CAPITALIZATION_NONE),
787 fOptBreakIterator(nullptr),
788 fLocale(locale) {
789 init(nfToAdopt, nullptr, status);
790 }
791
RelativeDateTimeFormatter(const Locale & locale,NumberFormat * nfToAdopt,UDateRelativeDateTimeFormatterStyle styl,UDisplayContext capitalizationContext,UErrorCode & status)792 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
793 const Locale& locale,
794 NumberFormat *nfToAdopt,
795 UDateRelativeDateTimeFormatterStyle styl,
796 UDisplayContext capitalizationContext,
797 UErrorCode& status) :
798 fCache(nullptr),
799 fNumberFormat(nullptr),
800 fPluralRules(nullptr),
801 fStyle(styl),
802 fContext(capitalizationContext),
803 fOptBreakIterator(nullptr),
804 fLocale(locale) {
805 if (U_FAILURE(status)) {
806 return;
807 }
808 if (styl < 0 || UDAT_STYLE_COUNT <= styl) {
809 status = U_ILLEGAL_ARGUMENT_ERROR;
810 return;
811 }
812 if ((capitalizationContext >> 8) != UDISPCTX_TYPE_CAPITALIZATION) {
813 status = U_ILLEGAL_ARGUMENT_ERROR;
814 return;
815 }
816 if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) {
817 #if !UCONFIG_NO_BREAK_ITERATION
818 BreakIterator *bi = BreakIterator::createSentenceInstance(locale, status);
819 if (U_FAILURE(status)) {
820 return;
821 }
822 init(nfToAdopt, bi, status);
823 #else
824 status = U_UNSUPPORTED_ERROR;
825 return;
826 #endif // !UCONFIG_NO_BREAK_ITERATION
827 } else {
828 init(nfToAdopt, nullptr, status);
829 }
830 }
831
RelativeDateTimeFormatter(const RelativeDateTimeFormatter & other)832 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
833 const RelativeDateTimeFormatter& other)
834 : UObject(other),
835 fCache(other.fCache),
836 fNumberFormat(other.fNumberFormat),
837 fPluralRules(other.fPluralRules),
838 fStyle(other.fStyle),
839 fContext(other.fContext),
840 fOptBreakIterator(other.fOptBreakIterator),
841 fLocale(other.fLocale) {
842 fCache->addRef();
843 fNumberFormat->addRef();
844 fPluralRules->addRef();
845 #if !UCONFIG_NO_BREAK_ITERATION
846 if (fOptBreakIterator != nullptr) {
847 fOptBreakIterator->addRef();
848 }
849 #endif // !UCONFIG_NO_BREAK_ITERATION
850 }
851
operator =(const RelativeDateTimeFormatter & other)852 RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=(
853 const RelativeDateTimeFormatter& other) {
854 if (this != &other) {
855 SharedObject::copyPtr(other.fCache, fCache);
856 SharedObject::copyPtr(other.fNumberFormat, fNumberFormat);
857 SharedObject::copyPtr(other.fPluralRules, fPluralRules);
858 #if !UCONFIG_NO_BREAK_ITERATION
859 SharedObject::copyPtr(other.fOptBreakIterator, fOptBreakIterator);
860 #endif // !UCONFIG_NO_BREAK_ITERATION
861 fStyle = other.fStyle;
862 fContext = other.fContext;
863 fLocale = other.fLocale;
864 }
865 return *this;
866 }
867
~RelativeDateTimeFormatter()868 RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
869 if (fCache != nullptr) {
870 fCache->removeRef();
871 }
872 if (fNumberFormat != nullptr) {
873 fNumberFormat->removeRef();
874 }
875 if (fPluralRules != nullptr) {
876 fPluralRules->removeRef();
877 }
878 #if !UCONFIG_NO_BREAK_ITERATION
879 if (fOptBreakIterator != nullptr) {
880 fOptBreakIterator->removeRef();
881 }
882 #endif // !UCONFIG_NO_BREAK_ITERATION
883 }
884
getNumberFormat() const885 const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const {
886 return **fNumberFormat;
887 }
888
getCapitalizationContext() const889 UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const {
890 return fContext;
891 }
892
getFormatStyle() const893 UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const {
894 return fStyle;
895 }
896
897
898 // To reduce boilerplate code, we use a helper function that forwards variadic
899 // arguments to the formatImpl function.
900
901 template<typename F, typename... Args>
doFormat(F callback,UnicodeString & appendTo,UErrorCode & status,Args...args) const902 UnicodeString& RelativeDateTimeFormatter::doFormat(
903 F callback,
904 UnicodeString& appendTo,
905 UErrorCode& status,
906 Args... args) const {
907 FormattedRelativeDateTimeData output;
908 (this->*callback)(std::forward<Args>(args)..., output, status);
909 if (U_FAILURE(status)) {
910 return appendTo;
911 }
912 UnicodeString result = output.getStringRef().toUnicodeString();
913 return appendTo.append(adjustForContext(result));
914 }
915
916 template<typename F, typename... Args>
doFormatToValue(F callback,UErrorCode & status,Args...args) const917 FormattedRelativeDateTime RelativeDateTimeFormatter::doFormatToValue(
918 F callback,
919 UErrorCode& status,
920 Args... args) const {
921 if (!checkNoAdjustForContext(status)) {
922 return FormattedRelativeDateTime(status);
923 }
924 LocalPointer<FormattedRelativeDateTimeData> output(
925 new FormattedRelativeDateTimeData(), status);
926 if (U_FAILURE(status)) {
927 return FormattedRelativeDateTime(status);
928 }
929 (this->*callback)(std::forward<Args>(args)..., *output, status);
930 output->getStringRef().writeTerminator(status);
931 return FormattedRelativeDateTime(output.orphan());
932 }
933
format(double quantity,UDateDirection direction,UDateRelativeUnit unit,UnicodeString & appendTo,UErrorCode & status) const934 UnicodeString& RelativeDateTimeFormatter::format(
935 double quantity,
936 UDateDirection direction,
937 UDateRelativeUnit unit,
938 UnicodeString& appendTo,
939 UErrorCode& status) const {
940 return doFormat(
941 &RelativeDateTimeFormatter::formatImpl,
942 appendTo,
943 status,
944 quantity,
945 direction,
946 unit);
947 }
948
formatToValue(double quantity,UDateDirection direction,UDateRelativeUnit unit,UErrorCode & status) const949 FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue(
950 double quantity,
951 UDateDirection direction,
952 UDateRelativeUnit unit,
953 UErrorCode& status) const {
954 return doFormatToValue(
955 &RelativeDateTimeFormatter::formatImpl,
956 status,
957 quantity,
958 direction,
959 unit);
960 }
961
formatImpl(double quantity,UDateDirection direction,UDateRelativeUnit unit,FormattedRelativeDateTimeData & output,UErrorCode & status) const962 void RelativeDateTimeFormatter::formatImpl(
963 double quantity,
964 UDateDirection direction,
965 UDateRelativeUnit unit,
966 FormattedRelativeDateTimeData& output,
967 UErrorCode& status) const {
968 if (U_FAILURE(status)) {
969 return;
970 }
971 if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
972 status = U_ILLEGAL_ARGUMENT_ERROR;
973 return;
974 }
975 int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
976
977 StandardPlural::Form pluralForm;
978 QuantityFormatter::formatAndSelect(
979 quantity,
980 **fNumberFormat,
981 **fPluralRules,
982 output.getStringRef(),
983 pluralForm,
984 status);
985 if (U_FAILURE(status)) {
986 return;
987 }
988
989 const SimpleFormatter* formatter =
990 fCache->getRelativeUnitFormatter(fStyle, unit, bFuture, pluralForm);
991 if (formatter == nullptr) {
992 // TODO: WARN - look at quantity formatter's action with an error.
993 status = U_INVALID_FORMAT_ERROR;
994 return;
995 }
996
997 number::impl::SimpleModifier modifier(*formatter, kRDTLiteralField, false);
998 modifier.formatAsPrefixSuffix(
999 output.getStringRef(), 0, output.getStringRef().length(), status);
1000 }
1001
formatNumeric(double offset,URelativeDateTimeUnit unit,UnicodeString & appendTo,UErrorCode & status) const1002 UnicodeString& RelativeDateTimeFormatter::formatNumeric(
1003 double offset,
1004 URelativeDateTimeUnit unit,
1005 UnicodeString& appendTo,
1006 UErrorCode& status) const {
1007 return doFormat(
1008 &RelativeDateTimeFormatter::formatNumericImpl,
1009 appendTo,
1010 status,
1011 offset,
1012 unit);
1013 }
1014
formatNumericToValue(double offset,URelativeDateTimeUnit unit,UErrorCode & status) const1015 FormattedRelativeDateTime RelativeDateTimeFormatter::formatNumericToValue(
1016 double offset,
1017 URelativeDateTimeUnit unit,
1018 UErrorCode& status) const {
1019 return doFormatToValue(
1020 &RelativeDateTimeFormatter::formatNumericImpl,
1021 status,
1022 offset,
1023 unit);
1024 }
1025
formatNumericImpl(double offset,URelativeDateTimeUnit unit,FormattedRelativeDateTimeData & output,UErrorCode & status) const1026 void RelativeDateTimeFormatter::formatNumericImpl(
1027 double offset,
1028 URelativeDateTimeUnit unit,
1029 FormattedRelativeDateTimeData& output,
1030 UErrorCode& status) const {
1031 if (U_FAILURE(status)) {
1032 return;
1033 }
1034 if (unit < 0 || UDAT_REL_UNIT_COUNT <= unit) {
1035 status = U_ILLEGAL_ARGUMENT_ERROR;
1036 return;
1037 }
1038 UDateDirection direction = UDAT_DIRECTION_NEXT;
1039 if (std::signbit(offset)) { // needed to handle -0.0
1040 direction = UDAT_DIRECTION_LAST;
1041 offset = -offset;
1042 }
1043 if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
1044 status = U_ILLEGAL_ARGUMENT_ERROR;
1045 return;
1046 }
1047 int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
1048
1049 StandardPlural::Form pluralForm;
1050 QuantityFormatter::formatAndSelect(
1051 offset,
1052 **fNumberFormat,
1053 **fPluralRules,
1054 output.getStringRef(),
1055 pluralForm,
1056 status);
1057 if (U_FAILURE(status)) {
1058 return;
1059 }
1060
1061 const SimpleFormatter* formatter =
1062 fCache->getRelativeDateTimeUnitFormatter(fStyle, unit, bFuture, pluralForm);
1063 if (formatter == nullptr) {
1064 // TODO: WARN - look at quantity formatter's action with an error.
1065 status = U_INVALID_FORMAT_ERROR;
1066 return;
1067 }
1068
1069 number::impl::SimpleModifier modifier(*formatter, kRDTLiteralField, false);
1070 modifier.formatAsPrefixSuffix(
1071 output.getStringRef(), 0, output.getStringRef().length(), status);
1072 }
1073
format(UDateDirection direction,UDateAbsoluteUnit unit,UnicodeString & appendTo,UErrorCode & status) const1074 UnicodeString& RelativeDateTimeFormatter::format(
1075 UDateDirection direction,
1076 UDateAbsoluteUnit unit,
1077 UnicodeString& appendTo,
1078 UErrorCode& status) const {
1079 return doFormat(
1080 &RelativeDateTimeFormatter::formatAbsoluteImpl,
1081 appendTo,
1082 status,
1083 direction,
1084 unit);
1085 }
1086
formatToValue(UDateDirection direction,UDateAbsoluteUnit unit,UErrorCode & status) const1087 FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue(
1088 UDateDirection direction,
1089 UDateAbsoluteUnit unit,
1090 UErrorCode& status) const {
1091 return doFormatToValue(
1092 &RelativeDateTimeFormatter::formatAbsoluteImpl,
1093 status,
1094 direction,
1095 unit);
1096 }
1097
formatAbsoluteImpl(UDateDirection direction,UDateAbsoluteUnit unit,FormattedRelativeDateTimeData & output,UErrorCode & status) const1098 void RelativeDateTimeFormatter::formatAbsoluteImpl(
1099 UDateDirection direction,
1100 UDateAbsoluteUnit unit,
1101 FormattedRelativeDateTimeData& output,
1102 UErrorCode& status) const {
1103 if (U_FAILURE(status)) {
1104 return;
1105 }
1106 if ((unit < 0 || UDAT_ABSOLUTE_UNIT_COUNT <= unit) ||
1107 (direction < 0 || UDAT_DIRECTION_COUNT <= direction) ||
1108 (unit == UDAT_ABSOLUTE_NOW && direction != UDAT_DIRECTION_PLAIN)) {
1109 status = U_ILLEGAL_ARGUMENT_ERROR;
1110 return;
1111 }
1112
1113 // Get string using fallback.
1114 output.getStringRef().append(
1115 fCache->getAbsoluteUnitString(fStyle, unit, direction),
1116 kRDTLiteralField,
1117 status);
1118 }
1119
format(double offset,URelativeDateTimeUnit unit,UnicodeString & appendTo,UErrorCode & status) const1120 UnicodeString& RelativeDateTimeFormatter::format(
1121 double offset,
1122 URelativeDateTimeUnit unit,
1123 UnicodeString& appendTo,
1124 UErrorCode& status) const {
1125 return doFormat(
1126 &RelativeDateTimeFormatter::formatRelativeImpl,
1127 appendTo,
1128 status,
1129 offset,
1130 unit);
1131 }
1132
formatToValue(double offset,URelativeDateTimeUnit unit,UErrorCode & status) const1133 FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue(
1134 double offset,
1135 URelativeDateTimeUnit unit,
1136 UErrorCode& status) const {
1137 return doFormatToValue(
1138 &RelativeDateTimeFormatter::formatRelativeImpl,
1139 status,
1140 offset,
1141 unit);
1142 }
1143
formatRelativeImpl(double offset,URelativeDateTimeUnit unit,FormattedRelativeDateTimeData & output,UErrorCode & status) const1144 void RelativeDateTimeFormatter::formatRelativeImpl(
1145 double offset,
1146 URelativeDateTimeUnit unit,
1147 FormattedRelativeDateTimeData& output,
1148 UErrorCode& status) const {
1149 if (U_FAILURE(status)) {
1150 return;
1151 }
1152 // TODO:
1153 // The full implementation of this depends on CLDR data that is not yet available,
1154 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
1155 // In the meantime do a quick bring-up by calling the old format method; this
1156 // leaves some holes (even for data that is currently available, such as quarter).
1157 // When the new CLDR data is available, update the data storage accordingly,
1158 // rewrite this to use it directly, and rewrite the old format method to call this
1159 // new one; that is covered by https://unicode-org.atlassian.net/browse/ICU-12171.
1160 UDateDirection direction = UDAT_DIRECTION_COUNT;
1161 if (offset > -2.1 && offset < 2.1) {
1162 // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST
1163 double offsetx100 = offset * 100.0;
1164 int32_t intoffset = (offsetx100 < 0)? (int32_t)(offsetx100-0.5) : (int32_t)(offsetx100+0.5);
1165 switch (intoffset) {
1166 case -200/*-2*/: direction = UDAT_DIRECTION_LAST_2; break;
1167 case -100/*-1*/: direction = UDAT_DIRECTION_LAST; break;
1168 case 0/* 0*/: direction = UDAT_DIRECTION_THIS; break;
1169 case 100/* 1*/: direction = UDAT_DIRECTION_NEXT; break;
1170 case 200/* 2*/: direction = UDAT_DIRECTION_NEXT_2; break;
1171 default: break;
1172 }
1173 }
1174 UDateAbsoluteUnit absunit = UDAT_ABSOLUTE_UNIT_COUNT;
1175 switch (unit) {
1176 case UDAT_REL_UNIT_YEAR: absunit = UDAT_ABSOLUTE_YEAR; break;
1177 case UDAT_REL_UNIT_QUARTER: absunit = UDAT_ABSOLUTE_QUARTER; break;
1178 case UDAT_REL_UNIT_MONTH: absunit = UDAT_ABSOLUTE_MONTH; break;
1179 case UDAT_REL_UNIT_WEEK: absunit = UDAT_ABSOLUTE_WEEK; break;
1180 case UDAT_REL_UNIT_DAY: absunit = UDAT_ABSOLUTE_DAY; break;
1181 case UDAT_REL_UNIT_SECOND:
1182 if (direction == UDAT_DIRECTION_THIS) {
1183 absunit = UDAT_ABSOLUTE_NOW;
1184 direction = UDAT_DIRECTION_PLAIN;
1185 }
1186 break;
1187 case UDAT_REL_UNIT_SUNDAY: absunit = UDAT_ABSOLUTE_SUNDAY; break;
1188 case UDAT_REL_UNIT_MONDAY: absunit = UDAT_ABSOLUTE_MONDAY; break;
1189 case UDAT_REL_UNIT_TUESDAY: absunit = UDAT_ABSOLUTE_TUESDAY; break;
1190 case UDAT_REL_UNIT_WEDNESDAY: absunit = UDAT_ABSOLUTE_WEDNESDAY; break;
1191 case UDAT_REL_UNIT_THURSDAY: absunit = UDAT_ABSOLUTE_THURSDAY; break;
1192 case UDAT_REL_UNIT_FRIDAY: absunit = UDAT_ABSOLUTE_FRIDAY; break;
1193 case UDAT_REL_UNIT_SATURDAY: absunit = UDAT_ABSOLUTE_SATURDAY; break;
1194 case UDAT_REL_UNIT_HOUR: absunit = UDAT_ABSOLUTE_HOUR; break;
1195 case UDAT_REL_UNIT_MINUTE: absunit = UDAT_ABSOLUTE_MINUTE; break;
1196 default: break;
1197 }
1198 if (direction != UDAT_DIRECTION_COUNT && absunit != UDAT_ABSOLUTE_UNIT_COUNT) {
1199 formatAbsoluteImpl(direction, absunit, output, status);
1200 if (output.getStringRef().length() != 0) {
1201 return;
1202 }
1203 }
1204 // otherwise fallback to formatNumeric
1205 formatNumericImpl(offset, unit, output, status);
1206 }
1207
combineDateAndTime(const UnicodeString & relativeDateString,const UnicodeString & timeString,UnicodeString & appendTo,UErrorCode & status) const1208 UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
1209 const UnicodeString& relativeDateString, const UnicodeString& timeString,
1210 UnicodeString& appendTo, UErrorCode& status) const {
1211 return fCache->getCombinedDateAndTime()->format(
1212 timeString, relativeDateString, appendTo, status);
1213 }
1214
adjustForContext(UnicodeString & str) const1215 UnicodeString& RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const {
1216 #if !UCONFIG_NO_BREAK_ITERATION
1217 if (fOptBreakIterator == nullptr
1218 || str.length() == 0 || !u_islower(str.char32At(0))) {
1219 return str;
1220 }
1221
1222 // Must guarantee that one thread at a time accesses the shared break
1223 // iterator.
1224 static UMutex gBrkIterMutex;
1225 Mutex lock(&gBrkIterMutex);
1226 str.toTitle(
1227 fOptBreakIterator->get(),
1228 fLocale,
1229 U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
1230 #endif // !UCONFIG_NO_BREAK_ITERATION
1231 return str;
1232 }
1233
checkNoAdjustForContext(UErrorCode & status) const1234 UBool RelativeDateTimeFormatter::checkNoAdjustForContext(UErrorCode& status) const {
1235 #if !UCONFIG_NO_BREAK_ITERATION
1236 // This is unsupported because it's hard to keep fields in sync with title
1237 // casing. The code could be written and tested if there is demand.
1238 if (fOptBreakIterator != nullptr) {
1239 status = U_UNSUPPORTED_ERROR;
1240 return false;
1241 }
1242 #else
1243 (void)status; // suppress unused argument warning
1244 #endif // !UCONFIG_NO_BREAK_ITERATION
1245 return true;
1246 }
1247
init(NumberFormat * nfToAdopt,BreakIterator * biToAdopt,UErrorCode & status)1248 void RelativeDateTimeFormatter::init(
1249 NumberFormat *nfToAdopt,
1250 #if !UCONFIG_NO_BREAK_ITERATION
1251 BreakIterator *biToAdopt,
1252 #else
1253 std::nullptr_t,
1254 #endif // !UCONFIG_NO_BREAK_ITERATION
1255 UErrorCode &status) {
1256 LocalPointer<NumberFormat> nf(nfToAdopt);
1257 #if !UCONFIG_NO_BREAK_ITERATION
1258 LocalPointer<BreakIterator> bi(biToAdopt);
1259 #endif // !UCONFIG_NO_BREAK_ITERATION
1260 UnifiedCache::getByLocale(fLocale, fCache, status);
1261 if (U_FAILURE(status)) {
1262 return;
1263 }
1264 const SharedPluralRules *pr = PluralRules::createSharedInstance(
1265 fLocale, UPLURAL_TYPE_CARDINAL, status);
1266 if (U_FAILURE(status)) {
1267 return;
1268 }
1269 SharedObject::copyPtr(pr, fPluralRules);
1270 pr->removeRef();
1271 if (nf.isNull()) {
1272 const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
1273 fLocale, UNUM_DECIMAL, status);
1274 if (U_FAILURE(status)) {
1275 return;
1276 }
1277 SharedObject::copyPtr(shared, fNumberFormat);
1278 shared->removeRef();
1279 } else {
1280 SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
1281 if (shared == nullptr) {
1282 status = U_MEMORY_ALLOCATION_ERROR;
1283 return;
1284 }
1285 nf.orphan();
1286 SharedObject::copyPtr(shared, fNumberFormat);
1287 }
1288 #if !UCONFIG_NO_BREAK_ITERATION
1289 if (bi.isNull()) {
1290 SharedObject::clearPtr(fOptBreakIterator);
1291 } else {
1292 SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias());
1293 if (shared == nullptr) {
1294 status = U_MEMORY_ALLOCATION_ERROR;
1295 return;
1296 }
1297 bi.orphan();
1298 SharedObject::copyPtr(shared, fOptBreakIterator);
1299 }
1300 #endif // !UCONFIG_NO_BREAK_ITERATION
1301 }
1302
1303 U_NAMESPACE_END
1304
1305 // Plain C API
1306
1307 U_NAMESPACE_USE
1308
1309
1310 // Magic number: "FRDT" (FormattedRelativeDateTime) in ASCII
1311 UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL(
1312 FormattedRelativeDateTime,
1313 UFormattedRelativeDateTime,
1314 UFormattedRelativeDateTimeImpl,
1315 UFormattedRelativeDateTimeApiHelper,
1316 ureldatefmt,
1317 0x46524454)
1318
1319
1320 U_CAPI URelativeDateTimeFormatter* U_EXPORT2
ureldatefmt_open(const char * locale,UNumberFormat * nfToAdopt,UDateRelativeDateTimeFormatterStyle width,UDisplayContext capitalizationContext,UErrorCode * status)1321 ureldatefmt_open( const char* locale,
1322 UNumberFormat* nfToAdopt,
1323 UDateRelativeDateTimeFormatterStyle width,
1324 UDisplayContext capitalizationContext,
1325 UErrorCode* status )
1326 {
1327 if (U_FAILURE(*status)) {
1328 return nullptr;
1329 }
1330 LocalPointer<RelativeDateTimeFormatter> formatter(new RelativeDateTimeFormatter(Locale(locale),
1331 (NumberFormat*)nfToAdopt, width,
1332 capitalizationContext, *status), *status);
1333 if (U_FAILURE(*status)) {
1334 return nullptr;
1335 }
1336 return (URelativeDateTimeFormatter*)formatter.orphan();
1337 }
1338
1339 U_CAPI void U_EXPORT2
ureldatefmt_close(URelativeDateTimeFormatter * reldatefmt)1340 ureldatefmt_close(URelativeDateTimeFormatter *reldatefmt)
1341 {
1342 delete (RelativeDateTimeFormatter*)reldatefmt;
1343 }
1344
1345 U_CAPI int32_t U_EXPORT2
ureldatefmt_formatNumeric(const URelativeDateTimeFormatter * reldatefmt,double offset,URelativeDateTimeUnit unit,char16_t * result,int32_t resultCapacity,UErrorCode * status)1346 ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt,
1347 double offset,
1348 URelativeDateTimeUnit unit,
1349 char16_t* result,
1350 int32_t resultCapacity,
1351 UErrorCode* status)
1352 {
1353 if (U_FAILURE(*status)) {
1354 return 0;
1355 }
1356 if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) {
1357 *status = U_ILLEGAL_ARGUMENT_ERROR;
1358 return 0;
1359 }
1360 UnicodeString res;
1361 if (result != nullptr) {
1362 // nullptr destination for pure preflighting: empty dummy string
1363 // otherwise, alias the destination buffer (copied from udat_format)
1364 res.setTo(result, 0, resultCapacity);
1365 }
1366 ((RelativeDateTimeFormatter*)reldatefmt)->formatNumeric(offset, unit, res, *status);
1367 if (U_FAILURE(*status)) {
1368 return 0;
1369 }
1370 return res.extract(result, resultCapacity, *status);
1371 }
1372
1373 U_CAPI void U_EXPORT2
ureldatefmt_formatNumericToResult(const URelativeDateTimeFormatter * reldatefmt,double offset,URelativeDateTimeUnit unit,UFormattedRelativeDateTime * result,UErrorCode * status)1374 ureldatefmt_formatNumericToResult(
1375 const URelativeDateTimeFormatter* reldatefmt,
1376 double offset,
1377 URelativeDateTimeUnit unit,
1378 UFormattedRelativeDateTime* result,
1379 UErrorCode* status) {
1380 if (U_FAILURE(*status)) {
1381 return;
1382 }
1383 auto* fmt = reinterpret_cast<const RelativeDateTimeFormatter*>(reldatefmt);
1384 auto* resultImpl = UFormattedRelativeDateTimeApiHelper::validate(result, *status);
1385 resultImpl->fImpl = fmt->formatNumericToValue(offset, unit, *status);
1386 }
1387
1388 U_CAPI int32_t U_EXPORT2
ureldatefmt_format(const URelativeDateTimeFormatter * reldatefmt,double offset,URelativeDateTimeUnit unit,char16_t * result,int32_t resultCapacity,UErrorCode * status)1389 ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt,
1390 double offset,
1391 URelativeDateTimeUnit unit,
1392 char16_t* result,
1393 int32_t resultCapacity,
1394 UErrorCode* status)
1395 {
1396 if (U_FAILURE(*status)) {
1397 return 0;
1398 }
1399 if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) {
1400 *status = U_ILLEGAL_ARGUMENT_ERROR;
1401 return 0;
1402 }
1403 UnicodeString res;
1404 if (result != nullptr) {
1405 // nullptr destination for pure preflighting: empty dummy string
1406 // otherwise, alias the destination buffer (copied from udat_format)
1407 res.setTo(result, 0, resultCapacity);
1408 }
1409 ((RelativeDateTimeFormatter*)reldatefmt)->format(offset, unit, res, *status);
1410 if (U_FAILURE(*status)) {
1411 return 0;
1412 }
1413 return res.extract(result, resultCapacity, *status);
1414 }
1415
1416 U_CAPI void U_EXPORT2
ureldatefmt_formatToResult(const URelativeDateTimeFormatter * reldatefmt,double offset,URelativeDateTimeUnit unit,UFormattedRelativeDateTime * result,UErrorCode * status)1417 ureldatefmt_formatToResult(
1418 const URelativeDateTimeFormatter* reldatefmt,
1419 double offset,
1420 URelativeDateTimeUnit unit,
1421 UFormattedRelativeDateTime* result,
1422 UErrorCode* status) {
1423 if (U_FAILURE(*status)) {
1424 return;
1425 }
1426 auto* fmt = reinterpret_cast<const RelativeDateTimeFormatter*>(reldatefmt);
1427 auto* resultImpl = UFormattedRelativeDateTimeApiHelper::validate(result, *status);
1428 resultImpl->fImpl = fmt->formatToValue(offset, unit, *status);
1429 }
1430
1431 U_CAPI int32_t U_EXPORT2
ureldatefmt_combineDateAndTime(const URelativeDateTimeFormatter * reldatefmt,const char16_t * relativeDateString,int32_t relativeDateStringLen,const char16_t * timeString,int32_t timeStringLen,char16_t * result,int32_t resultCapacity,UErrorCode * status)1432 ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter* reldatefmt,
1433 const char16_t * relativeDateString,
1434 int32_t relativeDateStringLen,
1435 const char16_t * timeString,
1436 int32_t timeStringLen,
1437 char16_t* result,
1438 int32_t resultCapacity,
1439 UErrorCode* status )
1440 {
1441 if (U_FAILURE(*status)) {
1442 return 0;
1443 }
1444 if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0 ||
1445 (relativeDateString == nullptr ? relativeDateStringLen != 0 : relativeDateStringLen < -1) ||
1446 (timeString == nullptr ? timeStringLen != 0 : timeStringLen < -1)) {
1447 *status = U_ILLEGAL_ARGUMENT_ERROR;
1448 return 0;
1449 }
1450 UnicodeString relDateStr((UBool)(relativeDateStringLen == -1), relativeDateString, relativeDateStringLen);
1451 UnicodeString timeStr((UBool)(timeStringLen == -1), timeString, timeStringLen);
1452 UnicodeString res(result, 0, resultCapacity);
1453 ((RelativeDateTimeFormatter*)reldatefmt)->combineDateAndTime(relDateStr, timeStr, res, *status);
1454 if (U_FAILURE(*status)) {
1455 return 0;
1456 }
1457 return res.extract(result, resultCapacity, *status);
1458 }
1459
1460 #endif /* !UCONFIG_NO_FORMATTING */
1461