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