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 "formatted_string_builder.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
54U_NAMESPACE_BEGIN
55
56// RelativeDateTimeFormatter specific data for a single locale
57class RelativeDateTimeCacheData: public SharedObject {
58public:
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
109private:
110 SimpleFormatter *combinedDateAndTime;
111 RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other);
112 RelativeDateTimeCacheData& operator=(
113 const RelativeDateTimeCacheData &other);
114};
115
116RelativeDateTimeCacheData::~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.
131const 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
187static 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
203static 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
218namespace {
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 */
226struct 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 case HOUR:
319 return UDAT_ABSOLUTE_HOUR;
320 case MINUTE:
321 return UDAT_ABSOLUTE_MINUTE;
322 default:
323 return -1;
324 }
325 }
326
327 static int32_t keyToDirection(const char* key) {
328 if (uprv_strcmp(key, "-2") == 0) {
329 return UDAT_DIRECTION_LAST_2;
330 }
331 if (uprv_strcmp(key, "-1") == 0) {
332 return UDAT_DIRECTION_LAST;
333 }
334 if (uprv_strcmp(key, "0") == 0) {
335 return UDAT_DIRECTION_THIS;
336 }
337 if (uprv_strcmp(key, "1") == 0) {
338 return UDAT_DIRECTION_NEXT;
339 }
340 if (uprv_strcmp(key, "2") == 0) {
341 return UDAT_DIRECTION_NEXT_2;
342 }
343 return -1;
344 }
345
346 // Values kept between levels of parsing the CLDR data.
347 int32_t pastFutureIndex; // 0 == past or 1 == future
348 UDateRelativeDateTimeFormatterStyle style; // {LONG, SHORT, NARROW}
349 RelAbsUnit genericUnit;
350
351 RelativeDateTimeCacheData &outputData;
352
353 // Constructor
354 RelDateTimeFmtDataSink(RelativeDateTimeCacheData& cacheData)
355 : outputData(cacheData) {
356 // Clear cacheData.fallBackCache
357 cacheData.fallBackCache[UDAT_STYLE_LONG] = -1;
358 cacheData.fallBackCache[UDAT_STYLE_SHORT] = -1;
359 cacheData.fallBackCache[UDAT_STYLE_NARROW] = -1;
360 }
361
362 ~RelDateTimeFmtDataSink();
363
364 // Utility functions
365 static UDateRelativeDateTimeFormatterStyle styleFromString(const char *s) {
366 int32_t len = static_cast<int32_t>(uprv_strlen(s));
367 if (len >= 7 && uprv_strcmp(s + len - 7, "-narrow") == 0) {
368 return UDAT_STYLE_NARROW;
369 }
370 if (len >= 6 && uprv_strcmp(s + len - 6, "-short") == 0) {
371 return UDAT_STYLE_SHORT;
372 }
373 return UDAT_STYLE_LONG;
374 }
375
376 static int32_t styleSuffixLength(UDateRelativeDateTimeFormatterStyle style) {
377 switch (style) {
378 case UDAT_STYLE_NARROW:
379 return 7;
380 case UDAT_STYLE_SHORT:
381 return 6;
382 default:
383 return 0;
384 }
385 }
386
387 // Utility functions
388 static UDateRelativeDateTimeFormatterStyle styleFromAliasUnicodeString(UnicodeString s) {
389 static const UChar narrow[7] = {0x002D, 0x006E, 0x0061, 0x0072, 0x0072, 0x006F, 0x0077};
390 static const UChar sshort[6] = {0x002D, 0x0073, 0x0068, 0x006F, 0x0072, 0x0074,};
391 if (s.endsWith(narrow, 7)) {
392 return UDAT_STYLE_NARROW;
393 }
394 if (s.endsWith(sshort, 6)) {
395 return UDAT_STYLE_SHORT;
396 }
397 return UDAT_STYLE_LONG;
398 }
399
400 static RelAbsUnit unitOrNegativeFromString(const char* keyword, int32_t length) {
401 // Quick check from string to enum.
402 switch (length) {
403 case 3:
404 if (uprv_strncmp(keyword, "day", length) == 0) {
405 return DAY;
406 } else if (uprv_strncmp(keyword, "sun", length) == 0) {
407 return SUNDAY;
408 } else if (uprv_strncmp(keyword, "mon", length) == 0) {
409 return MONDAY;
410 } else if (uprv_strncmp(keyword, "tue", length) == 0) {
411 return TUESDAY;
412 } else if (uprv_strncmp(keyword, "wed", length) == 0) {
413 return WEDNESDAY;
414 } else if (uprv_strncmp(keyword, "thu", length) == 0) {
415 return THURSDAY;
416 } else if (uprv_strncmp(keyword, "fri", length) == 0) {
417 return FRIDAY;
418 } else if (uprv_strncmp(keyword, "sat", length) == 0) {
419 return SATURDAY;
420 }
421 break;
422 case 4:
423 if (uprv_strncmp(keyword, "hour", length) == 0) {
424 return HOUR;
425 } else if (uprv_strncmp(keyword, "week", length) == 0) {
426 return WEEK;
427 } else if (uprv_strncmp(keyword, "year", length) == 0) {
428 return YEAR;
429 }
430 break;
431 case 5:
432 if (uprv_strncmp(keyword, "month", length) == 0) {
433 return MONTH;
434 }
435 break;
436 case 6:
437 if (uprv_strncmp(keyword, "minute", length) == 0) {
438 return MINUTE;
439 } else if (uprv_strncmp(keyword, "second", length) == 0) {
440 return SECOND;
441 }
442 break;
443 case 7:
444 if (uprv_strncmp(keyword, "quarter", length) == 0) {
445 return QUARTER; // TODO: Check @provisional
446 }
447 break;
448 default:
449 break;
450 }
451 return INVALID_UNIT;
452 }
453
454 void handlePlainDirection(ResourceValue &value, UErrorCode &errorCode) {
455 // Handle Display Name for PLAIN direction for some units.
456 if (U_FAILURE(errorCode)) { return; }
457
458 int32_t absUnit = absUnitFromGeneric(genericUnit);
459 if (absUnit < 0) {
460 return; // Not interesting.
461 }
462
463 // Store displayname if not set.
464 if (outputData.absoluteUnits[style]
465 [absUnit][UDAT_DIRECTION_PLAIN].isEmpty()) {
466 outputData.absoluteUnits[style]
467 [absUnit][UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
468 return;
469 }
470 }
471
472 void consumeTableRelative(const char *key, ResourceValue &value, UErrorCode &errorCode) {
473 ResourceTable unitTypesTable = value.getTable(errorCode);
474 if (U_FAILURE(errorCode)) { return; }
475
476 for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
477 if (value.getType() == URES_STRING) {
478 int32_t direction = keyToDirection(key);
479 if (direction < 0) {
480 continue;
481 }
482
483 int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
484 if (relUnitIndex == UDAT_REL_UNIT_SECOND && uprv_strcmp(key, "0") == 0 &&
485 outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN].isEmpty()) {
486 // Handle "NOW"
487 outputData.absoluteUnits[style][UDAT_ABSOLUTE_NOW]
488 [UDAT_DIRECTION_PLAIN].fastCopyFrom(value.getUnicodeString(errorCode));
489 }
490
491 int32_t absUnitIndex = absUnitFromGeneric(genericUnit);
492 if (absUnitIndex < 0) {
493 continue;
494 }
495 // Only reset if slot is empty.
496 if (outputData.absoluteUnits[style][absUnitIndex][direction].isEmpty()) {
497 outputData.absoluteUnits[style][absUnitIndex]
498 [direction].fastCopyFrom(value.getUnicodeString(errorCode));
499 }
500 }
501 }
502 }
503
504 void consumeTimeDetail(int32_t relUnitIndex,
505 const char *key, ResourceValue &value, UErrorCode &errorCode) {
506 ResourceTable unitTypesTable = value.getTable(errorCode);
507 if (U_FAILURE(errorCode)) { return; }
508
509 for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
510 if (value.getType() == URES_STRING) {
511 int32_t pluralIndex = StandardPlural::indexOrNegativeFromString(key);
512 if (pluralIndex >= 0) {
513 SimpleFormatter **patterns =
514 outputData.relativeUnitsFormatters[style][relUnitIndex]
515 [pastFutureIndex];
516 // Only set if not already established.
517 if (patterns[pluralIndex] == nullptr) {
518 patterns[pluralIndex] = new SimpleFormatter(
519 value.getUnicodeString(errorCode), 0, 1, errorCode);
520 if (patterns[pluralIndex] == nullptr) {
521 errorCode = U_MEMORY_ALLOCATION_ERROR;
522 }
523 }
524 }
525 }
526 }
527 }
528
529 void consumeTableRelativeTime(const char *key, ResourceValue &value, UErrorCode &errorCode) {
530 ResourceTable relativeTimeTable = value.getTable(errorCode);
531 if (U_FAILURE(errorCode)) { return; }
532
533 int32_t relUnitIndex = relUnitFromGeneric(genericUnit);
534 if (relUnitIndex < 0) {
535 return;
536 }
537 for (int32_t i = 0; relativeTimeTable.getKeyAndValue(i, key, value); ++i) {
538 if (uprv_strcmp(key, "past") == 0) {
539 pastFutureIndex = 0;
540 } else if (uprv_strcmp(key, "future") == 0) {
541 pastFutureIndex = 1;
542 } else {
543 // Unknown key.
544 continue;
545 }
546 consumeTimeDetail(relUnitIndex, key, value, errorCode);
547 }
548 }
549
550 void consumeAlias(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
551
552 UDateRelativeDateTimeFormatterStyle sourceStyle = styleFromString(key);
553 const UnicodeString valueStr = value.getAliasUnicodeString(errorCode);
554 if (U_FAILURE(errorCode)) { return; }
555
556 UDateRelativeDateTimeFormatterStyle targetStyle =
557 styleFromAliasUnicodeString(valueStr);
558
559 if (sourceStyle == targetStyle) {
560 errorCode = U_INVALID_FORMAT_ERROR;
561 return;
562 }
563 if (outputData.fallBackCache[sourceStyle] != -1 &&
564 outputData.fallBackCache[sourceStyle] != targetStyle) {
565 errorCode = U_INVALID_FORMAT_ERROR;
566 return;
567 }
568 outputData.fallBackCache[sourceStyle] = targetStyle;
569 }
570
571 void consumeTimeUnit(const char *key, ResourceValue &value, UErrorCode &errorCode) {
572 ResourceTable unitTypesTable = value.getTable(errorCode);
573 if (U_FAILURE(errorCode)) { return; }
574
575 for (int32_t i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
576 // Handle display name.
577 if (uprv_strcmp(key, "dn") == 0 && value.getType() == URES_STRING) {
578 handlePlainDirection(value, errorCode);
579 }
580 if (value.getType() == URES_TABLE) {
581 if (uprv_strcmp(key, "relative") == 0) {
582 consumeTableRelative(key, value, errorCode);
583 } else if (uprv_strcmp(key, "relativeTime") == 0) {
584 consumeTableRelativeTime(key, value, errorCode);
585 }
586 }
587 }
588 }
589
590 virtual void put(const char *key, ResourceValue &value,
591 UBool /*noFallback*/, UErrorCode &errorCode) {
592 // Main entry point to sink
593 ResourceTable table = value.getTable(errorCode);
594 if (U_FAILURE(errorCode)) { return; }
595 for (int32_t i = 0; table.getKeyAndValue(i, key, value); ++i) {
596 if (value.getType() == URES_ALIAS) {
597 consumeAlias(key, value, errorCode);
598 } else {
599 style = styleFromString(key);
600 int32_t unitSize = static_cast<int32_t>(uprv_strlen(key)) - styleSuffixLength(style);
601 genericUnit = unitOrNegativeFromString(key, unitSize);
602 if (style >= 0 && genericUnit != INVALID_UNIT) {
603 consumeTimeUnit(key, value, errorCode);
604 }
605 }
606 }
607 }
608
609};
610
611// Virtual destructors must be defined out of line.
612RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
613} // namespace
614
615static const DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth[UDAT_STYLE_COUNT] = {
616 DateFormatSymbols::WIDE, DateFormatSymbols::SHORT, DateFormatSymbols::NARROW
617};
618
619// Get days of weeks from the DateFormatSymbols class.
620static void loadWeekdayNames(UnicodeString absoluteUnits[UDAT_STYLE_COUNT]
621 [UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT],
622 const char* localeId,
623 UErrorCode& status) {
624 if (U_FAILURE(status)) {
625 return;
626 }
627 Locale locale(localeId);
628 DateFormatSymbols dfSym(locale, status);
629 if (U_FAILURE(status)) {
630 return;
631 }
632 for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
633 DateFormatSymbols::DtWidthType dtfmtWidth = styleToDateFormatSymbolWidth[style];
634 int32_t count;
635 const UnicodeString* weekdayNames =
636 dfSym.getWeekdays(count, DateFormatSymbols::STANDALONE, dtfmtWidth);
637 for (int32_t dayIndex = UDAT_ABSOLUTE_SUNDAY;
638 dayIndex <= UDAT_ABSOLUTE_SATURDAY; ++ dayIndex) {
639 int32_t dateSymbolIndex = (dayIndex - UDAT_ABSOLUTE_SUNDAY) + UCAL_SUNDAY;
640 absoluteUnits[style][dayIndex][UDAT_DIRECTION_PLAIN].fastCopyFrom(
641 weekdayNames[dateSymbolIndex]);
642 }
643 }
644}
645
646static UBool loadUnitData(
647 const UResourceBundle *resource,
648 RelativeDateTimeCacheData &cacheData,
649 const char* localeId,
650 UErrorCode &status) {
651
652 RelDateTimeFmtDataSink sink(cacheData);
653
654 ures_getAllItemsWithFallback(resource, "fields", sink, status);
655 if (U_FAILURE(status)) {
656 return false;
657 }
658
659 // Get the weekday names from DateFormatSymbols.
660 loadWeekdayNames(cacheData.absoluteUnits, localeId, status);
661 return U_SUCCESS(status);
662}
663
664static UBool getDateTimePattern(
665 const UResourceBundle *resource,
666 UnicodeString &result,
667 UErrorCode &status) {
668 UnicodeString defaultCalendarName;
669 if (!getStringWithFallback(
670 resource,
671 "calendar/default",
672 defaultCalendarName,
673 status)) {
674 return FALSE;
675 }
676 CharString pathBuffer;
677 pathBuffer.append("calendar/", status)
678 .appendInvariantChars(defaultCalendarName, status)
679 .append("/DateTimePatterns", status);
680 LocalUResourceBundlePointer topLevel(
681 ures_getByKeyWithFallback(
682 resource, pathBuffer.data(), nullptr, &status));
683 if (U_FAILURE(status)) {
684 return FALSE;
685 }
686 int32_t size = ures_getSize(topLevel.getAlias());
687 if (size <= 8) {
688 // Oops, size is too small to access the index that we want, fallback
689 // to a hard-coded value.
690 result = UNICODE_STRING_SIMPLE("{1} {0}");
691 return TRUE;
692 }
693 return getStringByIndex(topLevel.getAlias(), 8, result, status);
694}
695
696template<> U_I18N_API
697const RelativeDateTimeCacheData *LocaleCacheKey<RelativeDateTimeCacheData>::createObject(const void * /*unused*/, UErrorCode &status) const {
698 const char *localeId = fLoc.getName();
699 LocalUResourceBundlePointer topLevel(ures_open(nullptr, localeId, &status));
700 if (U_FAILURE(status)) {
701 return nullptr;
702 }
703 LocalPointer<RelativeDateTimeCacheData> result(
704 new RelativeDateTimeCacheData());
705 if (result.isNull()) {
706 status = U_MEMORY_ALLOCATION_ERROR;
707 return nullptr;
708 }
709 if (!loadUnitData(
710 topLevel.getAlias(),
711 *result,
712 localeId,
713 status)) {
714 return nullptr;
715 }
716 UnicodeString dateTimePattern;
717 if (!getDateTimePattern(topLevel.getAlias(), dateTimePattern, status)) {
718 return nullptr;
719 }
720 result->adoptCombinedDateAndTime(
721 new SimpleFormatter(dateTimePattern, 2, 2, status));
722 if (U_FAILURE(status)) {
723 return nullptr;
724 }
725 result->addRef();
726 return result.orphan();
727}
728
729
730
731static constexpr FormattedStringBuilder::Field kRDTNumericField
732 = {UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_NUMERIC_FIELD};
733
734static constexpr FormattedStringBuilder::Field kRDTLiteralField
735 = {UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_LITERAL_FIELD};
736
737class FormattedRelativeDateTimeData : public FormattedValueStringBuilderImpl {
738public:
739 FormattedRelativeDateTimeData() : FormattedValueStringBuilderImpl(kRDTNumericField) {}
740 virtual ~FormattedRelativeDateTimeData();
741};
742
743FormattedRelativeDateTimeData::~FormattedRelativeDateTimeData() = default;
744
745
746UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedRelativeDateTime)
747
748
749RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) :
750 fCache(nullptr),
751 fNumberFormat(nullptr),
752 fPluralRules(nullptr),
753 fStyle(UDAT_STYLE_LONG),
754 fContext(UDISPCTX_CAPITALIZATION_NONE),
755 fOptBreakIterator(nullptr) {
756 init(nullptr, nullptr, status);
757}
758
759RelativeDateTimeFormatter::RelativeDateTimeFormatter(
760 const Locale& locale, UErrorCode& status) :
761 fCache(nullptr),
762 fNumberFormat(nullptr),
763 fPluralRules(nullptr),
764 fStyle(UDAT_STYLE_LONG),
765 fContext(UDISPCTX_CAPITALIZATION_NONE),
766 fOptBreakIterator(nullptr),
767 fLocale(locale) {
768 init(nullptr, nullptr, status);
769}
770
771RelativeDateTimeFormatter::RelativeDateTimeFormatter(
772 const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) :
773 fCache(nullptr),
774 fNumberFormat(nullptr),
775 fPluralRules(nullptr),
776 fStyle(UDAT_STYLE_LONG),
777 fContext(UDISPCTX_CAPITALIZATION_NONE),
778 fOptBreakIterator(nullptr),
779 fLocale(locale) {
780 init(nfToAdopt, nullptr, status);
781}
782
783RelativeDateTimeFormatter::RelativeDateTimeFormatter(
784 const Locale& locale,
785 NumberFormat *nfToAdopt,
786 UDateRelativeDateTimeFormatterStyle styl,
787 UDisplayContext capitalizationContext,
788 UErrorCode& status) :
789 fCache(nullptr),
790 fNumberFormat(nullptr),
791 fPluralRules(nullptr),
792 fStyle(styl),
793 fContext(capitalizationContext),
794 fOptBreakIterator(nullptr),
795 fLocale(locale) {
796 if (U_FAILURE(status)) {
797 return;
798 }
799 if ((capitalizationContext >> 8) != UDISPCTX_TYPE_CAPITALIZATION) {
800 status = U_ILLEGAL_ARGUMENT_ERROR;
801 return;
802 }
803 if (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) {
804 BreakIterator *bi = BreakIterator::createSentenceInstance(locale, status);
805 if (U_FAILURE(status)) {
806 return;
807 }
808 init(nfToAdopt, bi, status);
809 } else {
810 init(nfToAdopt, nullptr, status);
811 }
812}
813
814RelativeDateTimeFormatter::RelativeDateTimeFormatter(
815 const RelativeDateTimeFormatter& other)
816 : UObject(other),
817 fCache(other.fCache),
818 fNumberFormat(other.fNumberFormat),
819 fPluralRules(other.fPluralRules),
820 fStyle(other.fStyle),
821 fContext(other.fContext),
822 fOptBreakIterator(other.fOptBreakIterator),
823 fLocale(other.fLocale) {
824 fCache->addRef();
825 fNumberFormat->addRef();
826 fPluralRules->addRef();
827 if (fOptBreakIterator != nullptr) {
828 fOptBreakIterator->addRef();
829 }
830}
831
832RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=(
833 const RelativeDateTimeFormatter& other) {
834 if (this != &other) {
835 SharedObject::copyPtr(other.fCache, fCache);
836 SharedObject::copyPtr(other.fNumberFormat, fNumberFormat);
837 SharedObject::copyPtr(other.fPluralRules, fPluralRules);
838 SharedObject::copyPtr(other.fOptBreakIterator, fOptBreakIterator);
839 fStyle = other.fStyle;
840 fContext = other.fContext;
841 fLocale = other.fLocale;
842 }
843 return *this;
844}
845
846RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
847 if (fCache != nullptr) {
848 fCache->removeRef();
849 }
850 if (fNumberFormat != nullptr) {
851 fNumberFormat->removeRef();
852 }
853 if (fPluralRules != nullptr) {
854 fPluralRules->removeRef();
855 }
856 if (fOptBreakIterator != nullptr) {
857 fOptBreakIterator->removeRef();
858 }
859}
860
861const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const {
862 return **fNumberFormat;
863}
864
865UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const {
866 return fContext;
867}
868
869UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const {
870 return fStyle;
871}
872
873
874// To reduce boilerplate code, we use a helper function that forwards variadic
875// arguments to the formatImpl function.
876
877template<typename F, typename... Args>
878UnicodeString& RelativeDateTimeFormatter::doFormat(
879 F callback,
880 UnicodeString& appendTo,
881 UErrorCode& status,
882 Args... args) const {
883 FormattedRelativeDateTimeData output;
884 (this->*callback)(std::forward<Args>(args)..., output, status);
885 if (U_FAILURE(status)) {
886 return appendTo;
887 }
888 UnicodeString result = output.getStringRef().toUnicodeString();
889 return appendTo.append(adjustForContext(result));
890}
891
892template<typename F, typename... Args>
893FormattedRelativeDateTime RelativeDateTimeFormatter::doFormatToValue(
894 F callback,
895 UErrorCode& status,
896 Args... args) const {
897 if (!checkNoAdjustForContext(status)) {
898 return FormattedRelativeDateTime(status);
899 }
900 LocalPointer<FormattedRelativeDateTimeData> output(
901 new FormattedRelativeDateTimeData(), status);
902 if (U_FAILURE(status)) {
903 return FormattedRelativeDateTime(status);
904 }
905 (this->*callback)(std::forward<Args>(args)..., *output, status);
906 output->getStringRef().writeTerminator(status);
907 return FormattedRelativeDateTime(output.orphan());
908}
909
910UnicodeString& RelativeDateTimeFormatter::format(
911 double quantity,
912 UDateDirection direction,
913 UDateRelativeUnit unit,
914 UnicodeString& appendTo,
915 UErrorCode& status) const {
916 return doFormat(
917 &RelativeDateTimeFormatter::formatImpl,
918 appendTo,
919 status,
920 quantity,
921 direction,
922 unit);
923}
924
925FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue(
926 double quantity,
927 UDateDirection direction,
928 UDateRelativeUnit unit,
929 UErrorCode& status) const {
930 return doFormatToValue(
931 &RelativeDateTimeFormatter::formatImpl,
932 status,
933 quantity,
934 direction,
935 unit);
936}
937
938void RelativeDateTimeFormatter::formatImpl(
939 double quantity,
940 UDateDirection direction,
941 UDateRelativeUnit unit,
942 FormattedRelativeDateTimeData& output,
943 UErrorCode& status) const {
944 if (U_FAILURE(status)) {
945 return;
946 }
947 if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
948 status = U_ILLEGAL_ARGUMENT_ERROR;
949 return;
950 }
951 int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
952
953 StandardPlural::Form pluralForm;
954 QuantityFormatter::formatAndSelect(
955 quantity,
956 **fNumberFormat,
957 **fPluralRules,
958 output.getStringRef(),
959 pluralForm,
960 status);
961 if (U_FAILURE(status)) {
962 return;
963 }
964
965 const SimpleFormatter* formatter =
966 fCache->getRelativeUnitFormatter(fStyle, unit, bFuture, pluralForm);
967 if (formatter == nullptr) {
968 // TODO: WARN - look at quantity formatter's action with an error.
969 status = U_INVALID_FORMAT_ERROR;
970 return;
971 }
972
973 number::impl::SimpleModifier modifier(*formatter, kRDTLiteralField, false);
974 modifier.formatAsPrefixSuffix(
975 output.getStringRef(), 0, output.getStringRef().length(), status);
976}
977
978UnicodeString& RelativeDateTimeFormatter::formatNumeric(
979 double offset,
980 URelativeDateTimeUnit unit,
981 UnicodeString& appendTo,
982 UErrorCode& status) const {
983 return doFormat(
984 &RelativeDateTimeFormatter::formatNumericImpl,
985 appendTo,
986 status,
987 offset,
988 unit);
989}
990
991FormattedRelativeDateTime RelativeDateTimeFormatter::formatNumericToValue(
992 double offset,
993 URelativeDateTimeUnit unit,
994 UErrorCode& status) const {
995 return doFormatToValue(
996 &RelativeDateTimeFormatter::formatNumericImpl,
997 status,
998 offset,
999 unit);
1000}
1001
1002void RelativeDateTimeFormatter::formatNumericImpl(
1003 double offset,
1004 URelativeDateTimeUnit unit,
1005 FormattedRelativeDateTimeData& output,
1006 UErrorCode& status) const {
1007 if (U_FAILURE(status)) {
1008 return;
1009 }
1010 UDateDirection direction = UDAT_DIRECTION_NEXT;
1011 if (std::signbit(offset)) { // needed to handle -0.0
1012 direction = UDAT_DIRECTION_LAST;
1013 offset = -offset;
1014 }
1015 if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
1016 status = U_ILLEGAL_ARGUMENT_ERROR;
1017 return;
1018 }
1019 int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
1020
1021 StandardPlural::Form pluralForm;
1022 QuantityFormatter::formatAndSelect(
1023 offset,
1024 **fNumberFormat,
1025 **fPluralRules,
1026 output.getStringRef(),
1027 pluralForm,
1028 status);
1029 if (U_FAILURE(status)) {
1030 return;
1031 }
1032
1033 const SimpleFormatter* formatter =
1034 fCache->getRelativeDateTimeUnitFormatter(fStyle, unit, bFuture, pluralForm);
1035 if (formatter == nullptr) {
1036 // TODO: WARN - look at quantity formatter's action with an error.
1037 status = U_INVALID_FORMAT_ERROR;
1038 return;
1039 }
1040
1041 number::impl::SimpleModifier modifier(*formatter, kRDTLiteralField, false);
1042 modifier.formatAsPrefixSuffix(
1043 output.getStringRef(), 0, output.getStringRef().length(), status);
1044}
1045
1046UnicodeString& RelativeDateTimeFormatter::format(
1047 UDateDirection direction,
1048 UDateAbsoluteUnit unit,
1049 UnicodeString& appendTo,
1050 UErrorCode& status) const {
1051 return doFormat(
1052 &RelativeDateTimeFormatter::formatAbsoluteImpl,
1053 appendTo,
1054 status,
1055 direction,
1056 unit);
1057}
1058
1059FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue(
1060 UDateDirection direction,
1061 UDateAbsoluteUnit unit,
1062 UErrorCode& status) const {
1063 return doFormatToValue(
1064 &RelativeDateTimeFormatter::formatAbsoluteImpl,
1065 status,
1066 direction,
1067 unit);
1068}
1069
1070void RelativeDateTimeFormatter::formatAbsoluteImpl(
1071 UDateDirection direction,
1072 UDateAbsoluteUnit unit,
1073 FormattedRelativeDateTimeData& output,
1074 UErrorCode& status) const {
1075 if (U_FAILURE(status)) {
1076 return;
1077 }
1078 if (unit == UDAT_ABSOLUTE_NOW && direction != UDAT_DIRECTION_PLAIN) {
1079 status = U_ILLEGAL_ARGUMENT_ERROR;
1080 return;
1081 }
1082
1083 // Get string using fallback.
1084 output.getStringRef().append(
1085 fCache->getAbsoluteUnitString(fStyle, unit, direction),
1086 kRDTLiteralField,
1087 status);
1088}
1089
1090UnicodeString& RelativeDateTimeFormatter::format(
1091 double offset,
1092 URelativeDateTimeUnit unit,
1093 UnicodeString& appendTo,
1094 UErrorCode& status) const {
1095 return doFormat(
1096 &RelativeDateTimeFormatter::formatRelativeImpl,
1097 appendTo,
1098 status,
1099 offset,
1100 unit);
1101}
1102
1103FormattedRelativeDateTime RelativeDateTimeFormatter::formatToValue(
1104 double offset,
1105 URelativeDateTimeUnit unit,
1106 UErrorCode& status) const {
1107 return doFormatToValue(
1108 &RelativeDateTimeFormatter::formatRelativeImpl,
1109 status,
1110 offset,
1111 unit);
1112}
1113
1114void RelativeDateTimeFormatter::formatRelativeImpl(
1115 double offset,
1116 URelativeDateTimeUnit unit,
1117 FormattedRelativeDateTimeData& output,
1118 UErrorCode& status) const {
1119 if (U_FAILURE(status)) {
1120 return;
1121 }
1122 // TODO:
1123 // The full implementation of this depends on CLDR data that is not yet available,
1124 // see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
1125 // In the meantime do a quick bring-up by calling the old format method; this
1126 // leaves some holes (even for data that is currently available, such as quarter).
1127 // When the new CLDR data is available, update the data storage accordingly,
1128 // rewrite this to use it directly, and rewrite the old format method to call this
1129 // new one; that is covered by http://bugs.icu-project.org/trac/ticket/12171.
1130 UDateDirection direction = UDAT_DIRECTION_COUNT;
1131 if (offset > -2.1 && offset < 2.1) {
1132 // Allow a 1% epsilon, so offsets in -1.01..-0.99 map to LAST
1133 double offsetx100 = offset * 100.0;
1134 int32_t intoffset = (offsetx100 < 0)? (int32_t)(offsetx100-0.5) : (int32_t)(offsetx100+0.5);
1135 switch (intoffset) {
1136 case -200/*-2*/: direction = UDAT_DIRECTION_LAST_2; break;
1137 case -100/*-1*/: direction = UDAT_DIRECTION_LAST; break;
1138 case 0/* 0*/: direction = UDAT_DIRECTION_THIS; break;
1139 case 100/* 1*/: direction = UDAT_DIRECTION_NEXT; break;
1140 case 200/* 2*/: direction = UDAT_DIRECTION_NEXT_2; break;
1141 default: break;
1142 }
1143 }
1144 UDateAbsoluteUnit absunit = UDAT_ABSOLUTE_UNIT_COUNT;
1145 switch (unit) {
1146 case UDAT_REL_UNIT_YEAR: absunit = UDAT_ABSOLUTE_YEAR; break;
1147 case UDAT_REL_UNIT_QUARTER: absunit = UDAT_ABSOLUTE_QUARTER; break;
1148 case UDAT_REL_UNIT_MONTH: absunit = UDAT_ABSOLUTE_MONTH; break;
1149 case UDAT_REL_UNIT_WEEK: absunit = UDAT_ABSOLUTE_WEEK; break;
1150 case UDAT_REL_UNIT_DAY: absunit = UDAT_ABSOLUTE_DAY; break;
1151 case UDAT_REL_UNIT_SECOND:
1152 if (direction == UDAT_DIRECTION_THIS) {
1153 absunit = UDAT_ABSOLUTE_NOW;
1154 direction = UDAT_DIRECTION_PLAIN;
1155 }
1156 break;
1157 case UDAT_REL_UNIT_SUNDAY: absunit = UDAT_ABSOLUTE_SUNDAY; break;
1158 case UDAT_REL_UNIT_MONDAY: absunit = UDAT_ABSOLUTE_MONDAY; break;
1159 case UDAT_REL_UNIT_TUESDAY: absunit = UDAT_ABSOLUTE_TUESDAY; break;
1160 case UDAT_REL_UNIT_WEDNESDAY: absunit = UDAT_ABSOLUTE_WEDNESDAY; break;
1161 case UDAT_REL_UNIT_THURSDAY: absunit = UDAT_ABSOLUTE_THURSDAY; break;
1162 case UDAT_REL_UNIT_FRIDAY: absunit = UDAT_ABSOLUTE_FRIDAY; break;
1163 case UDAT_REL_UNIT_SATURDAY: absunit = UDAT_ABSOLUTE_SATURDAY; break;
1164 case UDAT_REL_UNIT_HOUR: absunit = UDAT_ABSOLUTE_HOUR; break;
1165 case UDAT_REL_UNIT_MINUTE: absunit = UDAT_ABSOLUTE_MINUTE; break;
1166 default: break;
1167 }
1168 if (direction != UDAT_DIRECTION_COUNT && absunit != UDAT_ABSOLUTE_UNIT_COUNT) {
1169 formatAbsoluteImpl(direction, absunit, output, status);
1170 if (output.getStringRef().length() != 0) {
1171 return;
1172 }
1173 }
1174 // otherwise fallback to formatNumeric
1175 formatNumericImpl(offset, unit, output, status);
1176}
1177
1178UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
1179 const UnicodeString& relativeDateString, const UnicodeString& timeString,
1180 UnicodeString& appendTo, UErrorCode& status) const {
1181 return fCache->getCombinedDateAndTime()->format(
1182 timeString, relativeDateString, appendTo, status);
1183}
1184
1185UnicodeString& RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const {
1186 if (fOptBreakIterator == nullptr
1187 || str.length() == 0 || !u_islower(str.char32At(0))) {
1188 return str;
1189 }
1190
1191 // Must guarantee that one thread at a time accesses the shared break
1192 // iterator.
1193 static UMutex gBrkIterMutex;
1194 Mutex lock(&gBrkIterMutex);
1195 str.toTitle(
1196 fOptBreakIterator->get(),
1197 fLocale,
1198 U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
1199 return str;
1200}
1201
1202UBool RelativeDateTimeFormatter::checkNoAdjustForContext(UErrorCode& status) const {
1203 // This is unsupported because it's hard to keep fields in sync with title
1204 // casing. The code could be written and tested if there is demand.
1205 if (fOptBreakIterator != nullptr) {
1206 status = U_UNSUPPORTED_ERROR;
1207 return FALSE;
1208 }
1209 return TRUE;
1210}
1211
1212void RelativeDateTimeFormatter::init(
1213 NumberFormat *nfToAdopt,
1214 BreakIterator *biToAdopt,
1215 UErrorCode &status) {
1216 LocalPointer<NumberFormat> nf(nfToAdopt);
1217 LocalPointer<BreakIterator> bi(biToAdopt);
1218 UnifiedCache::getByLocale(fLocale, fCache, status);
1219 if (U_FAILURE(status)) {
1220 return;
1221 }
1222 const SharedPluralRules *pr = PluralRules::createSharedInstance(
1223 fLocale, UPLURAL_TYPE_CARDINAL, status);
1224 if (U_FAILURE(status)) {
1225 return;
1226 }
1227 SharedObject::copyPtr(pr, fPluralRules);
1228 pr->removeRef();
1229 if (nf.isNull()) {
1230 const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
1231 fLocale, UNUM_DECIMAL, status);
1232 if (U_FAILURE(status)) {
1233 return;
1234 }
1235 SharedObject::copyPtr(shared, fNumberFormat);
1236 shared->removeRef();
1237 } else {
1238 SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
1239 if (shared == nullptr) {
1240 status = U_MEMORY_ALLOCATION_ERROR;
1241 return;
1242 }
1243 nf.orphan();
1244 SharedObject::copyPtr(shared, fNumberFormat);
1245 }
1246 if (bi.isNull()) {
1247 SharedObject::clearPtr(fOptBreakIterator);
1248 } else {
1249 SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias());
1250 if (shared == nullptr) {
1251 status = U_MEMORY_ALLOCATION_ERROR;
1252 return;
1253 }
1254 bi.orphan();
1255 SharedObject::copyPtr(shared, fOptBreakIterator);
1256 }
1257}
1258
1259U_NAMESPACE_END
1260
1261// Plain C API
1262
1263U_NAMESPACE_USE
1264
1265
1266// Magic number: "FRDT" (FormattedRelativeDateTime) in ASCII
1267UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL(
1268 FormattedRelativeDateTime,
1269 UFormattedRelativeDateTime,
1270 UFormattedRelativeDateTimeImpl,
1271 UFormattedRelativeDateTimeApiHelper,
1272 ureldatefmt,
1273 0x46524454)
1274
1275
1276U_CAPI URelativeDateTimeFormatter* U_EXPORT2
1277ureldatefmt_open( const char* locale,
1278 UNumberFormat* nfToAdopt,
1279 UDateRelativeDateTimeFormatterStyle width,
1280 UDisplayContext capitalizationContext,
1281 UErrorCode* status )
1282{
1283 if (U_FAILURE(*status)) {
1284 return nullptr;
1285 }
1286 LocalPointer<RelativeDateTimeFormatter> formatter(new RelativeDateTimeFormatter(Locale(locale),
1287 (NumberFormat*)nfToAdopt, width,
1288 capitalizationContext, *status), *status);
1289 if (U_FAILURE(*status)) {
1290 return nullptr;
1291 }
1292 return (URelativeDateTimeFormatter*)formatter.orphan();
1293}
1294
1295U_CAPI void U_EXPORT2
1296ureldatefmt_close(URelativeDateTimeFormatter *reldatefmt)
1297{
1298 delete (RelativeDateTimeFormatter*)reldatefmt;
1299}
1300
1301U_CAPI int32_t U_EXPORT2
1302ureldatefmt_formatNumeric( const URelativeDateTimeFormatter* reldatefmt,
1303 double offset,
1304 URelativeDateTimeUnit unit,
1305 UChar* result,
1306 int32_t resultCapacity,
1307 UErrorCode* status)
1308{
1309 if (U_FAILURE(*status)) {
1310 return 0;
1311 }
1312 if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) {
1313 *status = U_ILLEGAL_ARGUMENT_ERROR;
1314 return 0;
1315 }
1316 UnicodeString res;
1317 if (result != nullptr) {
1318 // nullptr destination for pure preflighting: empty dummy string
1319 // otherwise, alias the destination buffer (copied from udat_format)
1320 res.setTo(result, 0, resultCapacity);
1321 }
1322 ((RelativeDateTimeFormatter*)reldatefmt)->formatNumeric(offset, unit, res, *status);
1323 if (U_FAILURE(*status)) {
1324 return 0;
1325 }
1326 return res.extract(result, resultCapacity, *status);
1327}
1328
1329U_STABLE void U_EXPORT2
1330ureldatefmt_formatNumericToResult(
1331 const URelativeDateTimeFormatter* reldatefmt,
1332 double offset,
1333 URelativeDateTimeUnit unit,
1334 UFormattedRelativeDateTime* result,
1335 UErrorCode* status) {
1336 if (U_FAILURE(*status)) {
1337 return;
1338 }
1339 auto* fmt = reinterpret_cast<const RelativeDateTimeFormatter*>(reldatefmt);
1340 auto* resultImpl = UFormattedRelativeDateTimeApiHelper::validate(result, *status);
1341 resultImpl->fImpl = fmt->formatNumericToValue(offset, unit, *status);
1342}
1343
1344U_CAPI int32_t U_EXPORT2
1345ureldatefmt_format( const URelativeDateTimeFormatter* reldatefmt,
1346 double offset,
1347 URelativeDateTimeUnit unit,
1348 UChar* result,
1349 int32_t resultCapacity,
1350 UErrorCode* status)
1351{
1352 if (U_FAILURE(*status)) {
1353 return 0;
1354 }
1355 if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) {
1356 *status = U_ILLEGAL_ARGUMENT_ERROR;
1357 return 0;
1358 }
1359 UnicodeString res;
1360 if (result != nullptr) {
1361 // nullptr destination for pure preflighting: empty dummy string
1362 // otherwise, alias the destination buffer (copied from udat_format)
1363 res.setTo(result, 0, resultCapacity);
1364 }
1365 ((RelativeDateTimeFormatter*)reldatefmt)->format(offset, unit, res, *status);
1366 if (U_FAILURE(*status)) {
1367 return 0;
1368 }
1369 return res.extract(result, resultCapacity, *status);
1370}
1371
1372U_DRAFT void U_EXPORT2
1373ureldatefmt_formatToResult(
1374 const URelativeDateTimeFormatter* reldatefmt,
1375 double offset,
1376 URelativeDateTimeUnit unit,
1377 UFormattedRelativeDateTime* result,
1378 UErrorCode* status) {
1379 if (U_FAILURE(*status)) {
1380 return;
1381 }
1382 auto* fmt = reinterpret_cast<const RelativeDateTimeFormatter*>(reldatefmt);
1383 auto* resultImpl = UFormattedRelativeDateTimeApiHelper::validate(result, *status);
1384 resultImpl->fImpl = fmt->formatToValue(offset, unit, *status);
1385}
1386
1387U_CAPI int32_t U_EXPORT2
1388ureldatefmt_combineDateAndTime( const URelativeDateTimeFormatter* reldatefmt,
1389 const UChar * relativeDateString,
1390 int32_t relativeDateStringLen,
1391 const UChar * timeString,
1392 int32_t timeStringLen,
1393 UChar* result,
1394 int32_t resultCapacity,
1395 UErrorCode* status )
1396{
1397 if (U_FAILURE(*status)) {
1398 return 0;
1399 }
1400 if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0 ||
1401 (relativeDateString == nullptr ? relativeDateStringLen != 0 : relativeDateStringLen < -1) ||
1402 (timeString == nullptr ? timeStringLen != 0 : timeStringLen < -1)) {
1403 *status = U_ILLEGAL_ARGUMENT_ERROR;
1404 return 0;
1405 }
1406 UnicodeString relDateStr((UBool)(relativeDateStringLen == -1), relativeDateString, relativeDateStringLen);
1407 UnicodeString timeStr((UBool)(timeStringLen == -1), timeString, timeStringLen);
1408 UnicodeString res(result, 0, resultCapacity);
1409 ((RelativeDateTimeFormatter*)reldatefmt)->combineDateAndTime(relDateStr, timeStr, res, *status);
1410 if (U_FAILURE(*status)) {
1411 return 0;
1412 }
1413 return res.extract(result, resultCapacity, *status);
1414}
1415
1416#endif /* !UCONFIG_NO_FORMATTING */
1417