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