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