]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/reldatefmt.cpp
ICU-64260.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>
3d1f044b 18#include <functional>
2ca993e8 19#include "unicode/dtfmtsym.h"
f3c0d7a5 20#include "unicode/ucasemap.h"
2ca993e8
A
21#include "unicode/ureldatefmt.h"
22#include "unicode/udisplaycontext.h"
23#include "unicode/unum.h"
57a6839d 24#include "unicode/localpointer.h"
57a6839d 25#include "unicode/plurrule.h"
2ca993e8 26#include "unicode/simpleformatter.h"
57a6839d
A
27#include "unicode/decimfmt.h"
28#include "unicode/numfmt.h"
b331163b 29#include "unicode/brkiter.h"
2ca993e8 30#include "unicode/simpleformatter.h"
57a6839d
A
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"
b331163b 37#include "uassert.h"
2ca993e8
A
38#include "quantityformatter.h"
39#include "resource.h"
b331163b 40#include "sharedbreakiterator.h"
57a6839d
A
41#include "sharedpluralrules.h"
42#include "sharednumberformat.h"
2ca993e8 43#include "standardplural.h"
b331163b 44#include "unifiedcache.h"
3d1f044b
A
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"
57a6839d
A
51
52// Copied from uscript_props.cpp
57a6839d 53
57a6839d
A
54U_NAMESPACE_BEGIN
55
56// RelativeDateTimeFormatter specific data for a single locale
57class RelativeDateTimeCacheData: public SharedObject {
58public:
3d1f044b 59 RelativeDateTimeCacheData() : combinedDateAndTime(nullptr) {
2ca993e8
A
60 // Initialize the cache arrays
61 for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
3d1f044b 62 for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) {
2ca993e8 63 for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) {
3d1f044b
A
64 relativeUnitsFormatters[style][relUnit][0][pl] = nullptr;
65 relativeUnitsFormatters[style][relUnit][1][pl] = nullptr;
2ca993e8
A
66 }
67 }
68 }
69 for (int32_t i = 0; i < UDAT_STYLE_COUNT; ++i) {
70 fallBackCache[i] = -1;
71 }
72 }
57a6839d
A
73 virtual ~RelativeDateTimeCacheData();
74
75 // no numbers: e.g Next Tuesday; Yesterday; etc.
b331163b 76 UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT];
57a6839d 77
2ca993e8
A
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]
3d1f044b 82 [UDAT_REL_UNIT_COUNT][2][StandardPlural::COUNT];
2ca993e8
A
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;
3d1f044b
A
91 const SimpleFormatter* getRelativeDateTimeUnitFormatter(int32_t fStyle,
92 URelativeDateTimeUnit unit,
93 int32_t pastFutureIndex,
94 int32_t pluralUnit) const;
2ca993e8
A
95
96 const UnicodeString emptyString;
97
98 // Mappping from source to target styles for alias fallback.
99 int32_t fallBackCache[UDAT_STYLE_COUNT];
57a6839d 100
2ca993e8 101 void adoptCombinedDateAndTime(SimpleFormatter *fmtToAdopt) {
57a6839d 102 delete combinedDateAndTime;
2ca993e8 103 combinedDateAndTime = fmtToAdopt;
57a6839d 104 }
2ca993e8 105 const SimpleFormatter *getCombinedDateAndTime() const {
57a6839d
A
106 return combinedDateAndTime;
107 }
2ca993e8 108
57a6839d 109private:
2ca993e8 110 SimpleFormatter *combinedDateAndTime;
57a6839d
A
111 RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other);
112 RelativeDateTimeCacheData& operator=(
113 const RelativeDateTimeCacheData &other);
114};
115
116RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
2ca993e8
A
117 // clear out the cache arrays
118 for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) {
3d1f044b 119 for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) {
2ca993e8
A
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 }
57a6839d
A
126 delete combinedDateAndTime;
127}
128
2ca993e8
A
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
2ca993e8
A
143 const SimpleFormatter* RelativeDateTimeCacheData::getRelativeUnitFormatter(
144 int32_t fStyle,
145 UDateRelativeUnit unit,
146 int32_t pastFutureIndex,
147 int32_t pluralUnit) const {
3d1f044b
A
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;
2ca993e8 181 }
3d1f044b
A
182 pluralUnit = StandardPlural::OTHER;
183 }
184 return nullptr; // No formatter found.
2ca993e8
A
185 }
186
57a6839d 187static UBool getStringWithFallback(
2ca993e8 188 const UResourceBundle *resource,
57a6839d
A
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
57a6839d
A
202
203static UBool getStringByIndex(
2ca993e8 204 const UResourceBundle *resource,
57a6839d
A
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
2ca993e8 218namespace {
57a6839d 219
2ca993e8
A
220/**
221 * Sink for enumerating all of the measurement unit display names.
2ca993e8
A
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.
2ca993e8 225 */
f3c0d7a5 226struct RelDateTimeFmtDataSink : public ResourceSink {
2ca993e8
A
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:
3d1f044b 257 return UDAT_REL_UNIT_SECOND;
2ca993e8 258 case MINUTE:
3d1f044b 259 return UDAT_REL_UNIT_MINUTE;
2ca993e8 260 case HOUR:
3d1f044b 261 return UDAT_REL_UNIT_HOUR;
2ca993e8 262 case DAY:
3d1f044b 263 return UDAT_REL_UNIT_DAY;
2ca993e8 264 case WEEK:
3d1f044b 265 return UDAT_REL_UNIT_WEEK;
2ca993e8 266 case MONTH:
3d1f044b
A
267 return UDAT_REL_UNIT_MONTH;
268 case QUARTER:
269 return UDAT_REL_UNIT_QUARTER;
2ca993e8 270 case YEAR:
3d1f044b
A
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;
2ca993e8
A
286 default:
287 return -1;
288 }
57a6839d 289 }
2ca993e8
A
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;
3d1f044b
A
300 case QUARTER:
301 return UDAT_ABSOLUTE_QUARTER;
2ca993e8
A
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;
57a6839d 320 }
2ca993e8
A
321 }
322
323 static int32_t keyToDirection(const char* key) {
324 if (uprv_strcmp(key, "-2") == 0) {
325 return UDAT_DIRECTION_LAST_2;
57a6839d 326 }
2ca993e8
A
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;
57a6839d 332 }
2ca993e8
A
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;
57a6839d 340 }
57a6839d 341
2ca993e8
A
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;
2ca993e8
A
346
347 RelativeDateTimeCacheData &outputData;
348
349 // Constructor
f3c0d7a5
A
350 RelDateTimeFmtDataSink(RelativeDateTimeCacheData& cacheData)
351 : outputData(cacheData) {
2ca993e8
A
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;
57a6839d 356 }
2ca993e8
A
357
358 ~RelDateTimeFmtDataSink();
359
360 // Utility functions
361 static UDateRelativeDateTimeFormatterStyle styleFromString(const char *s) {
3d1f044b 362 int32_t len = static_cast<int32_t>(uprv_strlen(s));
2ca993e8
A
363 if (len >= 7 && uprv_strcmp(s + len - 7, "-narrow") == 0) {
364 return UDAT_STYLE_NARROW;
57a6839d 365 }
2ca993e8
A
366 if (len >= 6 && uprv_strcmp(s + len - 6, "-short") == 0) {
367 return UDAT_STYLE_SHORT;
368 }
369 return UDAT_STYLE_LONG;
57a6839d 370 }
57a6839d 371
2ca993e8
A
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 }
57a6839d 381 }
57a6839d 382
2ca993e8
A
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;
b331163b 394 }
2ca993e8
A
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;
b331163b 448 }
b331163b 449
f3c0d7a5
A
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; }
2ca993e8 471
f3c0d7a5
A
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);
3d1f044b 480 if (relUnitIndex == UDAT_REL_UNIT_SECOND && uprv_strcmp(key, "0") == 0 &&
f3c0d7a5
A
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 }
2ca993e8 496 }
f3c0d7a5
A
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.
3d1f044b 513 if (patterns[pluralIndex] == nullptr) {
f3c0d7a5
A
514 patterns[pluralIndex] = new SimpleFormatter(
515 value.getUnicodeString(errorCode), 0, 1, errorCode);
3d1f044b 516 if (patterns[pluralIndex] == nullptr) {
f3c0d7a5
A
517 errorCode = U_MEMORY_ALLOCATION_ERROR;
518 }
519 }
2ca993e8 520 }
f3c0d7a5
A
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);
2ca993e8 581 }
2ca993e8
A
582 }
583 }
b331163b 584 }
b331163b 585
f3c0d7a5
A
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);
3d1f044b 596 int32_t unitSize = static_cast<int32_t>(uprv_strlen(key)) - styleSuffixLength(style);
f3c0d7a5
A
597 genericUnit = unitOrNegativeFromString(key, unitSize);
598 if (style >= 0 && genericUnit != INVALID_UNIT) {
599 consumeTimeUnit(key, value, errorCode);
600 }
601 }
602 }
b331163b 603 }
f3c0d7a5 604
2ca993e8
A
605};
606
607// Virtual destructors must be defined out of line.
2ca993e8 608RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {}
2ca993e8
A
609} // namespace
610
0f5d89e8 611static const DateFormatSymbols::DtWidthType styleToDateFormatSymbolWidth[UDAT_STYLE_COUNT] = {
2ca993e8
A
612 DateFormatSymbols::WIDE, DateFormatSymbols::SHORT, DateFormatSymbols::NARROW
613};
614
615// Get days of weeks from the DateFormatSymbols class.
616static void loadWeekdayNames(UnicodeString absoluteUnits[UDAT_STYLE_COUNT]
617 [UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT],
618 const char* localeId,
619 UErrorCode& status) {
3d1f044b
A
620 if (U_FAILURE(status)) {
621 return;
622 }
2ca993e8
A
623 Locale locale(localeId);
624 DateFormatSymbols dfSym(locale, status);
3d1f044b
A
625 if (U_FAILURE(status)) {
626 return;
627 }
2ca993e8
A
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 }
b331163b
A
639 }
640}
641
642static UBool loadUnitData(
643 const UResourceBundle *resource,
644 RelativeDateTimeCacheData &cacheData,
2ca993e8 645 const char* localeId,
b331163b 646 UErrorCode &status) {
f3c0d7a5
A
647
648 RelDateTimeFmtDataSink sink(cacheData);
649
650 ures_getAllItemsWithFallback(resource, "fields", sink, status);
3d1f044b
A
651 if (U_FAILURE(status)) {
652 return false;
653 }
2ca993e8
A
654
655 // Get the weekday names from DateFormatSymbols.
656 loadWeekdayNames(cacheData.absoluteUnits, localeId, status);
57a6839d
A
657 return U_SUCCESS(status);
658}
659
660static 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(
3d1f044b 678 resource, pathBuffer.data(), nullptr, &status));
57a6839d
A
679 if (U_FAILURE(status)) {
680 return FALSE;
681 }
682 int32_t size = ures_getSize(topLevel.getAlias());
683 if (size <= 8) {
2ca993e8 684 // Oops, size is too small to access the index that we want, fallback
57a6839d
A
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
b331163b
A
692template<> U_I18N_API
693const RelativeDateTimeCacheData *LocaleCacheKey<RelativeDateTimeCacheData>::createObject(const void * /*unused*/, UErrorCode &status) const {
694 const char *localeId = fLoc.getName();
3d1f044b 695 LocalUResourceBundlePointer topLevel(ures_open(nullptr, localeId, &status));
57a6839d 696 if (U_FAILURE(status)) {
3d1f044b 697 return nullptr;
57a6839d
A
698 }
699 LocalPointer<RelativeDateTimeCacheData> result(
700 new RelativeDateTimeCacheData());
701 if (result.isNull()) {
702 status = U_MEMORY_ALLOCATION_ERROR;
3d1f044b 703 return nullptr;
57a6839d
A
704 }
705 if (!loadUnitData(
706 topLevel.getAlias(),
707 *result,
2ca993e8 708 localeId,
57a6839d 709 status)) {
3d1f044b 710 return nullptr;
57a6839d
A
711 }
712 UnicodeString dateTimePattern;
713 if (!getDateTimePattern(topLevel.getAlias(), dateTimePattern, status)) {
3d1f044b 714 return nullptr;
57a6839d
A
715 }
716 result->adoptCombinedDateAndTime(
2ca993e8 717 new SimpleFormatter(dateTimePattern, 2, 2, status));
57a6839d 718 if (U_FAILURE(status)) {
3d1f044b 719 return nullptr;
57a6839d 720 }
b331163b 721 result->addRef();
57a6839d
A
722 return result.orphan();
723}
724
3d1f044b
A
725
726
727static constexpr number::impl::Field kRDTNumericField
728 = number::impl::NumFieldUtils::compress<UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_NUMERIC_FIELD>();
729
730static constexpr number::impl::Field kRDTLiteralField
731 = number::impl::NumFieldUtils::compress<UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_LITERAL_FIELD>();
732
733class FormattedRelativeDateTimeData : public FormattedValueNumberStringBuilderImpl {
734public:
735 FormattedRelativeDateTimeData() : FormattedValueNumberStringBuilderImpl(kRDTNumericField) {}
736 virtual ~FormattedRelativeDateTimeData();
737};
738
739FormattedRelativeDateTimeData::~FormattedRelativeDateTimeData() = default;
740
741
742UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedRelativeDateTime)
743
744
b331163b 745RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status) :
3d1f044b
A
746 fCache(nullptr),
747 fNumberFormat(nullptr),
748 fPluralRules(nullptr),
b331163b
A
749 fStyle(UDAT_STYLE_LONG),
750 fContext(UDISPCTX_CAPITALIZATION_NONE),
3d1f044b
A
751 fOptBreakIterator(nullptr) {
752 init(nullptr, nullptr, status);
57a6839d
A
753}
754
b331163b
A
755RelativeDateTimeFormatter::RelativeDateTimeFormatter(
756 const Locale& locale, UErrorCode& status) :
3d1f044b
A
757 fCache(nullptr),
758 fNumberFormat(nullptr),
759 fPluralRules(nullptr),
b331163b
A
760 fStyle(UDAT_STYLE_LONG),
761 fContext(UDISPCTX_CAPITALIZATION_NONE),
3d1f044b 762 fOptBreakIterator(nullptr),
b331163b 763 fLocale(locale) {
3d1f044b 764 init(nullptr, nullptr, status);
57a6839d
A
765}
766
767RelativeDateTimeFormatter::RelativeDateTimeFormatter(
b331163b 768 const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status) :
3d1f044b
A
769 fCache(nullptr),
770 fNumberFormat(nullptr),
771 fPluralRules(nullptr),
b331163b
A
772 fStyle(UDAT_STYLE_LONG),
773 fContext(UDISPCTX_CAPITALIZATION_NONE),
3d1f044b 774 fOptBreakIterator(nullptr),
b331163b 775 fLocale(locale) {
3d1f044b 776 init(nfToAdopt, nullptr, status);
57a6839d
A
777}
778
779RelativeDateTimeFormatter::RelativeDateTimeFormatter(
b331163b
A
780 const Locale& locale,
781 NumberFormat *nfToAdopt,
782 UDateRelativeDateTimeFormatterStyle styl,
783 UDisplayContext capitalizationContext,
784 UErrorCode& status) :
3d1f044b
A
785 fCache(nullptr),
786 fNumberFormat(nullptr),
787 fPluralRules(nullptr),
b331163b
A
788 fStyle(styl),
789 fContext(capitalizationContext),
3d1f044b 790 fOptBreakIterator(nullptr),
b331163b
A
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 {
3d1f044b 806 init(nfToAdopt, nullptr, status);
b331163b 807 }
57a6839d
A
808}
809
810RelativeDateTimeFormatter::RelativeDateTimeFormatter(
811 const RelativeDateTimeFormatter& other)
b331163b
A
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();
3d1f044b 823 if (fOptBreakIterator != nullptr) {
b331163b
A
824 fOptBreakIterator->addRef();
825 }
57a6839d
A
826}
827
828RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=(
829 const RelativeDateTimeFormatter& other) {
830 if (this != &other) {
b331163b
A
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;
57a6839d
A
838 }
839 return *this;
840}
841
842RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
3d1f044b 843 if (fCache != nullptr) {
b331163b
A
844 fCache->removeRef();
845 }
3d1f044b 846 if (fNumberFormat != nullptr) {
b331163b 847 fNumberFormat->removeRef();
57a6839d 848 }
3d1f044b 849 if (fPluralRules != nullptr) {
b331163b 850 fPluralRules->removeRef();
57a6839d 851 }
3d1f044b 852 if (fOptBreakIterator != nullptr) {
b331163b 853 fOptBreakIterator->removeRef();
57a6839d
A
854 }
855}
856
857const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const {
b331163b
A
858 return **fNumberFormat;
859}
860
861UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const {
862 return fContext;
863}
864
865UDateRelativeDateTimeFormatterStyle RelativeDateTimeFormatter::getFormatStyle() const {
866 return fStyle;
57a6839d
A
867}
868
3d1f044b
A
869
870// To reduce boilerplate code, we use a helper function that forwards variadic
871// arguments to the formatImpl function.
872
873template<typename F, typename... Args>
874UnicodeString& 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);
57a6839d
A
881 if (U_FAILURE(status)) {
882 return appendTo;
883 }
3d1f044b
A
884 UnicodeString result = output.getStringRef().toUnicodeString();
885 return appendTo.append(adjustForContext(result));
886}
887
888template<typename F, typename... Args>
889FormattedRelativeDateTime 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
906UnicodeString& 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
921FormattedRelativeDateTime 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
934void 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 }
57a6839d
A
943 if (direction != UDAT_DIRECTION_LAST && direction != UDAT_DIRECTION_NEXT) {
944 status = U_ILLEGAL_ARGUMENT_ERROR;
3d1f044b 945 return;
57a6839d
A
946 }
947 int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
2ca993e8 948
3d1f044b
A
949 StandardPlural::Form pluralForm;
950 QuantityFormatter::formatAndSelect(
951 quantity,
952 **fNumberFormat,
953 **fPluralRules,
954 output.getStringRef(),
955 pluralForm,
2ca993e8 956 status);
3d1f044b
A
957 if (U_FAILURE(status)) {
958 return;
959 }
2ca993e8
A
960
961 const SimpleFormatter* formatter =
3d1f044b
A
962 fCache->getRelativeUnitFormatter(fStyle, unit, bFuture, pluralForm);
963 if (formatter == nullptr) {
2ca993e8
A
964 // TODO: WARN - look at quantity formatter's action with an error.
965 status = U_INVALID_FORMAT_ERROR;
3d1f044b 966 return;
2ca993e8 967 }
3d1f044b
A
968
969 number::impl::SimpleModifier modifier(*formatter, kRDTLiteralField, false);
970 modifier.formatAsPrefixSuffix(
971 output.getStringRef(), 0, output.getStringRef().length(), status);
57a6839d
A
972}
973
2ca993e8 974UnicodeString& RelativeDateTimeFormatter::formatNumeric(
3d1f044b
A
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
987FormattedRelativeDateTime 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
998void RelativeDateTimeFormatter::formatNumericImpl(
999 double offset,
1000 URelativeDateTimeUnit unit,
1001 FormattedRelativeDateTimeData& output,
1002 UErrorCode& status) const {
2ca993e8 1003 if (U_FAILURE(status)) {
3d1f044b 1004 return;
2ca993e8
A
1005 }
1006 UDateDirection direction = UDAT_DIRECTION_NEXT;
0f5d89e8 1007 if (std::signbit(offset)) { // needed to handle -0.0
2ca993e8
A
1008 direction = UDAT_DIRECTION_LAST;
1009 offset = -offset;
1010 }
3d1f044b
A
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);
2ca993e8
A
1040}
1041
57a6839d 1042UnicodeString& RelativeDateTimeFormatter::format(
3d1f044b
A
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
1055FormattedRelativeDateTime 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
1066void RelativeDateTimeFormatter::formatAbsoluteImpl(
1067 UDateDirection direction,
1068 UDateAbsoluteUnit unit,
1069 FormattedRelativeDateTimeData& output,
1070 UErrorCode& status) const {
57a6839d 1071 if (U_FAILURE(status)) {
3d1f044b 1072 return;
57a6839d
A
1073 }
1074 if (unit == UDAT_ABSOLUTE_NOW && direction != UDAT_DIRECTION_PLAIN) {
1075 status = U_ILLEGAL_ARGUMENT_ERROR;
3d1f044b 1076 return;
57a6839d 1077 }
2ca993e8
A
1078
1079 // Get string using fallback.
3d1f044b
A
1080 output.getStringRef().append(
1081 fCache->getAbsoluteUnitString(fStyle, unit, direction),
1082 kRDTLiteralField,
1083 status);
57a6839d
A
1084}
1085
2ca993e8 1086UnicodeString& RelativeDateTimeFormatter::format(
3d1f044b
A
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
1099FormattedRelativeDateTime 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
1110void RelativeDateTimeFormatter::formatRelativeImpl(
1111 double offset,
1112 URelativeDateTimeUnit unit,
1113 FormattedRelativeDateTimeData& output,
1114 UErrorCode& status) const {
2ca993e8 1115 if (U_FAILURE(status)) {
3d1f044b 1116 return;
2ca993e8
A
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;
3d1f044b 1143 case UDAT_REL_UNIT_QUARTER: absunit = UDAT_ABSOLUTE_QUARTER; break;
2ca993e8
A
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) {
3d1f044b
A
1163 formatAbsoluteImpl(direction, absunit, output, status);
1164 if (output.getStringRef().length() != 0) {
1165 return;
2ca993e8
A
1166 }
1167 }
1168 // otherwise fallback to formatNumeric
3d1f044b 1169 formatNumericImpl(offset, unit, output, status);
2ca993e8
A
1170}
1171
57a6839d 1172UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
2ca993e8
A
1173 const UnicodeString& relativeDateString, const UnicodeString& timeString,
1174 UnicodeString& appendTo, UErrorCode& status) const {
b331163b 1175 return fCache->getCombinedDateAndTime()->format(
2ca993e8 1176 timeString, relativeDateString, appendTo, status);
57a6839d
A
1177}
1178
3d1f044b
A
1179UnicodeString& RelativeDateTimeFormatter::adjustForContext(UnicodeString &str) const {
1180 if (fOptBreakIterator == nullptr
b331163b 1181 || str.length() == 0 || !u_islower(str.char32At(0))) {
3d1f044b 1182 return str;
b331163b
A
1183 }
1184
1185 // Must guarantee that one thread at a time accesses the shared break
1186 // iterator.
3d1f044b
A
1187 static UMutex *gBrkIterMutex = STATIC_NEW(UMutex);
1188 Mutex lock(gBrkIterMutex);
b331163b
A
1189 str.toTitle(
1190 fOptBreakIterator->get(),
1191 fLocale,
1192 U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
3d1f044b
A
1193 return str;
1194}
1195
1196UBool 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;
b331163b
A
1204}
1205
57a6839d 1206void RelativeDateTimeFormatter::init(
b331163b
A
1207 NumberFormat *nfToAdopt,
1208 BreakIterator *biToAdopt,
1209 UErrorCode &status) {
57a6839d 1210 LocalPointer<NumberFormat> nf(nfToAdopt);
b331163b
A
1211 LocalPointer<BreakIterator> bi(biToAdopt);
1212 UnifiedCache::getByLocale(fLocale, fCache, status);
1213 if (U_FAILURE(status)) {
57a6839d
A
1214 return;
1215 }
b331163b
A
1216 const SharedPluralRules *pr = PluralRules::createSharedInstance(
1217 fLocale, UPLURAL_TYPE_CARDINAL, status);
57a6839d
A
1218 if (U_FAILURE(status)) {
1219 return;
1220 }
b331163b
A
1221 SharedObject::copyPtr(pr, fPluralRules);
1222 pr->removeRef();
57a6839d 1223 if (nf.isNull()) {
b331163b
A
1224 const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
1225 fLocale, UNUM_DECIMAL, status);
57a6839d
A
1226 if (U_FAILURE(status)) {
1227 return;
1228 }
b331163b
A
1229 SharedObject::copyPtr(shared, fNumberFormat);
1230 shared->removeRef();
57a6839d
A
1231 } else {
1232 SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
3d1f044b 1233 if (shared == nullptr) {
57a6839d
A
1234 status = U_MEMORY_ALLOCATION_ERROR;
1235 return;
1236 }
1237 nf.orphan();
b331163b
A
1238 SharedObject::copyPtr(shared, fNumberFormat);
1239 }
1240 if (bi.isNull()) {
1241 SharedObject::clearPtr(fOptBreakIterator);
1242 } else {
1243 SharedBreakIterator *shared = new SharedBreakIterator(bi.getAlias());
3d1f044b 1244 if (shared == nullptr) {
b331163b
A
1245 status = U_MEMORY_ALLOCATION_ERROR;
1246 return;
1247 }
1248 bi.orphan();
1249 SharedObject::copyPtr(shared, fOptBreakIterator);
57a6839d
A
1250 }
1251}
1252
57a6839d
A
1253U_NAMESPACE_END
1254
2ca993e8
A
1255// Plain C API
1256
1257U_NAMESPACE_USE
1258
3d1f044b
A
1259
1260// Magic number: "FRDT" (FormattedRelativeDateTime) in ASCII
1261UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL(
1262 FormattedRelativeDateTime,
1263 UFormattedRelativeDateTime,
1264 UFormattedRelativeDateTimeImpl,
1265 UFormattedRelativeDateTimeApiHelper,
1266 ureldatefmt,
1267 0x46524454)
1268
1269
2ca993e8
A
1270U_CAPI URelativeDateTimeFormatter* U_EXPORT2
1271ureldatefmt_open( const char* locale,
1272 UNumberFormat* nfToAdopt,
1273 UDateRelativeDateTimeFormatterStyle width,
1274 UDisplayContext capitalizationContext,
1275 UErrorCode* status )
1276{
1277 if (U_FAILURE(*status)) {
3d1f044b 1278 return nullptr;
2ca993e8
A
1279 }
1280 LocalPointer<RelativeDateTimeFormatter> formatter(new RelativeDateTimeFormatter(Locale(locale),
1281 (NumberFormat*)nfToAdopt, width,
1282 capitalizationContext, *status), *status);
1283 if (U_FAILURE(*status)) {
3d1f044b 1284 return nullptr;
2ca993e8
A
1285 }
1286 return (URelativeDateTimeFormatter*)formatter.orphan();
1287}
1288
1289U_CAPI void U_EXPORT2
1290ureldatefmt_close(URelativeDateTimeFormatter *reldatefmt)
1291{
1292 delete (RelativeDateTimeFormatter*)reldatefmt;
1293}
1294
1295U_CAPI int32_t U_EXPORT2
1296ureldatefmt_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 }
3d1f044b 1306 if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) {
2ca993e8
A
1307 *status = U_ILLEGAL_ARGUMENT_ERROR;
1308 return 0;
1309 }
1310 UnicodeString res;
3d1f044b
A
1311 if (result != nullptr) {
1312 // nullptr destination for pure preflighting: empty dummy string
2ca993e8
A
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}
57a6839d 1322
3d1f044b
A
1323U_STABLE void U_EXPORT2
1324ureldatefmt_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
2ca993e8
A
1338U_CAPI int32_t U_EXPORT2
1339ureldatefmt_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 }
3d1f044b 1349 if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0) {
2ca993e8
A
1350 *status = U_ILLEGAL_ARGUMENT_ERROR;
1351 return 0;
1352 }
1353 UnicodeString res;
3d1f044b
A
1354 if (result != nullptr) {
1355 // nullptr destination for pure preflighting: empty dummy string
2ca993e8
A
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
3d1f044b
A
1366U_DRAFT void U_EXPORT2
1367ureldatefmt_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
2ca993e8
A
1381U_CAPI int32_t U_EXPORT2
1382ureldatefmt_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 }
3d1f044b
A
1394 if (result == nullptr ? resultCapacity != 0 : resultCapacity < 0 ||
1395 (relativeDateString == nullptr ? relativeDateStringLen != 0 : relativeDateStringLen < -1) ||
1396 (timeString == nullptr ? timeStringLen != 0 : timeStringLen < -1)) {
2ca993e8
A
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 */