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 <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__anonefb583900111::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__anonefb583900111::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__anonefb583900111::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__anonefb583900111::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__anonefb583900111::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__anonefb583900111::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__anonefb583900111::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__anonefb583900111::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__anonefb583900111::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__anonefb583900111::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__anonefb583900111::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__anonefb583900111::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__anonefb583900111::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__anonefb583900111::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__anonefb583900111::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 init(nullptr, nullptr, status);
765 }
766
RelativeDateTimeFormatter(const Locale & locale,UErrorCode & status)767 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
768 const Locale& locale, UErrorCode& status) :
769 fCache(nullptr),
770 fNumberFormat(nullptr),
771 fPluralRules(nullptr),
772 fStyle(UDAT_STYLE_LONG),
773 fContext(UDISPCTX_CAPITALIZATION_NONE),
774 fOptBreakIterator(nullptr),
775 fLocale(locale) {
776 init(nullptr, nullptr, status);
777 }
778
RelativeDateTimeFormatter(const Locale & locale,NumberFormat * nfToAdopt,UErrorCode & status)779 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
780 const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) :
781 fCache(nullptr),
782 fNumberFormat(nullptr),
783 fPluralRules(nullptr),
784 fStyle(UDAT_STYLE_LONG),
785 fContext(UDISPCTX_CAPITALIZATION_NONE),
786 fOptBreakIterator(nullptr),
787 fLocale(locale) {
788 init(nfToAdopt, nullptr, status);
789 }
790
RelativeDateTimeFormatter(const Locale & locale,NumberFormat * nfToAdopt,UDateRelativeDateTimeFormatterStyle styl,UDisplayContext capitalizationContext,UErrorCode & status)791 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
792 const Locale& locale,
793 NumberFormat *nfToAdopt,
794 UDateRelativeDateTimeFormatterStyle styl,
795 UDisplayContext capitalizationContext,
796 UErrorCode& status) :
797 fCache(nullptr),
798 fNumberFormat(nullptr),
799 fPluralRules(nullptr),
800 fStyle(styl),
801 fContext(capitalizationContext),
802 fOptBreakIterator(nullptr),
803 fLocale(locale) {
804 if (U_FAILURE(status)) {
805 return;
806 }
807 if ((capitalizationContext >> 8) != UDISPCTX_TYPE_CAPITALIZATION) {
808 status = U_ILLEGAL_ARGUMENT_ERROR;
809 return;
810 }
811 if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) {
812 BreakIterator *bi = BreakIterator::createSentenceInstance(locale, status);
813 if (U_FAILURE(status)) {
814 return;
815 }
816 init(nfToAdopt, bi, status);
817 } else {
818 init(nfToAdopt, nullptr, status);
819 }
820 }
821
RelativeDateTimeFormatter(const RelativeDateTimeFormatter & other)822 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
823 const RelativeDateTimeFormatter& other)
824 : UObject(other),
825 fCache(other.fCache),
826 fNumberFormat(other.fNumberFormat),
827 fPluralRules(other.fPluralRules),
828 fStyle(other.fStyle),
829 fContext(other.fContext),
830 fOptBreakIterator(other.fOptBreakIterator),
831 fLocale(other.fLocale) {
832 fCache->addRef();
833 fNumberFormat->addRef();
834 fPluralRules->addRef();
835 if (fOptBreakIterator != nullptr) {
836 fOptBreakIterator->addRef();
837 }
838 }
839
operator =(const RelativeDateTimeFormatter & other)840 RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=(
841 const RelativeDateTimeFormatter& other) {
842 if (this != &other) {
843 SharedObject::copyPtr(other.fCache, fCache);
844 SharedObject::copyPtr(other.fNumberFormat, fNumberFormat);
845 SharedObject::copyPtr(other.fPluralRules, fPluralRules);
846 SharedObject::copyPtr(other.fOptBreakIterator, fOptBreakIterator);
847 fStyle = other.fStyle;
848 fContext = other.fContext;
849 fLocale = other.fLocale;
850 }
851 return *this;
852 }
853
~RelativeDateTimeFormatter()854 RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
855 if (fCache != nullptr) {
856 fCache->removeRef();
857 }
858 if (fNumberFormat != nullptr) {
859 fNumberFormat->removeRef();
860 }
861 if (fPluralRules != nullptr) {
862 fPluralRules->removeRef();
863 }
864 if (fOptBreakIterator != nullptr) {
865 fOptBreakIterator->removeRef();
866 }
867 }
868
getNumberFormat() const869 const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const {
870 return **fNumberFormat;
871 }
872
getCapitalizationContext() const873 UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const {
874 return fContext;
875 }
876
getFormatStyle() const877 UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const {
878 return fStyle;
879 }
880
881
882 // To reduce boilerplate code, we use a helper function that forwards variadic
883 // arguments to the formatImpl function.
884
885 template<typename F, typename... Args>
doFormat(F callback,UnicodeString & appendTo,UErrorCode & status,Args...args) const886 UnicodeString& RelativeDateTimeFormatter::doFormat(
887 F callback,
888 UnicodeString& appendTo,
889 UErrorCode& status,
890 Args... args) const {
891 FormattedRelativeDateTimeData output;
892 (this->*callback)(std::forward<Args>(args)..., output, status);
893 if (U_FAILURE(status)) {
894 return appendTo;
895 }
896 UnicodeString result = output.getStringRef().toUnicodeString();
897 return appendTo.append(adjustForContext(result));
898 }
899
900 template<typename F, typename... Args>
doFormatToValue(F callback,UErrorCode & status,Args...args) const901 FormattedRelativeDateTime RelativeDateTimeFormatter::doFormatToValue(
902 F callback,
903 UErrorCode& status,
904 Args... args) const {
905 if (!checkNoAdjustForContext(status)) {
906 return FormattedRelativeDateTime(status);
907 }
908 LocalPointer<FormattedRelativeDateTimeData> output(
909 new FormattedRelativeDateTimeData(), status);
910 if (U_FAILURE(status)) {
911 return FormattedRelativeDateTime(status);
912 }
913 (this->*callback)(std::forward<Args>(args)..., *output, status);
914 output->getStringRef().writeTerminator(status);
915 return FormattedRelativeDateTime(output.orphan());
916 }
917
format(double quantity,UDateDirection direction,UDateRelativeUnit unit,UnicodeString & appendTo,UErrorCode & status) const918 UnicodeString& RelativeDateTimeFormatter::format(
919 double quantity,
920 UDateDirection direction,
921 UDateRelativeUnit unit,
922 UnicodeString& appendTo,
923 UErrorCode& status) const {
924 return doFormat(
925 &RelativeDateTimeFormatter::formatImpl,
926 appendTo,
927 status,
928 quantity,
929 direction,
930 unit);
931 }
932
formatToValue(double quantity,UDateDirection direction,UDateRelativeUnit unit,UErrorCode & status) const933 FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue(
934 double quantity,
935 UDateDirection direction,
936 UDateRelativeUnit unit,
937 UErrorCode& status) const {
938 return doFormatToValue(
939 &RelativeDateTimeFormatter::formatImpl,
940 status,
941 quantity,
942 direction,
943 unit);
944 }
945
formatImpl(double quantity,UDateDirection direction,UDateRelativeUnit unit,FormattedRelativeDateTimeData & output,UErrorCode & status) const946 void RelativeDateTimeFormatter::formatImpl(
947 double quantity,
948 UDateDirection direction,
949 UDateRelativeUnit unit,
950 FormattedRelativeDateTimeData& output,
951 UErrorCode& status) const {
952 if (U_FAILURE(status)) {
953 return;
954 }
955 if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
956 status = U_ILLEGAL_ARGUMENT_ERROR;
957 return;
958 }
959 int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
960
961 StandardPlural::Form pluralForm;
962 QuantityFormatter::formatAndSelect(
963 quantity,
964 **fNumberFormat,
965 **fPluralRules,
966 output.getStringRef(),
967 pluralForm,
968 status);
969 if (U_FAILURE(status)) {
970 return;
971 }
972
973 const SimpleFormatter* formatter =
974 fCache->getRelativeUnitFormatter(fStyle, unit, bFuture, pluralForm);
975 if (formatter == nullptr) {
976 // TODO: WARN - look at quantity formatter's action with an error.
977 status = U_INVALID_FORMAT_ERROR;
978 return;
979 }
980
981 number::impl::SimpleModifier modifier(*formatter, kRDTLiteralField, false);
982 modifier.formatAsPrefixSuffix(
983 output.getStringRef(), 0, output.getStringRef().length(), status);
984 }
985
formatNumeric(double offset,URelativeDateTimeUnit unit,UnicodeString & appendTo,UErrorCode & status) const986 UnicodeString& RelativeDateTimeFormatter::formatNumeric(
987 double offset,
988 URelativeDateTimeUnit unit,
989 UnicodeString& appendTo,
990 UErrorCode& status) const {
991 return doFormat(
992 &RelativeDateTimeFormatter::formatNumericImpl,
993 appendTo,
994 status,
995 offset,
996 unit);
997 }
998
formatNumericToValue(double offset,URelativeDateTimeUnit unit,UErrorCode & status) const999 FormattedRelativeDateTime RelativeDateTimeFormatter::formatNumericToValue(
1000 double offset,
1001 URelativeDateTimeUnit unit,
1002 UErrorCode& status) const {
1003 return doFormatToValue(
1004 &RelativeDateTimeFormatter::formatNumericImpl,
1005 status,
1006 offset,
1007 unit);
1008 }
1009
formatNumericImpl(double offset,URelativeDateTimeUnit unit,FormattedRelativeDateTimeData & output,UErrorCode & status) const1010 void RelativeDateTimeFormatter::formatNumericImpl(
1011 double offset,
1012 URelativeDateTimeUnit unit,
1013 FormattedRelativeDateTimeData& output,
1014 UErrorCode& status) const {
1015 if (U_FAILURE(status)) {
1016 return;
1017 }
1018 UDateDirection direction = UDAT_DIRECTION_NEXT;
1019 if (std::signbit(offset)) { // needed to handle -0.0
1020 direction = UDAT_DIRECTION_LAST;
1021 offset = -offset;
1022 }
1023 if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
1024 status = U_ILLEGAL_ARGUMENT_ERROR;
1025 return;
1026 }
1027 int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
1028
1029 StandardPlural::Form pluralForm;
1030 QuantityFormatter::formatAndSelect(
1031 offset,
1032 **fNumberFormat,
1033 **fPluralRules,
1034 output.getStringRef(),
1035 pluralForm,
1036 status);
1037 if (U_FAILURE(status)) {
1038 return;
1039 }
1040
1041 const SimpleFormatter* formatter =
1042 fCache->getRelativeDateTimeUnitFormatter(fStyle, unit, bFuture, pluralForm);
1043 if (formatter == nullptr) {
1044 // TODO: WARN - look at quantity formatter's action with an error.
1045 status = U_INVALID_FORMAT_ERROR;
1046 return;
1047 }
1048
1049 number::impl::SimpleModifier modifier(*formatter, kRDTLiteralField, false);
1050 modifier.formatAsPrefixSuffix(
1051 output.getStringRef(), 0, output.getStringRef().length(), status);
1052 }
1053
format(UDateDirection direction,UDateAbsoluteUnit unit,UnicodeString & appendTo,UErrorCode & status) const1054 UnicodeString& RelativeDateTimeFormatter::format(
1055 UDateDirection direction,
1056 UDateAbsoluteUnit unit,
1057 UnicodeString& appendTo,
1058 UErrorCode& status) const {
1059 return doFormat(
1060 &RelativeDateTimeFormatter::formatAbsoluteImpl,
1061 appendTo,
1062 status,
1063 direction,
1064 unit);
1065 }
1066
formatToValue(UDateDirection direction,UDateAbsoluteUnit unit,UErrorCode & status) const1067 FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue(
1068 UDateDirection direction,
1069 UDateAbsoluteUnit unit,
1070 UErrorCode& status) const {
1071 return doFormatToValue(
1072 &RelativeDateTimeFormatter::formatAbsoluteImpl,
1073 status,
1074 direction,
1075 unit);
1076 }
1077
formatAbsoluteImpl(UDateDirection direction,UDateAbsoluteUnit unit,FormattedRelativeDateTimeData & output,UErrorCode & status) const1078 void RelativeDateTimeFormatter::formatAbsoluteImpl(
1079 UDateDirection direction,
1080 UDateAbsoluteUnit unit,
1081 FormattedRelativeDateTimeData& output,
1082 UErrorCode& status) const {
1083 if (U_FAILURE(status)) {
1084 return;
1085 }
1086 if (unit == UDAT_ABSOLUTE_NOW && direction != UDAT_DIRECTION_PLAIN) {
1087 status = U_ILLEGAL_ARGUMENT_ERROR;
1088 return;
1089 }
1090
1091 // Get string using fallback.
1092 output.getStringRef().append(
1093 fCache->getAbsoluteUnitString(fStyle, unit, direction),
1094 kRDTLiteralField,
1095 status);
1096 }
1097
format(double offset,URelativeDateTimeUnit unit,UnicodeString & appendTo,UErrorCode & status) const1098 UnicodeString& RelativeDateTimeFormatter::format(
1099 double offset,
1100 URelativeDateTimeUnit unit,
1101 UnicodeString& appendTo,
1102 UErrorCode& status) const {
1103 return doFormat(
1104 &RelativeDateTimeFormatter::formatRelativeImpl,
1105 appendTo,
1106 status,
1107 offset,
1108 unit);
1109 }
1110
formatToValue(double offset,URelativeDateTimeUnit unit,UErrorCode & status) const1111 FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue(
1112 double offset,
1113 URelativeDateTimeUnit unit,
1114 UErrorCode& status) const {
1115 return doFormatToValue(
1116 &RelativeDateTimeFormatter::formatRelativeImpl,
1117 status,
1118 offset,
1119 unit);
1120 }
1121
formatRelativeImpl(double offset,URelativeDateTimeUnit unit,FormattedRelativeDateTimeData & output,UErrorCode & status) const1122 void RelativeDateTimeFormatter::formatRelativeImpl(
1123 double offset,
1124 URelativeDateTimeUnit unit,
1125 FormattedRelativeDateTimeData& output,
1126 UErrorCode& status) const {
1127 if (U_FAILURE(status)) {
1128 return;
1129 }
1130 // TODO:
1131 // The full implementation of this depends on CLDR data that is not yet available,
1132 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
1133 // In the meantime do a quick bring-up by calling the old format method; this
1134 // leaves some holes (even for data that is currently available, such as quarter).
1135 // When the new CLDR data is available, update the data storage accordingly,
1136 // rewrite this to use it directly, and rewrite the old format method to call this
1137 // new one; that is covered by https://unicode-org.atlassian.net/browse/ICU-12171.
1138 UDateDirection direction = UDAT_DIRECTION_COUNT;
1139 if (offset > -2.1 && offset < 2.1) {
1140 // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST
1141 double offsetx100 = offset * 100.0;
1142 int32_t intoffset = (offsetx100 < 0)? (int32_t)(offsetx100-0.5) : (int32_t)(offsetx100+0.5);
1143 switch (intoffset) {
1144 case -200/*-2*/: direction = UDAT_DIRECTION_LAST_2; break;
1145 case -100/*-1*/: direction = UDAT_DIRECTION_LAST; break;
1146 case 0/* 0*/: direction = UDAT_DIRECTION_THIS; break;
1147 case 100/* 1*/: direction = UDAT_DIRECTION_NEXT; break;
1148 case 200/* 2*/: direction = UDAT_DIRECTION_NEXT_2; break;
1149 default: break;
1150 }
1151 }
1152 UDateAbsoluteUnit absunit = UDAT_ABSOLUTE_UNIT_COUNT;
1153 switch (unit) {
1154 case UDAT_REL_UNIT_YEAR: absunit = UDAT_ABSOLUTE_YEAR; break;
1155 case UDAT_REL_UNIT_QUARTER: absunit = UDAT_ABSOLUTE_QUARTER; break;
1156 case UDAT_REL_UNIT_MONTH: absunit = UDAT_ABSOLUTE_MONTH; break;
1157 case UDAT_REL_UNIT_WEEK: absunit = UDAT_ABSOLUTE_WEEK; break;
1158 case UDAT_REL_UNIT_DAY: absunit = UDAT_ABSOLUTE_DAY; break;
1159 case UDAT_REL_UNIT_SECOND:
1160 if (direction == UDAT_DIRECTION_THIS) {
1161 absunit = UDAT_ABSOLUTE_NOW;
1162 direction = UDAT_DIRECTION_PLAIN;
1163 }
1164 break;
1165 case UDAT_REL_UNIT_SUNDAY: absunit = UDAT_ABSOLUTE_SUNDAY; break;
1166 case UDAT_REL_UNIT_MONDAY: absunit = UDAT_ABSOLUTE_MONDAY; break;
1167 case UDAT_REL_UNIT_TUESDAY: absunit = UDAT_ABSOLUTE_TUESDAY; break;
1168 case UDAT_REL_UNIT_WEDNESDAY: absunit = UDAT_ABSOLUTE_WEDNESDAY; break;
1169 case UDAT_REL_UNIT_THURSDAY: absunit = UDAT_ABSOLUTE_THURSDAY; break;
1170 case UDAT_REL_UNIT_FRIDAY: absunit = UDAT_ABSOLUTE_FRIDAY; break;
1171 case UDAT_REL_UNIT_SATURDAY: absunit = UDAT_ABSOLUTE_SATURDAY; break;
1172 case UDAT_REL_UNIT_HOUR: absunit = UDAT_ABSOLUTE_HOUR; break;
1173 case UDAT_REL_UNIT_MINUTE: absunit = UDAT_ABSOLUTE_MINUTE; break;
1174 default: break;
1175 }
1176 if (direction != UDAT_DIRECTION_COUNT && absunit != UDAT_ABSOLUTE_UNIT_COUNT) {
1177 formatAbsoluteImpl(direction, absunit, output, status);
1178 if (output.getStringRef().length() != 0) {
1179 return;
1180 }
1181 }
1182 // otherwise fallback to formatNumeric
1183 formatNumericImpl(offset, unit, output, status);
1184 }
1185
combineDateAndTime(const UnicodeString & relativeDateString,const UnicodeString & timeString,UnicodeString & appendTo,UErrorCode & status) const1186 UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
1187 const UnicodeString& relativeDateString, const UnicodeString& timeString,
1188 UnicodeString& appendTo, UErrorCode& status) const {
1189 return fCache->getCombinedDateAndTime()->format(
1190 timeString, relativeDateString, appendTo, status);
1191 }
1192
adjustForContext(UnicodeString & str) const1193 UnicodeString& RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const {
1194 if (fOptBreakIterator == nullptr
1195 || str.length() == 0 || !u_islower(str.char32At(0))) {
1196 return str;
1197 }
1198
1199 // Must guarantee that one thread at a time accesses the shared break
1200 // iterator.
1201 static UMutex gBrkIterMutex;
1202 Mutex lock(&gBrkIterMutex);
1203 str.toTitle(
1204 fOptBreakIterator->get(),
1205 fLocale,
1206 U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
1207 return str;
1208 }
1209
checkNoAdjustForContext(UErrorCode & status) const1210 UBool RelativeDateTimeFormatter::checkNoAdjustForContext(UErrorCode& status) const {
1211 // This is unsupported because it's hard to keep fields in sync with title
1212 // casing. The code could be written and tested if there is demand.
1213 if (fOptBreakIterator != nullptr) {
1214 status = U_UNSUPPORTED_ERROR;
1215 return false;
1216 }
1217 return true;
1218 }
1219
init(NumberFormat * nfToAdopt,BreakIterator * biToAdopt,UErrorCode & status)1220 void RelativeDateTimeFormatter::init(
1221 NumberFormat *nfToAdopt,
1222 BreakIterator *biToAdopt,
1223 UErrorCode &status) {
1224 LocalPointer<NumberFormat> nf(nfToAdopt);
1225 LocalPointer<BreakIterator> bi(biToAdopt);
1226 UnifiedCache::getByLocale(fLocale, fCache, status);
1227 if (U_FAILURE(status)) {
1228 return;
1229 }
1230 const SharedPluralRules *pr = PluralRules::createSharedInstance(
1231 fLocale, UPLURAL_TYPE_CARDINAL, status);
1232 if (U_FAILURE(status)) {
1233 return;
1234 }
1235 SharedObject::copyPtr(pr, fPluralRules);
1236 pr->removeRef();
1237 if (nf.isNull()) {
1238 const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
1239 fLocale, UNUM_DECIMAL, status);
1240 if (U_FAILURE(status)) {
1241 return;
1242 }
1243 SharedObject::copyPtr(shared, fNumberFormat);
1244 shared->removeRef();
1245 } else {
1246 SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
1247 if (shared == nullptr) {
1248 status = U_MEMORY_ALLOCATION_ERROR;
1249 return;
1250 }
1251 nf.orphan();
1252 SharedObject::copyPtr(shared, fNumberFormat);
1253 }
1254 if (bi.isNull()) {
1255 SharedObject::clearPtr(fOptBreakIterator);
1256 } else {
1257 SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias());
1258 if (shared == nullptr) {
1259 status = U_MEMORY_ALLOCATION_ERROR;
1260 return;
1261 }
1262 bi.orphan();
1263 SharedObject::copyPtr(shared, fOptBreakIterator);
1264 }
1265 }
1266
1267 U_NAMESPACE_END
1268
1269 // Plain C API
1270
1271 U_NAMESPACE_USE
1272
1273
1274 // Magic number: "FRDT" (FormattedRelativeDateTime) in ASCII
1275 UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL(
1276 FormattedRelativeDateTime,
1277 UFormattedRelativeDateTime,
1278 UFormattedRelativeDateTimeImpl,
1279 UFormattedRelativeDateTimeApiHelper,
1280 ureldatefmt,
1281 0x46524454)
1282
1283
1284 U_CAPI URelativeDateTimeFormatter* U_EXPORT2
ureldatefmt_open(const char * locale,UNumberFormat * nfToAdopt,UDateRelativeDateTimeFormatterStyle width,UDisplayContext capitalizationContext,UErrorCode * status)1285 ureldatefmt_open( const char* locale,
1286 UNumberFormat* nfToAdopt,
1287 UDateRelativeDateTimeFormatterStyle width,
1288 UDisplayContext capitalizationContext,
1289 UErrorCode* status )
1290 {
1291 if (U_FAILURE(*status)) {
1292 return nullptr;
1293 }
1294 LocalPointer<RelativeDateTimeFormatter> formatter(new RelativeDateTimeFormatter(Locale(locale),
1295 (NumberFormat*)nfToAdopt, width,
1296 capitalizationContext, *status), *status);
1297 if (U_FAILURE(*status)) {
1298 return nullptr;
1299 }
1300 return (URelativeDateTimeFormatter*)formatter.orphan();
1301 }
1302
1303 U_CAPI void U_EXPORT2
ureldatefmt_close(URelativeDateTimeFormatter * reldatefmt)1304 ureldatefmt_close(URelativeDateTimeFormatter *reldatefmt)
1305 {
1306 delete (RelativeDateTimeFormatter*)reldatefmt;
1307 }
1308
1309 U_CAPI int32_t U_EXPORT2
ureldatefmt_formatNumeric(const URelativeDateTimeFormatter * reldatefmt,double offset,URelativeDateTimeUnit unit,char16_t * result,int32_t resultCapacity,UErrorCode * status)1310 ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt,
1311 double offset,
1312 URelativeDateTimeUnit unit,
1313 char16_t* result,
1314 int32_t resultCapacity,
1315 UErrorCode* status)
1316 {
1317 if (U_FAILURE(*status)) {
1318 return 0;
1319 }
1320 if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) {
1321 *status = U_ILLEGAL_ARGUMENT_ERROR;
1322 return 0;
1323 }
1324 UnicodeString res;
1325 if (result != nullptr) {
1326 // nullptr destination for pure preflighting: empty dummy string
1327 // otherwise, alias the destination buffer (copied from udat_format)
1328 res.setTo(result, 0, resultCapacity);
1329 }
1330 ((RelativeDateTimeFormatter*)reldatefmt)->formatNumeric(offset, unit, res, *status);
1331 if (U_FAILURE(*status)) {
1332 return 0;
1333 }
1334 return res.extract(result, resultCapacity, *status);
1335 }
1336
1337 U_CAPI void U_EXPORT2
ureldatefmt_formatNumericToResult(const URelativeDateTimeFormatter * reldatefmt,double offset,URelativeDateTimeUnit unit,UFormattedRelativeDateTime * result,UErrorCode * status)1338 ureldatefmt_formatNumericToResult(
1339 const URelativeDateTimeFormatter* reldatefmt,
1340 double offset,
1341 URelativeDateTimeUnit unit,
1342 UFormattedRelativeDateTime* result,
1343 UErrorCode* status) {
1344 if (U_FAILURE(*status)) {
1345 return;
1346 }
1347 auto* fmt = reinterpret_cast<const RelativeDateTimeFormatter*>(reldatefmt);
1348 auto* resultImpl = UFormattedRelativeDateTimeApiHelper::validate(result, *status);
1349 resultImpl->fImpl = fmt->formatNumericToValue(offset, unit, *status);
1350 }
1351
1352 U_CAPI int32_t U_EXPORT2
ureldatefmt_format(const URelativeDateTimeFormatter * reldatefmt,double offset,URelativeDateTimeUnit unit,char16_t * result,int32_t resultCapacity,UErrorCode * status)1353 ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt,
1354 double offset,
1355 URelativeDateTimeUnit unit,
1356 char16_t* result,
1357 int32_t resultCapacity,
1358 UErrorCode* status)
1359 {
1360 if (U_FAILURE(*status)) {
1361 return 0;
1362 }
1363 if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) {
1364 *status = U_ILLEGAL_ARGUMENT_ERROR;
1365 return 0;
1366 }
1367 UnicodeString res;
1368 if (result != nullptr) {
1369 // nullptr destination for pure preflighting: empty dummy string
1370 // otherwise, alias the destination buffer (copied from udat_format)
1371 res.setTo(result, 0, resultCapacity);
1372 }
1373 ((RelativeDateTimeFormatter*)reldatefmt)->format(offset, unit, res, *status);
1374 if (U_FAILURE(*status)) {
1375 return 0;
1376 }
1377 return res.extract(result, resultCapacity, *status);
1378 }
1379
1380 U_CAPI void U_EXPORT2
ureldatefmt_formatToResult(const URelativeDateTimeFormatter * reldatefmt,double offset,URelativeDateTimeUnit unit,UFormattedRelativeDateTime * result,UErrorCode * status)1381 ureldatefmt_formatToResult(
1382 const URelativeDateTimeFormatter* reldatefmt,
1383 double offset,
1384 URelativeDateTimeUnit unit,
1385 UFormattedRelativeDateTime* result,
1386 UErrorCode* status) {
1387 if (U_FAILURE(*status)) {
1388 return;
1389 }
1390 auto* fmt = reinterpret_cast<const RelativeDateTimeFormatter*>(reldatefmt);
1391 auto* resultImpl = UFormattedRelativeDateTimeApiHelper::validate(result, *status);
1392 resultImpl->fImpl = fmt->formatToValue(offset, unit, *status);
1393 }
1394
1395 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)1396 ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter* reldatefmt,
1397 const char16_t * relativeDateString,
1398 int32_t relativeDateStringLen,
1399 const char16_t * timeString,
1400 int32_t timeStringLen,
1401 char16_t* result,
1402 int32_t resultCapacity,
1403 UErrorCode* status )
1404 {
1405 if (U_FAILURE(*status)) {
1406 return 0;
1407 }
1408 if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0 ||
1409 (relativeDateString == nullptr ? relativeDateStringLen != 0 : relativeDateStringLen < -1) ||
1410 (timeString == nullptr ? timeStringLen != 0 : timeStringLen < -1)) {
1411 *status = U_ILLEGAL_ARGUMENT_ERROR;
1412 return 0;
1413 }
1414 UnicodeString relDateStr((UBool)(relativeDateStringLen == -1), relativeDateString, relativeDateStringLen);
1415 UnicodeString timeStr((UBool)(timeStringLen == -1), timeString, timeStringLen);
1416 UnicodeString res(result, 0, resultCapacity);
1417 ((RelativeDateTimeFormatter*)reldatefmt)->combineDateAndTime(relDateStr, timeStr, res, *status);
1418 if (U_FAILURE(*status)) {
1419 return 0;
1420 }
1421 return res.extract(result, resultCapacity, *status);
1422 }
1423
1424 #endif /* !UCONFIG_NO_FORMATTING */
1425