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