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