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