Coverage Report

Created: 2026-06-07 06:21

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