Coverage Report

Created: 2025-06-24 06:54

/src/icu/icu4c/source/i18n/reldtfmt.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) 2007-2016, International Business Machines Corporation and
6
* others. All Rights Reserved.
7
*******************************************************************************
8
*/
9
10
#include "unicode/utypes.h"
11
12
#if !UCONFIG_NO_FORMATTING
13
14
#include <stdlib.h>
15
16
#include "unicode/datefmt.h"
17
#include "unicode/reldatefmt.h"
18
#include "unicode/simpleformatter.h"
19
#include "unicode/smpdtfmt.h"
20
#include "unicode/udisplaycontext.h"
21
#include "unicode/uchar.h"
22
#include "unicode/brkiter.h"
23
#include "unicode/ucasemap.h"
24
#include "reldtfmt.h"
25
#include "cmemory.h"
26
#include "uresimp.h"
27
28
U_NAMESPACE_BEGIN
29
30
31
/**
32
 * An array of URelativeString structs is used to store the resource data loaded out of the bundle.
33
 */
34
struct URelativeString {
35
    int32_t offset;         /** offset of this item, such as, the relative date **/
36
    int32_t len;            /** length of the string **/
37
    const char16_t* string;    /** string, or nullptr if not set **/
38
};
39
40
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat)
41
42
RelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) :
43
0
 DateFormat(other), fDateTimeFormatter(nullptr), fDatePattern(other.fDatePattern),
44
0
 fTimePattern(other.fTimePattern), fCombinedFormat(nullptr),
45
0
 fDateStyle(other.fDateStyle), fLocale(other.fLocale),
46
0
 fDatesLen(other.fDatesLen), fDates(nullptr),
47
0
 fCombinedHasDateAtStart(other.fCombinedHasDateAtStart),
48
0
 fCapitalizationInfoSet(other.fCapitalizationInfoSet),
49
0
 fCapitalizationOfRelativeUnitsForUIListMenu(other.fCapitalizationOfRelativeUnitsForUIListMenu),
50
0
 fCapitalizationOfRelativeUnitsForStandAlone(other.fCapitalizationOfRelativeUnitsForStandAlone),
51
0
 fCapitalizationBrkIter(nullptr)
52
0
{
53
0
    if(other.fDateTimeFormatter != nullptr) {
54
0
        fDateTimeFormatter = other.fDateTimeFormatter->clone();
55
0
    }
56
0
    if(other.fCombinedFormat != nullptr) {
57
0
        fCombinedFormat = new SimpleFormatter(*other.fCombinedFormat);
58
0
    }
59
0
    if (fDatesLen > 0) {
60
0
        fDates = static_cast<URelativeString*>(uprv_malloc(sizeof(fDates[0]) * static_cast<size_t>(fDatesLen)));
61
0
        uprv_memcpy(fDates, other.fDates, sizeof(fDates[0])*(size_t)fDatesLen);
62
0
    }
63
0
#if !UCONFIG_NO_BREAK_ITERATION
64
0
    if (other.fCapitalizationBrkIter != nullptr) {
65
0
        fCapitalizationBrkIter = (other.fCapitalizationBrkIter)->clone();
66
0
    }
67
0
#endif
68
0
}
69
70
RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle,
71
                                        const Locale& locale, UErrorCode& status) :
72
10.9k
 DateFormat(), fDateTimeFormatter(nullptr), fDatePattern(), fTimePattern(), fCombinedFormat(nullptr),
73
10.9k
 fDateStyle(dateStyle), fLocale(locale), fDatesLen(0), fDates(nullptr),
74
10.9k
 fCombinedHasDateAtStart(false), fCapitalizationInfoSet(false),
75
10.9k
 fCapitalizationOfRelativeUnitsForUIListMenu(false), fCapitalizationOfRelativeUnitsForStandAlone(false),
76
10.9k
 fCapitalizationBrkIter(nullptr)
77
10.9k
{
78
10.9k
    if(U_FAILURE(status) ) {
79
0
        return;
80
0
    }
81
10.9k
    if (dateStyle != UDAT_FULL_RELATIVE &&
82
10.9k
        dateStyle != UDAT_LONG_RELATIVE &&
83
10.9k
        dateStyle != UDAT_MEDIUM_RELATIVE &&
84
10.9k
        dateStyle != UDAT_SHORT_RELATIVE &&
85
10.9k
        dateStyle != UDAT_RELATIVE) {
86
3.57k
        status = U_ILLEGAL_ARGUMENT_ERROR;
87
3.57k
        return;
88
3.57k
    }
89
90
7.33k
    if (timeStyle < UDAT_NONE || timeStyle > UDAT_SHORT) {
91
        // don't support other time styles (e.g. relative styles), for now
92
2.24k
        status = U_ILLEGAL_ARGUMENT_ERROR;
93
2.24k
        return;
94
2.24k
    }
95
5.09k
    UDateFormatStyle baseDateStyle = (dateStyle > UDAT_SHORT) ? static_cast<UDateFormatStyle>(dateStyle & ~UDAT_RELATIVE) : dateStyle;
96
5.09k
    DateFormat * df;
97
    // Get fDateTimeFormatter from either date or time style (does not matter, we will override the pattern).
98
    // We do need to get separate patterns for the date & time styles.
99
5.09k
    if (baseDateStyle != UDAT_NONE) {
100
5.09k
        df = createDateInstance(static_cast<EStyle>(baseDateStyle), locale);
101
5.09k
        fDateTimeFormatter=dynamic_cast<SimpleDateFormat *>(df);
102
5.09k
        if (fDateTimeFormatter == nullptr) {
103
141
            status = U_UNSUPPORTED_ERROR;
104
141
             return;
105
141
        }
106
4.95k
        fDateTimeFormatter->toPattern(fDatePattern);
107
4.95k
        if (timeStyle != UDAT_NONE) {
108
4.17k
            df = createTimeInstance(static_cast<EStyle>(timeStyle), locale);
109
4.17k
            SimpleDateFormat *sdf = dynamic_cast<SimpleDateFormat *>(df);
110
4.17k
            if (sdf != nullptr) {
111
4.17k
                sdf->toPattern(fTimePattern);
112
4.17k
                delete sdf;
113
4.17k
            }
114
4.17k
        }
115
4.95k
    } else {
116
        // does not matter whether timeStyle is UDAT_NONE, we need something for fDateTimeFormatter
117
0
        df = createTimeInstance(static_cast<EStyle>(timeStyle), locale);
118
0
        fDateTimeFormatter=dynamic_cast<SimpleDateFormat *>(df);
119
0
        if (fDateTimeFormatter == nullptr) {
120
0
            status = U_UNSUPPORTED_ERROR;
121
0
            delete df;
122
0
            return;
123
0
        }
124
0
        fDateTimeFormatter->toPattern(fTimePattern);
125
0
    }
126
127
    // Initialize the parent fCalendar, so that parse() works correctly.
128
4.95k
    initializeCalendar(nullptr, locale, status);
129
4.95k
    loadDates(status);
130
4.95k
}
131
132
10.9k
RelativeDateFormat::~RelativeDateFormat() {
133
10.9k
    delete fDateTimeFormatter;
134
10.9k
    delete fCombinedFormat;
135
10.9k
    uprv_free(fDates);
136
10.9k
#if !UCONFIG_NO_BREAK_ITERATION
137
10.9k
    delete fCapitalizationBrkIter;
138
10.9k
#endif
139
10.9k
}
140
141
142
0
RelativeDateFormat* RelativeDateFormat::clone() const {
143
0
    return new RelativeDateFormat(*this);
144
0
}
145
146
0
bool RelativeDateFormat::operator==(const Format& other) const {
147
0
    if(DateFormat::operator==(other)) {
148
        // The DateFormat::operator== check for fCapitalizationContext equality above
149
        //   is sufficient to check equality of all derived context-related data.
150
        // DateFormat::operator== guarantees following cast is safe
151
0
        RelativeDateFormat* that = (RelativeDateFormat*)&other;
152
0
        return (fDateStyle==that->fDateStyle   &&
153
0
                fDatePattern==that->fDatePattern   &&
154
0
                fTimePattern==that->fTimePattern   &&
155
0
                fLocale==that->fLocale );
156
0
    }
157
0
    return false;
158
0
}
159
160
static const char16_t APOSTROPHE = static_cast<char16_t>(0x0027);
161
162
UnicodeString& RelativeDateFormat::format(  Calendar& cal,
163
                                UnicodeString& appendTo,
164
2.52k
                                FieldPosition& pos) const {
165
                                
166
2.52k
    UErrorCode status = U_ZERO_ERROR;
167
2.52k
    UnicodeString relativeDayString;
168
2.52k
    UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status);
169
    
170
    // calculate the difference, in days, between 'cal' and now.
171
2.52k
    int dayDiff = dayDifference(cal, status);
172
173
    // look up string
174
2.52k
    int32_t len = 0;
175
2.52k
    const char16_t *theString = getStringForDay(dayDiff, len, status);
176
2.52k
    if(U_SUCCESS(status) && (theString!=nullptr)) {
177
        // found a relative string
178
11
        relativeDayString.setTo(theString, len);
179
11
    }
180
181
2.52k
    if ( relativeDayString.length() > 0 && !fDatePattern.isEmpty() && 
182
2.52k
         (fTimePattern.isEmpty() || fCombinedFormat == nullptr || fCombinedHasDateAtStart)) {
183
11
#if !UCONFIG_NO_BREAK_ITERATION
184
        // capitalize relativeDayString according to context for relative, set formatter no context
185
11
        if ( u_islower(relativeDayString.char32At(0)) && fCapitalizationBrkIter!= nullptr &&
186
11
             ( capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
187
0
               (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && fCapitalizationOfRelativeUnitsForUIListMenu) ||
188
0
               (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && fCapitalizationOfRelativeUnitsForStandAlone) ) ) {
189
            // titlecase first word of relativeDayString
190
0
            relativeDayString.toTitle(fCapitalizationBrkIter, fLocale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
191
0
        }
192
11
#endif
193
11
        fDateTimeFormatter->setContext(UDISPCTX_CAPITALIZATION_NONE, status);
194
2.51k
    } else {
195
        // set our context for the formatter
196
2.51k
        fDateTimeFormatter->setContext(capitalizationContext, status);
197
2.51k
    }
198
199
2.52k
    if (fDatePattern.isEmpty()) {
200
0
        fDateTimeFormatter->applyPattern(fTimePattern);
201
0
        fDateTimeFormatter->format(cal,appendTo,pos);
202
2.52k
    } else if (fTimePattern.isEmpty() || fCombinedFormat == nullptr) {
203
412
        if (relativeDayString.length() > 0) {
204
5
            appendTo.append(relativeDayString);
205
407
        } else {
206
407
            fDateTimeFormatter->applyPattern(fDatePattern);
207
407
            fDateTimeFormatter->format(cal,appendTo,pos);
208
407
        }
209
2.11k
    } else {
210
2.11k
        UnicodeString datePattern;
211
2.11k
        if (relativeDayString.length() > 0) {
212
            // Need to quote the relativeDayString to make it a legal date pattern
213
6
            relativeDayString.findAndReplace(UNICODE_STRING("'", 1), UNICODE_STRING("''", 2)); // double any existing APOSTROPHE
214
6
            relativeDayString.insert(0, APOSTROPHE); // add APOSTROPHE at beginning...
215
6
            relativeDayString.append(APOSTROPHE); // and at end
216
6
            datePattern.setTo(relativeDayString);
217
2.10k
        } else {
218
2.10k
            datePattern.setTo(fDatePattern);
219
2.10k
        }
220
2.11k
        UnicodeString combinedPattern;
221
2.11k
        fCombinedFormat->format(fTimePattern, datePattern, combinedPattern, status);
222
2.11k
        fDateTimeFormatter->applyPattern(combinedPattern);
223
2.11k
        fDateTimeFormatter->format(cal,appendTo,pos);
224
2.11k
    }
225
226
2.52k
    return appendTo;
227
2.52k
}
228
229
230
231
UnicodeString&
232
RelativeDateFormat::format(const Formattable& obj, 
233
                         UnicodeString& appendTo, 
234
                         FieldPosition& pos,
235
                         UErrorCode& status) const
236
0
{
237
    // this is just here to get around the hiding problem
238
    // (the previous format() override would hide the version of
239
    // format() on DateFormat that this function correspond to, so we
240
    // have to redefine it here)
241
0
    return DateFormat::format(obj, appendTo, pos, status);
242
0
}
243
244
245
void RelativeDateFormat::parse( const UnicodeString& text,
246
                    Calendar& cal,
247
0
                    ParsePosition& pos) const {
248
249
0
    int32_t startIndex = pos.getIndex();
250
0
    if (fDatePattern.isEmpty()) {
251
        // no date pattern, try parsing as time
252
0
        fDateTimeFormatter->applyPattern(fTimePattern);
253
0
        fDateTimeFormatter->parse(text,cal,pos);
254
0
    } else if (fTimePattern.isEmpty() || fCombinedFormat == nullptr) {
255
        // no time pattern or way to combine, try parsing as date
256
        // first check whether text matches a relativeDayString
257
0
        UBool matchedRelative = false;
258
0
        for (int n=0; n < fDatesLen && !matchedRelative; n++) {
259
0
            if (fDates[n].string != nullptr &&
260
0
                    text.compare(startIndex, fDates[n].len, fDates[n].string) == 0) {
261
                // it matched, handle the relative day string
262
0
                UErrorCode status = U_ZERO_ERROR;
263
0
                matchedRelative = true;
264
265
                // Set the calendar to now+offset
266
0
                cal.setTime(Calendar::getNow(),status);
267
0
                cal.add(UCAL_DATE,fDates[n].offset, status);
268
269
0
                if(U_FAILURE(status)) { 
270
                    // failure in setting calendar field, set offset to beginning of rel day string
271
0
                    pos.setErrorIndex(startIndex);
272
0
                } else {
273
0
                    pos.setIndex(startIndex + fDates[n].len);
274
0
                }
275
0
            }
276
0
        }
277
0
        if (!matchedRelative) {
278
            // just parse as normal date
279
0
            fDateTimeFormatter->applyPattern(fDatePattern);
280
0
            fDateTimeFormatter->parse(text,cal,pos);
281
0
        }
282
0
    } else {
283
        // Here we replace any relativeDayString in text with the equivalent date
284
        // formatted per fDatePattern, then parse text normally using the combined pattern.
285
0
        UnicodeString modifiedText(text);
286
0
        FieldPosition fPos;
287
0
        int32_t dateStart = 0, origDateLen = 0, modDateLen = 0;
288
0
        UErrorCode status = U_ZERO_ERROR;
289
0
        for (int n=0; n < fDatesLen; n++) {
290
0
            int32_t relativeStringOffset;
291
0
            if (fDates[n].string != nullptr &&
292
0
                    (relativeStringOffset = modifiedText.indexOf(fDates[n].string, fDates[n].len, startIndex)) >= startIndex) {
293
                // it matched, replace the relative date with a real one for parsing
294
0
                UnicodeString dateString;
295
0
                Calendar * tempCal = cal.clone();
296
297
                // Set the calendar to now+offset
298
0
                tempCal->setTime(Calendar::getNow(),status);
299
0
                tempCal->add(UCAL_DATE,fDates[n].offset, status);
300
0
                if(U_FAILURE(status)) { 
301
0
                    pos.setErrorIndex(startIndex);
302
0
                    delete tempCal;
303
0
                    return;
304
0
                }
305
306
0
                fDateTimeFormatter->applyPattern(fDatePattern);
307
0
                fDateTimeFormatter->format(*tempCal, dateString, fPos);
308
0
                dateStart = relativeStringOffset;
309
0
                origDateLen = fDates[n].len;
310
0
                modDateLen = dateString.length();
311
0
                modifiedText.replace(dateStart, origDateLen, dateString);
312
0
                delete tempCal;
313
0
                break;
314
0
            }
315
0
        }
316
0
        UnicodeString combinedPattern;
317
0
        fCombinedFormat->format(fTimePattern, fDatePattern, combinedPattern, status);
318
0
        fDateTimeFormatter->applyPattern(combinedPattern);
319
0
        fDateTimeFormatter->parse(modifiedText,cal,pos);
320
321
        // Adjust offsets
322
0
        UBool noError = (pos.getErrorIndex() < 0);
323
0
        int32_t offset = (noError)? pos.getIndex(): pos.getErrorIndex();
324
0
        if (offset >= dateStart + modDateLen) {
325
            // offset at or after the end of the replaced text,
326
            // correct by the difference between original and replacement
327
0
            offset -= (modDateLen - origDateLen);
328
0
        } else if (offset >= dateStart) {
329
            // offset in the replaced text, set it to the beginning of that text
330
            // (i.e. the beginning of the relative day string)
331
0
            offset = dateStart;
332
0
        }
333
0
        if (noError) {
334
0
            pos.setIndex(offset);
335
0
        } else {
336
0
            pos.setErrorIndex(offset);
337
0
        }
338
0
    }
339
0
}
340
341
UDate
342
RelativeDateFormat::parse( const UnicodeString& text,
343
0
                         ParsePosition& pos) const {
344
    // redefined here because the other parse() function hides this function's
345
    // counterpart on DateFormat
346
0
    return DateFormat::parse(text, pos);
347
0
}
348
349
UDate
350
RelativeDateFormat::parse(const UnicodeString& text, UErrorCode& status) const
351
0
{
352
    // redefined here because the other parse() function hides this function's
353
    // counterpart on DateFormat
354
0
    return DateFormat::parse(text, status);
355
0
}
356
357
358
2.52k
const char16_t *RelativeDateFormat::getStringForDay(int32_t day, int32_t &len, UErrorCode &status) const {
359
2.52k
    if(U_FAILURE(status)) {
360
0
        return nullptr;
361
0
    }
362
363
    // Is it inside the resource bundle's range?
364
2.52k
    int n = day + UDAT_DIRECTION_THIS;
365
2.52k
    if (n >= 0 && n < fDatesLen) {
366
14
        if (fDates[n].offset == day && fDates[n].string != nullptr) {
367
11
            len = fDates[n].len;
368
11
            return fDates[n].string;
369
11
        }
370
14
    }
371
2.51k
    return nullptr;  // not found.
372
2.52k
}
373
374
UnicodeString&
375
RelativeDateFormat::toPattern(UnicodeString& result, UErrorCode& status) const
376
0
{
377
0
    if (!U_FAILURE(status)) {
378
0
        result.remove();
379
0
        if (fDatePattern.isEmpty()) {
380
0
            result.setTo(fTimePattern);
381
0
        } else if (fTimePattern.isEmpty() || fCombinedFormat == nullptr) {
382
0
            result.setTo(fDatePattern);
383
0
        } else {
384
0
            fCombinedFormat->format(fTimePattern, fDatePattern, result, status);
385
0
        }
386
0
    }
387
0
    return result;
388
0
}
389
390
UnicodeString&
391
RelativeDateFormat::toPatternDate(UnicodeString& result, UErrorCode& status) const
392
0
{
393
0
    if (!U_FAILURE(status)) {
394
0
        result.remove();
395
0
        result.setTo(fDatePattern);
396
0
    }
397
0
    return result;
398
0
}
399
400
UnicodeString&
401
RelativeDateFormat::toPatternTime(UnicodeString& result, UErrorCode& status) const
402
0
{
403
0
    if (!U_FAILURE(status)) {
404
0
        result.remove();
405
0
        result.setTo(fTimePattern);
406
0
    }
407
0
    return result;
408
0
}
409
410
void
411
RelativeDateFormat::applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status)
412
0
{
413
0
    if (!U_FAILURE(status)) {
414
0
        fDatePattern.setTo(datePattern);
415
0
        fTimePattern.setTo(timePattern);
416
0
    }
417
0
}
418
419
const DateFormatSymbols*
420
RelativeDateFormat::getDateFormatSymbols() const
421
0
{
422
0
    return fDateTimeFormatter->getDateFormatSymbols();
423
0
}
424
425
// override the DateFormat implementation in order to
426
// lazily initialize relevant items
427
void
428
RelativeDateFormat::setContext(UDisplayContext value, UErrorCode& status)
429
0
{
430
0
    DateFormat::setContext(value, status);
431
0
    if (U_SUCCESS(status)) {
432
0
        if (!fCapitalizationInfoSet &&
433
0
                (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE)) {
434
0
            initCapitalizationContextInfo(fLocale);
435
0
            fCapitalizationInfoSet = true;
436
0
        }
437
0
#if !UCONFIG_NO_BREAK_ITERATION
438
0
        if ( fCapitalizationBrkIter == nullptr && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
439
0
                (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && fCapitalizationOfRelativeUnitsForUIListMenu) ||
440
0
                (value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && fCapitalizationOfRelativeUnitsForStandAlone)) ) {
441
0
            status = U_ZERO_ERROR;
442
0
            fCapitalizationBrkIter = BreakIterator::createSentenceInstance(fLocale, status);
443
0
            if (U_FAILURE(status)) {
444
0
                delete fCapitalizationBrkIter;
445
0
                fCapitalizationBrkIter = nullptr;
446
0
            }
447
0
        }
448
0
#endif
449
0
    }
450
0
}
451
452
void
453
RelativeDateFormat::initCapitalizationContextInfo(const Locale& thelocale)
454
0
{
455
0
#if !UCONFIG_NO_BREAK_ITERATION
456
0
    const char * localeID = (thelocale != nullptr)? thelocale.getBaseName(): nullptr;
457
0
    UErrorCode status = U_ZERO_ERROR;
458
0
    LocalUResourceBundlePointer rb(ures_open(nullptr, localeID, &status));
459
0
    ures_getByKeyWithFallback(rb.getAlias(),
460
0
                              "contextTransforms/relative",
461
0
                               rb.getAlias(), &status);
462
0
    if (U_SUCCESS(status) && rb != nullptr) {
463
0
        int32_t len = 0;
464
0
        const int32_t * intVector = ures_getIntVector(rb.getAlias(),
465
0
                                                      &len, &status);
466
0
        if (U_SUCCESS(status) && intVector != nullptr && len >= 2) {
467
0
            fCapitalizationOfRelativeUnitsForUIListMenu = static_cast<UBool>(intVector[0]);
468
0
            fCapitalizationOfRelativeUnitsForStandAlone = static_cast<UBool>(intVector[1]);
469
0
        }
470
0
    }
471
0
#endif
472
0
}
473
474
namespace {
475
476
/**
477
 * Sink for getting data from fields/day/relative data.
478
 * For loading relative day names, e.g., "yesterday", "today".
479
 */
480
481
struct RelDateFmtDataSink : public ResourceSink {
482
  URelativeString *fDatesPtr;
483
  int32_t fDatesLen;
484
485
4.95k
  RelDateFmtDataSink(URelativeString* fDates, int32_t len) : fDatesPtr(fDates), fDatesLen(len) {
486
34.6k
    for (int32_t i = 0; i < fDatesLen; ++i) {
487
29.7k
      fDatesPtr[i].offset = 0;
488
29.7k
      fDatesPtr[i].string = nullptr;
489
29.7k
      fDatesPtr[i].len = -1;
490
29.7k
    }
491
4.95k
  }
492
493
  virtual ~RelDateFmtDataSink();
494
495
  virtual void put(const char *key, ResourceValue &value,
496
8.50k
                   UBool /*noFallback*/, UErrorCode &errorCode) override {
497
8.50k
      ResourceTable relDayTable = value.getTable(errorCode);
498
8.50k
      int32_t n = 0;
499
8.50k
      int32_t len = 0;
500
36.7k
      for (int32_t i = 0; relDayTable.getKeyAndValue(i, key, value); ++i) {
501
        // Find the relative offset.
502
28.2k
        int32_t offset = atoi(key);
503
504
        // Put in the proper spot, but don't override existing data.
505
28.2k
        n = offset + UDAT_DIRECTION_THIS; // Converts to index in UDAT_R
506
28.2k
        if (0 <= n && n < fDatesLen && fDatesPtr[n].string == nullptr) {
507
          // Not found and n is an empty slot.
508
17.4k
          fDatesPtr[n].offset = offset;
509
17.4k
          fDatesPtr[n].string = value.getString(len, errorCode);
510
17.4k
          fDatesPtr[n].len = len;
511
17.4k
        }
512
28.2k
      }
513
8.50k
  }
514
};
515
516
517
// Virtual destructors must be defined out of line.
518
RelDateFmtDataSink::~RelDateFmtDataSink() {}
519
520
}  // Namespace
521
522
523
static const char16_t patItem1[] = {0x7B,0x31,0x7D}; // "{1}"
524
static const int32_t patItem1Len = 3;
525
526
4.95k
void RelativeDateFormat::loadDates(UErrorCode &status) {
527
4.95k
    UResourceBundle *rb = ures_open(nullptr, fLocale.getBaseName(), &status);
528
4.95k
    LocalUResourceBundlePointer dateTimePatterns(
529
4.95k
        ures_getByKeyWithFallback(rb,
530
4.95k
                                  "calendar/gregorian/DateTimePatterns",
531
4.95k
                                  (UResourceBundle*)nullptr, &status));
532
4.95k
    if(U_SUCCESS(status)) {
533
4.92k
        int32_t patternsSize = ures_getSize(dateTimePatterns.getAlias());
534
4.92k
        if (patternsSize > kDateTime) {
535
4.92k
            int32_t resStrLen = 0;
536
4.92k
            int32_t glueIndex = kDateTime;
537
4.92k
            if (patternsSize >= (kDateTimeOffset + kShort + 1)) {
538
4.92k
                int32_t offsetIncrement = (fDateStyle & ~kRelative); // Remove relative bit.
539
4.92k
                if (offsetIncrement >= static_cast<int32_t>(kFull) &&
540
4.92k
                    offsetIncrement <= static_cast<int32_t>(kShortRelative)) {
541
4.92k
                    glueIndex = kDateTimeOffset + offsetIncrement;
542
4.92k
                }
543
4.92k
            }
544
545
4.92k
            const char16_t *resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), glueIndex, &resStrLen, &status);
546
4.92k
            if (U_SUCCESS(status) && resStrLen >= patItem1Len && u_strncmp(resStr,patItem1,patItem1Len)==0) {
547
4.85k
                fCombinedHasDateAtStart = true;
548
4.85k
            }
549
4.92k
            fCombinedFormat = new SimpleFormatter(UnicodeString(true, resStr, resStrLen), 2, 2, status);
550
4.92k
        }
551
4.92k
    }
552
553
    // Data loading for relative names, e.g., "yesterday", "today", "tomorrow".
554
4.95k
    fDatesLen = UDAT_DIRECTION_COUNT; // Maximum defined by data.
555
4.95k
    fDates = static_cast<URelativeString*>(uprv_malloc(sizeof(fDates[0]) * fDatesLen));
556
557
4.95k
    RelDateFmtDataSink sink(fDates, fDatesLen);
558
4.95k
    ures_getAllItemsWithFallback(rb, "fields/day/relative", sink, status);
559
560
4.95k
    ures_close(rb);
561
562
4.95k
    if(U_FAILURE(status)) {
563
32
        fDatesLen=0;
564
32
        return;
565
32
    }
566
4.95k
}
567
568
//----------------------------------------------------------------------
569
570
// this should to be in DateFormat, instead it was copied from SimpleDateFormat.
571
572
Calendar*
573
RelativeDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status)
574
4.95k
{
575
4.95k
    if(!U_FAILURE(status)) {
576
4.95k
        fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status);
577
4.95k
    }
578
4.95k
    if (U_SUCCESS(status) && fCalendar == nullptr) {
579
0
        status = U_MEMORY_ALLOCATION_ERROR;
580
0
    }
581
4.95k
    return fCalendar;
582
4.95k
}
583
584
2.52k
int32_t RelativeDateFormat::dayDifference(Calendar &cal, UErrorCode &status) {
585
2.52k
    if(U_FAILURE(status)) {
586
0
        return 0;
587
0
    }
588
    // TODO: Cache the nowCal to avoid heap allocs? Would be difficult, don't know the calendar type
589
2.52k
    Calendar *nowCal = cal.clone();
590
2.52k
    nowCal->setTime(Calendar::getNow(), status);
591
592
    // For the day difference, we are interested in the difference in the (modified) julian day number
593
    // which is midnight to midnight.  Using fieldDifference() is NOT correct here, because 
594
    // 6pm Jan 4th  to 10am Jan 5th should be considered "tomorrow".
595
2.52k
    int32_t dayDiff = cal.get(UCAL_JULIAN_DAY, status) - nowCal->get(UCAL_JULIAN_DAY, status);
596
597
2.52k
    delete nowCal;
598
2.52k
    return dayDiff;
599
2.52k
}
600
601
U_NAMESPACE_END
602
603
#endif  /* !UCONFIG_NO_FORMATTING */