• 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 "unicode/dtfmtsym.h"
19 #include "unicode/ucasemap.h"
20 #include "unicode/ureldatefmt.h"
21 #include "unicode/udisplaycontext.h"
22 #include "unicode/unum.h"
23 #include "unicode/localpointer.h"
24 #include "unicode/plurrule.h"
25 #include "unicode/simpleformatter.h"
26 #include "unicode/decimfmt.h"
27 #include "unicode/numfmt.h"
28 #include "unicode/brkiter.h"
29 #include "unicode/simpleformatter.h"
30 #include "uresimp.h"
31 #include "unicode/ures.h"
32 #include "cstring.h"
33 #include "ucln_in.h"
34 #include "mutex.h"
35 #include "charstr.h"
36 #include "uassert.h"
37 #include "quantityformatter.h"
38 #include "resource.h"
39 #include "sharedbreakiterator.h"
40 #include "sharedpluralrules.h"
41 #include "sharednumberformat.h"
42 #include "standardplural.h"
43 #include "unifiedcache.h"
44 
45 // Copied from uscript_props.cpp
46 
47 static UMutex gBrkIterMutex = U_MUTEX_INITIALIZER;
48 
49 U_NAMESPACE_BEGIN
50 
51 // RelativeDateTimeFormatter specific data for a single locale
52 class RelativeDateTimeCacheData: public SharedObject {
53 public:
RelativeDateTimeCacheData()54     RelativeDateTimeCacheData() : combinedDateAndTime(nullptr) {
55         // Initialize the cache arrays
56         for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
57             for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) {
58                 for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
59                     relativeUnitsFormatters[style][relUnit][0][pl] = nullptr;
60                     relativeUnitsFormatters[style][relUnit][1][pl] = nullptr;
61                 }
62             }
63         }
64         for (int32_t i = 0; i < UDAT_STYLE_COUNT; ++i) {
65           fallBackCache[i] = -1;
66         }
67     }
68     virtual ~RelativeDateTimeCacheData();
69 
70     // no numbers: e.g Next Tuesday; Yesterday; etc.
71     UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT];
72 
73     // SimpleFormatter pointers for relative unit format,
74     // e.g., Next Tuesday; Yesterday; etc. For third index, 0
75     // means past, e.g., 5 days ago; 1 means future, e.g., in 5 days.
76     SimpleFormatter *relativeUnitsFormatters[UDAT_STYLE_COUNT]
77         [UDAT_REL_UNIT_COUNT][2][StandardPlural::COUNT];
78 
79     const UnicodeString& getAbsoluteUnitString(int32_t fStyle,
80                                                UDateAbsoluteUnit unit,
81                                                UDateDirection direction) const;
82     const SimpleFormatter* getRelativeUnitFormatter(int32_t fStyle,
83                                                     UDateRelativeUnit unit,
84                                                     int32_t pastFutureIndex,
85                                                     int32_t pluralUnit) const;
86     const SimpleFormatter* getRelativeDateTimeUnitFormatter(int32_t fStyle,
87                                                     URelativeDateTimeUnit unit,
88                                                     int32_t pastFutureIndex,
89                                                     int32_t pluralUnit) const;
90 
91     const UnicodeString emptyString;
92 
93     // Mappping from source to target styles for alias fallback.
94     int32_t fallBackCache[UDAT_STYLE_COUNT];
95 
adoptCombinedDateAndTime(SimpleFormatter * fmtToAdopt)96     void adoptCombinedDateAndTime(SimpleFormatter *fmtToAdopt) {
97         delete combinedDateAndTime;
98         combinedDateAndTime = fmtToAdopt;
99     }
getCombinedDateAndTime() const100     const SimpleFormatter *getCombinedDateAndTime() const {
101         return combinedDateAndTime;
102     }
103 
104 private:
105     SimpleFormatter *combinedDateAndTime;
106     RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other);
107     RelativeDateTimeCacheData& operator=(
108             const RelativeDateTimeCacheData &other);
109 };
110 
~RelativeDateTimeCacheData()111 RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
112     // clear out the cache arrays
113     for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
114         for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) {
115             for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
116                 delete relativeUnitsFormatters[style][relUnit][0][pl];
117                 delete relativeUnitsFormatters[style][relUnit][1][pl];
118             }
119         }
120     }
121     delete combinedDateAndTime;
122 }
123 
124 
125 // Use fallback cache for absolute units.
getAbsoluteUnitString(int32_t fStyle,UDateAbsoluteUnit unit,UDateDirection direction) const126 const UnicodeString& RelativeDateTimeCacheData::getAbsoluteUnitString(
127         int32_t fStyle, UDateAbsoluteUnit unit, UDateDirection direction) const {
128     int32_t style = fStyle;
129     do {
130         if (!absoluteUnits[style][unit][direction].isEmpty()) {
131             return absoluteUnits[style][unit][direction];
132         }
133         style = fallBackCache[style];
134     } while (style != -1);
135     return emptyString;
136 }
137 
getRelativeUnitFormatter(int32_t fStyle,UDateRelativeUnit unit,int32_t pastFutureIndex,int32_t pluralUnit) const138  const SimpleFormatter* RelativeDateTimeCacheData::getRelativeUnitFormatter(
139         int32_t fStyle,
140         UDateRelativeUnit unit,
141         int32_t pastFutureIndex,
142         int32_t pluralUnit) const {
143    URelativeDateTimeUnit rdtunit = UDAT_REL_UNIT_COUNT;
144    switch (unit) {
145        case UDAT_RELATIVE_YEARS:   rdtunit = UDAT_REL_UNIT_YEAR; break;
146        case UDAT_RELATIVE_MONTHS:  rdtunit = UDAT_REL_UNIT_MONTH; break;
147        case UDAT_RELATIVE_WEEKS:   rdtunit = UDAT_REL_UNIT_WEEK; break;
148        case UDAT_RELATIVE_DAYS:    rdtunit = UDAT_REL_UNIT_DAY; break;
149        case UDAT_RELATIVE_HOURS:   rdtunit = UDAT_REL_UNIT_HOUR; break;
150        case UDAT_RELATIVE_MINUTES: rdtunit = UDAT_REL_UNIT_MINUTE; break;
151        case UDAT_RELATIVE_SECONDS: rdtunit = UDAT_REL_UNIT_SECOND; break;
152        default: // a unit that the above method does not handle
153             return nullptr;
154    }
155 
156    return getRelativeDateTimeUnitFormatter(fStyle, rdtunit, pastFutureIndex, pluralUnit);
157  }
158 
159  // Use fallback cache for SimpleFormatter relativeUnits.
getRelativeDateTimeUnitFormatter(int32_t fStyle,URelativeDateTimeUnit unit,int32_t pastFutureIndex,int32_t pluralUnit) const160  const SimpleFormatter* RelativeDateTimeCacheData::getRelativeDateTimeUnitFormatter(
161         int32_t fStyle,
162         URelativeDateTimeUnit unit,
163         int32_t pastFutureIndex,
164         int32_t pluralUnit) const {
165     int32_t style = fStyle;
166     do {
167         if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != nullptr) {
168             return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit];
169         }
170         style = fallBackCache[style];
171     } while (style != -1);
172     return nullptr;  // No formatter found.
173  }
174 
getStringWithFallback(const UResourceBundle * resource,const char * key,UnicodeString & result,UErrorCode & status)175 static UBool getStringWithFallback(
176         const UResourceBundle *resource,
177         const char *key,
178         UnicodeString &result,
179         UErrorCode &status) {
180     int32_t len = 0;
181     const UChar *resStr = ures_getStringByKeyWithFallback(
182         resource, key, &len, &status);
183     if (U_FAILURE(status)) {
184         return FALSE;
185     }
186     result.setTo(TRUE, resStr, len);
187     return TRUE;
188 }
189 
190 
getStringByIndex(const UResourceBundle * resource,int32_t idx,UnicodeString & result,UErrorCode & status)191 static UBool getStringByIndex(
192         const UResourceBundle *resource,
193         int32_t idx,
194         UnicodeString &result,
195         UErrorCode &status) {
196     int32_t len = 0;
197     const UChar *resStr = ures_getStringByIndex(
198             resource, idx, &len, &status);
199     if (U_FAILURE(status)) {
200         return FALSE;
201     }
202     result.setTo(TRUE, resStr, len);
203     return TRUE;
204 }
205 
206 namespace {
207 
208 /**
209  * Sink for enumerating all of the measurement unit display names.
210  *
211  * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
212  * Only store a value if it is still missing, that is, it has not been overridden.
213  */
214 struct RelDateTimeFmtDataSink : public ResourceSink {
215 
216     /**
217      * Sink for patterns for relative dates and times. For example,
218      * fields/relative/...
219      */
220 
221     // Generic unit enum for storing Unit info.
222     typedef enum RelAbsUnit {
223         INVALID_UNIT = -1,
224         SECOND,
225         MINUTE,
226         HOUR,
227         DAY,
228         WEEK,
229         MONTH,
230         QUARTER,
231         YEAR,
232         SUNDAY,
233         MONDAY,
234         TUESDAY,
235         WEDNESDAY,
236         THURSDAY,
237         FRIDAY,
238         SATURDAY
239     } RelAbsUnit;
240 
relUnitFromGeneric__anona2f48bd90111::RelDateTimeFmtDataSink241     static int32_t relUnitFromGeneric(RelAbsUnit genUnit) {
242         // Converts the generic units to UDAT_RELATIVE version.
243         switch (genUnit) {
244             case SECOND:
245                 return UDAT_REL_UNIT_SECOND;
246             case MINUTE:
247                 return UDAT_REL_UNIT_MINUTE;
248             case HOUR:
249                 return UDAT_REL_UNIT_HOUR;
250             case DAY:
251                 return UDAT_REL_UNIT_DAY;
252             case WEEK:
253                 return UDAT_REL_UNIT_WEEK;
254             case MONTH:
255                 return UDAT_REL_UNIT_MONTH;
256             case QUARTER:
257                 return UDAT_REL_UNIT_QUARTER;
258             case YEAR:
259                 return UDAT_REL_UNIT_YEAR;
260             case SUNDAY:
261                 return UDAT_REL_UNIT_SUNDAY;
262             case MONDAY:
263                 return UDAT_REL_UNIT_MONDAY;
264             case TUESDAY:
265                 return UDAT_REL_UNIT_TUESDAY;
266             case WEDNESDAY:
267                 return UDAT_REL_UNIT_WEDNESDAY;
268             case THURSDAY:
269                 return UDAT_REL_UNIT_THURSDAY;
270             case FRIDAY:
271                 return UDAT_REL_UNIT_FRIDAY;
272             case SATURDAY:
273                 return UDAT_REL_UNIT_SATURDAY;
274             default:
275                 return -1;
276         }
277     }
278 
absUnitFromGeneric__anona2f48bd90111::RelDateTimeFmtDataSink279     static int32_t absUnitFromGeneric(RelAbsUnit genUnit) {
280         // Converts the generic units to UDAT_RELATIVE version.
281         switch (genUnit) {
282             case DAY:
283                 return UDAT_ABSOLUTE_DAY;
284             case WEEK:
285                 return UDAT_ABSOLUTE_WEEK;
286             case MONTH:
287                 return UDAT_ABSOLUTE_MONTH;
288             case QUARTER:
289                 return UDAT_ABSOLUTE_QUARTER;
290             case YEAR:
291                 return UDAT_ABSOLUTE_YEAR;
292             case SUNDAY:
293                 return UDAT_ABSOLUTE_SUNDAY;
294             case MONDAY:
295                 return UDAT_ABSOLUTE_MONDAY;
296             case TUESDAY:
297                 return UDAT_ABSOLUTE_TUESDAY;
298             case WEDNESDAY:
299                 return UDAT_ABSOLUTE_WEDNESDAY;
300             case THURSDAY:
301                 return UDAT_ABSOLUTE_THURSDAY;
302             case FRIDAY:
303                 return UDAT_ABSOLUTE_FRIDAY;
304             case SATURDAY:
305                 return UDAT_ABSOLUTE_SATURDAY;
306             default:
307                 return -1;
308         }
309     }
310 
keyToDirection__anona2f48bd90111::RelDateTimeFmtDataSink311     static int32_t keyToDirection(const char* key) {
312         if (uprv_strcmp(key, "-2") == 0) {
313             return UDAT_DIRECTION_LAST_2;
314         }
315         if (uprv_strcmp(key, "-1") == 0) {
316             return UDAT_DIRECTION_LAST;
317         }
318         if (uprv_strcmp(key, "0") == 0) {
319             return UDAT_DIRECTION_THIS;
320         }
321         if (uprv_strcmp(key, "1") == 0) {
322             return UDAT_DIRECTION_NEXT;
323         }
324         if (uprv_strcmp(key, "2") == 0) {
325             return UDAT_DIRECTION_NEXT_2;
326         }
327         return -1;
328     }
329 
330     // Values kept between levels of parsing the CLDR data.
331     int32_t pastFutureIndex;  // 0 == past or 1 ==  future
332     UDateRelativeDateTimeFormatterStyle style;  // {LONG, SHORT, NARROW}
333     RelAbsUnit genericUnit;
334 
335     RelativeDateTimeCacheData &outputData;
336 
337     // Constructor
RelDateTimeFmtDataSink__anona2f48bd90111::RelDateTimeFmtDataSink338     RelDateTimeFmtDataSink(RelativeDateTimeCacheData& cacheData)
339         : outputData(cacheData) {
340         // Clear cacheData.fallBackCache
341         cacheData.fallBackCache[UDAT_STYLE_LONG] = -1;
342         cacheData.fallBackCache[UDAT_STYLE_SHORT] = -1;
343         cacheData.fallBackCache[UDAT_STYLE_NARROW] = -1;
344     }
345 
346     ~RelDateTimeFmtDataSink();
347 
348     // Utility functions
styleFromString__anona2f48bd90111::RelDateTimeFmtDataSink349     static UDateRelativeDateTimeFormatterStyle styleFromString(const char *s) {
350         int32_t len = static_cast<int32_t>(uprv_strlen(s));
351         if (len >= 7 && uprv_strcmp(s + len - 7, "-narrow") == 0) {
352             return UDAT_STYLE_NARROW;
353         }
354         if (len >= 6 && uprv_strcmp(s + len - 6, "-short") == 0) {
355             return UDAT_STYLE_SHORT;
356         }
357         return UDAT_STYLE_LONG;
358     }
359 
styleSuffixLength__anona2f48bd90111::RelDateTimeFmtDataSink360     static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style) {
361         switch (style) {
362             case UDAT_STYLE_NARROW:
363                 return 7;
364             case UDAT_STYLE_SHORT:
365                 return 6;
366             default:
367                 return 0;
368         }
369     }
370 
371     // Utility functions
styleFromAliasUnicodeString__anona2f48bd90111::RelDateTimeFmtDataSink372     static UDateRelativeDateTimeFormatterStyle styleFromAliasUnicodeString(UnicodeString s) {
373         static const UChar narrow[7] = {0x002D, 0x006E, 0x0061, 0x0072, 0x0072, 0x006F, 0x0077};
374         static const UChar sshort[6] = {0x002D, 0x0073, 0x0068, 0x006F, 0x0072, 0x0074,};
375         if (s.endsWith(narrow, 7)) {
376             return UDAT_STYLE_NARROW;
377         }
378         if (s.endsWith(sshort, 6)) {
379             return UDAT_STYLE_SHORT;
380         }
381         return UDAT_STYLE_LONG;
382     }
383 
unitOrNegativeFromString__anona2f48bd90111::RelDateTimeFmtDataSink384     static RelAbsUnit unitOrNegativeFromString(const char* keyword, int32_t length) {
385         // Quick check from string to enum.
386         switch (length) {
387             case 3:
388                 if (uprv_strncmp(keyword, "day", length) == 0) {
389                     return DAY;
390                 } else if (uprv_strncmp(keyword, "sun", length) == 0) {
391                     return SUNDAY;
392                 } else if (uprv_strncmp(keyword, "mon", length) == 0) {
393                     return MONDAY;
394                 } else if (uprv_strncmp(keyword, "tue", length) == 0) {
395                     return TUESDAY;
396                 } else if (uprv_strncmp(keyword, "wed", length) == 0) {
397                     return WEDNESDAY;
398                 } else if (uprv_strncmp(keyword, "thu", length) == 0) {
399                     return THURSDAY;
400                 } else if (uprv_strncmp(keyword, "fri", length) == 0) {
401                     return FRIDAY;
402                 } else if (uprv_strncmp(keyword, "sat", length) == 0) {
403                     return SATURDAY;
404                 }
405                 break;
406             case 4:
407                 if (uprv_strncmp(keyword, "hour", length) == 0) {
408                     return HOUR;
409                 } else if (uprv_strncmp(keyword, "week", length) == 0) {
410                     return WEEK;
411                 } else if (uprv_strncmp(keyword, "year", length) == 0) {
412                     return YEAR;
413                 }
414                 break;
415             case 5:
416                 if (uprv_strncmp(keyword, "month", length) == 0) {
417                     return MONTH;
418                 }
419                 break;
420             case 6:
421                 if (uprv_strncmp(keyword, "minute", length) == 0) {
422                     return MINUTE;
423                 } else if (uprv_strncmp(keyword, "second", length) == 0) {
424                     return SECOND;
425                 }
426                 break;
427             case 7:
428                 if (uprv_strncmp(keyword, "quarter", length) == 0) {
429                     return QUARTER;  // TODO: Check @provisional
430                   }
431                 break;
432             default:
433                 break;
434         }
435         return INVALID_UNIT;
436     }
437 
handlePlainDirection__anona2f48bd90111::RelDateTimeFmtDataSink438     void handlePlainDirection(ResourceValue &value, UErrorCode &errorCode) {
439         // Handle Display Name for PLAIN direction for some units.
440         if (U_FAILURE(errorCode)) { return; }
441 
442         int32_t absUnit = absUnitFromGeneric(genericUnit);
443         if (absUnit < 0) {
444           return;  // Not interesting.
445         }
446 
447         // Store displayname if not set.
448         if (outputData.absoluteUnits[style]
449             [absUnit][UDAT_DIRECTION_PLAIN].isEmpty()) {
450             outputData.absoluteUnits[style]
451                 [absUnit][UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
452             return;
453         }
454     }
455 
consumeTableRelative__anona2f48bd90111::RelDateTimeFmtDataSink456     void consumeTableRelative(const char *key, ResourceValue &value, UErrorCode &errorCode) {
457         ResourceTable unitTypesTable = value.getTable(errorCode);
458         if (U_FAILURE(errorCode)) { return; }
459 
460         for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
461             if (value.getType() == URES_STRING) {
462                 int32_t direction = keyToDirection(key);
463                 if (direction < 0) {
464                   continue;
465                 }
466 
467                 int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
468                 if (relUnitIndex == UDAT_REL_UNIT_SECOND && uprv_strcmp(key, "0") == 0 &&
469                     outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN].isEmpty()) {
470                     // Handle "NOW"
471                     outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW]
472                         [UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
473                 }
474 
475                 int32_t absUnitIndex = absUnitFromGeneric(genericUnit);
476                 if (absUnitIndex < 0) {
477                     continue;
478                 }
479                 // Only reset if slot is empty.
480                 if (outputData.absoluteUnits[style][absUnitIndex][direction].isEmpty()) {
481                     outputData.absoluteUnits[style][absUnitIndex]
482                         [direction].fastCopyFrom(value.getUnicodeString(errorCode));
483                 }
484             }
485         }
486     }
487 
consumeTimeDetail__anona2f48bd90111::RelDateTimeFmtDataSink488     void consumeTimeDetail(int32_t relUnitIndex,
489                            const char *key, ResourceValue &value, UErrorCode &errorCode) {
490         ResourceTable unitTypesTable = value.getTable(errorCode);
491         if (U_FAILURE(errorCode)) { return; }
492 
493           for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
494             if (value.getType() == URES_STRING) {
495                 int32_t pluralIndex = StandardPlural::indexOrNegativeFromString(key);
496                 if (pluralIndex >= 0) {
497                     SimpleFormatter **patterns =
498                         outputData.relativeUnitsFormatters[style][relUnitIndex]
499                         [pastFutureIndex];
500                     // Only set if not already established.
501                     if (patterns[pluralIndex] == nullptr) {
502                         patterns[pluralIndex] = new SimpleFormatter(
503                             value.getUnicodeString(errorCode), 0, 1, errorCode);
504                         if (patterns[pluralIndex] == nullptr) {
505                             errorCode = U_MEMORY_ALLOCATION_ERROR;
506                         }
507                     }
508                 }
509             }
510         }
511     }
512 
consumeTableRelativeTime__anona2f48bd90111::RelDateTimeFmtDataSink513     void consumeTableRelativeTime(const char *key, ResourceValue &value, UErrorCode &errorCode) {
514         ResourceTable relativeTimeTable = value.getTable(errorCode);
515         if (U_FAILURE(errorCode)) { return; }
516 
517         int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
518         if (relUnitIndex < 0) {
519             return;
520         }
521         for (int32_t i = 0; relativeTimeTable.getKeyAndValue(i, key, value); ++i) {
522             if (uprv_strcmp(key, "past") == 0) {
523                 pastFutureIndex = 0;
524             } else if (uprv_strcmp(key, "future") == 0) {
525                 pastFutureIndex = 1;
526             } else {
527                 // Unknown key.
528                 continue;
529             }
530             consumeTimeDetail(relUnitIndex, key, value, errorCode);
531         }
532     }
533 
consumeAlias__anona2f48bd90111::RelDateTimeFmtDataSink534     void consumeAlias(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
535 
536         UDateRelativeDateTimeFormatterStyle sourceStyle = styleFromString(key);
537         const UnicodeString valueStr = value.getAliasUnicodeString(errorCode);
538         if (U_FAILURE(errorCode)) { return; }
539 
540         UDateRelativeDateTimeFormatterStyle targetStyle =
541             styleFromAliasUnicodeString(valueStr);
542 
543         if (sourceStyle == targetStyle) {
544             errorCode = U_INVALID_FORMAT_ERROR;
545             return;
546         }
547         if (outputData.fallBackCache[sourceStyle] != -1 &&
548             outputData.fallBackCache[sourceStyle] != targetStyle) {
549             errorCode = U_INVALID_FORMAT_ERROR;
550             return;
551         }
552         outputData.fallBackCache[sourceStyle] = targetStyle;
553     }
554 
consumeTimeUnit__anona2f48bd90111::RelDateTimeFmtDataSink555     void consumeTimeUnit(const char *key, ResourceValue &value, UErrorCode &errorCode) {
556         ResourceTable unitTypesTable = value.getTable(errorCode);
557         if (U_FAILURE(errorCode)) { return; }
558 
559         for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
560             // Handle display name.
561             if (uprv_strcmp(key, "dn") == 0 && value.getType() == URES_STRING) {
562                 handlePlainDirection(value, errorCode);
563             }
564             if (value.getType() == URES_TABLE) {
565                 if (uprv_strcmp(key, "relative") == 0) {
566                     consumeTableRelative(key, value, errorCode);
567                 } else if (uprv_strcmp(key, "relativeTime") == 0) {
568                     consumeTableRelativeTime(key, value, errorCode);
569                 }
570             }
571         }
572     }
573 
put__anona2f48bd90111::RelDateTimeFmtDataSink574     virtual void put(const char *key, ResourceValue &value,
575                      UBool /*noFallback*/, UErrorCode &errorCode) {
576         // Main entry point to sink
577         ResourceTable table = value.getTable(errorCode);
578         if (U_FAILURE(errorCode)) { return; }
579         for (int32_t i = 0; table.getKeyAndValue(i, key, value); ++i) {
580             if (value.getType() == URES_ALIAS) {
581                 consumeAlias(key, value, errorCode);
582             } else {
583                 style = styleFromString(key);
584                 int32_t unitSize = static_cast<int32_t>(uprv_strlen(key)) - styleSuffixLength(style);
585                 genericUnit = unitOrNegativeFromString(key, unitSize);
586                 if (style >= 0 && genericUnit != INVALID_UNIT) {
587                     consumeTimeUnit(key, value, errorCode);
588                 }
589             }
590         }
591     }
592 
593 };
594 
595 // Virtual destructors must be defined out of line.
~RelDateTimeFmtDataSink()596 RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
597 } // namespace
598 
599 static const DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth[UDAT_STYLE_COUNT] = {
600   DateFormatSymbols::WIDE, DateFormatSymbols::SHORT, DateFormatSymbols::NARROW
601 };
602 
603 // 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)604 static void loadWeekdayNames(UnicodeString absoluteUnits[UDAT_STYLE_COUNT]
605                                  [UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT],
606                              const char* localeId,
607                              UErrorCode& status) {
608     if (U_FAILURE(status)) {
609         return;
610     }
611     Locale locale(localeId);
612     DateFormatSymbols dfSym(locale, status);
613     if (U_FAILURE(status)) {
614         return;
615     }
616     for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
617         DateFormatSymbols::DtWidthType dtfmtWidth = styleToDateFormatSymbolWidth[style];
618         int32_t count;
619         const UnicodeString* weekdayNames =
620             dfSym.getWeekdays(count, DateFormatSymbols::STANDALONE, dtfmtWidth);
621         for (int32_t dayIndex = UDAT_ABSOLUTE_SUNDAY;
622                 dayIndex <= UDAT_ABSOLUTE_SATURDAY; ++ dayIndex) {
623             int32_t dateSymbolIndex = (dayIndex - UDAT_ABSOLUTE_SUNDAY) + UCAL_SUNDAY;
624             absoluteUnits[style][dayIndex][UDAT_DIRECTION_PLAIN].fastCopyFrom(
625                 weekdayNames[dateSymbolIndex]);
626         }
627     }
628 }
629 
loadUnitData(const UResourceBundle * resource,RelativeDateTimeCacheData & cacheData,const char * localeId,UErrorCode & status)630 static UBool loadUnitData(
631         const UResourceBundle *resource,
632         RelativeDateTimeCacheData &cacheData,
633         const char* localeId,
634         UErrorCode &status) {
635 
636     RelDateTimeFmtDataSink sink(cacheData);
637 
638     ures_getAllItemsWithFallback(resource, "fields", sink, status);
639     if (U_FAILURE(status)) {
640         return false;
641     }
642 
643     // Get the weekday names from DateFormatSymbols.
644     loadWeekdayNames(cacheData.absoluteUnits, localeId, status);
645     return U_SUCCESS(status);
646 }
647 
getDateTimePattern(const UResourceBundle * resource,UnicodeString & result,UErrorCode & status)648 static UBool getDateTimePattern(
649         const UResourceBundle *resource,
650         UnicodeString &result,
651         UErrorCode &status) {
652     UnicodeString defaultCalendarName;
653     if (!getStringWithFallback(
654             resource,
655             "calendar/default",
656             defaultCalendarName,
657             status)) {
658         return FALSE;
659     }
660     CharString pathBuffer;
661     pathBuffer.append("calendar/", status)
662             .appendInvariantChars(defaultCalendarName, status)
663             .append("/DateTimePatterns", status);
664     LocalUResourceBundlePointer topLevel(
665             ures_getByKeyWithFallback(
666                     resource, pathBuffer.data(), nullptr, &status));
667     if (U_FAILURE(status)) {
668         return FALSE;
669     }
670     int32_t size = ures_getSize(topLevel.getAlias());
671     if (size <= 8) {
672         // Oops, size is too small to access the index that we want, fallback
673         // to a hard-coded value.
674         result = UNICODE_STRING_SIMPLE("{1} {0}");
675         return TRUE;
676     }
677     return getStringByIndex(topLevel.getAlias(), 8, result, status);
678 }
679 
680 template<> U_I18N_API
createObject(const void *,UErrorCode & status) const681 const RelativeDateTimeCacheData *LocaleCacheKey<RelativeDateTimeCacheData>::createObject(const void * /*unused*/, UErrorCode &status) const {
682     const char *localeId = fLoc.getName();
683     LocalUResourceBundlePointer topLevel(ures_open(nullptr, localeId, &status));
684     if (U_FAILURE(status)) {
685         return nullptr;
686     }
687     LocalPointer<RelativeDateTimeCacheData> result(
688             new RelativeDateTimeCacheData());
689     if (result.isNull()) {
690         status = U_MEMORY_ALLOCATION_ERROR;
691         return nullptr;
692     }
693     if (!loadUnitData(
694             topLevel.getAlias(),
695             *result,
696             localeId,
697             status)) {
698         return nullptr;
699     }
700     UnicodeString dateTimePattern;
701     if (!getDateTimePattern(topLevel.getAlias(), dateTimePattern, status)) {
702         return nullptr;
703     }
704     result->adoptCombinedDateAndTime(
705             new SimpleFormatter(dateTimePattern, 2, 2, status));
706     if (U_FAILURE(status)) {
707         return nullptr;
708     }
709     result->addRef();
710     return result.orphan();
711 }
712 
RelativeDateTimeFormatter(UErrorCode & status)713 RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) :
714         fCache(nullptr),
715         fNumberFormat(nullptr),
716         fPluralRules(nullptr),
717         fStyle(UDAT_STYLE_LONG),
718         fContext(UDISPCTX_CAPITALIZATION_NONE),
719         fOptBreakIterator(nullptr) {
720     init(nullptr, nullptr, status);
721 }
722 
RelativeDateTimeFormatter(const Locale & locale,UErrorCode & status)723 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
724         const Locale& locale, UErrorCode& status) :
725         fCache(nullptr),
726         fNumberFormat(nullptr),
727         fPluralRules(nullptr),
728         fStyle(UDAT_STYLE_LONG),
729         fContext(UDISPCTX_CAPITALIZATION_NONE),
730         fOptBreakIterator(nullptr),
731         fLocale(locale) {
732     init(nullptr, nullptr, status);
733 }
734 
RelativeDateTimeFormatter(const Locale & locale,NumberFormat * nfToAdopt,UErrorCode & status)735 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
736         const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) :
737         fCache(nullptr),
738         fNumberFormat(nullptr),
739         fPluralRules(nullptr),
740         fStyle(UDAT_STYLE_LONG),
741         fContext(UDISPCTX_CAPITALIZATION_NONE),
742         fOptBreakIterator(nullptr),
743         fLocale(locale) {
744     init(nfToAdopt, nullptr, status);
745 }
746 
RelativeDateTimeFormatter(const Locale & locale,NumberFormat * nfToAdopt,UDateRelativeDateTimeFormatterStyle styl,UDisplayContext capitalizationContext,UErrorCode & status)747 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
748         const Locale& locale,
749         NumberFormat *nfToAdopt,
750         UDateRelativeDateTimeFormatterStyle styl,
751         UDisplayContext capitalizationContext,
752         UErrorCode& status) :
753         fCache(nullptr),
754         fNumberFormat(nullptr),
755         fPluralRules(nullptr),
756         fStyle(styl),
757         fContext(capitalizationContext),
758         fOptBreakIterator(nullptr),
759         fLocale(locale) {
760     if (U_FAILURE(status)) {
761         return;
762     }
763     if ((capitalizationContext >> 8) != UDISPCTX_TYPE_CAPITALIZATION) {
764         status = U_ILLEGAL_ARGUMENT_ERROR;
765         return;
766     }
767     if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) {
768         BreakIterator *bi = BreakIterator::createSentenceInstance(locale, status);
769         if (U_FAILURE(status)) {
770             return;
771         }
772         init(nfToAdopt, bi, status);
773     } else {
774         init(nfToAdopt, nullptr, status);
775     }
776 }
777 
RelativeDateTimeFormatter(const RelativeDateTimeFormatter & other)778 RelativeDateTimeFormatter::RelativeDateTimeFormatter(
779         const RelativeDateTimeFormatter& other)
780         : UObject(other),
781           fCache(other.fCache),
782           fNumberFormat(other.fNumberFormat),
783           fPluralRules(other.fPluralRules),
784           fStyle(other.fStyle),
785           fContext(other.fContext),
786           fOptBreakIterator(other.fOptBreakIterator),
787           fLocale(other.fLocale) {
788     fCache->addRef();
789     fNumberFormat->addRef();
790     fPluralRules->addRef();
791     if (fOptBreakIterator != nullptr) {
792       fOptBreakIterator->addRef();
793     }
794 }
795 
operator =(const RelativeDateTimeFormatter & other)796 RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=(
797         const RelativeDateTimeFormatter& other) {
798     if (this != &other) {
799         SharedObject::copyPtr(other.fCache, fCache);
800         SharedObject::copyPtr(other.fNumberFormat, fNumberFormat);
801         SharedObject::copyPtr(other.fPluralRules, fPluralRules);
802         SharedObject::copyPtr(other.fOptBreakIterator, fOptBreakIterator);
803         fStyle = other.fStyle;
804         fContext = other.fContext;
805         fLocale = other.fLocale;
806     }
807     return *this;
808 }
809 
~RelativeDateTimeFormatter()810 RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
811     if (fCache != nullptr) {
812         fCache->removeRef();
813     }
814     if (fNumberFormat != nullptr) {
815         fNumberFormat->removeRef();
816     }
817     if (fPluralRules != nullptr) {
818         fPluralRules->removeRef();
819     }
820     if (fOptBreakIterator != nullptr) {
821         fOptBreakIterator->removeRef();
822     }
823 }
824 
getNumberFormat() const825 const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const {
826     return **fNumberFormat;
827 }
828 
getCapitalizationContext() const829 UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const {
830     return fContext;
831 }
832 
getFormatStyle() const833 UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const {
834     return fStyle;
835 }
836 
format(double quantity,UDateDirection direction,UDateRelativeUnit unit,UnicodeString & appendTo,UErrorCode & status) const837 UnicodeString& RelativeDateTimeFormatter::format(
838         double quantity, UDateDirection direction, UDateRelativeUnit unit,
839         UnicodeString& appendTo, UErrorCode& status) const {
840     if (U_FAILURE(status)) {
841         return appendTo;
842     }
843     if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
844         status = U_ILLEGAL_ARGUMENT_ERROR;
845         return appendTo;
846     }
847     int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
848     FieldPosition pos(FieldPosition::DONT_CARE);
849 
850     UnicodeString result;
851     UnicodeString formattedNumber;
852 
853     StandardPlural::Form pluralIndex = QuantityFormatter::selectPlural(
854         quantity, **fNumberFormat, **fPluralRules, formattedNumber, pos,
855         status);
856 
857     const SimpleFormatter* formatter =
858         fCache->getRelativeUnitFormatter(fStyle, unit, bFuture, pluralIndex);
859     if (formatter == nullptr) {
860         // TODO: WARN - look at quantity formatter's action with an error.
861         status = U_INVALID_FORMAT_ERROR;
862         return appendTo;
863     }
864     formatter->format(formattedNumber, result, status);
865     adjustForContext(result);
866     return appendTo.append(result);
867 }
868 
formatNumeric(double offset,URelativeDateTimeUnit unit,UnicodeString & appendTo,UErrorCode & status) const869 UnicodeString& RelativeDateTimeFormatter::formatNumeric(
870         double offset, URelativeDateTimeUnit unit,
871         UnicodeString& appendTo, UErrorCode& status) const {
872     if (U_FAILURE(status)) {
873         return appendTo;
874     }
875     UDateDirection direction = UDAT_DIRECTION_NEXT;
876     if (std::signbit(offset)) { // needed to handle -0.0
877         direction = UDAT_DIRECTION_LAST;
878         offset = -offset;
879     }
880     if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
881         status = U_ILLEGAL_ARGUMENT_ERROR;
882         return appendTo;
883     }
884     int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
885     FieldPosition pos(FieldPosition::DONT_CARE);
886 
887     UnicodeString result;
888     UnicodeString formattedNumber;
889 
890     StandardPlural::Form pluralIndex = QuantityFormatter::selectPlural(
891         offset, **fNumberFormat, **fPluralRules, formattedNumber, pos,
892         status);
893 
894     const SimpleFormatter* formatter =
895         fCache->getRelativeDateTimeUnitFormatter(fStyle, unit, bFuture, pluralIndex);
896     if (formatter == nullptr) {
897         // TODO: WARN - look at quantity formatter's action with an error.
898         status = U_INVALID_FORMAT_ERROR;
899         return appendTo;
900     }
901     formatter->format(formattedNumber, result, status);
902     adjustForContext(result);
903     return appendTo.append(result);
904 }
905 
format(UDateDirection direction,UDateAbsoluteUnit unit,UnicodeString & appendTo,UErrorCode & status) const906 UnicodeString& RelativeDateTimeFormatter::format(
907         UDateDirection direction, UDateAbsoluteUnit unit,
908         UnicodeString& appendTo, UErrorCode& status) const {
909     if (U_FAILURE(status)) {
910         return appendTo;
911     }
912     if (unit == UDAT_ABSOLUTE_NOW && direction != UDAT_DIRECTION_PLAIN) {
913         status = U_ILLEGAL_ARGUMENT_ERROR;
914         return appendTo;
915     }
916 
917     // Get string using fallback.
918     UnicodeString result;
919     result.fastCopyFrom(fCache->getAbsoluteUnitString(fStyle, unit, direction));
920     if (fOptBreakIterator != nullptr) {
921         adjustForContext(result);
922     }
923     return appendTo.append(result);
924 }
925 
format(double offset,URelativeDateTimeUnit unit,UnicodeString & appendTo,UErrorCode & status) const926 UnicodeString& RelativeDateTimeFormatter::format(
927         double offset, URelativeDateTimeUnit unit,
928         UnicodeString& appendTo, UErrorCode& status) const {
929     if (U_FAILURE(status)) {
930         return appendTo;
931     }
932     // TODO:
933     // The full implementation of this depends on CLDR data that is not yet available,
934     // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
935     // In the meantime do a quick bring-up by calling the old format method; this
936     // leaves some holes (even for data that is currently available, such as quarter).
937     // When the new CLDR data is available, update the data storage accordingly,
938     // rewrite this to use it directly, and rewrite the old format method to call this
939     // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
940     UDateDirection direction = UDAT_DIRECTION_COUNT;
941     if (offset > -2.1 && offset < 2.1) {
942         // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST
943         double offsetx100 = offset * 100.0;
944         int32_t intoffset = (offsetx100 < 0)? (int32_t)(offsetx100-0.5) : (int32_t)(offsetx100+0.5);
945         switch (intoffset) {
946             case -200/*-2*/: direction = UDAT_DIRECTION_LAST_2; break;
947             case -100/*-1*/: direction = UDAT_DIRECTION_LAST; break;
948             case    0/* 0*/: direction = UDAT_DIRECTION_THIS; break;
949             case  100/* 1*/: direction = UDAT_DIRECTION_NEXT; break;
950             case  200/* 2*/: direction = UDAT_DIRECTION_NEXT_2; break;
951             default: break;
952     	}
953     }
954     UDateAbsoluteUnit absunit = UDAT_ABSOLUTE_UNIT_COUNT;
955     switch (unit) {
956         case UDAT_REL_UNIT_YEAR:    absunit = UDAT_ABSOLUTE_YEAR; break;
957         case UDAT_REL_UNIT_QUARTER: absunit = UDAT_ABSOLUTE_QUARTER; break;
958         case UDAT_REL_UNIT_MONTH:   absunit = UDAT_ABSOLUTE_MONTH; break;
959         case UDAT_REL_UNIT_WEEK:    absunit = UDAT_ABSOLUTE_WEEK; break;
960         case UDAT_REL_UNIT_DAY:     absunit = UDAT_ABSOLUTE_DAY; break;
961         case UDAT_REL_UNIT_SECOND:
962             if (direction == UDAT_DIRECTION_THIS) {
963                 absunit = UDAT_ABSOLUTE_NOW;
964                 direction = UDAT_DIRECTION_PLAIN;
965             }
966             break;
967         case UDAT_REL_UNIT_SUNDAY:  absunit = UDAT_ABSOLUTE_SUNDAY; break;
968         case UDAT_REL_UNIT_MONDAY:  absunit = UDAT_ABSOLUTE_MONDAY; break;
969         case UDAT_REL_UNIT_TUESDAY:  absunit = UDAT_ABSOLUTE_TUESDAY; break;
970         case UDAT_REL_UNIT_WEDNESDAY:  absunit = UDAT_ABSOLUTE_WEDNESDAY; break;
971         case UDAT_REL_UNIT_THURSDAY:  absunit = UDAT_ABSOLUTE_THURSDAY; break;
972         case UDAT_REL_UNIT_FRIDAY:  absunit = UDAT_ABSOLUTE_FRIDAY; break;
973         case UDAT_REL_UNIT_SATURDAY:  absunit = UDAT_ABSOLUTE_SATURDAY; break;
974         default: break;
975     }
976     if (direction != UDAT_DIRECTION_COUNT && absunit != UDAT_ABSOLUTE_UNIT_COUNT) {
977         const UnicodeString &unitFormatString =
978             fCache->getAbsoluteUnitString(fStyle, absunit, direction);
979         if (!unitFormatString.isEmpty()) {
980             if (fOptBreakIterator != nullptr) {
981                 UnicodeString result(unitFormatString);
982                 adjustForContext(result);
983                 return appendTo.append(result);
984             } else {
985                 return appendTo.append(unitFormatString);
986             }
987         }
988     }
989     // otherwise fallback to formatNumeric
990     return formatNumeric(offset, unit, appendTo, status);
991 }
992 
combineDateAndTime(const UnicodeString & relativeDateString,const UnicodeString & timeString,UnicodeString & appendTo,UErrorCode & status) const993 UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
994         const UnicodeString& relativeDateString, const UnicodeString& timeString,
995         UnicodeString& appendTo, UErrorCode& status) const {
996     return fCache->getCombinedDateAndTime()->format(
997             timeString, relativeDateString, appendTo, status);
998 }
999 
adjustForContext(UnicodeString & str) const1000 void RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const {
1001     if (fOptBreakIterator == nullptr
1002         || str.length() == 0 || !u_islower(str.char32At(0))) {
1003         return;
1004     }
1005 
1006     // Must guarantee that one thread at a time accesses the shared break
1007     // iterator.
1008     Mutex lock(&gBrkIterMutex);
1009     str.toTitle(
1010             fOptBreakIterator->get(),
1011             fLocale,
1012             U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
1013 }
1014 
init(NumberFormat * nfToAdopt,BreakIterator * biToAdopt,UErrorCode & status)1015 void RelativeDateTimeFormatter::init(
1016         NumberFormat *nfToAdopt,
1017         BreakIterator *biToAdopt,
1018         UErrorCode &status) {
1019     LocalPointer<NumberFormat> nf(nfToAdopt);
1020     LocalPointer<BreakIterator> bi(biToAdopt);
1021     UnifiedCache::getByLocale(fLocale, fCache, status);
1022     if (U_FAILURE(status)) {
1023         return;
1024     }
1025     const SharedPluralRules *pr = PluralRules::createSharedInstance(
1026             fLocale, UPLURAL_TYPE_CARDINAL, status);
1027     if (U_FAILURE(status)) {
1028         return;
1029     }
1030     SharedObject::copyPtr(pr, fPluralRules);
1031     pr->removeRef();
1032     if (nf.isNull()) {
1033        const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
1034                fLocale, UNUM_DECIMAL, status);
1035         if (U_FAILURE(status)) {
1036             return;
1037         }
1038         SharedObject::copyPtr(shared, fNumberFormat);
1039         shared->removeRef();
1040     } else {
1041         SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
1042         if (shared == nullptr) {
1043             status = U_MEMORY_ALLOCATION_ERROR;
1044             return;
1045         }
1046         nf.orphan();
1047         SharedObject::copyPtr(shared, fNumberFormat);
1048     }
1049     if (bi.isNull()) {
1050         SharedObject::clearPtr(fOptBreakIterator);
1051     } else {
1052         SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias());
1053         if (shared == nullptr) {
1054             status = U_MEMORY_ALLOCATION_ERROR;
1055             return;
1056         }
1057         bi.orphan();
1058         SharedObject::copyPtr(shared, fOptBreakIterator);
1059     }
1060 }
1061 
1062 U_NAMESPACE_END
1063 
1064 // Plain C API
1065 
1066 U_NAMESPACE_USE
1067 
1068 U_CAPI URelativeDateTimeFormatter* U_EXPORT2
ureldatefmt_open(const char * locale,UNumberFormat * nfToAdopt,UDateRelativeDateTimeFormatterStyle width,UDisplayContext capitalizationContext,UErrorCode * status)1069 ureldatefmt_open( const char*          locale,
1070                   UNumberFormat*       nfToAdopt,
1071                   UDateRelativeDateTimeFormatterStyle width,
1072                   UDisplayContext      capitalizationContext,
1073                   UErrorCode*          status )
1074 {
1075     if (U_FAILURE(*status)) {
1076         return nullptr;
1077     }
1078     LocalPointer<RelativeDateTimeFormatter> formatter(new RelativeDateTimeFormatter(Locale(locale),
1079                                                               (NumberFormat*)nfToAdopt, width,
1080                                                               capitalizationContext, *status), *status);
1081     if (U_FAILURE(*status)) {
1082         return nullptr;
1083     }
1084     return (URelativeDateTimeFormatter*)formatter.orphan();
1085 }
1086 
1087 U_CAPI void U_EXPORT2
ureldatefmt_close(URelativeDateTimeFormatter * reldatefmt)1088 ureldatefmt_close(URelativeDateTimeFormatter *reldatefmt)
1089 {
1090     delete (RelativeDateTimeFormatter*)reldatefmt;
1091 }
1092 
1093 U_CAPI int32_t U_EXPORT2
ureldatefmt_formatNumeric(const URelativeDateTimeFormatter * reldatefmt,double offset,URelativeDateTimeUnit unit,UChar * result,int32_t resultCapacity,UErrorCode * status)1094 ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt,
1095                     double                offset,
1096                     URelativeDateTimeUnit unit,
1097                     UChar*                result,
1098                     int32_t               resultCapacity,
1099                     UErrorCode*           status)
1100 {
1101     if (U_FAILURE(*status)) {
1102         return 0;
1103     }
1104     if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) {
1105         *status = U_ILLEGAL_ARGUMENT_ERROR;
1106         return 0;
1107     }
1108     UnicodeString res;
1109     if (result != nullptr) {
1110         // nullptr destination for pure preflighting: empty dummy string
1111         // otherwise, alias the destination buffer (copied from udat_format)
1112         res.setTo(result, 0, resultCapacity);
1113     }
1114     ((RelativeDateTimeFormatter*)reldatefmt)->formatNumeric(offset, unit, res, *status);
1115     if (U_FAILURE(*status)) {
1116         return 0;
1117     }
1118     return res.extract(result, resultCapacity, *status);
1119 }
1120 
1121 U_CAPI int32_t U_EXPORT2
ureldatefmt_format(const URelativeDateTimeFormatter * reldatefmt,double offset,URelativeDateTimeUnit unit,UChar * result,int32_t resultCapacity,UErrorCode * status)1122 ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt,
1123                     double                offset,
1124                     URelativeDateTimeUnit unit,
1125                     UChar*                result,
1126                     int32_t               resultCapacity,
1127                     UErrorCode*           status)
1128 {
1129     if (U_FAILURE(*status)) {
1130         return 0;
1131     }
1132     if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) {
1133         *status = U_ILLEGAL_ARGUMENT_ERROR;
1134         return 0;
1135     }
1136     UnicodeString res;
1137     if (result != nullptr) {
1138         // nullptr destination for pure preflighting: empty dummy string
1139         // otherwise, alias the destination buffer (copied from udat_format)
1140         res.setTo(result, 0, resultCapacity);
1141     }
1142     ((RelativeDateTimeFormatter*)reldatefmt)->format(offset, unit, res, *status);
1143     if (U_FAILURE(*status)) {
1144         return 0;
1145     }
1146     return res.extract(result, resultCapacity, *status);
1147 }
1148 
1149 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)1150 ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter* reldatefmt,
1151                     const UChar *     relativeDateString,
1152                     int32_t           relativeDateStringLen,
1153                     const UChar *     timeString,
1154                     int32_t           timeStringLen,
1155                     UChar*            result,
1156                     int32_t           resultCapacity,
1157                     UErrorCode*       status )
1158 {
1159     if (U_FAILURE(*status)) {
1160         return 0;
1161     }
1162     if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0 ||
1163             (relativeDateString == nullptr ? relativeDateStringLen != 0 : relativeDateStringLen < -1) ||
1164             (timeString == nullptr ? timeStringLen != 0 : timeStringLen < -1)) {
1165         *status = U_ILLEGAL_ARGUMENT_ERROR;
1166         return 0;
1167     }
1168     UnicodeString relDateStr((UBool)(relativeDateStringLen == -1), relativeDateString, relativeDateStringLen);
1169     UnicodeString timeStr((UBool)(timeStringLen == -1), timeString, timeStringLen);
1170     UnicodeString res(result, 0, resultCapacity);
1171     ((RelativeDateTimeFormatter*)reldatefmt)->combineDateAndTime(relDateStr, timeStr, res, *status);
1172     if (U_FAILURE(*status)) {
1173         return 0;
1174     }
1175     return res.extract(result, resultCapacity, *status);
1176 }
1177 
1178 #endif /* !UCONFIG_NO_FORMATTING */
1179