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