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