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