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