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 | |
54 | U_NAMESPACE_BEGIN |
55 | |
56 | // RelativeDateTimeFormatter specific data for a single locale |
57 | class RelativeDateTimeCacheData: public SharedObject { |
58 | public: |
59 | RelativeDateTimeCacheData() : combinedDateAndTime(nullptr) { |
60 | // Initialize the cache arrays |
61 | for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) { |
62 | for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) { |
63 | for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) { |
64 | relativeUnitsFormatters[style][relUnit][0][pl] = nullptr; |
65 | relativeUnitsFormatters[style][relUnit][1][pl] = nullptr; |
66 | } |
67 | } |
68 | } |
69 | for (int32_t i = 0; i < UDAT_STYLE_COUNT; ++i) { |
70 | fallBackCache[i] = -1; |
71 | } |
72 | } |
73 | virtual ~RelativeDateTimeCacheData(); |
74 | |
75 | // no numbers: e.g Next Tuesday; Yesterday; etc. |
76 | UnicodeString absoluteUnits[UDAT_STYLE_COUNT][UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT]; |
77 | |
78 | // SimpleFormatter pointers for relative unit format, |
79 | // e.g., Next Tuesday; Yesterday; etc. For third index, 0 |
80 | // means past, e.g., 5 days ago; 1 means future, e.g., in 5 days. |
81 | SimpleFormatter *relativeUnitsFormatters[UDAT_STYLE_COUNT] |
82 | [UDAT_REL_UNIT_COUNT][2][StandardPlural::COUNT]; |
83 | |
84 | const UnicodeString& getAbsoluteUnitString(int32_t fStyle, |
85 | UDateAbsoluteUnit unit, |
86 | UDateDirection direction) const; |
87 | const SimpleFormatter* getRelativeUnitFormatter(int32_t fStyle, |
88 | UDateRelativeUnit unit, |
89 | int32_t pastFutureIndex, |
90 | int32_t pluralUnit) const; |
91 | const SimpleFormatter* getRelativeDateTimeUnitFormatter(int32_t fStyle, |
92 | URelativeDateTimeUnit unit, |
93 | int32_t pastFutureIndex, |
94 | int32_t pluralUnit) const; |
95 | |
96 | const UnicodeString emptyString; |
97 | |
98 | // Mappping from source to target styles for alias fallback. |
99 | int32_t fallBackCache[UDAT_STYLE_COUNT]; |
100 | |
101 | void adoptCombinedDateAndTime(SimpleFormatter *fmtToAdopt) { |
102 | delete combinedDateAndTime; |
103 | combinedDateAndTime = fmtToAdopt; |
104 | } |
105 | const SimpleFormatter *getCombinedDateAndTime() const { |
106 | return combinedDateAndTime; |
107 | } |
108 | |
109 | private: |
110 | SimpleFormatter *combinedDateAndTime; |
111 | RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other); |
112 | RelativeDateTimeCacheData& operator=( |
113 | const RelativeDateTimeCacheData &other); |
114 | }; |
115 | |
116 | RelativeDateTimeCacheData::~RelativeDateTimeCacheData() { |
117 | // clear out the cache arrays |
118 | for (int32_t style = 0; style < UDAT_STYLE_COUNT; ++style) { |
119 | for (int32_t relUnit = 0; relUnit < UDAT_REL_UNIT_COUNT; ++relUnit) { |
120 | for (int32_t pl = 0; pl < StandardPlural::COUNT; ++pl) { |
121 | delete relativeUnitsFormatters[style][relUnit][0][pl]; |
122 | delete relativeUnitsFormatters[style][relUnit][1][pl]; |
123 | } |
124 | } |
125 | } |
126 | delete combinedDateAndTime; |
127 | } |
128 | |
129 | |
130 | // Use fallback cache for absolute units. |
131 | const UnicodeString& RelativeDateTimeCacheData::getAbsoluteUnitString( |
132 | int32_t fStyle, UDateAbsoluteUnit unit, UDateDirection direction) const { |
133 | int32_t style = fStyle; |
134 | do { |
135 | if (!absoluteUnits[style][unit][direction].isEmpty()) { |
136 | return absoluteUnits[style][unit][direction]; |
137 | } |
138 | style = fallBackCache[style]; |
139 | } while (style != -1); |
140 | return emptyString; |
141 | } |
142 | |
143 | const SimpleFormatter* RelativeDateTimeCacheData::getRelativeUnitFormatter( |
144 | int32_t fStyle, |
145 | UDateRelativeUnit unit, |
146 | int32_t pastFutureIndex, |
147 | int32_t pluralUnit) const { |
148 | URelativeDateTimeUnit rdtunit = UDAT_REL_UNIT_COUNT; |
149 | switch (unit) { |
150 | case UDAT_RELATIVE_YEARS: rdtunit = UDAT_REL_UNIT_YEAR; break; |
151 | case UDAT_RELATIVE_MONTHS: rdtunit = UDAT_REL_UNIT_MONTH; break; |
152 | case UDAT_RELATIVE_WEEKS: rdtunit = UDAT_REL_UNIT_WEEK; break; |
153 | case UDAT_RELATIVE_DAYS: rdtunit = UDAT_REL_UNIT_DAY; break; |
154 | case UDAT_RELATIVE_HOURS: rdtunit = UDAT_REL_UNIT_HOUR; break; |
155 | case UDAT_RELATIVE_MINUTES: rdtunit = UDAT_REL_UNIT_MINUTE; break; |
156 | case UDAT_RELATIVE_SECONDS: rdtunit = UDAT_REL_UNIT_SECOND; break; |
157 | default: // a unit that the above method does not handle |
158 | return nullptr; |
159 | } |
160 | |
161 | return getRelativeDateTimeUnitFormatter(fStyle, rdtunit, pastFutureIndex, pluralUnit); |
162 | } |
163 | |
164 | // Use fallback cache for SimpleFormatter relativeUnits. |
165 | const SimpleFormatter* RelativeDateTimeCacheData::getRelativeDateTimeUnitFormatter( |
166 | int32_t fStyle, |
167 | URelativeDateTimeUnit unit, |
168 | int32_t pastFutureIndex, |
169 | int32_t pluralUnit) const { |
170 | while (true) { |
171 | int32_t style = fStyle; |
172 | do { |
173 | if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != nullptr) { |
174 | return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit]; |
175 | } |
176 | style = fallBackCache[style]; |
177 | } while (style != -1); |
178 | |
179 | if (pluralUnit == StandardPlural::OTHER) { |
180 | break; |
181 | } |
182 | pluralUnit = StandardPlural::OTHER; |
183 | } |
184 | return nullptr; // No formatter found. |
185 | } |
186 | |
187 | static UBool getStringWithFallback( |
188 | const UResourceBundle *resource, |
189 | const char *key, |
190 | UnicodeString &result, |
191 | UErrorCode &status) { |
192 | int32_t len = 0; |
193 | const UChar *resStr = ures_getStringByKeyWithFallback( |
194 | resource, key, &len, &status); |
195 | if (U_FAILURE(status)) { |
196 | return FALSE; |
197 | } |
198 | result.setTo(TRUE, resStr, len); |
199 | return TRUE; |
200 | } |
201 | |
202 | |
203 | static UBool getStringByIndex( |
204 | const UResourceBundle *resource, |
205 | int32_t idx, |
206 | UnicodeString &result, |
207 | UErrorCode &status) { |
208 | int32_t len = 0; |
209 | const UChar *resStr = ures_getStringByIndex( |
210 | resource, idx, &len, &status); |
211 | if (U_FAILURE(status)) { |
212 | return FALSE; |
213 | } |
214 | result.setTo(TRUE, resStr, len); |
215 | return TRUE; |
216 | } |
217 | |
218 | namespace { |
219 | |
220 | /** |
221 | * Sink for enumerating all of the measurement unit display names. |
222 | * |
223 | * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root): |
224 | * Only store a value if it is still missing, that is, it has not been overridden. |
225 | */ |
226 | struct RelDateTimeFmtDataSink : public ResourceSink { |
227 | |
228 | /** |
229 | * Sink for patterns for relative dates and times. For example, |
230 | * fields/relative/... |
231 | */ |
232 | |
233 | // Generic unit enum for storing Unit info. |
234 | typedef enum RelAbsUnit { |
235 | INVALID_UNIT = -1, |
236 | SECOND, |
237 | MINUTE, |
238 | HOUR, |
239 | DAY, |
240 | WEEK, |
241 | MONTH, |
242 | QUARTER, |
243 | YEAR, |
244 | SUNDAY, |
245 | MONDAY, |
246 | TUESDAY, |
247 | WEDNESDAY, |
248 | THURSDAY, |
249 | FRIDAY, |
250 | SATURDAY |
251 | } RelAbsUnit; |
252 | |
253 | static int32_t relUnitFromGeneric(RelAbsUnit genUnit) { |
254 | // Converts the generic units to UDAT_RELATIVE version. |
255 | switch (genUnit) { |
256 | case SECOND: |
257 | return UDAT_REL_UNIT_SECOND; |
258 | case MINUTE: |
259 | return UDAT_REL_UNIT_MINUTE; |
260 | case HOUR: |
261 | return UDAT_REL_UNIT_HOUR; |
262 | case DAY: |
263 | return UDAT_REL_UNIT_DAY; |
264 | case WEEK: |
265 | return UDAT_REL_UNIT_WEEK; |
266 | case MONTH: |
267 | return UDAT_REL_UNIT_MONTH; |
268 | case QUARTER: |
269 | return UDAT_REL_UNIT_QUARTER; |
270 | case YEAR: |
271 | return UDAT_REL_UNIT_YEAR; |
272 | case SUNDAY: |
273 | return UDAT_REL_UNIT_SUNDAY; |
274 | case MONDAY: |
275 | return UDAT_REL_UNIT_MONDAY; |
276 | case TUESDAY: |
277 | return UDAT_REL_UNIT_TUESDAY; |
278 | case WEDNESDAY: |
279 | return UDAT_REL_UNIT_WEDNESDAY; |
280 | case THURSDAY: |
281 | return UDAT_REL_UNIT_THURSDAY; |
282 | case FRIDAY: |
283 | return UDAT_REL_UNIT_FRIDAY; |
284 | case SATURDAY: |
285 | return UDAT_REL_UNIT_SATURDAY; |
286 | default: |
287 | return -1; |
288 | } |
289 | } |
290 | |
291 | static int32_t absUnitFromGeneric(RelAbsUnit genUnit) { |
292 | // Converts the generic units to UDAT_RELATIVE version. |
293 | switch (genUnit) { |
294 | case DAY: |
295 | return UDAT_ABSOLUTE_DAY; |
296 | case WEEK: |
297 | return UDAT_ABSOLUTE_WEEK; |
298 | case MONTH: |
299 | return UDAT_ABSOLUTE_MONTH; |
300 | case QUARTER: |
301 | return UDAT_ABSOLUTE_QUARTER; |
302 | case YEAR: |
303 | return UDAT_ABSOLUTE_YEAR; |
304 | case SUNDAY: |
305 | return UDAT_ABSOLUTE_SUNDAY; |
306 | case MONDAY: |
307 | return UDAT_ABSOLUTE_MONDAY; |
308 | case TUESDAY: |
309 | return UDAT_ABSOLUTE_TUESDAY; |
310 | case WEDNESDAY: |
311 | return UDAT_ABSOLUTE_WEDNESDAY; |
312 | case THURSDAY: |
313 | return UDAT_ABSOLUTE_THURSDAY; |
314 | case FRIDAY: |
315 | return UDAT_ABSOLUTE_FRIDAY; |
316 | case SATURDAY: |
317 | return UDAT_ABSOLUTE_SATURDAY; |
318 | 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. |
612 | RelDateTimeFmtDataSink::~RelDateTimeFmtDataSink() {} |
613 | } // namespace |
614 | |
615 | static 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. |
620 | static 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 | |
646 | static 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 | |
664 | static 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 | |
696 | template<> U_I18N_API |
697 | const 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 | |
731 | static constexpr number::impl::Field kRDTNumericField |
732 | = StringBuilderFieldUtils::compress<UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_NUMERIC_FIELD>(); |
733 | |
734 | static constexpr number::impl::Field kRDTLiteralField |
735 | = StringBuilderFieldUtils::compress<UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_LITERAL_FIELD>(); |
736 | |
737 | class FormattedRelativeDateTimeData : public FormattedValueStringBuilderImpl { |
738 | public: |
739 | FormattedRelativeDateTimeData() : FormattedValueStringBuilderImpl(kRDTNumericField) {} |
740 | virtual ~FormattedRelativeDateTimeData(); |
741 | }; |
742 | |
743 | FormattedRelativeDateTimeData::~FormattedRelativeDateTimeData() = default; |
744 | |
745 | |
746 | UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedRelativeDateTime) |
747 | |
748 | |
749 | RelativeDateTimeFormatter::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 | |
759 | RelativeDateTimeFormatter::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 | |
771 | RelativeDateTimeFormatter::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 | |
783 | RelativeDateTimeFormatter::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 | |
814 | RelativeDateTimeFormatter::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 | |
832 | RelativeDateTimeFormatter& 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 | |
846 | RelativeDateTimeFormatter::~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 | |
861 | const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const { |
862 | return **fNumberFormat; |
863 | } |
864 | |
865 | UDisplayContext RelativeDateTimeFormatter::getCapitalizationContext() const { |
866 | return fContext; |
867 | } |
868 | |
869 | UDateRelativeDateTimeFormatterStyle 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 | |
877 | template<typename F, typename... Args> |
878 | UnicodeString& 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 | |
892 | template<typename F, typename... Args> |
893 | FormattedRelativeDateTime 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 | |
910 | UnicodeString& 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 | |
925 | FormattedRelativeDateTime 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 | |
938 | void 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 | |
978 | UnicodeString& 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 | |
991 | FormattedRelativeDateTime 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 | |
1002 | void 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 | |
1046 | UnicodeString& 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 | |
1059 | FormattedRelativeDateTime 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 | |
1070 | void 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 | |
1090 | UnicodeString& 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 | |
1103 | FormattedRelativeDateTime 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 | |
1114 | void 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 | |
1178 | UnicodeString& 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 | |
1185 | UnicodeString& 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 | |
1202 | UBool 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 | |
1212 | void 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 | |
1259 | U_NAMESPACE_END |
1260 | |
1261 | // Plain C API |
1262 | |
1263 | U_NAMESPACE_USE |
1264 | |
1265 | |
1266 | // Magic number: "FRDT" (FormattedRelativeDateTime) in ASCII |
1267 | UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL( |
1268 | FormattedRelativeDateTime, |
1269 | UFormattedRelativeDateTime, |
1270 | UFormattedRelativeDateTimeImpl, |
1271 | UFormattedRelativeDateTimeApiHelper, |
1272 | ureldatefmt, |
1273 | 0x46524454) |
1274 | |
1275 | |
1276 | U_CAPI URelativeDateTimeFormatter* U_EXPORT2 |
1277 | ureldatefmt_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 | |
1295 | U_CAPI void U_EXPORT2 |
1296 | ureldatefmt_close(URelativeDateTimeFormatter *reldatefmt) |
1297 | { |
1298 | delete (RelativeDateTimeFormatter*)reldatefmt; |
1299 | } |
1300 | |
1301 | U_CAPI int32_t U_EXPORT2 |
1302 | ureldatefmt_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 | |
1329 | U_STABLE void U_EXPORT2 |
1330 | ureldatefmt_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 | |
1344 | U_CAPI int32_t U_EXPORT2 |
1345 | ureldatefmt_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 | |
1372 | U_DRAFT void U_EXPORT2 |
1373 | ureldatefmt_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 | |
1387 | U_CAPI int32_t U_EXPORT2 |
1388 | ureldatefmt_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 | |