Coverage Report

Created: 2018-09-25 14:53

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