• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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__anon5ac9a7660111::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__anon5ac9a7660111::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__anon5ac9a7660111::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__anon5ac9a7660111::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__anon5ac9a7660111::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__anon5ac9a7660111::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__anon5ac9a7660111::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__anon5ac9a7660111::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__anon5ac9a7660111::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__anon5ac9a7660111::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__anon5ac9a7660111::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__anon5ac9a7660111::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__anon5ac9a7660111::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__anon5ac9a7660111::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__anon5ac9a7660111::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 ? static_cast<int32_t>(offsetx100 - 0.5)
1165                                            : static_cast<int32_t>(offsetx100 + 0.5);
1166         switch (intoffset) {
1167             case -200/*-2*/: direction = UDAT_DIRECTION_LAST_2; break;
1168             case -100/*-1*/: direction = UDAT_DIRECTION_LAST; break;
1169             case    0/* 0*/: direction = UDAT_DIRECTION_THIS; break;
1170             case  100/* 1*/: direction = UDAT_DIRECTION_NEXT; break;
1171             case  200/* 2*/: direction = UDAT_DIRECTION_NEXT_2; break;
1172             default: break;
1173     	}
1174     }
1175     UDateAbsoluteUnit absunit = UDAT_ABSOLUTE_UNIT_COUNT;
1176     switch (unit) {
1177         case UDAT_REL_UNIT_YEAR:    absunit = UDAT_ABSOLUTE_YEAR; break;
1178         case UDAT_REL_UNIT_QUARTER: absunit = UDAT_ABSOLUTE_QUARTER; break;
1179         case UDAT_REL_UNIT_MONTH:   absunit = UDAT_ABSOLUTE_MONTH; break;
1180         case UDAT_REL_UNIT_WEEK:    absunit = UDAT_ABSOLUTE_WEEK; break;
1181         case UDAT_REL_UNIT_DAY:     absunit = UDAT_ABSOLUTE_DAY; break;
1182         case UDAT_REL_UNIT_SECOND:
1183             if (direction == UDAT_DIRECTION_THIS) {
1184                 absunit = UDAT_ABSOLUTE_NOW;
1185                 direction = UDAT_DIRECTION_PLAIN;
1186             }
1187             break;
1188         case UDAT_REL_UNIT_SUNDAY:  absunit = UDAT_ABSOLUTE_SUNDAY; break;
1189         case UDAT_REL_UNIT_MONDAY:  absunit = UDAT_ABSOLUTE_MONDAY; break;
1190         case UDAT_REL_UNIT_TUESDAY:  absunit = UDAT_ABSOLUTE_TUESDAY; break;
1191         case UDAT_REL_UNIT_WEDNESDAY:  absunit = UDAT_ABSOLUTE_WEDNESDAY; break;
1192         case UDAT_REL_UNIT_THURSDAY:  absunit = UDAT_ABSOLUTE_THURSDAY; break;
1193         case UDAT_REL_UNIT_FRIDAY:  absunit = UDAT_ABSOLUTE_FRIDAY; break;
1194         case UDAT_REL_UNIT_SATURDAY:  absunit = UDAT_ABSOLUTE_SATURDAY; break;
1195         case UDAT_REL_UNIT_HOUR:  absunit = UDAT_ABSOLUTE_HOUR; break;
1196         case UDAT_REL_UNIT_MINUTE:  absunit = UDAT_ABSOLUTE_MINUTE; break;
1197         default: break;
1198     }
1199     if (direction != UDAT_DIRECTION_COUNT && absunit != UDAT_ABSOLUTE_UNIT_COUNT) {
1200         formatAbsoluteImpl(direction, absunit, output, status);
1201         if (output.getStringRef().length() != 0) {
1202             return;
1203         }
1204     }
1205     // otherwise fallback to formatNumeric
1206     formatNumericImpl(offset, unit, output, status);
1207 }
1208 
combineDateAndTime(const UnicodeString & relativeDateString,const UnicodeString & timeString,UnicodeString & appendTo,UErrorCode & status) const1209 UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
1210         const UnicodeString& relativeDateString, const UnicodeString& timeString,
1211         UnicodeString& appendTo, UErrorCode& status) const {
1212     return fCache->getCombinedDateAndTime()->format(
1213             timeString, relativeDateString, appendTo, status);
1214 }
1215 
adjustForContext(UnicodeString & str) const1216 UnicodeString& RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const {
1217 #if !UCONFIG_NO_BREAK_ITERATION
1218     if (fOptBreakIterator == nullptr
1219         || str.length() == 0 || !u_islower(str.char32At(0))) {
1220         return str;
1221     }
1222 
1223     // Must guarantee that one thread at a time accesses the shared break
1224     // iterator.
1225     static UMutex gBrkIterMutex;
1226     Mutex lock(&gBrkIterMutex);
1227     str.toTitle(
1228             fOptBreakIterator->get(),
1229             fLocale,
1230             U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
1231 #endif // !UCONFIG_NO_BREAK_ITERATION
1232     return str;
1233 }
1234 
checkNoAdjustForContext(UErrorCode & status) const1235 UBool RelativeDateTimeFormatter::checkNoAdjustForContext(UErrorCode& status) const {
1236 #if !UCONFIG_NO_BREAK_ITERATION
1237     // This is unsupported because it's hard to keep fields in sync with title
1238     // casing. The code could be written and tested if there is demand.
1239     if (fOptBreakIterator != nullptr) {
1240         status = U_UNSUPPORTED_ERROR;
1241         return false;
1242     }
1243 #else
1244     (void)status; // suppress unused argument warning
1245 #endif // !UCONFIG_NO_BREAK_ITERATION
1246     return true;
1247 }
1248 
init(NumberFormat * nfToAdopt,BreakIterator * biToAdopt,UErrorCode & status)1249 void RelativeDateTimeFormatter::init(
1250         NumberFormat *nfToAdopt,
1251 #if !UCONFIG_NO_BREAK_ITERATION
1252         BreakIterator *biToAdopt,
1253 #else
1254         std::nullptr_t,
1255 #endif // !UCONFIG_NO_BREAK_ITERATION
1256         UErrorCode &status) {
1257     LocalPointer<NumberFormat> nf(nfToAdopt);
1258 #if !UCONFIG_NO_BREAK_ITERATION
1259     LocalPointer<BreakIterator> bi(biToAdopt);
1260 #endif // !UCONFIG_NO_BREAK_ITERATION
1261     UnifiedCache::getByLocale(fLocale, fCache, status);
1262     if (U_FAILURE(status)) {
1263         return;
1264     }
1265     const SharedPluralRules *pr = PluralRules::createSharedInstance(
1266             fLocale, UPLURAL_TYPE_CARDINAL, status);
1267     if (U_FAILURE(status)) {
1268         return;
1269     }
1270     SharedObject::copyPtr(pr, fPluralRules);
1271     pr->removeRef();
1272     if (nf.isNull()) {
1273        const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
1274                fLocale, UNUM_DECIMAL, status);
1275         if (U_FAILURE(status)) {
1276             return;
1277         }
1278         SharedObject::copyPtr(shared, fNumberFormat);
1279         shared->removeRef();
1280     } else {
1281         SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
1282         if (shared == nullptr) {
1283             status = U_MEMORY_ALLOCATION_ERROR;
1284             return;
1285         }
1286         nf.orphan();
1287         SharedObject::copyPtr(shared, fNumberFormat);
1288     }
1289 #if !UCONFIG_NO_BREAK_ITERATION
1290     if (bi.isNull()) {
1291         SharedObject::clearPtr(fOptBreakIterator);
1292     } else {
1293         SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias());
1294         if (shared == nullptr) {
1295             status = U_MEMORY_ALLOCATION_ERROR;
1296             return;
1297         }
1298         bi.orphan();
1299         SharedObject::copyPtr(shared, fOptBreakIterator);
1300     }
1301 #endif // !UCONFIG_NO_BREAK_ITERATION
1302 }
1303 
1304 U_NAMESPACE_END
1305 
1306 // Plain C API
1307 
1308 U_NAMESPACE_USE
1309 
1310 
1311 // Magic number: "FRDT" (FormattedRelativeDateTime) in ASCII
1312 UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL(
1313     FormattedRelativeDateTime,
1314     UFormattedRelativeDateTime,
1315     UFormattedRelativeDateTimeImpl,
1316     UFormattedRelativeDateTimeApiHelper,
1317     ureldatefmt,
1318     0x46524454)
1319 
1320 
1321 U_CAPI URelativeDateTimeFormatter* U_EXPORT2
ureldatefmt_open(const char * locale,UNumberFormat * nfToAdopt,UDateRelativeDateTimeFormatterStyle width,UDisplayContext capitalizationContext,UErrorCode * status)1322 ureldatefmt_open( const char*          locale,
1323                   UNumberFormat*       nfToAdopt,
1324                   UDateRelativeDateTimeFormatterStyle width,
1325                   UDisplayContext      capitalizationContext,
1326                   UErrorCode*          status )
1327 {
1328     if (U_FAILURE(*status)) {
1329         return nullptr;
1330     }
1331     LocalPointer<RelativeDateTimeFormatter> formatter(new RelativeDateTimeFormatter(Locale(locale),
1332                                                               (NumberFormat*)nfToAdopt, width,
1333                                                               capitalizationContext, *status), *status);
1334     if (U_FAILURE(*status)) {
1335         return nullptr;
1336     }
1337     return (URelativeDateTimeFormatter*)formatter.orphan();
1338 }
1339 
1340 U_CAPI void U_EXPORT2
ureldatefmt_close(URelativeDateTimeFormatter * reldatefmt)1341 ureldatefmt_close(URelativeDateTimeFormatter *reldatefmt)
1342 {
1343     delete (RelativeDateTimeFormatter*)reldatefmt;
1344 }
1345 
1346 U_CAPI int32_t U_EXPORT2
ureldatefmt_formatNumeric(const URelativeDateTimeFormatter * reldatefmt,double offset,URelativeDateTimeUnit unit,char16_t * result,int32_t resultCapacity,UErrorCode * status)1347 ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt,
1348                     double                offset,
1349                     URelativeDateTimeUnit unit,
1350                     char16_t*                result,
1351                     int32_t               resultCapacity,
1352                     UErrorCode*           status)
1353 {
1354     if (U_FAILURE(*status)) {
1355         return 0;
1356     }
1357     if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) {
1358         *status = U_ILLEGAL_ARGUMENT_ERROR;
1359         return 0;
1360     }
1361     UnicodeString res;
1362     if (result != nullptr) {
1363         // nullptr destination for pure preflighting: empty dummy string
1364         // otherwise, alias the destination buffer (copied from udat_format)
1365         res.setTo(result, 0, resultCapacity);
1366     }
1367     ((RelativeDateTimeFormatter*)reldatefmt)->formatNumeric(offset, unit, res, *status);
1368     if (U_FAILURE(*status)) {
1369         return 0;
1370     }
1371     return res.extract(result, resultCapacity, *status);
1372 }
1373 
1374 U_CAPI void U_EXPORT2
ureldatefmt_formatNumericToResult(const URelativeDateTimeFormatter * reldatefmt,double offset,URelativeDateTimeUnit unit,UFormattedRelativeDateTime * result,UErrorCode * status)1375 ureldatefmt_formatNumericToResult(
1376         const URelativeDateTimeFormatter* reldatefmt,
1377         double                            offset,
1378         URelativeDateTimeUnit             unit,
1379         UFormattedRelativeDateTime*       result,
1380         UErrorCode*                       status) {
1381     if (U_FAILURE(*status)) {
1382         return;
1383     }
1384     const auto* fmt = reinterpret_cast<const RelativeDateTimeFormatter*>(reldatefmt);
1385     auto* resultImpl = UFormattedRelativeDateTimeApiHelper::validate(result, *status);
1386     resultImpl->fImpl = fmt->formatNumericToValue(offset, unit, *status);
1387 }
1388 
1389 U_CAPI int32_t U_EXPORT2
ureldatefmt_format(const URelativeDateTimeFormatter * reldatefmt,double offset,URelativeDateTimeUnit unit,char16_t * result,int32_t resultCapacity,UErrorCode * status)1390 ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt,
1391                     double                offset,
1392                     URelativeDateTimeUnit unit,
1393                     char16_t*                result,
1394                     int32_t               resultCapacity,
1395                     UErrorCode*           status)
1396 {
1397     if (U_FAILURE(*status)) {
1398         return 0;
1399     }
1400     if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) {
1401         *status = U_ILLEGAL_ARGUMENT_ERROR;
1402         return 0;
1403     }
1404     UnicodeString res;
1405     if (result != nullptr) {
1406         // nullptr destination for pure preflighting: empty dummy string
1407         // otherwise, alias the destination buffer (copied from udat_format)
1408         res.setTo(result, 0, resultCapacity);
1409     }
1410     ((RelativeDateTimeFormatter*)reldatefmt)->format(offset, unit, res, *status);
1411     if (U_FAILURE(*status)) {
1412         return 0;
1413     }
1414     return res.extract(result, resultCapacity, *status);
1415 }
1416 
1417 U_CAPI void U_EXPORT2
ureldatefmt_formatToResult(const URelativeDateTimeFormatter * reldatefmt,double offset,URelativeDateTimeUnit unit,UFormattedRelativeDateTime * result,UErrorCode * status)1418 ureldatefmt_formatToResult(
1419         const URelativeDateTimeFormatter* reldatefmt,
1420         double                            offset,
1421         URelativeDateTimeUnit             unit,
1422         UFormattedRelativeDateTime*       result,
1423         UErrorCode*                       status) {
1424     if (U_FAILURE(*status)) {
1425         return;
1426     }
1427     const auto* fmt = reinterpret_cast<const RelativeDateTimeFormatter*>(reldatefmt);
1428     auto* resultImpl = UFormattedRelativeDateTimeApiHelper::validate(result, *status);
1429     resultImpl->fImpl = fmt->formatToValue(offset, unit, *status);
1430 }
1431 
1432 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)1433 ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter* reldatefmt,
1434                     const char16_t *     relativeDateString,
1435                     int32_t           relativeDateStringLen,
1436                     const char16_t *     timeString,
1437                     int32_t           timeStringLen,
1438                     char16_t*            result,
1439                     int32_t           resultCapacity,
1440                     UErrorCode*       status )
1441 {
1442     if (U_FAILURE(*status)) {
1443         return 0;
1444     }
1445     if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0 ||
1446             (relativeDateString == nullptr ? relativeDateStringLen != 0 : relativeDateStringLen < -1) ||
1447             (timeString == nullptr ? timeStringLen != 0 : timeStringLen < -1)) {
1448         *status = U_ILLEGAL_ARGUMENT_ERROR;
1449         return 0;
1450     }
1451     UnicodeString relDateStr(relativeDateStringLen == -1, relativeDateString, relativeDateStringLen);
1452     UnicodeString timeStr(timeStringLen == -1, timeString, timeStringLen);
1453     UnicodeString res(result, 0, resultCapacity);
1454     ((RelativeDateTimeFormatter*)reldatefmt)->combineDateAndTime(relDateStr, timeStr, res, *status);
1455     if (U_FAILURE(*status)) {
1456         return 0;
1457     }
1458     return res.extract(result, resultCapacity, *status);
1459 }
1460 
1461 #endif /* !UCONFIG_NO_FORMATTING */
1462