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/smpdtfmt.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) 1997-2016, International Business Machines Corporation and    *
6
* others. All Rights Reserved.                                                *
7
*******************************************************************************
8
*
9
* File SMPDTFMT.CPP
10
*
11
* Modification History:
12
*
13
*   Date        Name        Description
14
*   02/19/97    aliu        Converted from java.
15
*   03/31/97    aliu        Modified extensively to work with 50 locales.
16
*   04/01/97    aliu        Added support for centuries.
17
*   07/09/97    helena      Made ParsePosition into a class.
18
*   07/21/98    stephen     Added initializeDefaultCentury.
19
*                             Removed getZoneIndex (added in DateFormatSymbols)
20
*                             Removed subParseLong
21
*                             Removed chk
22
*   02/22/99    stephen     Removed character literals for EBCDIC safety
23
*   10/14/99    aliu        Updated 2-digit year parsing so that only "00" thru
24
*                           "99" are recognized. {j28 4182066}
25
*   11/15/99    weiv        Added support for week of year/day of week format
26
********************************************************************************
27
*/
28
29
#define ZID_KEY_MAX 128
30
31
#include "unicode/utypes.h"
32
33
#if !UCONFIG_NO_FORMATTING
34
#include "unicode/smpdtfmt.h"
35
#include "unicode/dtfmtsym.h"
36
#include "unicode/ures.h"
37
#include "unicode/msgfmt.h"
38
#include "unicode/calendar.h"
39
#include "unicode/gregocal.h"
40
#include "unicode/timezone.h"
41
#include "unicode/decimfmt.h"
42
#include "unicode/dcfmtsym.h"
43
#include "unicode/uchar.h"
44
#include "unicode/uniset.h"
45
#include "unicode/ustring.h"
46
#include "unicode/basictz.h"
47
#include "unicode/simpleformatter.h"
48
#include "unicode/simplenumberformatter.h"
49
#include "unicode/simpletz.h"
50
#include "unicode/rbtz.h"
51
#include "unicode/tzfmt.h"
52
#include "unicode/ucasemap.h"
53
#include "unicode/utf16.h"
54
#include "unicode/vtzone.h"
55
#include "unicode/udisplaycontext.h"
56
#include "unicode/brkiter.h"
57
#include "unicode/rbnf.h"
58
#include "unicode/dtptngen.h"
59
#include "uresimp.h"
60
#include "olsontz.h"
61
#include "patternprops.h"
62
#include "fphdlimp.h"
63
#include "hebrwcal.h"
64
#include "cstring.h"
65
#include "uassert.h"
66
#include "cmemory.h"
67
#include "umutex.h"
68
#include "mutex.h"
69
#include <float.h>
70
#include "smpdtfst.h"
71
#include "sharednumberformat.h"
72
#include "ucasemap_imp.h"
73
#include "ustr_imp.h"
74
#include "charstr.h"
75
#include "uvector.h"
76
#include "cstr.h"
77
#include "dayperiodrules.h"
78
#include "tznames_impl.h"   // ZONE_NAME_U16_MAX
79
#include "number_utypes.h"
80
#include "chnsecal.h"
81
#include "dangical.h"
82
#include "japancal.h"
83
#include <typeinfo>
84
85
#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
86
#include <stdio.h>
87
#endif
88
89
// *****************************************************************************
90
// class SimpleDateFormat
91
// *****************************************************************************
92
93
U_NAMESPACE_BEGIN
94
95
/**
96
 * Last-resort string to use for "GMT" when constructing time zone strings.
97
 */
98
// For time zones that have no names, use strings GMT+minutes and
99
// GMT-minutes. For instance, in France the time zone is GMT+60.
100
// Also accepted are GMT+H:MM or GMT-H:MM.
101
// Currently not being used
102
//static const char16_t gGmt[]      = {0x0047, 0x004D, 0x0054, 0x0000};         // "GMT"
103
//static const char16_t gGmtPlus[]  = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+"
104
//static const char16_t gGmtMinus[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-"
105
//static const char16_t gDefGmtPat[]       = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
106
//static const char16_t gDefGmtNegHmsPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */
107
//static const char16_t gDefGmtNegHmPat[]  = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */
108
//static const char16_t gDefGmtPosHmsPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */
109
//static const char16_t gDefGmtPosHmPat[]  = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */
110
//static const char16_t gUt[]       = {0x0055, 0x0054, 0x0000};  // "UT"
111
//static const char16_t gUtc[]      = {0x0055, 0x0054, 0x0043, 0x0000};  // "UT"
112
113
typedef enum GmtPatSize {
114
    kGmtLen = 3,
115
    kGmtPatLen = 6,
116
    kNegHmsLen = 9,
117
    kNegHmLen = 6,
118
    kPosHmsLen = 9,
119
    kPosHmLen = 6,
120
    kUtLen = 2,
121
    kUtcLen = 3
122
} GmtPatSize;
123
124
// Stuff needed for numbering system overrides
125
126
typedef enum OvrStrType {
127
    kOvrStrDate = 0,
128
    kOvrStrTime = 1,
129
    kOvrStrBoth = 2
130
} OvrStrType;
131
132
static const UDateFormatField kDateFields[] = {
133
    UDAT_YEAR_FIELD,
134
    UDAT_MONTH_FIELD,
135
    UDAT_DATE_FIELD,
136
    UDAT_DAY_OF_YEAR_FIELD,
137
    UDAT_DAY_OF_WEEK_IN_MONTH_FIELD,
138
    UDAT_WEEK_OF_YEAR_FIELD,
139
    UDAT_WEEK_OF_MONTH_FIELD,
140
    UDAT_YEAR_WOY_FIELD,
141
    UDAT_EXTENDED_YEAR_FIELD,
142
    UDAT_JULIAN_DAY_FIELD,
143
    UDAT_STANDALONE_DAY_FIELD,
144
    UDAT_STANDALONE_MONTH_FIELD,
145
    UDAT_QUARTER_FIELD,
146
    UDAT_STANDALONE_QUARTER_FIELD,
147
    UDAT_YEAR_NAME_FIELD,
148
    UDAT_RELATED_YEAR_FIELD };
149
static const int8_t kDateFieldsCount = 16;
150
151
static const UDateFormatField kTimeFields[] = {
152
    UDAT_HOUR_OF_DAY1_FIELD,
153
    UDAT_HOUR_OF_DAY0_FIELD,
154
    UDAT_MINUTE_FIELD,
155
    UDAT_SECOND_FIELD,
156
    UDAT_FRACTIONAL_SECOND_FIELD,
157
    UDAT_HOUR1_FIELD,
158
    UDAT_HOUR0_FIELD,
159
    UDAT_MILLISECONDS_IN_DAY_FIELD,
160
    UDAT_TIMEZONE_RFC_FIELD,
161
    UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD };
162
static const int8_t kTimeFieldsCount = 10;
163
164
165
// This is a pattern-of-last-resort used when we can't load a usable pattern out
166
// of a resource.
167
static const char16_t gDefaultPattern[] =
168
{
169
    0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0
170
};  /* "yMMdd hh:mm a" */
171
172
// This prefix is designed to NEVER MATCH real text, in order to
173
// suppress the parsing of negative numbers.  Adjust as needed (if
174
// this becomes valid Unicode).
175
static const char16_t SUPPRESS_NEGATIVE_PREFIX[] = {0xAB00, 0};
176
177
/**
178
 * These are the tags we expect to see in normal resource bundle files associated
179
 * with a locale.
180
 */
181
static const char16_t QUOTE = 0x27; // Single quote
182
183
/*
184
 * The field range check bias for each UDateFormatField.
185
 * The bias is added to the minimum and maximum values
186
 * before they are compared to the parsed number.
187
 * For example, the calendar stores zero-based month numbers
188
 * but the parsed month numbers start at 1, so the bias is 1.
189
 *
190
 * A value of -1 means that the value is not checked.
191
 */
192
static const int32_t gFieldRangeBias[] = {
193
    -1,  // 'G' - UDAT_ERA_FIELD
194
    -1,  // 'y' - UDAT_YEAR_FIELD
195
     1,  // 'M' - UDAT_MONTH_FIELD
196
     0,  // 'd' - UDAT_DATE_FIELD
197
    -1,  // 'k' - UDAT_HOUR_OF_DAY1_FIELD
198
    -1,  // 'H' - UDAT_HOUR_OF_DAY0_FIELD
199
     0,  // 'm' - UDAT_MINUTE_FIELD
200
     0,  // 's' - UDAT_SECOND_FIELD
201
    -1,  // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
202
    -1,  // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
203
    -1,  // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
204
    -1,  // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
205
    -1,  // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
206
    -1,  // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
207
    -1,  // 'a' - UDAT_AM_PM_FIELD
208
    -1,  // 'h' - UDAT_HOUR1_FIELD
209
    -1,  // 'K' - UDAT_HOUR0_FIELD
210
    -1,  // 'z' - UDAT_TIMEZONE_FIELD
211
    -1,  // 'Y' - UDAT_YEAR_WOY_FIELD
212
    -1,  // 'e' - UDAT_DOW_LOCAL_FIELD
213
    -1,  // 'u' - UDAT_EXTENDED_YEAR_FIELD
214
    -1,  // 'g' - UDAT_JULIAN_DAY_FIELD
215
    -1,  // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
216
    -1,  // 'Z' - UDAT_TIMEZONE_RFC_FIELD
217
    -1,  // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
218
     0,  // 'c' - UDAT_STANDALONE_DAY_FIELD
219
     1,  // 'L' - UDAT_STANDALONE_MONTH_FIELD
220
    -1,  // 'Q' - UDAT_QUARTER_FIELD (1-4?)
221
    -1,  // 'q' - UDAT_STANDALONE_QUARTER_FIELD
222
    -1,  // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
223
    -1,  // 'U' - UDAT_YEAR_NAME_FIELD
224
    -1,  // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
225
    -1,  // 'X' - UDAT_TIMEZONE_ISO_FIELD
226
    -1,  // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
227
    -1,  // 'r' - UDAT_RELATED_YEAR_FIELD
228
#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
229
    -1,  // ':' - UDAT_TIME_SEPARATOR_FIELD
230
#else
231
    -1,  // (no pattern character currently) - UDAT_TIME_SEPARATOR_FIELD
232
#endif
233
};
234
235
// When calendar uses hebr numbering (i.e. he@calendar=hebrew),
236
// offset the years within the current millennium down to 1-999
237
static const int32_t HEBREW_CAL_CUR_MILLENIUM_START_YEAR = 5000;
238
static const int32_t HEBREW_CAL_CUR_MILLENIUM_END_YEAR = 6000;
239
static constexpr const char16_t HEBREW_CALENDAR_VALUE[] = u"hebr";
240
241
/**
242
 * Maximum range for detecting daylight offset of a time zone when parsed time zone
243
 * string indicates it's daylight saving time, but the detected time zone does not
244
 * observe daylight saving time at the parsed date.
245
 */
246
static const double MAX_DAYLIGHT_DETECTION_RANGE = 30*365*24*60*60*1000.0;
247
248
static UMutex LOCK;
249
250
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat)
251
252
1.58k
SimpleDateFormat::NSOverride::~NSOverride() {
253
1.58k
    if (snf != nullptr) {
254
1.57k
        snf->removeRef();
255
1.57k
    }
256
1.58k
}
257
258
259
1.57k
void SimpleDateFormat::NSOverride::free() {
260
1.57k
    NSOverride *cur = this;
261
3.15k
    while (cur) {
262
1.57k
        NSOverride *next_temp = cur->next;
263
1.57k
        delete cur;
264
1.57k
        cur = next_temp;
265
1.57k
    }
266
1.57k
}
267
268
// no matter what the locale's default number format looked like, we want
269
// to modify it so that it doesn't use thousands separators, doesn't always
270
// show the decimal point, and recognizes integers only when parsing
271
138k
static void fixNumberFormatForDates(NumberFormat &nf) {
272
138k
    nf.setGroupingUsed(false);
273
138k
    DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(&nf);
274
138k
    if (decfmt != nullptr) {
275
136k
        decfmt->setDecimalSeparatorAlwaysShown(false);
276
136k
    }
277
138k
    nf.setParseIntegerOnly(true);
278
138k
    nf.setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
279
138k
}
280
281
static const SharedNumberFormat *createSharedNumberFormat(
282
1.57k
        NumberFormat *nfToAdopt) {
283
1.57k
    fixNumberFormatForDates(*nfToAdopt);
284
1.57k
    const SharedNumberFormat *result = new SharedNumberFormat(nfToAdopt);
285
1.57k
    if (result == nullptr) {
286
0
        delete nfToAdopt;
287
0
    }
288
1.57k
    return result;
289
1.57k
}
290
291
static const SharedNumberFormat *createSharedNumberFormat(
292
1.58k
        const Locale &loc, UErrorCode &status) {
293
1.58k
    NumberFormat *nf = NumberFormat::createInstance(loc, status);
294
1.58k
    if (U_FAILURE(status)) {
295
5
        return nullptr;
296
5
    }
297
1.57k
    const SharedNumberFormat *result = createSharedNumberFormat(nf);
298
1.57k
    if (result == nullptr) {
299
0
        status = U_MEMORY_ALLOCATION_ERROR;
300
0
    }
301
1.57k
    return result;
302
1.58k
}
303
304
1.27k
static const SharedNumberFormat **allocSharedNumberFormatters() {
305
1.27k
    const SharedNumberFormat** result = static_cast<const SharedNumberFormat**>(
306
1.27k
            uprv_malloc(UDAT_FIELD_COUNT * sizeof(const SharedNumberFormat*)));
307
1.27k
    if (result == nullptr) {
308
0
        return nullptr;
309
0
    }
310
49.7k
    for (int32_t i = 0; i < UDAT_FIELD_COUNT; ++i) {
311
48.4k
        result[i] = nullptr;
312
48.4k
    }
313
1.27k
    return result;
314
1.27k
}
315
316
1.27k
static void freeSharedNumberFormatters(const SharedNumberFormat ** list) {
317
49.7k
    for (int32_t i = 0; i < UDAT_FIELD_COUNT; ++i) {
318
48.4k
        SharedObject::clearPtr(list[i]);
319
48.4k
    }
320
1.27k
    uprv_free(list);
321
1.27k
}
322
323
const NumberFormat *SimpleDateFormat::getNumberFormatByIndex(
324
129k
        UDateFormatField index) const {
325
129k
    if (fSharedNumberFormatters == nullptr ||
326
128k
        fSharedNumberFormatters[index] == nullptr) {
327
128k
        return fNumberFormat;
328
128k
    }
329
887
    return &(**fSharedNumberFormatters[index]);
330
129k
}
331
332
//----------------------------------------------------------------------
333
334
SimpleDateFormat::~SimpleDateFormat()
335
157k
{
336
157k
    delete fSymbols;
337
157k
    if (fSharedNumberFormatters) {
338
1.27k
        freeSharedNumberFormatters(fSharedNumberFormatters);
339
1.27k
    }
340
157k
    delete fTimeZoneFormat;
341
157k
    delete fSimpleNumberFormatter;
342
343
157k
#if !UCONFIG_NO_BREAK_ITERATION
344
157k
    delete fCapitalizationBrkIter;
345
157k
#endif
346
157k
}
347
348
//----------------------------------------------------------------------
349
350
SimpleDateFormat::SimpleDateFormat(UErrorCode& status)
351
0
  :   fLocale(Locale::getDefault())
352
0
{
353
0
    initializeBooleanAttributes();
354
0
    construct(kShort, static_cast<EStyle>(kShort + kDateOffset), fLocale, status);
355
0
    initializeDefaultCentury();
356
0
}
357
358
//----------------------------------------------------------------------
359
360
SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
361
                                   UErrorCode &status)
362
0
:   fPattern(pattern),
363
0
    fLocale(Locale::getDefault())
364
0
{
365
0
    fDateOverride.setToBogus();
366
0
    fTimeOverride.setToBogus();
367
0
    initializeBooleanAttributes();
368
0
    initializeCalendar(nullptr,fLocale,status);
369
0
    fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
370
0
    initialize(fLocale, status);
371
0
    initializeDefaultCentury();
372
373
0
}
374
//----------------------------------------------------------------------
375
376
SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
377
                                   const UnicodeString& override,
378
                                   UErrorCode &status)
379
0
:   fPattern(pattern),
380
0
    fLocale(Locale::getDefault())
381
0
{
382
0
    fDateOverride.setTo(override);
383
0
    fTimeOverride.setToBogus();
384
0
    initializeBooleanAttributes();
385
0
    initializeCalendar(nullptr,fLocale,status);
386
0
    fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
387
0
    initialize(fLocale, status);
388
0
    initializeDefaultCentury();
389
390
0
    processOverrideString(fLocale,override,kOvrStrBoth,status);
391
392
0
}
393
394
//----------------------------------------------------------------------
395
396
SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
397
                                   const Locale& locale,
398
                                   UErrorCode& status)
399
78.4k
:   fPattern(pattern),
400
78.4k
    fLocale(locale)
401
78.4k
{
402
403
78.4k
    fDateOverride.setToBogus();
404
78.4k
    fTimeOverride.setToBogus();
405
78.4k
    initializeBooleanAttributes();
406
407
78.4k
    initializeCalendar(nullptr,fLocale,status);
408
78.4k
    fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
409
78.4k
    initialize(fLocale, status);
410
78.4k
    initializeDefaultCentury();
411
78.4k
}
412
413
//----------------------------------------------------------------------
414
415
SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
416
                                   const UnicodeString& override,
417
                                   const Locale& locale,
418
                                   UErrorCode& status)
419
0
:   fPattern(pattern),
420
0
    fLocale(locale)
421
0
{
422
423
0
    fDateOverride.setTo(override);
424
0
    fTimeOverride.setToBogus();
425
0
    initializeBooleanAttributes();
426
427
0
    initializeCalendar(nullptr,fLocale,status);
428
0
    fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
429
0
    initialize(fLocale, status);
430
0
    initializeDefaultCentury();
431
432
0
    processOverrideString(locale,override,kOvrStrBoth,status);
433
434
0
}
435
436
//----------------------------------------------------------------------
437
438
SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
439
                                   DateFormatSymbols* symbolsToAdopt,
440
                                   UErrorCode& status)
441
0
:   fPattern(pattern),
442
0
    fLocale(Locale::getDefault()),
443
0
    fSymbols(symbolsToAdopt)
444
0
{
445
446
0
    fDateOverride.setToBogus();
447
0
    fTimeOverride.setToBogus();
448
0
    initializeBooleanAttributes();
449
450
0
    initializeCalendar(nullptr,fLocale,status);
451
0
    initialize(fLocale, status);
452
0
    initializeDefaultCentury();
453
0
}
454
455
//----------------------------------------------------------------------
456
457
SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
458
                                   const DateFormatSymbols& symbols,
459
                                   UErrorCode& status)
460
0
:   fPattern(pattern),
461
0
    fLocale(Locale::getDefault()),
462
0
    fSymbols(new DateFormatSymbols(symbols))
463
0
{
464
465
0
    fDateOverride.setToBogus();
466
0
    fTimeOverride.setToBogus();
467
0
    initializeBooleanAttributes();
468
469
0
    initializeCalendar(nullptr, fLocale, status);
470
0
    initialize(fLocale, status);
471
0
    initializeDefaultCentury();
472
0
}
473
474
//----------------------------------------------------------------------
475
476
// Not for public consumption; used by DateFormat
477
SimpleDateFormat::SimpleDateFormat(EStyle timeStyle,
478
                                   EStyle dateStyle,
479
                                   const Locale& locale,
480
                                   UErrorCode& status)
481
59.2k
:   fLocale(locale)
482
59.2k
{
483
59.2k
    initializeBooleanAttributes();
484
59.2k
    construct(timeStyle, dateStyle, fLocale, status);
485
59.2k
    if(U_SUCCESS(status)) {
486
39.7k
      initializeDefaultCentury();
487
39.7k
    }
488
59.2k
}
489
490
//----------------------------------------------------------------------
491
492
/**
493
 * Not for public consumption; used by DateFormat.  This constructor
494
 * never fails.  If the resource data is not available, it uses the
495
 * the last resort symbols.
496
 */
497
SimpleDateFormat::SimpleDateFormat(const Locale& locale,
498
                                   UErrorCode& status)
499
19.5k
:   fPattern(gDefaultPattern),
500
19.5k
    fLocale(locale)
501
19.5k
{
502
19.5k
    if (U_FAILURE(status)) return;
503
19.5k
    initializeBooleanAttributes();
504
19.5k
    initializeCalendar(nullptr, fLocale, status);
505
19.5k
    fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
506
19.5k
    if (U_FAILURE(status))
507
439
    {
508
439
        status = U_ZERO_ERROR;
509
439
        delete fSymbols;
510
        // This constructor doesn't fail; it uses last resort data
511
439
        fSymbols = new DateFormatSymbols(status);
512
        /* test for nullptr */
513
439
        if (fSymbols == nullptr) {
514
0
            status = U_MEMORY_ALLOCATION_ERROR;
515
0
            return;
516
0
        }
517
439
    }
518
519
19.5k
    fDateOverride.setToBogus();
520
19.5k
    fTimeOverride.setToBogus();
521
522
19.5k
    initialize(fLocale, status);
523
19.5k
    if(U_SUCCESS(status)) {
524
18.7k
      initializeDefaultCentury();
525
18.7k
    }
526
19.5k
}
527
528
//----------------------------------------------------------------------
529
530
SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other)
531
0
:   DateFormat(other),
532
0
    fLocale(other.fLocale)
533
0
{
534
0
    initializeBooleanAttributes();
535
0
    *this = other;
536
0
}
537
538
//----------------------------------------------------------------------
539
540
SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other)
541
0
{
542
0
    if (this == &other) {
543
0
        return *this;
544
0
    }
545
546
    // fSimpleNumberFormatter references fNumberFormatter, delete it
547
    // before we call the = operator which may invalidate fNumberFormatter
548
0
    delete fSimpleNumberFormatter;
549
0
    fSimpleNumberFormatter = nullptr;
550
551
0
    DateFormat::operator=(other);
552
0
    fDateOverride = other.fDateOverride;
553
0
    fTimeOverride = other.fTimeOverride;
554
555
0
    delete fSymbols;
556
0
    fSymbols = nullptr;
557
558
0
    if (other.fSymbols)
559
0
        fSymbols = new DateFormatSymbols(*other.fSymbols);
560
561
0
    fDefaultCenturyStart         = other.fDefaultCenturyStart;
562
0
    fDefaultCenturyStartYear     = other.fDefaultCenturyStartYear;
563
0
    fHaveDefaultCentury          = other.fHaveDefaultCentury;
564
565
0
    fPattern = other.fPattern;
566
0
    fHasMinute = other.fHasMinute;
567
0
    fHasSecond = other.fHasSecond;
568
569
0
    fLocale = other.fLocale;
570
571
    // TimeZoneFormat can now be set independently via setter.
572
    // If it is nullptr, it will be lazily initialized from locale.
573
0
    delete fTimeZoneFormat;
574
0
    fTimeZoneFormat = nullptr;
575
0
    TimeZoneFormat *otherTZFormat;
576
0
    {
577
        // Synchronization is required here, when accessing other.fTimeZoneFormat,
578
        // because another thread may be concurrently executing other.tzFormat(),
579
        // a logically const function that lazily creates other.fTimeZoneFormat.
580
        //
581
        // Without synchronization, reordered memory writes could allow us
582
        // to see a non-null fTimeZoneFormat before the object itself was
583
        // fully initialized. In case of a race, it doesn't matter whether
584
        // we see a null or a fully initialized other.fTimeZoneFormat,
585
        // only that we avoid seeing a partially initialized object.
586
        //
587
        // Once initialized, no const function can modify fTimeZoneFormat,
588
        // meaning that once we have safely grabbed the other.fTimeZoneFormat
589
        // pointer, continued synchronization is not required to use it.
590
0
        Mutex m(&LOCK);
591
0
        otherTZFormat = other.fTimeZoneFormat;
592
0
    }
593
0
    if (otherTZFormat) {
594
0
        fTimeZoneFormat = new TimeZoneFormat(*otherTZFormat);
595
0
    }
596
597
0
#if !UCONFIG_NO_BREAK_ITERATION
598
0
    if (other.fCapitalizationBrkIter != nullptr) {
599
0
        fCapitalizationBrkIter = (other.fCapitalizationBrkIter)->clone();
600
0
    }
601
0
#endif
602
603
0
    if (fSharedNumberFormatters != nullptr) {
604
0
        freeSharedNumberFormatters(fSharedNumberFormatters);
605
0
        fSharedNumberFormatters = nullptr;
606
0
    }
607
0
    if (other.fSharedNumberFormatters != nullptr) {
608
0
        fSharedNumberFormatters = allocSharedNumberFormatters();
609
0
        if (fSharedNumberFormatters) {
610
0
            for (int32_t i = 0; i < UDAT_FIELD_COUNT; ++i) {
611
0
                SharedObject::copyPtr(
612
0
                        other.fSharedNumberFormatters[i],
613
0
                        fSharedNumberFormatters[i]);
614
0
            }
615
0
        }
616
0
    }
617
618
0
    UErrorCode localStatus = U_ZERO_ERROR;
619
    // SimpleNumberFormatter does not have a copy constructor. Furthermore,
620
    // it references data from an internal field, fNumberFormatter,
621
    // so we must rematerialize that reference after copying over the number formatter.
622
0
    initSimpleNumberFormatter(localStatus);
623
0
    return *this;
624
0
}
625
626
//----------------------------------------------------------------------
627
628
SimpleDateFormat*
629
SimpleDateFormat::clone() const
630
0
{
631
0
    return new SimpleDateFormat(*this);
632
0
}
633
634
//----------------------------------------------------------------------
635
636
bool
637
SimpleDateFormat::operator==(const Format& other) const
638
0
{
639
0
    if (DateFormat::operator==(other)) {
640
        // The DateFormat::operator== check for fCapitalizationContext equality above
641
        //   is sufficient to check equality of all derived context-related data.
642
        // DateFormat::operator== guarantees following cast is safe
643
0
        SimpleDateFormat* that = (SimpleDateFormat*)&other;
644
0
        return (fPattern             == that->fPattern &&
645
0
                fSymbols             != nullptr && // Check for pathological object
646
0
                that->fSymbols       != nullptr && // Check for pathological object
647
0
                *fSymbols            == *that->fSymbols &&
648
0
                fHaveDefaultCentury  == that->fHaveDefaultCentury &&
649
0
                fDefaultCenturyStart == that->fDefaultCenturyStart);
650
0
    }
651
0
    return false;
652
0
}
653
654
//----------------------------------------------------------------------
655
static const char16_t* timeSkeletons[4] = {
656
    u"jmmsszzzz",   // kFull
657
    u"jmmssz",      // kLong
658
    u"jmmss",       // kMedium
659
    u"jmm",         // kShort
660
};
661
662
void SimpleDateFormat::construct(EStyle timeStyle,
663
                                 EStyle dateStyle,
664
                                 const Locale& locale,
665
                                 UErrorCode& status)
666
59.2k
{
667
    // called by several constructors to load pattern data from the resources
668
59.2k
    if (U_FAILURE(status)) return;
669
670
    // We will need the calendar to know what type of symbols to load.
671
59.2k
    initializeCalendar(nullptr, locale, status);
672
59.2k
    if (U_FAILURE(status)) return;
673
674
    // Load date time patterns directly from resources.
675
59.2k
    const char* cType = fCalendar ? fCalendar->getType() : nullptr;
676
59.2k
    LocalUResourceBundlePointer bundle(ures_open(nullptr, locale.getBaseName(), &status));
677
59.2k
    if (U_FAILURE(status)) return;
678
679
59.1k
    UBool cTypeIsGregorian = true;
680
59.1k
    LocalUResourceBundlePointer dateTimePatterns;
681
59.1k
    if (cType != nullptr && uprv_strcmp(cType, "gregorian") != 0) {
682
1.52k
        CharString resourcePath("calendar/", status);
683
1.52k
        resourcePath.append(cType, status).append("/DateTimePatterns", status);
684
1.52k
        dateTimePatterns.adoptInstead(
685
1.52k
            ures_getByKeyWithFallback(bundle.getAlias(), resourcePath.data(),
686
1.52k
                                      (UResourceBundle*)nullptr, &status));
687
1.52k
        cTypeIsGregorian = false;
688
1.52k
    }
689
690
    // Check for "gregorian" fallback.
691
59.1k
    if (cTypeIsGregorian || status == U_MISSING_RESOURCE_ERROR) {
692
57.6k
        status = U_ZERO_ERROR;
693
57.6k
        dateTimePatterns.adoptInstead(
694
57.6k
            ures_getByKeyWithFallback(bundle.getAlias(),
695
57.6k
                                      "calendar/gregorian/DateTimePatterns",
696
57.6k
                                      (UResourceBundle*)nullptr, &status));
697
57.6k
    }
698
59.1k
    if (U_FAILURE(status)) return;
699
700
58.8k
    LocalUResourceBundlePointer currentBundle;
701
702
58.8k
    if (ures_getSize(dateTimePatterns.getAlias()) <= kDateTime)
703
0
    {
704
0
        status = U_INVALID_FORMAT_ERROR;
705
0
        return;
706
0
    }
707
708
58.8k
    setLocaleIDs(ures_getLocaleByType(dateTimePatterns.getAlias(), ULOC_VALID_LOCALE, &status),
709
58.8k
                 ures_getLocaleByType(dateTimePatterns.getAlias(), ULOC_ACTUAL_LOCALE, &status));
710
711
    // create a symbols object from the locale
712
58.8k
    fSymbols = DateFormatSymbols::createForLocale(locale, status);
713
58.8k
    if (U_FAILURE(status)) return;
714
    /* test for nullptr */
715
58.8k
    if (fSymbols == nullptr) {
716
0
        status = U_MEMORY_ALLOCATION_ERROR;
717
0
        return;
718
0
    }
719
720
58.8k
    const char16_t *resStr,*ovrStr;
721
58.8k
    int32_t resStrLen,ovrStrLen = 0;
722
58.8k
    fDateOverride.setToBogus();
723
58.8k
    fTimeOverride.setToBogus();
724
725
58.8k
    UnicodeString timePattern;
726
58.8k
    if (timeStyle >= kFull && timeStyle <= kShort) {
727
12.0k
        bool hasRgOrHcSubtag = false;
728
        // also use DTPG if the locale has the "rg" or "hc" ("hours") subtag-- even if the overriding region
729
        // or hour cycle is the same as the one we get by default, we go through the DateTimePatternGenerator
730
12.0k
        UErrorCode dummyErr1 = U_ZERO_ERROR, dummyErr2 = U_ZERO_ERROR;
731
12.0k
        if (locale.getKeywordValue("rg", nullptr, 0, dummyErr1) > 0 || locale.getKeywordValue("hours", nullptr, 0, dummyErr2) > 0) {
732
134
            hasRgOrHcSubtag = true;
733
134
        }
734
735
12.0k
        const char* baseLocID = locale.getBaseName();
736
12.0k
        if (baseLocID != nullptr && uprv_strcmp(baseLocID,"und")!=0) {
737
12.0k
            UErrorCode useStatus = U_ZERO_ERROR;
738
12.0k
            Locale baseLoc(baseLocID);
739
12.0k
            Locale validLoc(getLocale(ULOC_VALID_LOCALE, useStatus));
740
12.0k
            if (hasRgOrHcSubtag || (U_SUCCESS(useStatus) && validLoc!=baseLoc)) {
741
4.06k
                bool useDTPG = hasRgOrHcSubtag;
742
4.06k
                const char* baseReg = baseLoc.getCountry(); // empty string if no region
743
4.06k
                if ((baseReg != nullptr && baseReg[0] != 0 &&
744
4.06k
                     uprv_strncmp(baseReg,validLoc.getCountry(),ULOC_COUNTRY_CAPACITY)!=0)
745
4.06k
                        || uprv_strncmp(baseLoc.getLanguage(),validLoc.getLanguage(),ULOC_LANG_CAPACITY)!=0) {
746
                    // use DTPG if
747
                    // * baseLoc has a region and validLoc does not have the same one (or has none), OR
748
                    // * validLoc has a different language code than baseLoc
749
                    // * the original locale has the rg or hc subtag
750
3.59k
                    useDTPG = true;
751
3.59k
                }
752
4.06k
                if (useDTPG) {
753
                    // The standard time formats may have the wrong time cycle, because:
754
                    // the valid locale differs in important ways (region, language) from
755
                    // the base locale.
756
                    // We could *also* check whether they do actually have a mismatch with
757
                    // the time cycle preferences for the region, but that is a lot more
758
                    // work for little or no additional benefit, since just going ahead
759
                    // and always synthesizing the time format as per the following should
760
                    // create a locale-appropriate pattern with cycle that matches the
761
                    // region preferences anyway.
762
3.66k
                    LocalPointer<DateTimePatternGenerator> dtpg(DateTimePatternGenerator::createInstanceNoStdPat(locale, useStatus));
763
3.66k
                    if (U_SUCCESS(useStatus)) {
764
3.25k
                        UnicodeString timeSkeleton(true, timeSkeletons[timeStyle], -1);
765
3.25k
                        timePattern = dtpg->getBestPattern(timeSkeleton, useStatus);
766
3.25k
                    }
767
3.66k
                }
768
4.06k
            }
769
12.0k
        }
770
12.0k
    }
771
772
    // if the pattern should include both date and time information, use the date/time
773
    // pattern string as a guide to tell use how to glue together the appropriate date
774
    // and time pattern strings.
775
58.8k
    if ((timeStyle != kNone) && (dateStyle != kNone))
776
20.1k
    {
777
20.1k
        UnicodeString tempus1(timePattern);
778
20.1k
        if (tempus1.length() == 0) {
779
18.3k
            currentBundle.adoptInstead(
780
18.3k
                    ures_getByIndex(dateTimePatterns.getAlias(), static_cast<int32_t>(timeStyle), nullptr, &status));
781
18.3k
            if (U_FAILURE(status)) {
782
12.0k
               status = U_INVALID_FORMAT_ERROR;
783
12.0k
               return;
784
12.0k
            }
785
6.33k
            switch (ures_getType(currentBundle.getAlias())) {
786
5.97k
                case URES_STRING: {
787
5.97k
                   resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status);
788
5.97k
                   break;
789
0
                }
790
359
                case URES_ARRAY: {
791
359
                   resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status);
792
359
                   ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status);
793
359
                   fTimeOverride.setTo(true, ovrStr, ovrStrLen);
794
359
                   break;
795
0
                }
796
0
                default: {
797
0
                   status = U_INVALID_FORMAT_ERROR;
798
0
                   return;
799
0
                }
800
6.33k
            }
801
802
6.33k
            tempus1.setTo(true, resStr, resStrLen);
803
6.33k
        }
804
805
8.06k
        currentBundle.adoptInstead(
806
8.06k
                ures_getByIndex(dateTimePatterns.getAlias(), static_cast<int32_t>(dateStyle), nullptr, &status));
807
8.06k
        if (U_FAILURE(status)) {
808
3.22k
           status = U_INVALID_FORMAT_ERROR;
809
3.22k
           return;
810
3.22k
        }
811
4.84k
        switch (ures_getType(currentBundle.getAlias())) {
812
4.35k
            case URES_STRING: {
813
4.35k
               resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status);
814
4.35k
               break;
815
0
            }
816
485
            case URES_ARRAY: {
817
485
               resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status);
818
485
               ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status);
819
485
               fDateOverride.setTo(true, ovrStr, ovrStrLen);
820
485
               break;
821
0
            }
822
0
            default: {
823
0
               status = U_INVALID_FORMAT_ERROR;
824
0
               return;
825
0
            }
826
4.84k
        }
827
828
4.84k
        UnicodeString tempus2(true, resStr, resStrLen);
829
830
        // Currently, for compatibility with pre-CLDR-42 data, we default to the "atTime"
831
        // combining patterns. Depending on guidance in CLDR 42 spec and on DisplayOptions,
832
        // we may change this.
833
4.84k
        LocalUResourceBundlePointer dateAtTimePatterns;
834
4.84k
        if (!cTypeIsGregorian) {
835
205
            CharString resourcePath("calendar/", status);
836
205
            resourcePath.append(cType, status).append("/DateTimePatterns%atTime", status);
837
205
            dateAtTimePatterns.adoptInstead(
838
205
                ures_getByKeyWithFallback(bundle.getAlias(), resourcePath.data(),
839
205
                                          nullptr, &status));
840
205
        }
841
4.84k
        if (cTypeIsGregorian || status == U_MISSING_RESOURCE_ERROR) {
842
4.82k
            status = U_ZERO_ERROR;
843
4.82k
            dateAtTimePatterns.adoptInstead(
844
4.82k
                ures_getByKeyWithFallback(bundle.getAlias(),
845
4.82k
                                          "calendar/gregorian/DateTimePatterns%atTime",
846
4.82k
                                          nullptr, &status));
847
4.82k
        }
848
4.84k
        if (U_SUCCESS(status) && ures_getSize(dateAtTimePatterns.getAlias()) >= 4) {
849
2.64k
            resStr = ures_getStringByIndex(dateAtTimePatterns.getAlias(), dateStyle - kDateOffset, &resStrLen, &status);
850
2.64k
        } else {
851
2.19k
            status = U_ZERO_ERROR;
852
2.19k
            int32_t glueIndex = kDateTime;
853
2.19k
            int32_t patternsSize = ures_getSize(dateTimePatterns.getAlias());
854
2.19k
            if (patternsSize >= (kDateTimeOffset + kShort + 1)) {
855
                // Get proper date time format
856
2.19k
                glueIndex = static_cast<int32_t>(kDateTimeOffset + (dateStyle - kDateOffset));
857
2.19k
            }
858
859
2.19k
            resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), glueIndex, &resStrLen, &status);
860
2.19k
        }
861
4.84k
        SimpleFormatter(UnicodeString(true, resStr, resStrLen), 2, 2, status).
862
4.84k
                format(tempus1, tempus2, fPattern, status);
863
4.84k
    }
864
    // if the pattern includes just time data or just date date, load the appropriate
865
    // pattern string from the resources
866
    // setTo() - see DateFormatSymbols::assignArray comments
867
38.6k
    else if (timeStyle != kNone) {
868
7.92k
        fPattern.setTo(timePattern);
869
7.92k
        if (fPattern.length() == 0) {
870
6.41k
            currentBundle.adoptInstead(
871
6.41k
                    ures_getByIndex(dateTimePatterns.getAlias(), static_cast<int32_t>(timeStyle), nullptr, &status));
872
6.41k
            if (U_FAILURE(status)) {
873
645
               status = U_INVALID_FORMAT_ERROR;
874
645
               return;
875
645
            }
876
5.76k
            switch (ures_getType(currentBundle.getAlias())) {
877
5.75k
                case URES_STRING: {
878
5.75k
                   resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status);
879
5.75k
                   break;
880
0
                }
881
9
                case URES_ARRAY: {
882
9
                   resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status);
883
9
                   ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status);
884
9
                   fDateOverride.setTo(true, ovrStr, ovrStrLen);
885
9
                   break;
886
0
                }
887
0
                default: {
888
0
                   status = U_INVALID_FORMAT_ERROR;
889
0
                   return;
890
0
                }
891
5.76k
            }
892
5.76k
            fPattern.setTo(true, resStr, resStrLen);
893
5.76k
        }
894
7.92k
    }
895
30.7k
    else if (dateStyle != kNone) {
896
29.4k
        currentBundle.adoptInstead(
897
29.4k
                ures_getByIndex(dateTimePatterns.getAlias(), static_cast<int32_t>(dateStyle), nullptr, &status));
898
29.4k
        if (U_FAILURE(status)) {
899
362
           status = U_INVALID_FORMAT_ERROR;
900
362
           return;
901
362
        }
902
29.1k
        switch (ures_getType(currentBundle.getAlias())) {
903
28.3k
            case URES_STRING: {
904
28.3k
               resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status);
905
28.3k
               break;
906
0
            }
907
770
            case URES_ARRAY: {
908
770
               resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status);
909
770
               ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status);
910
770
               fDateOverride.setTo(true, ovrStr, ovrStrLen);
911
770
               break;
912
0
            }
913
0
            default: {
914
0
               status = U_INVALID_FORMAT_ERROR;
915
0
               return;
916
0
            }
917
29.1k
        }
918
29.1k
        fPattern.setTo(true, resStr, resStrLen);
919
29.1k
    }
920
921
    // and if it includes _neither_, that's an error
922
1.28k
    else
923
1.28k
        status = U_INVALID_FORMAT_ERROR;
924
925
    // finally, finish initializing by creating a Calendar and a NumberFormat
926
42.5k
    initialize(locale, status);
927
42.5k
}
928
929
//----------------------------------------------------------------------
930
931
Calendar*
932
SimpleDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status)
933
157k
{
934
157k
    if(!U_FAILURE(status)) {
935
157k
        fCalendar = Calendar::createInstance(
936
157k
            adoptZone ? adoptZone : TimeZone::forLocaleOrDefault(locale), locale, status);
937
157k
    }
938
157k
    return fCalendar;
939
157k
}
940
941
void
942
SimpleDateFormat::initialize(const Locale& locale,
943
                             UErrorCode& status)
944
140k
{
945
140k
    if (U_FAILURE(status)) return;
946
947
137k
    parsePattern(); // Need this before initNumberFormatters(), to set fHasHanYearChar
948
949
    // Simple-minded hack to force Gannen year numbering for ja@calendar=japanese
950
    // if format is non-numeric (includes 年) and fDateOverride is not already specified.
951
    // Now this does get updated if applyPattern subsequently changes the pattern type.
952
137k
    if (fDateOverride.isBogus() && fHasHanYearChar &&
953
243
            fCalendar != nullptr &&
954
243
            typeid(*fCalendar) == typeid(JapaneseCalendar) &&
955
137k
            uprv_strcmp(fLocale.getLanguage(),"ja") == 0) {
956
0
        fDateOverride.setTo(u"y=jpanyear", -1);
957
0
    }
958
959
    // We don't need to check that the row count is >= 1, since all 2d arrays have at
960
    // least one row
961
137k
    fNumberFormat = NumberFormat::createInstance(locale, status);
962
137k
    if (fNumberFormat != nullptr && U_SUCCESS(status))
963
136k
    {
964
136k
        fixNumberFormatForDates(*fNumberFormat);
965
        //fNumberFormat->setLenient(true); // Java uses a custom DateNumberFormat to format/parse
966
967
136k
        initNumberFormatters(locale, status);
968
136k
        initSimpleNumberFormatter(status);
969
970
136k
    }
971
1.06k
    else if (U_SUCCESS(status))
972
0
    {
973
0
        status = U_MISSING_RESOURCE_ERROR;
974
0
    }
975
137k
}
976
977
/* Initialize the fields we use to disambiguate ambiguous years. Separate
978
 * so we can call it from readObject().
979
 */
980
void SimpleDateFormat::initializeDefaultCentury()
981
136k
{
982
136k
  if(fCalendar) {
983
136k
    fHaveDefaultCentury = fCalendar->haveDefaultCentury();
984
136k
    if(fHaveDefaultCentury) {
985
136k
      fDefaultCenturyStart = fCalendar->defaultCenturyStart();
986
136k
      fDefaultCenturyStartYear = fCalendar->defaultCenturyStartYear();
987
136k
    } else {
988
0
      fDefaultCenturyStart = DBL_MIN;
989
0
      fDefaultCenturyStartYear = -1;
990
0
    }
991
136k
  }
992
136k
}
993
994
/*
995
 * Initialize the boolean attributes. Separate so we can call it from all constructors.
996
 */
997
void SimpleDateFormat::initializeBooleanAttributes()
998
157k
{
999
157k
    UErrorCode status = U_ZERO_ERROR;
1000
1001
157k
    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status);
1002
157k
    setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
1003
157k
    setBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH, true, status);
1004
157k
    setBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, true, status);
1005
157k
}
1006
1007
/* Define one-century window into which to disambiguate dates using
1008
 * two-digit years. Make public in JDK 1.2.
1009
 */
1010
void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& status)
1011
0
{
1012
0
    if(U_FAILURE(status)) {
1013
0
        return;
1014
0
    }
1015
0
    if(!fCalendar) {
1016
0
      status = U_ILLEGAL_ARGUMENT_ERROR;
1017
0
      return;
1018
0
    }
1019
1020
0
    fCalendar->setTime(startDate, status);
1021
0
    if(U_SUCCESS(status)) {
1022
0
        fHaveDefaultCentury = true;
1023
0
        fDefaultCenturyStart = startDate;
1024
0
        fDefaultCenturyStartYear = fCalendar->get(UCAL_YEAR, status);
1025
0
    }
1026
0
}
1027
1028
//----------------------------------------------------------------------
1029
1030
UnicodeString&
1031
SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition& pos) const
1032
29.5k
{
1033
29.5k
  UErrorCode status = U_ZERO_ERROR;
1034
29.5k
  FieldPositionOnlyHandler handler(pos);
1035
29.5k
  return _format(cal, appendTo, handler, status);
1036
29.5k
}
1037
1038
//----------------------------------------------------------------------
1039
1040
UnicodeString&
1041
SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo,
1042
                         FieldPositionIterator* posIter, UErrorCode& status) const
1043
0
{
1044
0
  FieldPositionIteratorHandler handler(posIter, status);
1045
0
  return _format(cal, appendTo, handler, status);
1046
0
}
1047
1048
//----------------------------------------------------------------------
1049
1050
UnicodeString&
1051
SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo,
1052
                            FieldPositionHandler& handler, UErrorCode& status) const
1053
29.5k
{
1054
29.5k
    if ( U_FAILURE(status) ) {
1055
0
       return appendTo;
1056
0
    }
1057
29.5k
    Calendar* workCal = &cal;
1058
29.5k
    Calendar* calClone = nullptr;
1059
29.5k
    if (&cal != fCalendar && typeid(cal) != typeid(*fCalendar)) {
1060
        // Different calendar type
1061
        // We use the time and time zone from the input calendar, but
1062
        // do not use the input calendar for field calculation.
1063
0
        calClone = fCalendar->clone();
1064
0
        if (calClone != nullptr) {
1065
0
            UDate t = cal.getTime(status);
1066
0
            calClone->setTime(t, status);
1067
0
            calClone->setTimeZone(cal.getTimeZone());
1068
0
            workCal = calClone;
1069
0
        } else {
1070
0
            status = U_MEMORY_ALLOCATION_ERROR;
1071
0
            return appendTo;
1072
0
        }
1073
0
    }
1074
1075
29.5k
    UBool inQuote = false;
1076
29.5k
    char16_t prevCh = 0;
1077
29.5k
    int32_t count = 0;
1078
29.5k
    int32_t fieldNum = 0;
1079
29.5k
    UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status);
1080
1081
    // loop through the pattern string character by character
1082
29.5k
    int32_t patternLength = fPattern.length();
1083
450k
    for (int32_t i = 0; i < patternLength && U_SUCCESS(status); ++i) {
1084
421k
        char16_t ch = fPattern[i];
1085
1086
        // Use subFormat() to format a repeated pattern character
1087
        // when a different pattern or non-pattern character is seen
1088
421k
        if (ch != prevCh && count > 0) {
1089
105k
            subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++,
1090
105k
                      prevCh, handler, *workCal, status);
1091
105k
            count = 0;
1092
105k
        }
1093
421k
        if (ch == QUOTE) {
1094
            // Consecutive single quotes are a single quote literal,
1095
            // either outside of quotes or between quotes
1096
11.9k
            if ((i+1) < patternLength && fPattern[i+1] == QUOTE) {
1097
0
                appendTo += QUOTE;
1098
0
                ++i;
1099
11.9k
            } else {
1100
11.9k
                inQuote = ! inQuote;
1101
11.9k
            }
1102
11.9k
        }
1103
409k
        else if (!inQuote && isSyntaxChar(ch)) {
1104
            // ch is a date-time pattern character to be interpreted
1105
            // by subFormat(); count the number of times it is repeated
1106
254k
            prevCh = ch;
1107
254k
            ++count;
1108
254k
        }
1109
154k
        else {
1110
            // Append quoted characters and unquoted non-pattern characters
1111
154k
            appendTo += ch;
1112
154k
        }
1113
421k
    }
1114
1115
    // Format the last item in the pattern, if any
1116
29.5k
    if (count > 0) {
1117
22.9k
        subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++,
1118
22.9k
                  prevCh, handler, *workCal, status);
1119
22.9k
    }
1120
1121
29.5k
    delete calClone;
1122
1123
29.5k
    return appendTo;
1124
29.5k
}
1125
1126
//----------------------------------------------------------------------
1127
1128
/* Map calendar field into calendar field level.
1129
 * the larger the level, the smaller the field unit.
1130
 * For example, UCAL_ERA level is 0, UCAL_YEAR level is 10,
1131
 * UCAL_MONTH level is 20.
1132
 * NOTE: if new fields adds in, the table needs to update.
1133
 */
1134
const int32_t
1135
SimpleDateFormat::fgCalendarFieldToLevel[] =
1136
{
1137
    /*GyM*/ 0, 10, 20,
1138
    /*wW*/ 20, 30,
1139
    /*dDEF*/ 30, 20, 30, 30,
1140
    /*ahHm*/ 40, 50, 50, 60,
1141
    /*sS*/ 70, 80,
1142
    /*z?Y*/ 0, 0, 10,
1143
    /*eug*/ 30, 10, 0,
1144
    /*A?.*/ 40, 0, 0
1145
};
1146
1147
0
int32_t SimpleDateFormat::getLevelFromChar(char16_t ch) {
1148
    // Map date field LETTER into calendar field level.
1149
    // the larger the level, the smaller the field unit.
1150
    // NOTE: if new fields adds in, the table needs to update.
1151
0
    static const int32_t mapCharToLevel[] = {
1152
0
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1153
        //
1154
0
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1155
        //       !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /
1156
0
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1157
#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1158
        //   0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?
1159
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  0, -1, -1, -1, -1, -1,
1160
#else
1161
        //   0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?
1162
0
            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1163
0
#endif
1164
        //   @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
1165
0
            -1, 40, -1, -1, 20, 30, 30,  0, 50, -1, -1, 50, 20, 20, -1,  0,
1166
        //   P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _
1167
0
            -1, 20, -1, 80, -1, 10,  0, 30,  0, 10,  0, -1, -1, -1, -1, -1,
1168
        //   `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
1169
0
            -1, 40, -1, 30, 30, 30, -1,  0, 50, -1, -1, 50,  0, 60, -1, -1,
1170
        //   p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~
1171
0
            -1, 20, 10, 70, -1, 10,  0, 20,  0, 10,  0, -1, -1, -1, -1, -1
1172
0
    };
1173
1174
0
    return ch < UPRV_LENGTHOF(mapCharToLevel) ? mapCharToLevel[ch] : -1;
1175
0
}
1176
1177
375k
UBool SimpleDateFormat::isSyntaxChar(char16_t ch) {
1178
375k
    static const UBool mapCharToIsSyntax[] = {
1179
        //
1180
375k
        false, false, false, false, false, false, false, false,
1181
        //
1182
375k
        false, false, false, false, false, false, false, false,
1183
        //
1184
375k
        false, false, false, false, false, false, false, false,
1185
        //
1186
375k
        false, false, false, false, false, false, false, false,
1187
        //         !      "      #      $      %      &      '
1188
375k
        false, false, false, false, false, false, false, false,
1189
        //  (      )      *      +      ,      -      .      /
1190
375k
        false, false, false, false, false, false, false, false,
1191
        //  0      1      2      3      4      5      6      7
1192
375k
        false, false, false, false, false, false, false, false,
1193
#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1194
        //  8      9      :      ;      <      =      >      ?
1195
        false, false,  true, false, false, false, false, false,
1196
#else
1197
        //  8      9      :      ;      <      =      >      ?
1198
375k
        false, false, false, false, false, false, false, false,
1199
375k
#endif
1200
        //  @      A      B      C      D      E      F      G
1201
375k
        false,  true,  true,  true,  true,  true,  true,  true,
1202
        //  H      I      J      K      L      M      N      O
1203
375k
         true,  true,  true,  true,  true,  true,  true,  true,
1204
        //  P      Q      R      S      T      U      V      W
1205
375k
         true,  true,  true,  true,  true,  true,  true,  true,
1206
        //  X      Y      Z      [      \      ]      ^      _
1207
375k
         true,  true,  true, false, false, false, false, false,
1208
        //  `      a      b      c      d      e      f      g
1209
375k
        false,  true,  true,  true,  true,  true,  true,  true,
1210
        //  h      i      j      k      l      m      n      o
1211
375k
         true,  true,  true,  true,  true,  true,  true,  true,
1212
        //  p      q      r      s      t      u      v      w
1213
375k
         true,  true,  true,  true,  true,  true,  true,  true,
1214
        //  x      y      z      {      |      }      ~
1215
375k
         true,  true,  true, false, false, false, false, false
1216
375k
    };
1217
1218
375k
    return ch < UPRV_LENGTHOF(mapCharToIsSyntax) ? mapCharToIsSyntax[ch] : false;
1219
375k
}
1220
1221
// Map index into pattern character string to Calendar field number.
1222
const UCalendarDateFields
1223
SimpleDateFormat::fgPatternIndexToCalendarField[] =
1224
{
1225
    /*GyM*/ UCAL_ERA, UCAL_YEAR, UCAL_MONTH,
1226
    /*dkH*/ UCAL_DATE, UCAL_HOUR_OF_DAY, UCAL_HOUR_OF_DAY,
1227
    /*msS*/ UCAL_MINUTE, UCAL_SECOND, UCAL_MILLISECOND,
1228
    /*EDF*/ UCAL_DAY_OF_WEEK, UCAL_DAY_OF_YEAR, UCAL_DAY_OF_WEEK_IN_MONTH,
1229
    /*wWa*/ UCAL_WEEK_OF_YEAR, UCAL_WEEK_OF_MONTH, UCAL_AM_PM,
1230
    /*hKz*/ UCAL_HOUR, UCAL_HOUR, UCAL_ZONE_OFFSET,
1231
    /*Yeu*/ UCAL_YEAR_WOY, UCAL_DOW_LOCAL, UCAL_EXTENDED_YEAR,
1232
    /*gAZ*/ UCAL_JULIAN_DAY, UCAL_MILLISECONDS_IN_DAY, UCAL_ZONE_OFFSET,
1233
    /*v*/   UCAL_ZONE_OFFSET,
1234
    /*c*/   UCAL_DOW_LOCAL,
1235
    /*L*/   UCAL_MONTH,
1236
    /*Q*/   UCAL_MONTH,
1237
    /*q*/   UCAL_MONTH,
1238
    /*V*/   UCAL_ZONE_OFFSET,
1239
    /*U*/   UCAL_YEAR,
1240
    /*O*/   UCAL_ZONE_OFFSET,
1241
    /*Xx*/  UCAL_ZONE_OFFSET, UCAL_ZONE_OFFSET,
1242
    /*r*/   UCAL_EXTENDED_YEAR,
1243
    /*bB*/   UCAL_FIELD_COUNT, UCAL_FIELD_COUNT,  // no mappings to calendar fields
1244
#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1245
    /*:*/   UCAL_FIELD_COUNT, /* => no useful mapping to any calendar field */
1246
#else
1247
    /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/   UCAL_FIELD_COUNT, /* => no useful mapping to any calendar field */
1248
#endif
1249
};
1250
1251
// Map index into pattern character string to DateFormat field number
1252
const UDateFormatField
1253
SimpleDateFormat::fgPatternIndexToDateFormatField[] = {
1254
    /*GyM*/ UDAT_ERA_FIELD, UDAT_YEAR_FIELD, UDAT_MONTH_FIELD,
1255
    /*dkH*/ UDAT_DATE_FIELD, UDAT_HOUR_OF_DAY1_FIELD, UDAT_HOUR_OF_DAY0_FIELD,
1256
    /*msS*/ UDAT_MINUTE_FIELD, UDAT_SECOND_FIELD, UDAT_FRACTIONAL_SECOND_FIELD,
1257
    /*EDF*/ UDAT_DAY_OF_WEEK_FIELD, UDAT_DAY_OF_YEAR_FIELD, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD,
1258
    /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD, UDAT_WEEK_OF_MONTH_FIELD, UDAT_AM_PM_FIELD,
1259
    /*hKz*/ UDAT_HOUR1_FIELD, UDAT_HOUR0_FIELD, UDAT_TIMEZONE_FIELD,
1260
    /*Yeu*/ UDAT_YEAR_WOY_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_EXTENDED_YEAR_FIELD,
1261
    /*gAZ*/ UDAT_JULIAN_DAY_FIELD, UDAT_MILLISECONDS_IN_DAY_FIELD, UDAT_TIMEZONE_RFC_FIELD,
1262
    /*v*/   UDAT_TIMEZONE_GENERIC_FIELD,
1263
    /*c*/   UDAT_STANDALONE_DAY_FIELD,
1264
    /*L*/   UDAT_STANDALONE_MONTH_FIELD,
1265
    /*Q*/   UDAT_QUARTER_FIELD,
1266
    /*q*/   UDAT_STANDALONE_QUARTER_FIELD,
1267
    /*V*/   UDAT_TIMEZONE_SPECIAL_FIELD,
1268
    /*U*/   UDAT_YEAR_NAME_FIELD,
1269
    /*O*/   UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD,
1270
    /*Xx*/  UDAT_TIMEZONE_ISO_FIELD, UDAT_TIMEZONE_ISO_LOCAL_FIELD,
1271
    /*r*/   UDAT_RELATED_YEAR_FIELD,
1272
    /*bB*/  UDAT_AM_PM_MIDNIGHT_NOON_FIELD, UDAT_FLEXIBLE_DAY_PERIOD_FIELD,
1273
#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1274
    /*:*/   UDAT_TIME_SEPARATOR_FIELD,
1275
#else
1276
    /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/   UDAT_TIME_SEPARATOR_FIELD,
1277
#endif
1278
};
1279
1280
//----------------------------------------------------------------------
1281
1282
/**
1283
 * Append symbols[value] to dst.  Make sure the array index is not out
1284
 * of bounds.
1285
 */
1286
static inline void
1287
_appendSymbol(UnicodeString& dst,
1288
              int32_t value,
1289
              const UnicodeString* symbols,
1290
19.4k
              int32_t symbolsCount) {
1291
19.4k
    U_ASSERT(0 <= value && value < symbolsCount);
1292
19.4k
    if (0 <= value && value < symbolsCount) {
1293
19.4k
        dst += symbols[value];
1294
19.4k
    }
1295
19.4k
}
1296
1297
static inline void
1298
_appendSymbolWithMonthPattern(UnicodeString& dst, int32_t value, const UnicodeString* symbols, int32_t symbolsCount,
1299
17.6k
              const UnicodeString* monthPattern, UErrorCode& status) {
1300
17.6k
    U_ASSERT(0 <= value && value < symbolsCount);
1301
17.6k
    if (0 <= value && value < symbolsCount) {
1302
17.6k
        if (monthPattern == nullptr) {
1303
17.6k
            dst += symbols[value];
1304
17.6k
        } else {
1305
0
            SimpleFormatter(*monthPattern, 1, 1, status).format(symbols[value], dst, status);
1306
0
        }
1307
17.6k
    }
1308
17.6k
}
1309
1310
//----------------------------------------------------------------------
1311
1312
void
1313
136k
SimpleDateFormat::initSimpleNumberFormatter(UErrorCode &status) {
1314
136k
    if (U_FAILURE(status)) {
1315
5
        return;
1316
5
    }
1317
136k
    const auto* df = dynamic_cast<const DecimalFormat*>(fNumberFormat);
1318
136k
    if (df == nullptr) {
1319
749
        return;
1320
749
    }
1321
135k
    const DecimalFormatSymbols* syms = df->getDecimalFormatSymbols();
1322
135k
    if (syms == nullptr) {
1323
0
        return;
1324
0
    }
1325
135k
    fSimpleNumberFormatter = new number::SimpleNumberFormatter(
1326
135k
        number::SimpleNumberFormatter::forLocaleAndSymbolsAndGroupingStrategy(
1327
135k
            fLocale, *syms, UNUM_GROUPING_OFF, status
1328
135k
        )
1329
135k
    );
1330
135k
    if (fSimpleNumberFormatter == nullptr) {
1331
0
        status = U_MEMORY_ALLOCATION_ERROR;
1332
0
    }
1333
135k
}
1334
1335
void
1336
136k
SimpleDateFormat::initNumberFormatters(const Locale &locale,UErrorCode &status) {
1337
136k
    if (U_FAILURE(status)) {
1338
0
        return;
1339
0
    }
1340
136k
    if ( fDateOverride.isBogus() && fTimeOverride.isBogus() ) {
1341
135k
        return;
1342
135k
    }
1343
1.27k
    umtx_lock(&LOCK);
1344
1.27k
    if (fSharedNumberFormatters == nullptr) {
1345
1.27k
        fSharedNumberFormatters = allocSharedNumberFormatters();
1346
1.27k
        if (fSharedNumberFormatters == nullptr) {
1347
0
            status = U_MEMORY_ALLOCATION_ERROR;
1348
0
        }
1349
1.27k
    }
1350
1.27k
    umtx_unlock(&LOCK);
1351
1352
1.27k
    if (U_FAILURE(status)) {
1353
0
        return;
1354
0
    }
1355
1356
1.27k
    processOverrideString(locale,fDateOverride,kOvrStrDate,status);
1357
1.27k
    processOverrideString(locale,fTimeOverride,kOvrStrTime,status);
1358
1.27k
}
1359
1360
void
1361
2.55k
SimpleDateFormat::processOverrideString(const Locale &locale, const UnicodeString &str, int8_t type, UErrorCode &status) {
1362
2.55k
    if (str.isBogus() || U_FAILURE(status)) {
1363
971
        return;
1364
971
    }
1365
1366
1.58k
    int32_t start = 0;
1367
1.58k
    int32_t len;
1368
1.58k
    UnicodeString nsName;
1369
1.58k
    UnicodeString ovrField;
1370
1.58k
    UBool moreToProcess = true;
1371
1.58k
    NSOverride *overrideList = nullptr;
1372
1373
3.15k
    while (moreToProcess) {
1374
1.58k
        int32_t delimiterPosition = str.indexOf(static_cast<char16_t>(ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE), start);
1375
1.58k
        if (delimiterPosition == -1) {
1376
1.58k
            moreToProcess = false;
1377
1.58k
            len = str.length() - start;
1378
1.58k
        } else {
1379
0
            len = delimiterPosition - start;
1380
0
        }
1381
1.58k
        UnicodeString currentString(str,start,len);
1382
1.58k
        int32_t equalSignPosition = currentString.indexOf(static_cast<char16_t>(ULOC_KEYWORD_ASSIGN_UNICODE), 0);
1383
1.58k
        if (equalSignPosition == -1) { // Simple override string such as "hebrew"
1384
21
            nsName.setTo(currentString);
1385
21
            ovrField.setToBogus();
1386
1.56k
        } else { // Field specific override string such as "y=hebrew"
1387
1.56k
            nsName.setTo(currentString,equalSignPosition+1);
1388
1.56k
            ovrField.setTo(currentString,0,1); // We just need the first character.
1389
1.56k
        }
1390
1391
1.58k
        int32_t nsNameHash = nsName.hashCode();
1392
        // See if the numbering system is in the override list, if not, then add it.
1393
1.58k
        NSOverride *curr = overrideList;
1394
1.58k
        const SharedNumberFormat *snf = nullptr;
1395
1.58k
        UBool found = false;
1396
1.58k
        while ( curr && !found ) {
1397
0
            if ( curr->hash == nsNameHash ) {
1398
0
                snf = curr->snf;
1399
0
                found = true;
1400
0
            }
1401
0
            curr = curr->next;
1402
0
        }
1403
1404
1.58k
        if (!found) {
1405
1.58k
           LocalPointer<NSOverride> cur(new NSOverride);
1406
1.58k
           if (!cur.isNull()) {
1407
1.58k
               char kw[ULOC_KEYWORD_AND_VALUES_CAPACITY];
1408
1.58k
               uprv_strcpy(kw,"numbers=");
1409
1.58k
               nsName.extract(0,len,kw+8,ULOC_KEYWORD_AND_VALUES_CAPACITY-8,US_INV);
1410
1411
1.58k
               Locale ovrLoc(locale.getLanguage(),locale.getCountry(),locale.getVariant(),kw);
1412
1.58k
               cur->hash = nsNameHash;
1413
1.58k
               cur->next = overrideList;
1414
1.58k
               SharedObject::copyPtr(
1415
1.58k
                       createSharedNumberFormat(ovrLoc, status), cur->snf);
1416
1.58k
               if (U_FAILURE(status)) {
1417
5
                   if (overrideList) {
1418
0
                       overrideList->free();
1419
0
                   }
1420
5
                   return;
1421
5
               }
1422
1.57k
               snf = cur->snf;
1423
1.57k
               overrideList = cur.orphan();
1424
1.57k
           } else {
1425
0
               status = U_MEMORY_ALLOCATION_ERROR;
1426
0
               if (overrideList) {
1427
0
                   overrideList->free();
1428
0
               }
1429
0
               return;
1430
0
           }
1431
1.58k
        }
1432
1433
        // Now that we have an appropriate number formatter, fill in the appropriate spaces in the
1434
        // number formatters table.
1435
1.57k
        if (ovrField.isBogus()) {
1436
21
            switch (type) {
1437
13
                case kOvrStrDate:
1438
13
                case kOvrStrBoth: {
1439
221
                    for ( int8_t i=0 ; i<kDateFieldsCount; i++ ) {
1440
208
                        SharedObject::copyPtr(snf, fSharedNumberFormatters[kDateFields[i]]);
1441
208
                    }
1442
13
                    if (type==kOvrStrDate) {
1443
13
                        break;
1444
13
                    }
1445
0
                    U_FALLTHROUGH;
1446
0
                }
1447
8
                case kOvrStrTime : {
1448
88
                    for ( int8_t i=0 ; i<kTimeFieldsCount; i++ ) {
1449
80
                        SharedObject::copyPtr(snf, fSharedNumberFormatters[kTimeFields[i]]);
1450
80
                    }
1451
8
                    break;
1452
0
                }
1453
21
            }
1454
1.55k
        } else {
1455
           // if the pattern character is unrecognized, signal an error and bail out
1456
1.55k
           UDateFormatField patternCharIndex =
1457
1.55k
              DateFormatSymbols::getPatternCharIndex(ovrField.charAt(0));
1458
1.55k
           if (patternCharIndex == UDAT_FIELD_COUNT) {
1459
0
               status = U_INVALID_FORMAT_ERROR;
1460
0
               if (overrideList) {
1461
0
                   overrideList->free();
1462
0
               }
1463
0
               return;
1464
0
           }
1465
1.55k
           SharedObject::copyPtr(snf, fSharedNumberFormatters[patternCharIndex]);
1466
1.55k
        }
1467
1468
1.57k
        start = delimiterPosition + 1;
1469
1.57k
    }
1470
1.57k
    if (overrideList) {
1471
1.57k
        overrideList->free();
1472
1.57k
    }
1473
1.57k
}
1474
1475
//---------------------------------------------------------------------
1476
void
1477
SimpleDateFormat::subFormat(UnicodeString &appendTo,
1478
                            char16_t ch,
1479
                            int32_t count,
1480
                            UDisplayContext capitalizationContext,
1481
                            int32_t fieldNum,
1482
                            char16_t fieldToOutput,
1483
                            FieldPositionHandler& handler,
1484
                            Calendar& cal,
1485
                            UErrorCode& status) const
1486
129k
{
1487
129k
    static constexpr int32_t maxIntCount = 10;
1488
1489
129k
    if (U_FAILURE(status)) {
1490
0
        return;
1491
0
    }
1492
1493
    // this function gets called by format() to produce the appropriate substitution
1494
    // text for an individual pattern symbol (e.g., "HH" or "yyyy")
1495
1496
129k
    UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch);
1497
129k
    int32_t beginOffset = appendTo.length();
1498
129k
    DateFormatSymbols::ECapitalizationContextUsageType capContextUsageType = DateFormatSymbols::kCapContextUsageOther;
1499
1500
    // if the pattern character is unrecognized, signal an error and dump out
1501
129k
    if (patternCharIndex == UDAT_FIELD_COUNT)
1502
0
    {
1503
0
        if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
1504
0
            status = U_INVALID_FORMAT_ERROR;
1505
0
        }
1506
0
        return;
1507
0
    }
1508
1509
129k
    UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex];
1510
129k
    int32_t value = 0;
1511
    // Don't get value unless it is useful
1512
129k
    if (field < UCAL_FIELD_COUNT) {
1513
128k
        value = (patternCharIndex != UDAT_RELATED_YEAR_FIELD)? cal.get(field, status): cal.getRelatedYear(status);
1514
128k
        if (U_FAILURE(status)) {
1515
0
            return;
1516
0
        }
1517
128k
    }
1518
1519
129k
    const NumberFormat *currentNumberFormat = getNumberFormatByIndex(patternCharIndex);
1520
129k
    if (currentNumberFormat == nullptr) {
1521
0
        status = U_INTERNAL_PROGRAM_ERROR;
1522
0
        return;
1523
0
    }
1524
1525
129k
    switch (patternCharIndex) {
1526
1527
    // for any "G" symbol, write out the appropriate era string
1528
    // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name
1529
1.13k
    case UDAT_ERA_FIELD:
1530
1.13k
        {
1531
1.13k
            if (typeid(cal) == typeid(ChineseCalendar) ||
1532
1.13k
                typeid(cal) == typeid(DangiCalendar)) {
1533
0
                zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, 9); // as in ICU4J
1534
1.13k
            } else {
1535
1.13k
                if (count == 5) {
1536
19
                    _appendSymbol(appendTo, value, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount);
1537
19
                    capContextUsageType = DateFormatSymbols::kCapContextUsageEraNarrow;
1538
1.11k
                } else if (count == 4) {
1539
10
                    _appendSymbol(appendTo, value, fSymbols->fEraNames, fSymbols->fEraNamesCount);
1540
10
                    capContextUsageType = DateFormatSymbols::kCapContextUsageEraWide;
1541
1.10k
                } else {
1542
1.10k
                    _appendSymbol(appendTo, value, fSymbols->fEras, fSymbols->fErasCount);
1543
1.10k
                    capContextUsageType = DateFormatSymbols::kCapContextUsageEraAbbrev;
1544
1.10k
                }
1545
1.13k
            }
1546
1.13k
        }
1547
1.13k
        break;
1548
1549
0
     case UDAT_YEAR_NAME_FIELD:
1550
0
        if (fSymbols->fShortYearNames != nullptr && value <= fSymbols->fShortYearNamesCount) {
1551
            // the Calendar YEAR field runs 1 through 60 for cyclic years
1552
0
            _appendSymbol(appendTo, value - 1, fSymbols->fShortYearNames, fSymbols->fShortYearNamesCount);
1553
0
            break;
1554
0
        }
1555
        // else fall through to numeric year handling, do not break here
1556
0
        U_FALLTHROUGH;
1557
1558
   // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits
1559
    // NEW: UTS#35:
1560
//Year         y     yy     yyy     yyyy     yyyyy
1561
//AD 1         1     01     001     0001     00001
1562
//AD 12       12     12     012     0012     00012
1563
//AD 123     123     23     123     0123     00123
1564
//AD 1234   1234     34    1234     1234     01234
1565
//AD 12345 12345     45   12345    12345     12345
1566
19.3k
    case UDAT_YEAR_FIELD:
1567
19.8k
    case UDAT_YEAR_WOY_FIELD:
1568
19.8k
        if (fDateOverride == HEBREW_CALENDAR_VALUE && value>HEBREW_CAL_CUR_MILLENIUM_START_YEAR && value<HEBREW_CAL_CUR_MILLENIUM_END_YEAR) {
1569
0
            value-=HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
1570
0
        }
1571
19.8k
        if(count == 2)
1572
1.32k
            zeroPaddingNumber(currentNumberFormat, appendTo, value, 2, 2);
1573
18.4k
        else
1574
18.4k
            zeroPaddingNumber(currentNumberFormat, appendTo, value, count, maxIntCount);
1575
19.8k
        break;
1576
1577
    // for "MMMM"/"LLLL", write out the whole month name, for "MMM"/"LLL", write out the month
1578
    // abbreviation, for "M"/"L" or "MM"/"LL", write out the month as a number with the
1579
    // appropriate number of digits
1580
    // for "MMMMM"/"LLLLL", use the narrow form
1581
16.8k
    case UDAT_MONTH_FIELD:
1582
17.6k
    case UDAT_STANDALONE_MONTH_FIELD:
1583
17.6k
        if (typeid(cal) == typeid(HebrewCalendar)) {
1584
0
           if (HebrewCalendar::isLeapYear(cal.get(UCAL_YEAR,status)) && value == 6 && count >= 3 )
1585
0
               value = 13; // Show alternate form for Adar II in leap years in Hebrew calendar.
1586
0
           if (!HebrewCalendar::isLeapYear(cal.get(UCAL_YEAR,status)) && value >= 6 && count < 3 )
1587
0
               value--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7.
1588
0
        }
1589
17.6k
        {
1590
17.6k
            int32_t isLeapMonth = (fSymbols->fLeapMonthPatterns != nullptr && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount)?
1591
17.6k
                        cal.get(UCAL_IS_LEAP_MONTH, status): 0;
1592
            // should consolidate the next section by using arrays of pointers & counts for the right symbols...
1593
17.6k
            if (count == 5) {
1594
11
                if (patternCharIndex == UDAT_MONTH_FIELD) {
1595
5
                    _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fNarrowMonths, fSymbols->fNarrowMonthsCount,
1596
5
                            (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatNarrow]): nullptr, status);
1597
6
                } else {
1598
6
                    _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneNarrowMonths, fSymbols->fStandaloneNarrowMonthsCount,
1599
6
                            (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneNarrow]): nullptr, status);
1600
6
                }
1601
11
                capContextUsageType = DateFormatSymbols::kCapContextUsageMonthNarrow;
1602
17.6k
            } else if (count == 4) {
1603
2.22k
                if (patternCharIndex == UDAT_MONTH_FIELD) {
1604
2.21k
                    _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fMonths, fSymbols->fMonthsCount,
1605
2.21k
                            (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatWide]): nullptr, status);
1606
2.21k
                    capContextUsageType = DateFormatSymbols::kCapContextUsageMonthFormat;
1607
2.21k
                } else {
1608
15
                    _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount,
1609
15
                            (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneWide]): nullptr, status);
1610
15
                    capContextUsageType = DateFormatSymbols::kCapContextUsageMonthStandalone;
1611
15
                }
1612
15.4k
            } else if (count == 3) {
1613
577
                if (patternCharIndex == UDAT_MONTH_FIELD) {
1614
566
                    _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fShortMonths, fSymbols->fShortMonthsCount,
1615
566
                            (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatAbbrev]): nullptr, status);
1616
566
                    capContextUsageType = DateFormatSymbols::kCapContextUsageMonthFormat;
1617
566
                } else {
1618
11
                    _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount,
1619
11
                            (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev]): nullptr, status);
1620
11
                    capContextUsageType = DateFormatSymbols::kCapContextUsageMonthStandalone;
1621
11
                }
1622
14.8k
            } else {
1623
14.8k
                UnicodeString monthNumber;
1624
14.8k
                zeroPaddingNumber(currentNumberFormat,monthNumber, value + 1, count, maxIntCount);
1625
14.8k
                _appendSymbolWithMonthPattern(appendTo, 0, &monthNumber, 1,
1626
14.8k
                        (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric]): nullptr, status);
1627
14.8k
            }
1628
17.6k
        }
1629
17.6k
        break;
1630
1631
    // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
1632
0
    case UDAT_HOUR_OF_DAY1_FIELD:
1633
0
        if (value == 0)
1634
0
            zeroPaddingNumber(currentNumberFormat,appendTo, cal.getMaximum(UCAL_HOUR_OF_DAY) + 1, count, maxIntCount);
1635
0
        else
1636
0
            zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
1637
0
        break;
1638
1639
822
    case UDAT_FRACTIONAL_SECOND_FIELD:
1640
        // Fractional seconds left-justify
1641
822
        {
1642
822
            int32_t minDigits = (count > 3) ? 3 : count;
1643
822
            if (count == 1) {
1644
765
                value /= 100;
1645
765
            } else if (count == 2) {
1646
11
                value /= 10;
1647
11
            }
1648
822
            zeroPaddingNumber(currentNumberFormat, appendTo, value, minDigits, maxIntCount);
1649
822
            if (count > 3) {
1650
41
                zeroPaddingNumber(currentNumberFormat, appendTo, 0, count - 3, maxIntCount);
1651
41
            }
1652
822
        }
1653
822
        break;
1654
1655
    // for "ee" or "e", use local numeric day-of-the-week
1656
    // for "EEEEEE" or "eeeeee", write out the short day-of-the-week name
1657
    // for "EEEEE" or "eeeee", write out the narrow day-of-the-week name
1658
    // for "EEEE" or "eeee", write out the wide day-of-the-week name
1659
    // for "EEE" or "EE" or "E" or "eee", write out the abbreviated day-of-the-week name
1660
1.49k
    case UDAT_DOW_LOCAL_FIELD:
1661
1.49k
        if ( count < 3 ) {
1662
1.49k
            zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
1663
1.49k
            break;
1664
1.49k
        }
1665
        // fall through to EEEEE-EEE handling, but for that we don't want local day-of-week,
1666
        // we want standard day-of-week, so first fix value to work for EEEEE-EEE.
1667
0
        value = cal.get(UCAL_DAY_OF_WEEK, status);
1668
0
        if (U_FAILURE(status)) {
1669
0
            return;
1670
0
        }
1671
        // fall through, do not break here
1672
0
        U_FALLTHROUGH;
1673
1.69k
    case UDAT_DAY_OF_WEEK_FIELD:
1674
1.69k
        if (count == 5) {
1675
3
            _appendSymbol(appendTo, value, fSymbols->fNarrowWeekdays,
1676
3
                          fSymbols->fNarrowWeekdaysCount);
1677
3
            capContextUsageType = DateFormatSymbols::kCapContextUsageDayNarrow;
1678
1.69k
        } else if (count == 4) {
1679
1.52k
            _appendSymbol(appendTo, value, fSymbols->fWeekdays,
1680
1.52k
                          fSymbols->fWeekdaysCount);
1681
1.52k
            capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat;
1682
1.52k
        } else if (count == 6) {
1683
3
            _appendSymbol(appendTo, value, fSymbols->fShorterWeekdays,
1684
3
                          fSymbols->fShorterWeekdaysCount);
1685
3
            capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat;
1686
168
        } else {
1687
168
            _appendSymbol(appendTo, value, fSymbols->fShortWeekdays,
1688
168
                          fSymbols->fShortWeekdaysCount);
1689
168
            capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat;
1690
168
        }
1691
1.69k
        break;
1692
1693
    // for "ccc", write out the abbreviated day-of-the-week name
1694
    // for "cccc", write out the wide day-of-the-week name
1695
    // for "ccccc", use the narrow day-of-the-week name
1696
    // for "ccccc", use the short day-of-the-week name
1697
2.48k
    case UDAT_STANDALONE_DAY_FIELD:
1698
2.48k
        if ( count < 3 ) {
1699
2.15k
            zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, maxIntCount);
1700
2.15k
            break;
1701
2.15k
        }
1702
        // fall through to alpha DOW handling, but for that we don't want local day-of-week,
1703
        // we want standard day-of-week, so first fix value.
1704
331
        value = cal.get(UCAL_DAY_OF_WEEK, status);
1705
331
        if (U_FAILURE(status)) {
1706
0
            return;
1707
0
        }
1708
331
        if (count == 5) {
1709
9
            _appendSymbol(appendTo, value, fSymbols->fStandaloneNarrowWeekdays,
1710
9
                          fSymbols->fStandaloneNarrowWeekdaysCount);
1711
9
            capContextUsageType = DateFormatSymbols::kCapContextUsageDayNarrow;
1712
322
        } else if (count == 4) {
1713
16
            _appendSymbol(appendTo, value, fSymbols->fStandaloneWeekdays,
1714
16
                          fSymbols->fStandaloneWeekdaysCount);
1715
16
            capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone;
1716
306
        } else if (count == 6) {
1717
3
            _appendSymbol(appendTo, value, fSymbols->fStandaloneShorterWeekdays,
1718
3
                          fSymbols->fStandaloneShorterWeekdaysCount);
1719
3
            capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone;
1720
303
        } else { // count == 3
1721
303
            _appendSymbol(appendTo, value, fSymbols->fStandaloneShortWeekdays,
1722
303
                          fSymbols->fStandaloneShortWeekdaysCount);
1723
303
            capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone;
1724
303
        }
1725
331
        break;
1726
1727
    // for "a" symbol, write out the whole AM/PM string
1728
16.0k
    case UDAT_AM_PM_FIELD:
1729
16.0k
        if (count == 4) {
1730
22
            _appendSymbol(appendTo, value, fSymbols->fWideAmPms,
1731
22
                          fSymbols->fWideAmPmsCount);
1732
15.9k
        } else if (count == 5) {
1733
8
            _appendSymbol(appendTo, value, fSymbols->fNarrowAmPms,
1734
8
                          fSymbols->fNarrowAmPmsCount);
1735
15.9k
        } else {
1736
15.9k
            _appendSymbol(appendTo, value, fSymbols->fAmPms,
1737
15.9k
                          fSymbols->fAmPmsCount);
1738
15.9k
        }
1739
16.0k
        break;
1740
1741
    // if we see pattern character for UDAT_TIME_SEPARATOR_FIELD (none currently defined),
1742
    // write out the time separator string. Leave support in for future definition.
1743
0
    case UDAT_TIME_SEPARATOR_FIELD:
1744
0
        {
1745
0
            UnicodeString separator;
1746
0
            appendTo += fSymbols->getTimeSeparatorString(separator);
1747
0
        }
1748
0
        break;
1749
1750
    // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
1751
    // as "12"
1752
14.4k
    case UDAT_HOUR1_FIELD:
1753
14.4k
        if (value == 0)
1754
12.4k
            zeroPaddingNumber(currentNumberFormat,appendTo, cal.getLeastMaximum(UCAL_HOUR) + 1, count, maxIntCount);
1755
2.04k
        else
1756
2.04k
            zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
1757
14.4k
        break;
1758
1759
4.02k
    case UDAT_TIMEZONE_FIELD: // 'z'
1760
4.74k
    case UDAT_TIMEZONE_RFC_FIELD: // 'Z'
1761
6.02k
    case UDAT_TIMEZONE_GENERIC_FIELD: // 'v'
1762
6.32k
    case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V'
1763
6.66k
    case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: // 'O'
1764
7.24k
    case UDAT_TIMEZONE_ISO_FIELD: // 'X'
1765
7.76k
    case UDAT_TIMEZONE_ISO_LOCAL_FIELD: // 'x'
1766
7.76k
        {
1767
7.76k
            char16_t zsbuf[ZONE_NAME_U16_MAX];
1768
7.76k
            UnicodeString zoneString(zsbuf, 0, UPRV_LENGTHOF(zsbuf));
1769
7.76k
            const TimeZone& tz = cal.getTimeZone();
1770
7.76k
            UDate date = cal.getTime(status);
1771
7.76k
            const TimeZoneFormat *tzfmt = tzFormat(status);
1772
7.76k
            if (U_SUCCESS(status)) {
1773
7.76k
                switch (patternCharIndex) {
1774
4.02k
                case UDAT_TIMEZONE_FIELD:
1775
4.02k
                    if (count < 4) {
1776
                        // "z", "zz", "zzz"
1777
2.35k
                        tzfmt->format(UTZFMT_STYLE_SPECIFIC_SHORT, tz, date, zoneString);
1778
2.35k
                        capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort;
1779
2.35k
                    } else {
1780
                        // "zzzz" or longer
1781
1.66k
                        tzfmt->format(UTZFMT_STYLE_SPECIFIC_LONG, tz, date, zoneString);
1782
1.66k
                        capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong;
1783
1.66k
                    }
1784
4.02k
                    break;
1785
724
                case UDAT_TIMEZONE_RFC_FIELD:
1786
724
                    if (count < 4) {
1787
                        // "Z"
1788
688
                        tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString);
1789
688
                    } else if (count == 5) {
1790
                        // "ZZZZZ"
1791
4
                        tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString);
1792
32
                    } else {
1793
                        // "ZZ", "ZZZ", "ZZZZ"
1794
32
                        tzfmt->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString);
1795
32
                    }
1796
724
                    break;
1797
1.27k
                case UDAT_TIMEZONE_GENERIC_FIELD:
1798
1.27k
                    if (count == 1) {
1799
                        // "v"
1800
1.24k
                        tzfmt->format(UTZFMT_STYLE_GENERIC_SHORT, tz, date, zoneString);
1801
1.24k
                        capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort;
1802
1.24k
                    } else if (count == 4) {
1803
                        // "vvvv"
1804
7
                        tzfmt->format(UTZFMT_STYLE_GENERIC_LONG, tz, date, zoneString);
1805
7
                        capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong;
1806
7
                    }
1807
1.27k
                    break;
1808
298
                case UDAT_TIMEZONE_SPECIAL_FIELD:
1809
298
                    if (count == 1) {
1810
                        // "V"
1811
248
                        tzfmt->format(UTZFMT_STYLE_ZONE_ID_SHORT, tz, date, zoneString);
1812
248
                    } else if (count == 2) {
1813
                        // "VV"
1814
4
                        tzfmt->format(UTZFMT_STYLE_ZONE_ID, tz, date, zoneString);
1815
46
                    } else if (count == 3) {
1816
                        // "VVV"
1817
36
                        tzfmt->format(UTZFMT_STYLE_EXEMPLAR_LOCATION, tz, date, zoneString);
1818
36
                    } else if (count == 4) {
1819
                        // "VVVV"
1820
6
                        tzfmt->format(UTZFMT_STYLE_GENERIC_LOCATION, tz, date, zoneString);
1821
6
                        capContextUsageType = DateFormatSymbols::kCapContextUsageZoneLong;
1822
6
                    }
1823
298
                    break;
1824
342
                case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
1825
342
                    if (count == 1) {
1826
                        // "O"
1827
301
                        tzfmt->format(UTZFMT_STYLE_LOCALIZED_GMT_SHORT, tz, date, zoneString);
1828
301
                    } else if (count == 4) {
1829
                        // "OOOO"
1830
3
                        tzfmt->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString);
1831
3
                    }
1832
342
                    break;
1833
578
                case UDAT_TIMEZONE_ISO_FIELD:
1834
578
                    if (count == 1) {
1835
                        // "X"
1836
507
                        tzfmt->format(UTZFMT_STYLE_ISO_BASIC_SHORT, tz, date, zoneString);
1837
507
                    } else if (count == 2) {
1838
                        // "XX"
1839
14
                        tzfmt->format(UTZFMT_STYLE_ISO_BASIC_FIXED, tz, date, zoneString);
1840
57
                    } else if (count == 3) {
1841
                        // "XXX"
1842
4
                        tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_FIXED, tz, date, zoneString);
1843
53
                    } else if (count == 4) {
1844
                        // "XXXX"
1845
5
                        tzfmt->format(UTZFMT_STYLE_ISO_BASIC_FULL, tz, date, zoneString);
1846
48
                    } else if (count == 5) {
1847
                        // "XXXXX"
1848
6
                        tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString);
1849
6
                    }
1850
578
                    break;
1851
526
                case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
1852
526
                    if (count == 1) {
1853
                        // "x"
1854
497
                        tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT, tz, date, zoneString);
1855
497
                    } else if (count == 2) {
1856
                        // "xx"
1857
8
                        tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED, tz, date, zoneString);
1858
21
                    } else if (count == 3) {
1859
                        // "xxx"
1860
4
                        tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED, tz, date, zoneString);
1861
17
                    } else if (count == 4) {
1862
                        // "xxxx"
1863
3
                        tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString);
1864
14
                    } else if (count == 5) {
1865
                        // "xxxxx"
1866
2
                        tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL, tz, date, zoneString);
1867
2
                    }
1868
526
                    break;
1869
0
                default:
1870
0
                    UPRV_UNREACHABLE_EXIT;
1871
7.76k
                }
1872
7.76k
            }
1873
7.76k
            appendTo += zoneString;
1874
7.76k
        }
1875
0
        break;
1876
1877
285
    case UDAT_QUARTER_FIELD:
1878
285
        if (count >= 5)
1879
10
            _appendSymbol(appendTo, value/3, fSymbols->fNarrowQuarters,
1880
10
                          fSymbols->fNarrowQuartersCount);
1881
275
         else if (count == 4)
1882
10
            _appendSymbol(appendTo, value/3, fSymbols->fQuarters,
1883
10
                          fSymbols->fQuartersCount);
1884
265
        else if (count == 3)
1885
107
            _appendSymbol(appendTo, value/3, fSymbols->fShortQuarters,
1886
107
                          fSymbols->fShortQuartersCount);
1887
158
        else
1888
158
            zeroPaddingNumber(currentNumberFormat,appendTo, (value/3) + 1, count, maxIntCount);
1889
285
        break;
1890
1891
384
    case UDAT_STANDALONE_QUARTER_FIELD:
1892
384
        if (count >= 5)
1893
3
            _appendSymbol(appendTo, value/3, fSymbols->fStandaloneNarrowQuarters,
1894
3
                          fSymbols->fStandaloneNarrowQuartersCount);
1895
381
        else if (count == 4)
1896
3
            _appendSymbol(appendTo, value/3, fSymbols->fStandaloneQuarters,
1897
3
                          fSymbols->fStandaloneQuartersCount);
1898
378
        else if (count == 3)
1899
121
            _appendSymbol(appendTo, value/3, fSymbols->fStandaloneShortQuarters,
1900
121
                          fSymbols->fStandaloneShortQuartersCount);
1901
257
        else
1902
257
            zeroPaddingNumber(currentNumberFormat,appendTo, (value/3) + 1, count, maxIntCount);
1903
384
        break;
1904
1905
410
    case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
1906
410
    {
1907
410
        const UnicodeString *toAppend = nullptr;
1908
410
        int32_t hour = cal.get(UCAL_HOUR_OF_DAY, status);
1909
1910
        // Note: "midnight" can be ambiguous as to whether it refers to beginning of day or end of day.
1911
        // For ICU 57 output of "midnight" is temporarily suppressed.
1912
1913
        // For "midnight" and "noon":
1914
        // Time, as displayed, must be exactly noon or midnight.
1915
        // This means minutes and seconds, if present, must be zero.
1916
410
        if ((/*hour == 0 ||*/ hour == 12) &&
1917
32
                (!fHasMinute || cal.get(UCAL_MINUTE, status) == 0) &&
1918
29
                (!fHasSecond || cal.get(UCAL_SECOND, status) == 0)) {
1919
            // Stealing am/pm value to use as our array index.
1920
            // It works out: am/midnight are both 0, pm/noon are both 1,
1921
            // 12 am is 12 midnight, and 12 pm is 12 noon.
1922
20
            int32_t val = cal.get(UCAL_AM_PM, status);
1923
1924
20
            if (count <= 3) {
1925
11
                toAppend = &fSymbols->fAbbreviatedDayPeriods[val];
1926
11
            } else if (count == 4 || count > 5) {
1927
6
                toAppend = &fSymbols->fWideDayPeriods[val];
1928
6
            } else { // count == 5
1929
3
                toAppend = &fSymbols->fNarrowDayPeriods[val];
1930
3
            }
1931
20
        }
1932
1933
        // toAppend is nullptr if time isn't exactly midnight or noon (as displayed).
1934
        // toAppend is bogus if time is midnight or noon, but no localized string exists.
1935
        // In either case, fall back to am/pm.
1936
410
        if (toAppend == nullptr || toAppend->isBogus()) {
1937
            // Reformat with identical arguments except ch, now changed to 'a'.
1938
            // We are passing a different fieldToOutput because we want to add
1939
            // 'b' to field position. This makes this fallback stable when
1940
            // there is a data change on locales.
1941
400
            subFormat(appendTo, u'a', count, capitalizationContext, fieldNum, u'b', handler, cal, status);
1942
400
            return;
1943
400
        } else {
1944
10
            appendTo += *toAppend;
1945
10
        }
1946
1947
10
        break;
1948
410
    }
1949
1950
604
    case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
1951
604
    {
1952
        // TODO: Maybe fetch the DayperiodRules during initialization (instead of at the first
1953
        // loading of an instance) if a relevant pattern character (b or B) is used.
1954
604
        const DayPeriodRules *ruleSet = DayPeriodRules::getInstance(this->getSmpFmtLocale(), status);
1955
604
        if (U_FAILURE(status)) {
1956
            // Data doesn't conform to spec, therefore loading failed.
1957
0
            break;
1958
0
        }
1959
1960
        // Get current display time.
1961
604
        int32_t hour = cal.get(UCAL_HOUR_OF_DAY, status);
1962
604
        int32_t minute = 0;
1963
604
        if (fHasMinute) {
1964
207
            minute = cal.get(UCAL_MINUTE, status);
1965
207
        }
1966
604
        int32_t second = 0;
1967
604
        if (fHasSecond) {
1968
132
            second = cal.get(UCAL_SECOND, status);
1969
132
        }
1970
1971
        // Determine day period.
1972
604
        DayPeriodRules::DayPeriod periodType;
1973
604
        if (hour == 0 && minute == 0 && second == 0 && ruleSet->hasMidnight()) {
1974
352
            periodType = DayPeriodRules::DAYPERIOD_MIDNIGHT;
1975
352
        } else if (hour == 12 && minute == 0 && second == 0 && ruleSet->hasNoon()) {
1976
10
            periodType = DayPeriodRules::DAYPERIOD_NOON;
1977
242
        } else {
1978
242
            periodType = ruleSet->getDayPeriodForHour(hour);
1979
242
        }
1980
1981
        // Rule set exists, therefore periodType can't be UNKNOWN.
1982
        // Get localized string.
1983
604
        U_ASSERT(periodType != DayPeriodRules::DAYPERIOD_UNKNOWN);
1984
604
        UnicodeString *toAppend = nullptr;
1985
604
        int32_t index;
1986
1987
        // Note: "midnight" can be ambiguous as to whether it refers to beginning of day or end of day.
1988
        // For ICU 57 output of "midnight" is temporarily suppressed.
1989
1990
604
        if (periodType != DayPeriodRules::DAYPERIOD_AM &&
1991
535
                periodType != DayPeriodRules::DAYPERIOD_PM &&
1992
522
                periodType != DayPeriodRules::DAYPERIOD_MIDNIGHT) {
1993
170
            index = static_cast<int32_t>(periodType);
1994
170
            if (count <= 3) {
1995
138
                toAppend = &fSymbols->fAbbreviatedDayPeriods[index];  // i.e. short
1996
138
            } else if (count == 4 || count > 5) {
1997
29
                toAppend = &fSymbols->fWideDayPeriods[index];
1998
29
            } else {  // count == 5
1999
3
                toAppend = &fSymbols->fNarrowDayPeriods[index];
2000
3
            }
2001
170
        }
2002
2003
        // Fallback schedule:
2004
        // Midnight/Noon -> General Periods -> AM/PM.
2005
2006
        // Midnight/Noon -> General Periods.
2007
604
        if ((toAppend == nullptr || toAppend->isBogus()) &&
2008
441
                (periodType == DayPeriodRules::DAYPERIOD_MIDNIGHT ||
2009
355
                 periodType == DayPeriodRules::DAYPERIOD_NOON)) {
2010
355
            periodType = ruleSet->getDayPeriodForHour(hour);
2011
355
            index = static_cast<int32_t>(periodType);
2012
2013
355
            if (count <= 3) {
2014
322
                toAppend = &fSymbols->fAbbreviatedDayPeriods[index];  // i.e. short
2015
322
            } else if (count == 4 || count > 5) {
2016
28
                toAppend = &fSymbols->fWideDayPeriods[index];
2017
28
            } else {  // count == 5
2018
5
                toAppend = &fSymbols->fNarrowDayPeriods[index];
2019
5
            }
2020
355
        }
2021
2022
        // General Periods -> AM/PM.
2023
604
        if (periodType == DayPeriodRules::DAYPERIOD_AM ||
2024
535
            periodType == DayPeriodRules::DAYPERIOD_PM ||
2025
522
            toAppend->isBogus()) {
2026
            // We are passing a different fieldToOutput because we want to add
2027
            // 'B' to field position iterator. This makes this fallback stable when
2028
            // there is a data change on locales.
2029
90
            subFormat(appendTo, u'a', count, capitalizationContext, fieldNum, u'B', handler, cal, status);
2030
90
            return;
2031
90
        }
2032
514
        else {
2033
514
            appendTo += *toAppend;
2034
514
        }
2035
2036
514
        break;
2037
604
    }
2038
2039
    // all of the other pattern symbols can be formatted as simple numbers with
2040
    // appropriate zero padding
2041
44.2k
    default:
2042
44.2k
        zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
2043
44.2k
        break;
2044
129k
    }
2045
128k
#if !UCONFIG_NO_BREAK_ITERATION
2046
    // if first field, check to see whether we need to and are able to titlecase it
2047
128k
    if (fieldNum == 0 && fCapitalizationBrkIter != nullptr && appendTo.length() > beginOffset &&
2048
0
            u_islower(appendTo.char32At(beginOffset))) {
2049
0
        UBool titlecase = false;
2050
0
        switch (capitalizationContext) {
2051
0
            case UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE:
2052
0
                titlecase = true;
2053
0
                break;
2054
0
            case UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU:
2055
0
                titlecase = fSymbols->fCapitalization[capContextUsageType][0];
2056
0
                break;
2057
0
            case UDISPCTX_CAPITALIZATION_FOR_STANDALONE:
2058
0
                titlecase = fSymbols->fCapitalization[capContextUsageType][1];
2059
0
                break;
2060
0
            default:
2061
                // titlecase = false;
2062
0
                break;
2063
0
        }
2064
0
        if (titlecase) {
2065
0
            BreakIterator* const mutableCapitalizationBrkIter = fCapitalizationBrkIter->clone();
2066
0
            UnicodeString firstField(appendTo, beginOffset);
2067
0
            firstField.toTitle(mutableCapitalizationBrkIter, fLocale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
2068
0
            appendTo.replaceBetween(beginOffset, appendTo.length(), firstField);
2069
0
            delete mutableCapitalizationBrkIter;
2070
0
        }
2071
0
    }
2072
128k
#endif
2073
2074
128k
    handler.addAttribute(DateFormatSymbols::getPatternCharIndex(fieldToOutput), beginOffset, appendTo.length());
2075
128k
}
2076
2077
//----------------------------------------------------------------------
2078
2079
0
void SimpleDateFormat::adoptNumberFormat(NumberFormat *formatToAdopt) {
2080
    // Null out the fast formatter, it references fNumberFormat which we're
2081
    // about to invalidate
2082
0
    delete fSimpleNumberFormatter;
2083
0
    fSimpleNumberFormatter = nullptr;
2084
2085
0
    fixNumberFormatForDates(*formatToAdopt);
2086
0
    delete fNumberFormat;
2087
0
    fNumberFormat = formatToAdopt;
2088
2089
    // We successfully set the default number format. Now delete the overrides
2090
    // (can't fail).
2091
0
    if (fSharedNumberFormatters) {
2092
0
        freeSharedNumberFormatters(fSharedNumberFormatters);
2093
0
        fSharedNumberFormatters = nullptr;
2094
0
    }
2095
2096
    // Recompute fSimpleNumberFormatter if necessary
2097
0
    UErrorCode localStatus = U_ZERO_ERROR;
2098
0
    initSimpleNumberFormatter(localStatus);
2099
0
}
2100
2101
0
void SimpleDateFormat::adoptNumberFormat(const UnicodeString& fields, NumberFormat *formatToAdopt, UErrorCode &status){
2102
0
    fixNumberFormatForDates(*formatToAdopt);
2103
0
    LocalPointer<NumberFormat> fmt(formatToAdopt);
2104
0
    if (U_FAILURE(status)) {
2105
0
        return;
2106
0
    }
2107
2108
    // We must ensure fSharedNumberFormatters is allocated.
2109
0
    if (fSharedNumberFormatters == nullptr) {
2110
0
        fSharedNumberFormatters = allocSharedNumberFormatters();
2111
0
        if (fSharedNumberFormatters == nullptr) {
2112
0
            status = U_MEMORY_ALLOCATION_ERROR;
2113
0
            return;
2114
0
        }
2115
0
    }
2116
0
    const SharedNumberFormat *newFormat = createSharedNumberFormat(fmt.orphan());
2117
0
    if (newFormat == nullptr) {
2118
0
        status = U_MEMORY_ALLOCATION_ERROR;
2119
0
        return;
2120
0
    }
2121
0
    for (int i=0; i<fields.length(); i++) {
2122
0
        char16_t field = fields.charAt(i);
2123
        // if the pattern character is unrecognized, signal an error and bail out
2124
0
        UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(field);
2125
0
        if (patternCharIndex == UDAT_FIELD_COUNT) {
2126
0
            status = U_INVALID_FORMAT_ERROR;
2127
0
            newFormat->deleteIfZeroRefCount();
2128
0
            return;
2129
0
        }
2130
2131
        // Set the number formatter in the table
2132
0
        SharedObject::copyPtr(
2133
0
                newFormat, fSharedNumberFormatters[patternCharIndex]);
2134
0
    }
2135
0
    newFormat->deleteIfZeroRefCount();
2136
0
}
2137
2138
const NumberFormat *
2139
0
SimpleDateFormat::getNumberFormatForField(char16_t field) const {
2140
0
    UDateFormatField index = DateFormatSymbols::getPatternCharIndex(field);
2141
0
    if (index == UDAT_FIELD_COUNT) {
2142
0
        return nullptr;
2143
0
    }
2144
0
    return getNumberFormatByIndex(index);
2145
0
}
2146
2147
//----------------------------------------------------------------------
2148
void
2149
SimpleDateFormat::zeroPaddingNumber(
2150
        const NumberFormat *currentNumberFormat,
2151
        UnicodeString &appendTo,
2152
        int32_t value, int32_t minDigits, int32_t maxDigits) const
2153
98.3k
{
2154
2155
98.3k
    if (currentNumberFormat == fNumberFormat && fSimpleNumberFormatter) {
2156
        // Can use fast path
2157
        // We create UFormattedNumberData ourselves to avoid a heap allocation
2158
        // and corresponding free. Set the pointer to null afterwards to prevent
2159
        // the implementation from attempting to free it.
2160
97.4k
        UErrorCode localStatus = U_ZERO_ERROR;
2161
97.4k
        number::impl::UFormattedNumberData data;
2162
97.4k
        data.quantity.setToLong(value);
2163
97.4k
        number::SimpleNumber number(&data, localStatus);
2164
97.4k
        number.setMinimumIntegerDigits(minDigits, localStatus);
2165
97.4k
        number.setMaximumIntegerDigits(maxDigits, localStatus);
2166
2167
97.4k
        number::FormattedNumber result = fSimpleNumberFormatter->format(std::move(number), localStatus);
2168
97.4k
        if (U_FAILURE(localStatus)) {
2169
0
            result.fData = nullptr;
2170
0
            return;
2171
0
        }
2172
97.4k
        UnicodeStringAppendable appendable(appendTo);
2173
97.4k
        result.appendTo(appendable, localStatus);
2174
97.4k
        result.fData = nullptr;
2175
97.4k
        return;
2176
97.4k
    }
2177
2178
    // Check for RBNF (no clone necessary)
2179
863
    const auto* rbnf = dynamic_cast<const RuleBasedNumberFormat*>(currentNumberFormat);
2180
863
    if (rbnf != nullptr) {
2181
863
        FieldPosition pos(FieldPosition::DONT_CARE);
2182
863
        rbnf->format(value, appendTo, pos);  // 3rd arg is there to speed up processing
2183
863
        return;
2184
863
    }
2185
2186
    // Fall back to slow path (clone and mutate the NumberFormat)
2187
0
    if (currentNumberFormat != nullptr) {
2188
0
        FieldPosition pos(FieldPosition::DONT_CARE);
2189
0
        LocalPointer<NumberFormat> nf(currentNumberFormat->clone());
2190
0
        nf->setMinimumIntegerDigits(minDigits);
2191
0
        nf->setMaximumIntegerDigits(maxDigits);
2192
0
        nf->format(value, appendTo, pos);  // 3rd arg is there to speed up processing
2193
0
    }
2194
0
}
2195
2196
//----------------------------------------------------------------------
2197
2198
/**
2199
 * Return true if the given format character, occurring count
2200
 * times, represents a numeric field.
2201
 */
2202
0
UBool SimpleDateFormat::isNumeric(char16_t formatChar, int32_t count) {
2203
0
    return DateFormatSymbols::isNumericPatternChar(formatChar, count);
2204
0
}
2205
2206
UBool
2207
0
SimpleDateFormat::isAtNumericField(const UnicodeString &pattern, int32_t patternOffset) {
2208
0
    if (patternOffset >= pattern.length()) {
2209
        // not at any field
2210
0
        return false;
2211
0
    }
2212
0
    char16_t ch = pattern.charAt(patternOffset);
2213
0
    UDateFormatField f = DateFormatSymbols::getPatternCharIndex(ch);
2214
0
    if (f == UDAT_FIELD_COUNT) {
2215
        // not at any field
2216
0
        return false;
2217
0
    }
2218
0
    int32_t i = patternOffset;
2219
0
    while (pattern.charAt(++i) == ch) {}
2220
0
    return DateFormatSymbols::isNumericField(f, i - patternOffset);
2221
0
}
2222
2223
UBool
2224
0
SimpleDateFormat::isAfterNonNumericField(const UnicodeString &pattern, int32_t patternOffset) {
2225
0
    if (patternOffset <= 0) {
2226
        // not after any field
2227
0
        return false;
2228
0
    }
2229
0
    char16_t ch = pattern.charAt(--patternOffset);
2230
0
    UDateFormatField f = DateFormatSymbols::getPatternCharIndex(ch);
2231
0
    if (f == UDAT_FIELD_COUNT) {
2232
        // not after any field
2233
0
        return false;
2234
0
    }
2235
0
    int32_t i = patternOffset;
2236
0
    while (pattern.charAt(--i) == ch) {}
2237
0
    return !DateFormatSymbols::isNumericField(f, patternOffset - i);
2238
0
}
2239
2240
void
2241
SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& parsePos) const
2242
0
{
2243
0
    UErrorCode status = U_ZERO_ERROR;
2244
0
    int32_t pos = parsePos.getIndex();
2245
0
    if(parsePos.getIndex() < 0) {
2246
0
        parsePos.setErrorIndex(0);
2247
0
        return;
2248
0
    }
2249
0
    int32_t start = pos;
2250
2251
    // Hold the day period until everything else is parsed, because we need
2252
    // the hour to interpret time correctly.
2253
0
    int32_t dayPeriodInt = -1;
2254
2255
0
    UBool ambiguousYear[] = { false };
2256
0
    int32_t saveHebrewMonth = -1;
2257
0
    int32_t count = 0;
2258
0
    UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
2259
2260
    // For parsing abutting numeric fields. 'abutPat' is the
2261
    // offset into 'pattern' of the first of 2 or more abutting
2262
    // numeric fields.  'abutStart' is the offset into 'text'
2263
    // where parsing the fields begins. 'abutPass' starts off as 0
2264
    // and increments each time we try to parse the fields.
2265
0
    int32_t abutPat = -1; // If >=0, we are in a run of abutting numeric fields
2266
0
    int32_t abutStart = 0;
2267
0
    int32_t abutPass = 0;
2268
0
    UBool inQuote = false;
2269
2270
0
    MessageFormat * numericLeapMonthFormatter = nullptr;
2271
2272
0
    Calendar* calClone = nullptr;
2273
0
    Calendar *workCal = &cal;
2274
0
    if (&cal != fCalendar && typeid(cal) != typeid(*fCalendar)) {
2275
        // Different calendar type
2276
        // We use the time/zone from the input calendar, but
2277
        // do not use the input calendar for field calculation.
2278
0
        calClone = fCalendar->clone();
2279
0
        if (calClone != nullptr) {
2280
0
            calClone->setTime(cal.getTime(status),status);
2281
0
            if (U_FAILURE(status)) {
2282
0
                goto ExitParse;
2283
0
            }
2284
0
            calClone->setTimeZone(cal.getTimeZone());
2285
0
            workCal = calClone;
2286
0
        } else {
2287
0
            status = U_MEMORY_ALLOCATION_ERROR;
2288
0
            goto ExitParse;
2289
0
        }
2290
0
    }
2291
2292
0
    if (fSymbols->fLeapMonthPatterns != nullptr && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) {
2293
0
        numericLeapMonthFormatter = new MessageFormat(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric], fLocale, status);
2294
0
        if (numericLeapMonthFormatter == nullptr) {
2295
0
             status = U_MEMORY_ALLOCATION_ERROR;
2296
0
             goto ExitParse;
2297
0
        } else if (U_FAILURE(status)) {
2298
0
             goto ExitParse; // this will delete numericLeapMonthFormatter
2299
0
        }
2300
0
    }
2301
2302
0
    for (int32_t i=0; i<fPattern.length(); ++i) {
2303
0
        char16_t ch = fPattern.charAt(i);
2304
2305
        // Handle alphabetic field characters.
2306
0
        if (!inQuote && isSyntaxChar(ch)) {
2307
0
            int32_t fieldPat = i;
2308
2309
            // Count the length of this field specifier
2310
0
            count = 1;
2311
0
            while ((i+1)<fPattern.length() &&
2312
0
                   fPattern.charAt(i+1) == ch) {
2313
0
                ++count;
2314
0
                ++i;
2315
0
            }
2316
2317
0
            if (isNumeric(ch, count)) {
2318
0
                if (abutPat < 0) {
2319
                    // Determine if there is an abutting numeric field.
2320
                    // Record the start of a set of abutting numeric fields.
2321
0
                    if (isAtNumericField(fPattern, i + 1)) {
2322
0
                        abutPat = fieldPat;
2323
0
                        abutStart = pos;
2324
0
                        abutPass = 0;
2325
0
                    }
2326
0
                }
2327
0
            } else {
2328
0
                abutPat = -1; // End of any abutting fields
2329
0
            }
2330
2331
            // Handle fields within a run of abutting numeric fields.  Take
2332
            // the pattern "HHmmss" as an example. We will try to parse
2333
            // 2/2/2 characters of the input text, then if that fails,
2334
            // 1/2/2.  We only adjust the width of the leftmost field; the
2335
            // others remain fixed.  This allows "123456" => 12:34:56, but
2336
            // "12345" => 1:23:45.  Likewise, for the pattern "yyyyMMdd" we
2337
            // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
2338
0
            if (abutPat >= 0) {
2339
                // If we are at the start of a run of abutting fields, then
2340
                // shorten this field in each pass.  If we can't shorten
2341
                // this field any more, then the parse of this set of
2342
                // abutting numeric fields has failed.
2343
0
                if (fieldPat == abutPat) {
2344
0
                    count -= abutPass++;
2345
0
                    if (count == 0) {
2346
0
                        status = U_PARSE_ERROR;
2347
0
                        goto ExitParse;
2348
0
                    }
2349
0
                }
2350
2351
0
                pos = subParse(text, pos, ch, count,
2352
0
                               true, false, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType);
2353
2354
                // If the parse fails anywhere in the run, back up to the
2355
                // start of the run and retry.
2356
0
                if (pos < 0) {
2357
0
                    i = abutPat - 1;
2358
0
                    pos = abutStart;
2359
0
                    continue;
2360
0
                }
2361
0
            }
2362
2363
            // Handle non-numeric fields and non-abutting numeric
2364
            // fields.
2365
0
            else if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
2366
0
                int32_t s = subParse(text, pos, ch, count,
2367
0
                               false, true, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType, &dayPeriodInt);
2368
2369
0
                if (s == -pos-1) {
2370
                    // era not present, in special cases allow this to continue
2371
                    // from the position where the era was expected
2372
0
                    s = pos;
2373
2374
0
                    if (i+1 < fPattern.length()) {
2375
                        // move to next pattern character
2376
0
                        char16_t c = fPattern.charAt(i+1);
2377
2378
                        // check for whitespace
2379
0
                        if (PatternProps::isWhiteSpace(c)) {
2380
0
                            i++;
2381
                            // Advance over run in pattern
2382
0
                            while ((i+1)<fPattern.length() &&
2383
0
                                   PatternProps::isWhiteSpace(fPattern.charAt(i+1))) {
2384
0
                                ++i;
2385
0
                            }
2386
0
                        }
2387
0
                    }
2388
0
                }
2389
0
                else if (s <= 0) {
2390
0
                    status = U_PARSE_ERROR;
2391
0
                    goto ExitParse;
2392
0
                }
2393
0
                pos = s;
2394
0
            }
2395
0
        }
2396
2397
        // Handle literal pattern characters.  These are any
2398
        // quoted characters and non-alphabetic unquoted
2399
        // characters.
2400
0
        else {
2401
2402
0
            abutPat = -1; // End of any abutting fields
2403
2404
0
            if (! matchLiterals(fPattern, i, text, pos, getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status), getBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH, status), isLenient())) {
2405
0
                status = U_PARSE_ERROR;
2406
0
                goto ExitParse;
2407
0
            }
2408
0
        }
2409
0
    }
2410
2411
    // Special hack for trailing "." after non-numeric field.
2412
0
    if (text.charAt(pos) == 0x2e && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)) {
2413
        // only do if the last field is not numeric
2414
0
        if (isAfterNonNumericField(fPattern, fPattern.length())) {
2415
0
            pos++; // skip the extra "."
2416
0
        }
2417
0
    }
2418
2419
    // If dayPeriod is set, use it in conjunction with hour-of-day to determine am/pm.
2420
0
    if (dayPeriodInt >= 0) {
2421
0
        DayPeriodRules::DayPeriod dayPeriod = static_cast<DayPeriodRules::DayPeriod>(dayPeriodInt);
2422
0
        const DayPeriodRules *ruleSet = DayPeriodRules::getInstance(this->getSmpFmtLocale(), status);
2423
2424
0
        if (!cal.isSet(UCAL_HOUR) && !cal.isSet(UCAL_HOUR_OF_DAY)) {
2425
            // If hour is not set, set time to the midpoint of current day period, overwriting
2426
            // minutes if it's set.
2427
0
            double midPoint = ruleSet->getMidPointForDayPeriod(dayPeriod, status);
2428
2429
            // If we can't get midPoint we do nothing.
2430
0
            if (U_SUCCESS(status)) {
2431
                // Truncate midPoint toward zero to get the hour.
2432
                // Any leftover means it was a half-hour.
2433
0
                int32_t midPointHour = static_cast<int32_t>(midPoint);
2434
0
                int32_t midPointMinute = (midPoint - midPointHour) > 0 ? 30 : 0;
2435
2436
                // No need to set am/pm because hour-of-day is set last therefore takes precedence.
2437
0
                cal.set(UCAL_HOUR_OF_DAY, midPointHour);
2438
0
                cal.set(UCAL_MINUTE, midPointMinute);
2439
0
            }
2440
0
        } else {
2441
0
            int hourOfDay;
2442
2443
0
            if (cal.isSet(UCAL_HOUR_OF_DAY)) {  // Hour is parsed in 24-hour format.
2444
0
                hourOfDay = cal.get(UCAL_HOUR_OF_DAY, status);
2445
0
            } else {  // Hour is parsed in 12-hour format.
2446
0
                hourOfDay = cal.get(UCAL_HOUR, status);
2447
                // cal.get() turns 12 to 0 for 12-hour time; change 0 to 12
2448
                // so 0 unambiguously means a 24-hour time from above.
2449
0
                if (hourOfDay == 0) { hourOfDay = 12; }
2450
0
            }
2451
0
            U_ASSERT(0 <= hourOfDay && hourOfDay <= 23);
2452
2453
2454
            // If hour-of-day is 0 or 13 thru 23 then input time in unambiguously in 24-hour format.
2455
0
            if (hourOfDay == 0 || (13 <= hourOfDay && hourOfDay <= 23)) {
2456
                // Make hour-of-day take precedence over (hour + am/pm) by setting it again.
2457
0
                cal.set(UCAL_HOUR_OF_DAY, hourOfDay);
2458
0
            } else {
2459
                // We have a 12-hour time and need to choose between am and pm.
2460
                // Behave as if dayPeriod spanned 6 hours each way from its center point.
2461
                // This will parse correctly for consistent time + period (e.g. 10 at night) as
2462
                // well as provide a reasonable recovery for inconsistent time + period (e.g.
2463
                // 9 in the afternoon).
2464
2465
                // Assume current time is in the AM.
2466
                // - Change 12 back to 0 for easier handling of 12am.
2467
                // - Append minutes as fractional hours because e.g. 8:15 and 8:45 could be parsed
2468
                // into different half-days if center of dayPeriod is at 14:30.
2469
                // - cal.get(MINUTE) will return 0 if MINUTE is unset, which works.
2470
0
                if (hourOfDay == 12) { hourOfDay = 0; }
2471
0
                double currentHour = hourOfDay + (cal.get(UCAL_MINUTE, status)) / 60.0;
2472
0
                double midPointHour = ruleSet->getMidPointForDayPeriod(dayPeriod, status);
2473
2474
0
                if (U_SUCCESS(status)) {
2475
0
                    double hoursAheadMidPoint = currentHour - midPointHour;
2476
2477
                    // Assume current time is in the AM.
2478
0
                    if (-6 <= hoursAheadMidPoint && hoursAheadMidPoint < 6) {
2479
                        // Assumption holds; set time as such.
2480
0
                        cal.set(UCAL_AM_PM, 0);
2481
0
                    } else {
2482
0
                        cal.set(UCAL_AM_PM, 1);
2483
0
                    }
2484
0
                }
2485
0
            }
2486
0
        }
2487
0
    }
2488
2489
    // At this point the fields of Calendar have been set.  Calendar
2490
    // will fill in default values for missing fields when the time
2491
    // is computed.
2492
2493
0
    parsePos.setIndex(pos);
2494
2495
    // This part is a problem:  When we call parsedDate.after, we compute the time.
2496
    // Take the date April 3 2004 at 2:30 am.  When this is first set up, the year
2497
    // will be wrong if we're parsing a 2-digit year pattern.  It will be 1904.
2498
    // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day.  2:30 am
2499
    // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
2500
    // on that day.  It is therefore parsed out to fields as 3:30 am.  Then we
2501
    // add 100 years, and get April 3 2004 at 3:30 am.  Note that April 3 2004 is
2502
    // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
2503
    /*
2504
        UDate parsedDate = calendar.getTime();
2505
        if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
2506
            calendar.add(Calendar.YEAR, 100);
2507
            parsedDate = calendar.getTime();
2508
        }
2509
    */
2510
    // Because of the above condition, save off the fields in case we need to readjust.
2511
    // The procedure we use here is not particularly efficient, but there is no other
2512
    // way to do this given the API restrictions present in Calendar.  We minimize
2513
    // inefficiency by only performing this computation when it might apply, that is,
2514
    // when the two-digit year is equal to the start year, and thus might fall at the
2515
    // front or the back of the default century.  This only works because we adjust
2516
    // the year correctly to start with in other cases -- see subParse().
2517
0
    if (ambiguousYear[0] || tzTimeType != UTZFMT_TIME_TYPE_UNKNOWN) // If this is true then the two-digit year == the default start year
2518
0
    {
2519
        // We need a copy of the fields, and we need to avoid triggering a call to
2520
        // complete(), which will recalculate the fields.  Since we can't access
2521
        // the fields[] array in Calendar, we clone the entire object.  This will
2522
        // stop working if Calendar.clone() is ever rewritten to call complete().
2523
0
        Calendar *copy;
2524
0
        if (ambiguousYear[0]) {
2525
0
            copy = cal.clone();
2526
            // Check for failed cloning.
2527
0
            if (copy == nullptr) {
2528
0
                status = U_MEMORY_ALLOCATION_ERROR;
2529
0
                goto ExitParse;
2530
0
            }
2531
0
            UDate parsedDate = copy->getTime(status);
2532
            // {sfb} check internalGetDefaultCenturyStart
2533
0
            if (fHaveDefaultCentury && (parsedDate < fDefaultCenturyStart)) {
2534
                // We can't use add here because that does a complete() first.
2535
0
                cal.set(UCAL_YEAR, fDefaultCenturyStartYear + 100);
2536
0
            }
2537
0
            delete copy;
2538
0
        }
2539
2540
0
        if (tzTimeType != UTZFMT_TIME_TYPE_UNKNOWN) {
2541
0
            copy = cal.clone();
2542
            // Check for failed cloning.
2543
0
            if (copy == nullptr) {
2544
0
                status = U_MEMORY_ALLOCATION_ERROR;
2545
0
                goto ExitParse;
2546
0
            }
2547
0
            const TimeZone & tz = cal.getTimeZone();
2548
0
            BasicTimeZone *btz = nullptr;
2549
2550
0
            if (dynamic_cast<const OlsonTimeZone *>(&tz) != nullptr
2551
0
                || dynamic_cast<const SimpleTimeZone *>(&tz) != nullptr
2552
0
                || dynamic_cast<const RuleBasedTimeZone *>(&tz) != nullptr
2553
0
                || dynamic_cast<const VTimeZone *>(&tz) != nullptr) {
2554
0
                btz = (BasicTimeZone*)&tz;
2555
0
            }
2556
2557
            // Get local millis
2558
0
            copy->set(UCAL_ZONE_OFFSET, 0);
2559
0
            copy->set(UCAL_DST_OFFSET, 0);
2560
0
            UDate localMillis = copy->getTime(status);
2561
2562
            // Make sure parsed time zone type (Standard or Daylight)
2563
            // matches the rule used by the parsed time zone.
2564
0
            int32_t raw, dst;
2565
0
            if (btz != nullptr) {
2566
0
                if (tzTimeType == UTZFMT_TIME_TYPE_STANDARD) {
2567
0
                    btz->getOffsetFromLocal(localMillis,
2568
0
                        UCAL_TZ_LOCAL_STANDARD_FORMER, UCAL_TZ_LOCAL_STANDARD_LATTER, raw, dst, status);
2569
0
                } else {
2570
0
                    btz->getOffsetFromLocal(localMillis,
2571
0
                        UCAL_TZ_LOCAL_DAYLIGHT_FORMER, UCAL_TZ_LOCAL_DAYLIGHT_LATTER, raw, dst, status);
2572
0
                }
2573
0
            } else {
2574
                // No good way to resolve ambiguous time at transition,
2575
                // but following code work in most case.
2576
0
                tz.getOffset(localMillis, true, raw, dst, status);
2577
0
            }
2578
2579
            // Now, compare the results with parsed type, either standard or daylight saving time
2580
0
            int32_t resolvedSavings = dst;
2581
0
            if (tzTimeType == UTZFMT_TIME_TYPE_STANDARD) {
2582
0
                if (dst != 0) {
2583
                    // Override DST_OFFSET = 0 in the result calendar
2584
0
                    resolvedSavings = 0;
2585
0
                }
2586
0
            } else { // tztype == TZTYPE_DST
2587
0
                if (dst == 0) {
2588
0
                    if (btz != nullptr) {
2589
                        // This implementation resolves daylight saving time offset
2590
                        // closest rule after the given time.
2591
0
                        UDate baseTime = localMillis + raw;
2592
0
                        UDate time = baseTime;
2593
0
                        UDate limit = baseTime + MAX_DAYLIGHT_DETECTION_RANGE;
2594
0
                        TimeZoneTransition trs;
2595
0
                        UBool trsAvail;
2596
2597
                        // Search for DST rule after the given time
2598
0
                        while (time < limit) {
2599
0
                            trsAvail = btz->getNextTransition(time, false, trs);
2600
0
                            if (!trsAvail) {
2601
0
                                break;
2602
0
                            }
2603
0
                            resolvedSavings = trs.getTo()->getDSTSavings();
2604
0
                            if (resolvedSavings != 0) {
2605
0
                                break;
2606
0
                            }
2607
0
                            time = trs.getTime();
2608
0
                        }
2609
2610
0
                        if (resolvedSavings == 0) {
2611
                            // If no DST rule after the given time was found, search for
2612
                            // DST rule before.
2613
0
                            time = baseTime;
2614
0
                            limit = baseTime - MAX_DAYLIGHT_DETECTION_RANGE;
2615
0
                            while (time > limit) {
2616
0
                                trsAvail = btz->getPreviousTransition(time, true, trs);
2617
0
                                if (!trsAvail) {
2618
0
                                    break;
2619
0
                                }
2620
0
                                resolvedSavings = trs.getFrom()->getDSTSavings();
2621
0
                                if (resolvedSavings != 0) {
2622
0
                                    break;
2623
0
                                }
2624
0
                                time = trs.getTime() - 1;
2625
0
                            }
2626
2627
0
                            if (resolvedSavings == 0) {
2628
0
                                resolvedSavings = btz->getDSTSavings();
2629
0
                            }
2630
0
                        }
2631
0
                    } else {
2632
0
                        resolvedSavings = tz.getDSTSavings();
2633
0
                    }
2634
0
                    if (resolvedSavings == 0) {
2635
                        // final fallback
2636
0
                        resolvedSavings = U_MILLIS_PER_HOUR;
2637
0
                    }
2638
0
                }
2639
0
            }
2640
0
            cal.set(UCAL_ZONE_OFFSET, raw);
2641
0
            cal.set(UCAL_DST_OFFSET, resolvedSavings);
2642
0
            delete copy;
2643
0
        }
2644
0
    }
2645
0
ExitParse:
2646
    // Set the parsed result if local calendar is used
2647
    // instead of the input calendar
2648
0
    if (U_SUCCESS(status) && workCal != &cal) {
2649
0
        cal.setTimeZone(workCal->getTimeZone());
2650
0
        cal.setTime(workCal->getTime(status), status);
2651
0
    }
2652
2653
0
    delete numericLeapMonthFormatter;
2654
0
    delete calClone;
2655
2656
    // If any Calendar calls failed, we pretend that we
2657
    // couldn't parse the string, when in reality this isn't quite accurate--
2658
    // we did parse it; the Calendar calls just failed.
2659
0
    if (U_FAILURE(status)) {
2660
0
        parsePos.setErrorIndex(pos);
2661
0
        parsePos.setIndex(start);
2662
0
    }
2663
0
}
2664
2665
//----------------------------------------------------------------------
2666
2667
static int32_t
2668
matchStringWithOptionalDot(const UnicodeString &text,
2669
                            int32_t index,
2670
                            const UnicodeString &data);
2671
2672
int32_t SimpleDateFormat::matchQuarterString(const UnicodeString& text,
2673
                              int32_t start,
2674
                              UCalendarDateFields field,
2675
                              const UnicodeString* data,
2676
                              int32_t dataCount,
2677
                              Calendar& cal) const
2678
0
{
2679
0
    int32_t i = 0;
2680
0
    int32_t count = dataCount;
2681
2682
    // There may be multiple strings in the data[] array which begin with
2683
    // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2684
    // We keep track of the longest match, and return that.  Note that this
2685
    // unfortunately requires us to test all array elements.
2686
0
    int32_t bestMatchLength = 0, bestMatch = -1;
2687
0
    UnicodeString bestMatchName;
2688
2689
0
    for (; i < count; ++i) {
2690
0
        int32_t matchLength = 0;
2691
0
        if ((matchLength = matchStringWithOptionalDot(text, start, data[i])) > bestMatchLength) {
2692
0
            bestMatchLength = matchLength;
2693
0
            bestMatch = i;
2694
0
        }
2695
0
    }
2696
2697
0
    if (bestMatch >= 0) {
2698
0
        cal.set(field, bestMatch * 3);
2699
0
        return start + bestMatchLength;
2700
0
    }
2701
2702
0
    return -start;
2703
0
}
2704
2705
int32_t SimpleDateFormat::matchDayPeriodStrings(const UnicodeString& text, int32_t start,
2706
                              const UnicodeString* data, int32_t dataCount,
2707
                              int32_t &dayPeriod) const
2708
0
{
2709
2710
0
    int32_t bestMatchLength = 0, bestMatch = -1;
2711
2712
0
    for (int32_t i = 0; i < dataCount; ++i) {
2713
0
        int32_t matchLength = 0;
2714
0
        if ((matchLength = matchStringWithOptionalDot(text, start, data[i])) > bestMatchLength) {
2715
0
            bestMatchLength = matchLength;
2716
0
            bestMatch = i;
2717
0
        }
2718
0
    }
2719
2720
0
    if (bestMatch >= 0) {
2721
0
        dayPeriod = bestMatch;
2722
0
        return start + bestMatchLength;
2723
0
    }
2724
2725
0
    return -start;
2726
0
}
2727
2728
//----------------------------------------------------------------------
2729
UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern,
2730
                                      int32_t &patternOffset,
2731
                                      const UnicodeString &text,
2732
                                      int32_t &textOffset,
2733
                                      UBool whitespaceLenient,
2734
                                      UBool partialMatchLenient,
2735
                                      UBool oldLeniency)
2736
0
{
2737
0
    UBool inQuote = false;
2738
0
    UnicodeString literal;
2739
0
    int32_t i = patternOffset;
2740
2741
    // scan pattern looking for contiguous literal characters
2742
0
    for ( ; i < pattern.length(); i += 1) {
2743
0
        char16_t ch = pattern.charAt(i);
2744
2745
0
        if (!inQuote && isSyntaxChar(ch)) {
2746
0
            break;
2747
0
        }
2748
2749
0
        if (ch == QUOTE) {
2750
            // Match a quote literal ('') inside OR outside of quotes
2751
0
            if ((i + 1) < pattern.length() && pattern.charAt(i + 1) == QUOTE) {
2752
0
                i += 1;
2753
0
            } else {
2754
0
                inQuote = !inQuote;
2755
0
                continue;
2756
0
            }
2757
0
        }
2758
2759
0
        literal += ch;
2760
0
    }
2761
2762
    // at this point, literal contains the literal text
2763
    // and i is the index of the next non-literal pattern character.
2764
0
    int32_t p;
2765
0
    int32_t t = textOffset;
2766
2767
0
    if (whitespaceLenient) {
2768
        // trim leading, trailing whitespace from
2769
        // the literal text
2770
0
        literal.trim();
2771
2772
        // ignore any leading whitespace in the text
2773
0
        while (t < text.length() && u_isWhitespace(text.charAt(t))) {
2774
0
            t += 1;
2775
0
        }
2776
0
    }
2777
2778
0
    for (p = 0; p < literal.length() && t < text.length();) {
2779
0
        UBool needWhitespace = false;
2780
2781
0
        while (p < literal.length() && PatternProps::isWhiteSpace(literal.charAt(p))) {
2782
0
            needWhitespace = true;
2783
0
            p += 1;
2784
0
        }
2785
2786
0
        if (needWhitespace) {
2787
0
            int32_t tStart = t;
2788
2789
0
            while (t < text.length()) {
2790
0
                char16_t tch = text.charAt(t);
2791
2792
0
                if (!u_isUWhiteSpace(tch) && !PatternProps::isWhiteSpace(tch)) {
2793
0
                    break;
2794
0
                }
2795
2796
0
                t += 1;
2797
0
            }
2798
2799
            // TODO: should we require internal spaces
2800
            // in lenient mode? (There won't be any
2801
            // leading or trailing spaces)
2802
0
            if (!whitespaceLenient && t == tStart) {
2803
                // didn't find matching whitespace:
2804
                // an error in strict mode
2805
0
                return false;
2806
0
            }
2807
2808
            // In strict mode, this run of whitespace
2809
            // may have been at the end.
2810
0
            if (p >= literal.length()) {
2811
0
                break;
2812
0
            }
2813
0
        }
2814
0
        if (t >= text.length() || literal.charAt(p) != text.charAt(t)) {
2815
            // Ran out of text, or found a non-matching character:
2816
            // OK in lenient mode, an error in strict mode.
2817
0
            if (whitespaceLenient) {
2818
0
                if (t == textOffset && text.charAt(t) == 0x2e &&
2819
0
                        isAfterNonNumericField(pattern, patternOffset)) {
2820
                    // Lenient mode and the literal input text begins with a "." and
2821
                    // we are after a non-numeric field: We skip the "."
2822
0
                    ++t;
2823
0
                    continue;  // Do not update p.
2824
0
                }
2825
                // if it is actual whitespace and we're whitespace lenient it's OK
2826
2827
0
                char16_t wsc = text.charAt(t);
2828
0
                if(PatternProps::isWhiteSpace(wsc)) {
2829
                    // Lenient mode and it's just whitespace we skip it
2830
0
                    ++t;
2831
0
                    continue;  // Do not update p.
2832
0
                }
2833
0
            }
2834
            // hack around oldleniency being a bit of a catch-all bucket and we're just adding support specifically for partial matches
2835
0
            if(partialMatchLenient && oldLeniency) {
2836
0
                break;
2837
0
            }
2838
2839
0
            return false;
2840
0
        }
2841
0
        ++p;
2842
0
        ++t;
2843
0
    }
2844
2845
    // At this point if we're in strict mode we have a complete match.
2846
    // If we're in lenient mode we may have a partial match, or no
2847
    // match at all.
2848
0
    if (p <= 0) {
2849
        // no match. Pretend it matched a run of whitespace
2850
        // and ignorables in the text.
2851
0
        const  UnicodeSet *ignorables = nullptr;
2852
0
        UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(pattern.charAt(i));
2853
0
        if (patternCharIndex != UDAT_FIELD_COUNT) {
2854
0
            ignorables = SimpleDateFormatStaticSets::getIgnorables(patternCharIndex);
2855
0
        }
2856
2857
0
        for (t = textOffset; t < text.length(); t += 1) {
2858
0
            char16_t ch = text.charAt(t);
2859
2860
0
            if (ignorables == nullptr || !ignorables->contains(ch)) {
2861
0
                break;
2862
0
            }
2863
0
        }
2864
0
    }
2865
2866
    // if we get here, we've got a complete match.
2867
0
    patternOffset = i - 1;
2868
0
    textOffset = t;
2869
2870
0
    return true;
2871
0
}
2872
2873
//----------------------------------------------------------------------
2874
// check both wide and abbrev months.
2875
// Does not currently handle monthPattern.
2876
// UCalendarDateFields field = UCAL_MONTH
2877
2878
int32_t SimpleDateFormat::matchAlphaMonthStrings(const UnicodeString& text,
2879
                              int32_t start,
2880
                              const UnicodeString* wideData,
2881
                              const UnicodeString* shortData,
2882
                              int32_t dataCount,
2883
                              Calendar& cal) const
2884
0
{
2885
0
    int32_t i;
2886
0
    int32_t bestMatchLength = 0, bestMatch = -1;
2887
2888
0
    for (i = 0; i < dataCount; ++i) {
2889
0
        int32_t matchLen = 0;
2890
0
        if ((matchLen = matchStringWithOptionalDot(text, start, wideData[i])) > bestMatchLength) {
2891
0
            bestMatch = i;
2892
0
            bestMatchLength = matchLen;
2893
0
        }
2894
0
    }
2895
0
    for (i = 0; i < dataCount; ++i) {
2896
0
        int32_t matchLen = 0;
2897
0
        if ((matchLen = matchStringWithOptionalDot(text, start, shortData[i])) > bestMatchLength) {
2898
0
            bestMatch = i;
2899
0
            bestMatchLength = matchLen;
2900
0
        }
2901
0
    }
2902
2903
0
    if (bestMatch >= 0) { 
2904
        // Adjustment for Hebrew Calendar month Adar II
2905
0
        if (typeid(cal) == typeid(HebrewCalendar) && bestMatch==13) {
2906
0
            cal.set(UCAL_MONTH,6);
2907
0
        } else {
2908
0
            cal.set(UCAL_MONTH, bestMatch);
2909
0
        }
2910
0
        return start + bestMatchLength;
2911
0
    }
2912
2913
0
    return -start;
2914
0
}
2915
2916
//----------------------------------------------------------------------
2917
2918
int32_t SimpleDateFormat::matchString(const UnicodeString& text,
2919
                              int32_t start,
2920
                              UCalendarDateFields field,
2921
                              const UnicodeString* data,
2922
                              int32_t dataCount,
2923
                              const UnicodeString* monthPattern,
2924
                              Calendar& cal) const
2925
0
{
2926
0
    int32_t i = 0;
2927
0
    int32_t count = dataCount;
2928
2929
0
    if (field == UCAL_DAY_OF_WEEK) i = 1;
2930
2931
    // There may be multiple strings in the data[] array which begin with
2932
    // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2933
    // We keep track of the longest match, and return that.  Note that this
2934
    // unfortunately requires us to test all array elements.
2935
    // But this does not really work for cases such as Chuvash in which
2936
    // May is "ҫу" and August is "ҫурла"/"ҫур.", hence matchAlphaMonthStrings.
2937
0
    int32_t bestMatchLength = 0, bestMatch = -1;
2938
0
    UnicodeString bestMatchName;
2939
0
    int32_t isLeapMonth = 0;
2940
2941
0
    for (; i < count; ++i) {
2942
0
        int32_t matchLen = 0;
2943
0
        if ((matchLen = matchStringWithOptionalDot(text, start, data[i])) > bestMatchLength) {
2944
0
            bestMatch = i;
2945
0
            bestMatchLength = matchLen;
2946
0
        }
2947
2948
0
        if (monthPattern != nullptr) {
2949
0
            UErrorCode status = U_ZERO_ERROR;
2950
0
            UnicodeString leapMonthName;
2951
0
            SimpleFormatter(*monthPattern, 1, 1, status).format(data[i], leapMonthName, status);
2952
0
            if (U_SUCCESS(status)) {
2953
0
                if ((matchLen = matchStringWithOptionalDot(text, start, leapMonthName)) > bestMatchLength) {
2954
0
                    bestMatch = i;
2955
0
                    bestMatchLength = matchLen;
2956
0
                    isLeapMonth = 1;
2957
0
                }
2958
0
            }
2959
0
        }
2960
0
    }
2961
2962
0
    if (bestMatch >= 0) {
2963
0
        if (field < UCAL_FIELD_COUNT) {
2964
            // Adjustment for Hebrew Calendar month Adar II
2965
0
            if (typeid(cal) == typeid(HebrewCalendar) && field==UCAL_MONTH && bestMatch==13) {
2966
0
                cal.set(field,6);
2967
0
            } else {
2968
0
                if (field == UCAL_YEAR) {
2969
0
                    bestMatch++; // only get here for cyclic year names, which match 1-based years 1-60
2970
0
                }
2971
0
                cal.set(field, bestMatch);
2972
0
            }
2973
0
            if (monthPattern != nullptr) {
2974
0
                cal.set(UCAL_IS_LEAP_MONTH, isLeapMonth);
2975
0
            }
2976
0
        }
2977
2978
0
        return start + bestMatchLength;
2979
0
    }
2980
2981
0
    return -start;
2982
0
}
2983
2984
static int32_t
2985
matchStringWithOptionalDot(const UnicodeString &text,
2986
                            int32_t index,
2987
0
                            const UnicodeString &data) {
2988
0
    UErrorCode sts = U_ZERO_ERROR;
2989
0
    int32_t matchLenText = 0;
2990
0
    int32_t matchLenData = 0;
2991
2992
0
    u_caseInsensitivePrefixMatch(text.getBuffer() + index, text.length() - index,
2993
0
                                 data.getBuffer(), data.length(),
2994
0
                                 0 /* default case option */,
2995
0
                                 &matchLenText, &matchLenData,
2996
0
                                 &sts);
2997
0
    U_ASSERT (U_SUCCESS(sts));
2998
2999
0
    if (matchLenData == data.length() /* normal match */
3000
0
        || (data.charAt(data.length() - 1) == 0x2e
3001
0
            && matchLenData == data.length() - 1 /* match without trailing dot */)) {
3002
0
        return matchLenText;
3003
0
    }
3004
3005
0
    return 0;
3006
0
}
3007
3008
//----------------------------------------------------------------------
3009
3010
void
3011
SimpleDateFormat::set2DigitYearStart(UDate d, UErrorCode& status)
3012
0
{
3013
0
    parseAmbiguousDatesAsAfter(d, status);
3014
0
}
3015
3016
/**
3017
 * Private member function that converts the parsed date strings into
3018
 * timeFields. Returns -start (for ParsePosition) if failed.
3019
 */
3020
int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, char16_t ch, int32_t count,
3021
                           UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], int32_t& saveHebrewMonth, Calendar& cal,
3022
                           int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType,
3023
                           int32_t *dayPeriod) const
3024
0
{
3025
0
    Formattable number;
3026
0
    int32_t value = 0;
3027
0
    int32_t i;
3028
0
    int32_t ps = 0;
3029
0
    UErrorCode status = U_ZERO_ERROR;
3030
0
    ParsePosition pos(0);
3031
0
    UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch);
3032
0
    const NumberFormat *currentNumberFormat;
3033
0
    UnicodeString temp;
3034
0
    UBool gotNumber = false;
3035
3036
#if defined (U_DEBUG_CAL)
3037
    //fprintf(stderr, "%s:%d - [%c]  st=%d \n", __FILE__, __LINE__, (char) ch, start);
3038
#endif
3039
3040
0
    if (patternCharIndex == UDAT_FIELD_COUNT) {
3041
0
        return -start;
3042
0
    }
3043
3044
0
    currentNumberFormat = getNumberFormatByIndex(patternCharIndex);
3045
0
    if (currentNumberFormat == nullptr) {
3046
0
        return -start;
3047
0
    }
3048
0
    UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex]; // UCAL_FIELD_COUNT if irrelevant
3049
3050
0
    if (numericLeapMonthFormatter != nullptr) {
3051
0
        numericLeapMonthFormatter->setFormats(reinterpret_cast<const Format**>(&currentNumberFormat), 1);
3052
0
    }
3053
3054
    // If there are any spaces here, skip over them.  If we hit the end
3055
    // of the string, then fail.
3056
0
    for (;;) {
3057
0
        if (start >= text.length()) {
3058
0
            return -start;
3059
0
        }
3060
0
        UChar32 c = text.char32At(start);
3061
0
        if (!u_isUWhiteSpace(c) /*||*/ && !PatternProps::isWhiteSpace(c)) {
3062
0
            break;
3063
0
        }
3064
0
        start += U16_LENGTH(c);
3065
0
    }
3066
0
    pos.setIndex(start);
3067
3068
0
    UBool isChineseCalendar = typeid(cal) == typeid(ChineseCalendar) ||
3069
0
            typeid(cal) == typeid(DangiCalendar);
3070
    // We handle a few special cases here where we need to parse
3071
    // a number value.  We handle further, more generic cases below.  We need
3072
    // to handle some of them here because some fields require extra processing on
3073
    // the parsed value.
3074
0
    if (patternCharIndex == UDAT_HOUR_OF_DAY1_FIELD ||                       // k
3075
0
        patternCharIndex == UDAT_HOUR_OF_DAY0_FIELD ||                       // H
3076
0
        patternCharIndex == UDAT_HOUR1_FIELD ||                              // h
3077
0
        patternCharIndex == UDAT_HOUR0_FIELD ||                              // K
3078
0
        (patternCharIndex == UDAT_DOW_LOCAL_FIELD && count <= 2) ||          // e
3079
0
        (patternCharIndex == UDAT_STANDALONE_DAY_FIELD && count <= 2) ||     // c
3080
0
        (patternCharIndex == UDAT_MONTH_FIELD && count <= 2) ||              // M
3081
0
        (patternCharIndex == UDAT_STANDALONE_MONTH_FIELD && count <= 2) ||   // L
3082
0
        (patternCharIndex == UDAT_QUARTER_FIELD && count <= 2) ||            // Q
3083
0
        (patternCharIndex == UDAT_STANDALONE_QUARTER_FIELD && count <= 2) || // q
3084
0
        patternCharIndex == UDAT_YEAR_FIELD ||                               // y
3085
0
        patternCharIndex == UDAT_YEAR_WOY_FIELD ||                           // Y
3086
0
        patternCharIndex == UDAT_YEAR_NAME_FIELD ||                          // U (falls back to numeric)
3087
0
        (patternCharIndex == UDAT_ERA_FIELD && isChineseCalendar) ||         // G
3088
0
        patternCharIndex == UDAT_FRACTIONAL_SECOND_FIELD)                    // S
3089
0
    {
3090
0
        int32_t parseStart = pos.getIndex();
3091
        // It would be good to unify this with the obeyCount logic below,
3092
        // but that's going to be difficult.
3093
0
        const UnicodeString* src;
3094
3095
0
        UBool parsedNumericLeapMonth = false;
3096
0
        if (numericLeapMonthFormatter != nullptr && (patternCharIndex == UDAT_MONTH_FIELD || patternCharIndex == UDAT_STANDALONE_MONTH_FIELD)) {
3097
0
            int32_t argCount;
3098
0
            Formattable * args = numericLeapMonthFormatter->parse(text, pos, argCount);
3099
0
            if (args != nullptr && argCount == 1 && pos.getIndex() > parseStart && args[0].isNumeric()) {
3100
0
                parsedNumericLeapMonth = true;
3101
0
                number.setLong(args[0].getLong());
3102
0
                cal.set(UCAL_IS_LEAP_MONTH, 1);
3103
0
                delete[] args;
3104
0
            } else {
3105
0
                pos.setIndex(parseStart);
3106
0
                cal.set(UCAL_IS_LEAP_MONTH, 0);
3107
0
            }
3108
0
        }
3109
3110
0
        if (!parsedNumericLeapMonth) {
3111
0
            if (obeyCount) {
3112
0
                if ((start+count) > text.length()) {
3113
0
                    return -start;
3114
0
                }
3115
3116
0
                text.extractBetween(0, start + count, temp);
3117
0
                src = &temp;
3118
0
            } else {
3119
0
                src = &text;
3120
0
            }
3121
3122
0
            parseInt(*src, number, pos, allowNegative,currentNumberFormat);
3123
0
        }
3124
3125
0
        int32_t txtLoc = pos.getIndex();
3126
3127
0
        if (txtLoc > parseStart) {
3128
0
            value = number.getLong();
3129
0
            gotNumber = true;
3130
3131
            // suffix processing
3132
0
            if (value < 0 ) {
3133
0
                txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, true);
3134
0
                if (txtLoc != pos.getIndex()) {
3135
0
                    value *= -1;
3136
0
                }
3137
0
            }
3138
0
            else {
3139
0
                txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, false);
3140
0
            }
3141
3142
0
            if (!getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)) {
3143
                // Check the range of the value
3144
0
                int32_t bias = gFieldRangeBias[patternCharIndex];
3145
0
                if (bias >= 0 && (value > cal.getMaximum(field) + bias || value < cal.getMinimum(field) + bias)) {
3146
0
                    return -start;
3147
0
                }
3148
0
            }
3149
3150
0
            pos.setIndex(txtLoc);
3151
0
        }
3152
0
    }
3153
3154
    // Make sure that we got a number if
3155
    // we want one, and didn't get one
3156
    // if we don't want one.
3157
0
    switch (patternCharIndex) {
3158
0
        case UDAT_HOUR_OF_DAY1_FIELD:
3159
0
        case UDAT_HOUR_OF_DAY0_FIELD:
3160
0
        case UDAT_HOUR1_FIELD:
3161
0
        case UDAT_HOUR0_FIELD:
3162
            // special range check for hours:
3163
0
            if (value < 0 || value > 24) {
3164
0
                return -start;
3165
0
            }
3166
3167
            // fall through to gotNumber check
3168
0
            U_FALLTHROUGH;
3169
0
        case UDAT_YEAR_FIELD:
3170
0
        case UDAT_YEAR_WOY_FIELD:
3171
0
        case UDAT_FRACTIONAL_SECOND_FIELD:
3172
            // these must be a number
3173
0
            if (! gotNumber) {
3174
0
                return -start;
3175
0
            }
3176
3177
0
            break;
3178
3179
0
        default:
3180
            // we check the rest of the fields below.
3181
0
            break;
3182
0
    }
3183
3184
0
    switch (patternCharIndex) {
3185
0
    case UDAT_ERA_FIELD:
3186
0
        if (isChineseCalendar) {
3187
0
            if (!gotNumber) {
3188
0
                return -start;
3189
0
            }
3190
0
            cal.set(UCAL_ERA, value);
3191
0
            return pos.getIndex();
3192
0
        }
3193
0
        if (count == 5) {
3194
0
            ps = matchString(text, start, UCAL_ERA, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount, nullptr, cal);
3195
0
        } else if (count == 4) {
3196
0
            ps = matchString(text, start, UCAL_ERA, fSymbols->fEraNames, fSymbols->fEraNamesCount, nullptr, cal);
3197
0
        } else {
3198
0
            ps = matchString(text, start, UCAL_ERA, fSymbols->fEras, fSymbols->fErasCount, nullptr, cal);
3199
0
        }
3200
3201
        // check return position, if it equals -start, then matchString error
3202
        // special case the return code so we don't necessarily fail out until we
3203
        // verify no year information also
3204
0
        if (ps == -start)
3205
0
            ps--;
3206
3207
0
        return ps;
3208
3209
0
    case UDAT_YEAR_FIELD:
3210
        // If there are 3 or more YEAR pattern characters, this indicates
3211
        // that the year value is to be treated literally, without any
3212
        // two-digit year adjustments (e.g., from "01" to 2001).  Otherwise
3213
        // we made adjustments to place the 2-digit year in the proper
3214
        // century, for parsed strings from "00" to "99".  Any other string
3215
        // is treated literally:  "2250", "-1", "1", "002".
3216
0
        if (fDateOverride == HEBREW_CALENDAR_VALUE && value < 1000) {
3217
0
            value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
3218
0
        } else if (text.moveIndex32(start, 2) == pos.getIndex() && !isChineseCalendar
3219
0
            && u_isdigit(text.char32At(start))
3220
0
            && u_isdigit(text.char32At(text.moveIndex32(start, 1))))
3221
0
        {
3222
            // only adjust year for patterns less than 3.
3223
0
            if(count < 3) {
3224
                // Assume for example that the defaultCenturyStart is 6/18/1903.
3225
                // This means that two-digit years will be forced into the range
3226
                // 6/18/1903 to 6/17/2003.  As a result, years 00, 01, and 02
3227
                // correspond to 2000, 2001, and 2002.  Years 04, 05, etc. correspond
3228
                // to 1904, 1905, etc.  If the year is 03, then it is 2003 if the
3229
                // other fields specify a date before 6/18, or 1903 if they specify a
3230
                // date afterwards.  As a result, 03 is an ambiguous year.  All other
3231
                // two-digit years are unambiguous.
3232
0
                if(fHaveDefaultCentury) { // check if this formatter even has a pivot year
3233
0
                    int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
3234
0
                    ambiguousYear[0] = (value == ambiguousTwoDigitYear);
3235
0
                    value += (fDefaultCenturyStartYear/100)*100 +
3236
0
                            (value < ambiguousTwoDigitYear ? 100 : 0);
3237
0
                }
3238
0
            }
3239
0
        }
3240
0
        cal.set(UCAL_YEAR, value);
3241
3242
        // Delayed checking for adjustment of Hebrew month numbers in non-leap years.
3243
0
        if (saveHebrewMonth >= 0) {
3244
0
            HebrewCalendar *hc = (HebrewCalendar*)&cal;
3245
0
            if (!hc->isLeapYear(value) && saveHebrewMonth >= 6) {
3246
0
               cal.set(UCAL_MONTH,saveHebrewMonth);
3247
0
            } else {
3248
0
               cal.set(UCAL_MONTH,saveHebrewMonth-1);
3249
0
            }
3250
0
            saveHebrewMonth = -1;
3251
0
        }
3252
0
        return pos.getIndex();
3253
3254
0
    case UDAT_YEAR_WOY_FIELD:
3255
        // Comment is the same as for UDAT_Year_FIELDs - look above
3256
0
        if (fDateOverride == HEBREW_CALENDAR_VALUE && value < 1000) {
3257
0
            value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
3258
0
        } else if (text.moveIndex32(start, 2) == pos.getIndex()
3259
0
            && u_isdigit(text.char32At(start))
3260
0
            && u_isdigit(text.char32At(text.moveIndex32(start, 1)))
3261
0
            && fHaveDefaultCentury )
3262
0
        {
3263
0
            int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
3264
0
            ambiguousYear[0] = (value == ambiguousTwoDigitYear);
3265
0
            value += (fDefaultCenturyStartYear/100)*100 +
3266
0
                (value < ambiguousTwoDigitYear ? 100 : 0);
3267
0
        }
3268
0
        cal.set(UCAL_YEAR_WOY, value);
3269
0
        return pos.getIndex();
3270
3271
0
    case UDAT_YEAR_NAME_FIELD:
3272
0
        if (fSymbols->fShortYearNames != nullptr) {
3273
0
            int32_t newStart = matchString(text, start, UCAL_YEAR, fSymbols->fShortYearNames, fSymbols->fShortYearNamesCount, nullptr, cal);
3274
0
            if (newStart > 0) {
3275
0
                return newStart;
3276
0
            }
3277
0
        }
3278
0
        if (gotNumber && (getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC,status) || value > fSymbols->fShortYearNamesCount)) {
3279
0
            cal.set(UCAL_YEAR, value);
3280
0
            return pos.getIndex();
3281
0
        }
3282
0
        return -start;
3283
3284
0
    case UDAT_MONTH_FIELD:
3285
0
    case UDAT_STANDALONE_MONTH_FIELD:
3286
0
        if (gotNumber) // i.e., M or MM.
3287
0
        {
3288
            // When parsing month numbers from the Hebrew Calendar, we might need to adjust the month depending on whether
3289
            // or not it was a leap year.  We may or may not yet know what year it is, so might have to delay checking until
3290
            // the year is parsed.
3291
0
            if (typeid(cal) == typeid(HebrewCalendar)) {
3292
0
                HebrewCalendar *hc = (HebrewCalendar*)&cal;
3293
0
                if (cal.isSet(UCAL_YEAR)) {
3294
0
                   UErrorCode monthStatus = U_ZERO_ERROR;
3295
0
                   if (!hc->isLeapYear(hc->get(UCAL_YEAR, monthStatus)) && value >= 6) {
3296
0
                       cal.set(UCAL_MONTH, value);
3297
0
                   } else {
3298
0
                       cal.set(UCAL_MONTH, value - 1);
3299
0
                   }
3300
0
                } else {
3301
0
                    saveHebrewMonth = value;
3302
0
                }
3303
0
            } else {
3304
                // Don't want to parse the month if it is a string
3305
                // while pattern uses numeric style: M/MM, L/LL
3306
                // [We computed 'value' above.]
3307
0
                cal.set(UCAL_MONTH, value - 1);
3308
0
            }
3309
0
            return pos.getIndex();
3310
0
        } else {
3311
            // count >= 3 // i.e., MMM/MMMM, LLL/LLLL
3312
            // Want to be able to parse both short and long forms.
3313
            // Try count == 4 first:
3314
0
            UnicodeString * wideMonthPat = nullptr;
3315
0
            UnicodeString * shortMonthPat = nullptr;
3316
0
            if (fSymbols->fLeapMonthPatterns != nullptr && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) {
3317
0
                if (patternCharIndex==UDAT_MONTH_FIELD) {
3318
0
                    wideMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatWide];
3319
0
                    shortMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatAbbrev];
3320
0
                } else {
3321
0
                    wideMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneWide];
3322
0
                    shortMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev];
3323
0
                }
3324
0
            }
3325
0
            int32_t newStart = 0;
3326
0
            if (patternCharIndex==UDAT_MONTH_FIELD) {
3327
0
                if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) && count>=3 && count <=4 &&
3328
0
                        fSymbols->fLeapMonthPatterns==nullptr && fSymbols->fMonthsCount==fSymbols->fShortMonthsCount) {
3329
                    // single function to check both wide and short, an experiment
3330
0
                    newStart = matchAlphaMonthStrings(text, start, fSymbols->fMonths, fSymbols->fShortMonths, fSymbols->fMonthsCount, cal); // try MMMM,MMM
3331
0
                    if (newStart > 0) {
3332
0
                        return newStart;
3333
0
                    }
3334
0
                }
3335
0
                if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3336
0
                    newStart = matchString(text, start, UCAL_MONTH, fSymbols->fMonths, fSymbols->fMonthsCount, wideMonthPat, cal); // try MMMM
3337
0
                    if (newStart > 0) {
3338
0
                        return newStart;
3339
0
                    }
3340
0
                }
3341
0
                if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3342
0
                    newStart = matchString(text, start, UCAL_MONTH, fSymbols->fShortMonths, fSymbols->fShortMonthsCount, shortMonthPat, cal); // try MMM
3343
0
                }
3344
0
            } else {
3345
0
                if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) && count>=3 && count <=4 &&
3346
0
                        fSymbols->fLeapMonthPatterns==nullptr && fSymbols->fStandaloneMonthsCount==fSymbols->fStandaloneShortMonthsCount) {
3347
                    // single function to check both wide and short, an experiment
3348
0
                    newStart = matchAlphaMonthStrings(text, start, fSymbols->fStandaloneMonths, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneMonthsCount, cal); // try MMMM,MMM
3349
0
                    if (newStart > 0) {
3350
0
                        return newStart;
3351
0
                    }
3352
0
                }
3353
0
                if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3354
0
                    newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount, wideMonthPat, cal); // try LLLL
3355
0
                    if (newStart > 0) {
3356
0
                        return newStart;
3357
0
                    }
3358
0
                }
3359
0
                if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3360
0
                    newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount, shortMonthPat, cal); // try LLL
3361
0
                }
3362
0
            }
3363
0
            if (newStart > 0 || !getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))  // currently we do not try to parse MMMMM/LLLLL: #8860
3364
0
                return newStart;
3365
            // else we allowing parsing as number, below
3366
0
        }
3367
0
        break;
3368
3369
0
    case UDAT_HOUR_OF_DAY1_FIELD:
3370
        // [We computed 'value' above.]
3371
0
        if (value == cal.getMaximum(UCAL_HOUR_OF_DAY) + 1)
3372
0
            value = 0;
3373
3374
        // fall through to set field
3375
0
        U_FALLTHROUGH;
3376
0
    case UDAT_HOUR_OF_DAY0_FIELD:
3377
0
        cal.set(UCAL_HOUR_OF_DAY, value);
3378
0
        return pos.getIndex();
3379
3380
0
    case UDAT_FRACTIONAL_SECOND_FIELD:
3381
        // Fractional seconds left-justify
3382
0
        i = countDigits(text, start, pos.getIndex());
3383
0
        if (i < 3) {
3384
0
            while (i < 3) {
3385
0
                value *= 10;
3386
0
                i++;
3387
0
            }
3388
0
        } else {
3389
0
            int32_t a = 1;
3390
0
            while (i > 3) {
3391
0
                a *= 10;
3392
0
                i--;
3393
0
            }
3394
0
            value /= a;
3395
0
        }
3396
0
        cal.set(UCAL_MILLISECOND, value);
3397
0
        return pos.getIndex();
3398
3399
0
    case UDAT_DOW_LOCAL_FIELD:
3400
0
        if (gotNumber) // i.e., e or ee
3401
0
        {
3402
            // [We computed 'value' above.]
3403
0
            cal.set(UCAL_DOW_LOCAL, value);
3404
0
            return pos.getIndex();
3405
0
        }
3406
        // else for eee-eeeee fall through to handling of EEE-EEEEE
3407
        // fall through, do not break here
3408
0
        U_FALLTHROUGH;
3409
0
    case UDAT_DAY_OF_WEEK_FIELD:
3410
0
        {
3411
            // Want to be able to parse both short and long forms.
3412
            // Try count == 4 (EEEE) wide first:
3413
0
            int32_t newStart = 0;
3414
0
            if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3415
0
                if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3416
0
                                          fSymbols->fWeekdays, fSymbols->fWeekdaysCount, nullptr, cal)) > 0)
3417
0
                    return newStart;
3418
0
            }
3419
            // EEEE wide failed, now try EEE abbreviated
3420
0
            if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3421
0
                if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3422
0
                                       fSymbols->fShortWeekdays, fSymbols->fShortWeekdaysCount, nullptr, cal)) > 0)
3423
0
                    return newStart;
3424
0
            }
3425
            // EEE abbreviated failed, now try EEEEEE short
3426
0
            if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 6) {
3427
0
                if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3428
0
                                       fSymbols->fShorterWeekdays, fSymbols->fShorterWeekdaysCount, nullptr, cal)) > 0)
3429
0
                    return newStart;
3430
0
            }
3431
            // EEEEEE short failed, now try EEEEE narrow
3432
0
            if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
3433
0
                if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3434
0
                                       fSymbols->fNarrowWeekdays, fSymbols->fNarrowWeekdaysCount, nullptr, cal)) > 0)
3435
0
                    return newStart;
3436
0
            }
3437
0
            if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status) || patternCharIndex == UDAT_DAY_OF_WEEK_FIELD)
3438
0
                return newStart;
3439
            // else we allowing parsing as number, below
3440
0
        }
3441
0
        break;
3442
3443
0
    case UDAT_STANDALONE_DAY_FIELD:
3444
0
        {
3445
0
            if (gotNumber) // c or cc
3446
0
            {
3447
                // [We computed 'value' above.]
3448
0
                cal.set(UCAL_DOW_LOCAL, value);
3449
0
                return pos.getIndex();
3450
0
            }
3451
            // Want to be able to parse both short and long forms.
3452
            // Try count == 4 (cccc) first:
3453
0
            int32_t newStart = 0;
3454
0
            if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3455
0
                if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3456
0
                                      fSymbols->fStandaloneWeekdays, fSymbols->fStandaloneWeekdaysCount, nullptr, cal)) > 0)
3457
0
                    return newStart;
3458
0
            }
3459
0
            if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3460
0
                if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3461
0
                                          fSymbols->fStandaloneShortWeekdays, fSymbols->fStandaloneShortWeekdaysCount, nullptr, cal)) > 0)
3462
0
                    return newStart;
3463
0
            }
3464
0
            if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 6) {
3465
0
                if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3466
0
                                          fSymbols->fStandaloneShorterWeekdays, fSymbols->fStandaloneShorterWeekdaysCount, nullptr, cal)) > 0)
3467
0
                    return newStart;
3468
0
            }
3469
0
            if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))
3470
0
                return newStart;
3471
            // else we allowing parsing as number, below
3472
0
        }
3473
0
        break;
3474
3475
0
    case UDAT_AM_PM_FIELD:
3476
0
        {
3477
            // optionally try both wide/abbrev and narrow forms
3478
0
            int32_t newStart = 0;
3479
            // try wide
3480
0
            if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4 ) {
3481
0
                if ((newStart = matchString(text, start, UCAL_AM_PM, fSymbols->fWideAmPms, fSymbols->fWideAmPmsCount, nullptr, cal)) > 0) {
3482
0
                    return newStart;
3483
0
                }
3484
0
            }
3485
            // try abbreviated
3486
0
            if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count <= 3 ) {
3487
0
                if ((newStart = matchString(text, start, UCAL_AM_PM, fSymbols->fAmPms, fSymbols->fAmPmsCount, nullptr, cal)) > 0) {
3488
0
                    return newStart;
3489
0
                }
3490
0
            }
3491
            // try narrow
3492
0
            if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count >= 5 ) {
3493
0
                if ((newStart = matchString(text, start, UCAL_AM_PM, fSymbols->fNarrowAmPms, fSymbols->fNarrowAmPmsCount, nullptr, cal)) > 0) {
3494
0
                    return newStart;
3495
0
                }
3496
0
            }
3497
            // no matches for given options
3498
0
            return -start;
3499
0
        }
3500
3501
0
    case UDAT_HOUR1_FIELD:
3502
        // [We computed 'value' above.]
3503
0
        if (value == cal.getLeastMaximum(UCAL_HOUR)+1)
3504
0
            value = 0;
3505
3506
        // fall through to set field
3507
0
        U_FALLTHROUGH;
3508
0
    case UDAT_HOUR0_FIELD:
3509
0
        cal.set(UCAL_HOUR, value);
3510
0
        return pos.getIndex();
3511
3512
0
    case UDAT_QUARTER_FIELD:
3513
0
        if (gotNumber) // i.e., Q or QQ.
3514
0
        {
3515
            // Don't want to parse the month if it is a string
3516
            // while pattern uses numeric style: Q or QQ.
3517
            // [We computed 'value' above.]
3518
0
            cal.set(UCAL_MONTH, (value - 1) * 3);
3519
0
            return pos.getIndex();
3520
0
        } else {
3521
            // count >= 3 // i.e., QQQ or QQQQ
3522
            // Want to be able to parse short, long, and narrow forms.
3523
            // Try count == 4 first:
3524
0
            int32_t newStart = 0;
3525
3526
0
            if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3527
0
                if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
3528
0
                                      fSymbols->fQuarters, fSymbols->fQuartersCount, cal)) > 0)
3529
0
                    return newStart;
3530
0
            }
3531
0
            if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3532
0
                if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
3533
0
                                          fSymbols->fShortQuarters, fSymbols->fShortQuartersCount, cal)) > 0)
3534
0
                    return newStart;
3535
0
            }
3536
0
            if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
3537
0
                if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
3538
0
                                      fSymbols->fNarrowQuarters, fSymbols->fNarrowQuartersCount, cal)) > 0)
3539
0
                    return newStart;
3540
0
            }
3541
0
            if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))
3542
0
                return newStart;
3543
            // else we allowing parsing as number, below
3544
0
            if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status))
3545
0
                return -start;
3546
0
        }
3547
0
        break;
3548
3549
0
    case UDAT_STANDALONE_QUARTER_FIELD:
3550
0
        if (gotNumber) // i.e., q or qq.
3551
0
        {
3552
            // Don't want to parse the month if it is a string
3553
            // while pattern uses numeric style: q or q.
3554
            // [We computed 'value' above.]
3555
0
            cal.set(UCAL_MONTH, (value - 1) * 3);
3556
0
            return pos.getIndex();
3557
0
        } else {
3558
            // count >= 3 // i.e., qqq or qqqq
3559
            // Want to be able to parse both short and long forms.
3560
            // Try count == 4 first:
3561
0
            int32_t newStart = 0;
3562
3563
0
            if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3564
0
                if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
3565
0
                                      fSymbols->fStandaloneQuarters, fSymbols->fStandaloneQuartersCount, cal)) > 0)
3566
0
                    return newStart;
3567
0
            }
3568
0
            if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3569
0
                if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
3570
0
                                          fSymbols->fStandaloneShortQuarters, fSymbols->fStandaloneShortQuartersCount, cal)) > 0)
3571
0
                    return newStart;
3572
0
            }
3573
0
            if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
3574
0
                if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
3575
0
                                          fSymbols->fStandaloneNarrowQuarters, fSymbols->fStandaloneNarrowQuartersCount, cal)) > 0)
3576
0
                    return newStart;
3577
0
            }
3578
0
            if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))
3579
0
                return newStart;
3580
            // else we allowing parsing as number, below
3581
0
            if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status))
3582
0
                return -start;
3583
0
        }
3584
0
        break;
3585
3586
0
    case UDAT_TIMEZONE_FIELD: // 'z'
3587
0
        {
3588
0
            UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_SPECIFIC_SHORT : UTZFMT_STYLE_SPECIFIC_LONG;
3589
0
            const TimeZoneFormat *tzfmt = tzFormat(status);
3590
0
            if (U_SUCCESS(status)) {
3591
0
                TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
3592
0
                if (tz != nullptr) {
3593
0
                    cal.adoptTimeZone(tz);
3594
0
                    return pos.getIndex();
3595
0
                }
3596
0
            }
3597
0
            return -start;
3598
0
    }
3599
0
        break;
3600
0
    case UDAT_TIMEZONE_RFC_FIELD: // 'Z'
3601
0
        {
3602
0
            UTimeZoneFormatStyle style = (count < 4) ?
3603
0
                UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL : ((count == 5) ? UTZFMT_STYLE_ISO_EXTENDED_FULL: UTZFMT_STYLE_LOCALIZED_GMT);
3604
0
            const TimeZoneFormat *tzfmt = tzFormat(status);
3605
0
            if (U_SUCCESS(status)) {
3606
0
                TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
3607
0
                if (tz != nullptr) {
3608
0
                    cal.adoptTimeZone(tz);
3609
0
                    return pos.getIndex();
3610
0
                }
3611
0
            }
3612
0
            return -start;
3613
0
        }
3614
0
    case UDAT_TIMEZONE_GENERIC_FIELD: // 'v'
3615
0
        {
3616
0
            UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_GENERIC_SHORT : UTZFMT_STYLE_GENERIC_LONG;
3617
0
            const TimeZoneFormat *tzfmt = tzFormat(status);
3618
0
            if (U_SUCCESS(status)) {
3619
0
                TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
3620
0
                if (tz != nullptr) {
3621
0
                    cal.adoptTimeZone(tz);
3622
0
                    return pos.getIndex();
3623
0
                }
3624
0
            }
3625
0
            return -start;
3626
0
        }
3627
0
    case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V'
3628
0
        {
3629
0
            UTimeZoneFormatStyle style;
3630
0
            switch (count) {
3631
0
            case 1:
3632
0
                style = UTZFMT_STYLE_ZONE_ID_SHORT;
3633
0
                break;
3634
0
            case 2:
3635
0
                style = UTZFMT_STYLE_ZONE_ID;
3636
0
                break;
3637
0
            case 3:
3638
0
                style = UTZFMT_STYLE_EXEMPLAR_LOCATION;
3639
0
                break;
3640
0
            default:
3641
0
                style = UTZFMT_STYLE_GENERIC_LOCATION;
3642
0
                break;
3643
0
            }
3644
0
            const TimeZoneFormat *tzfmt = tzFormat(status);
3645
0
            if (U_SUCCESS(status)) {
3646
0
                TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
3647
0
                if (tz != nullptr) {
3648
0
                    cal.adoptTimeZone(tz);
3649
0
                    return pos.getIndex();
3650
0
                }
3651
0
            }
3652
0
            return -start;
3653
0
        }
3654
0
    case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: // 'O'
3655
0
        {
3656
0
            UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_LOCALIZED_GMT_SHORT : UTZFMT_STYLE_LOCALIZED_GMT;
3657
0
            const TimeZoneFormat *tzfmt = tzFormat(status);
3658
0
            if (U_SUCCESS(status)) {
3659
0
                TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
3660
0
                if (tz != nullptr) {
3661
0
                    cal.adoptTimeZone(tz);
3662
0
                    return pos.getIndex();
3663
0
                }
3664
0
            }
3665
0
            return -start;
3666
0
        }
3667
0
    case UDAT_TIMEZONE_ISO_FIELD: // 'X'
3668
0
        {
3669
0
            UTimeZoneFormatStyle style;
3670
0
            switch (count) {
3671
0
            case 1:
3672
0
                style = UTZFMT_STYLE_ISO_BASIC_SHORT;
3673
0
                break;
3674
0
            case 2:
3675
0
                style = UTZFMT_STYLE_ISO_BASIC_FIXED;
3676
0
                break;
3677
0
            case 3:
3678
0
                style = UTZFMT_STYLE_ISO_EXTENDED_FIXED;
3679
0
                break;
3680
0
            case 4:
3681
0
                style = UTZFMT_STYLE_ISO_BASIC_FULL;
3682
0
                break;
3683
0
            default:
3684
0
                style = UTZFMT_STYLE_ISO_EXTENDED_FULL;
3685
0
                break;
3686
0
            }
3687
0
            const TimeZoneFormat *tzfmt = tzFormat(status);
3688
0
            if (U_SUCCESS(status)) {
3689
0
                TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
3690
0
                if (tz != nullptr) {
3691
0
                    cal.adoptTimeZone(tz);
3692
0
                    return pos.getIndex();
3693
0
                }
3694
0
            }
3695
0
            return -start;
3696
0
        }
3697
0
    case UDAT_TIMEZONE_ISO_LOCAL_FIELD: // 'x'
3698
0
        {
3699
0
            UTimeZoneFormatStyle style;
3700
0
            switch (count) {
3701
0
            case 1:
3702
0
                style = UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT;
3703
0
                break;
3704
0
            case 2:
3705
0
                style = UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED;
3706
0
                break;
3707
0
            case 3:
3708
0
                style = UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED;
3709
0
                break;
3710
0
            case 4:
3711
0
                style = UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL;
3712
0
                break;
3713
0
            default:
3714
0
                style = UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL;
3715
0
                break;
3716
0
            }
3717
0
            const TimeZoneFormat *tzfmt = tzFormat(status);
3718
0
            if (U_SUCCESS(status)) {
3719
0
                TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
3720
0
                if (tz != nullptr) {
3721
0
                    cal.adoptTimeZone(tz);
3722
0
                    return pos.getIndex();
3723
0
                }
3724
0
            }
3725
0
            return -start;
3726
0
        }
3727
    // currently no pattern character is defined for UDAT_TIME_SEPARATOR_FIELD
3728
    // so we should not get here. Leave support in for future definition.
3729
0
    case UDAT_TIME_SEPARATOR_FIELD:
3730
0
        {
3731
0
            static const char16_t def_sep = DateFormatSymbols::DEFAULT_TIME_SEPARATOR;
3732
0
            static const char16_t alt_sep = DateFormatSymbols::ALTERNATE_TIME_SEPARATOR;
3733
3734
            // Try matching a time separator.
3735
0
            int32_t count_sep = 1;
3736
0
            UnicodeString data[3];
3737
0
            fSymbols->getTimeSeparatorString(data[0]);
3738
3739
            // Add the default, if different from the locale.
3740
0
            if (data[0].compare(&def_sep, 1) != 0) {
3741
0
                data[count_sep++].setTo(def_sep);
3742
0
            }
3743
3744
            // If lenient, add also the alternate, if different from the locale.
3745
0
            if (isLenient() && data[0].compare(&alt_sep, 1) != 0) {
3746
0
                data[count_sep++].setTo(alt_sep);
3747
0
            }
3748
3749
0
            return matchString(text, start, UCAL_FIELD_COUNT /* => nothing to set */, data, count_sep, nullptr, cal);
3750
0
        }
3751
3752
0
    case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
3753
0
    {
3754
0
        U_ASSERT(dayPeriod != nullptr);
3755
0
        int32_t ampmStart = subParse(text, start, 0x61, count,
3756
0
                           obeyCount, allowNegative, ambiguousYear, saveHebrewMonth, cal,
3757
0
                           patLoc, numericLeapMonthFormatter, tzTimeType);
3758
3759
0
        if (ampmStart > 0) {
3760
0
            return ampmStart;
3761
0
        } else {
3762
0
            int32_t newStart = 0;
3763
3764
            // Only match the first two strings from the day period strings array.
3765
0
            if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3766
0
                if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fAbbreviatedDayPeriods,
3767
0
                                                        2, *dayPeriod)) > 0) {
3768
0
                    return newStart;
3769
0
                }
3770
0
            }
3771
0
            if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
3772
0
                if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fNarrowDayPeriods,
3773
0
                                                        2, *dayPeriod)) > 0) {
3774
0
                    return newStart;
3775
0
                }
3776
0
            }
3777
            // count == 4, but allow other counts
3778
0
            if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status)) {
3779
0
                if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fWideDayPeriods,
3780
0
                                                        2, *dayPeriod)) > 0) {
3781
0
                    return newStart;
3782
0
                }
3783
0
            }
3784
3785
0
            return -start;
3786
0
        }
3787
0
    }
3788
3789
0
    case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
3790
0
    {
3791
0
        U_ASSERT(dayPeriod != nullptr);
3792
0
        int32_t newStart = 0;
3793
3794
0
        if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3795
0
            if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fAbbreviatedDayPeriods,
3796
0
                                fSymbols->fAbbreviatedDayPeriodsCount, *dayPeriod)) > 0) {
3797
0
                return newStart;
3798
0
            }
3799
0
        }
3800
0
        if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
3801
0
            if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fNarrowDayPeriods,
3802
0
                                fSymbols->fNarrowDayPeriodsCount, *dayPeriod)) > 0) {
3803
0
                return newStart;
3804
0
            }
3805
0
        }
3806
0
        if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3807
0
            if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fWideDayPeriods,
3808
0
                                fSymbols->fWideDayPeriodsCount, *dayPeriod)) > 0) {
3809
0
                return newStart;
3810
0
            }
3811
0
        }
3812
3813
0
        return -start;
3814
0
    }
3815
3816
0
    default:
3817
        // Handle "generic" fields
3818
        // this is now handled below, outside the switch block
3819
0
        break;
3820
0
    }
3821
    // Handle "generic" fields:
3822
    // switch default case now handled here (outside switch block) to allow
3823
    // parsing of some string fields as digits for lenient case
3824
3825
0
    int32_t parseStart = pos.getIndex();
3826
0
    const UnicodeString* src;
3827
0
    if (obeyCount) {
3828
0
        if ((start+count) > text.length()) {
3829
0
            return -start;
3830
0
        }
3831
0
        text.extractBetween(0, start + count, temp);
3832
0
        src = &temp;
3833
0
    } else {
3834
0
        src = &text;
3835
0
    }
3836
0
    parseInt(*src, number, pos, allowNegative,currentNumberFormat);
3837
0
    if (obeyCount && !isLenient() && pos.getIndex() < start + count) {
3838
0
        return -start;
3839
0
    }
3840
0
    if (pos.getIndex() != parseStart) {
3841
0
        int32_t val = number.getLong();
3842
3843
        // Don't need suffix processing here (as in number processing at the beginning of the function);
3844
        // the new fields being handled as numeric values (month, weekdays, quarters) should not have suffixes.
3845
3846
0
        if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) {
3847
            // Check the range of the value
3848
0
            int32_t bias = gFieldRangeBias[patternCharIndex];
3849
0
            if (bias >= 0 && (val > cal.getMaximum(field) + bias || val < cal.getMinimum(field) + bias)) {
3850
0
                return -start;
3851
0
            }
3852
0
        }
3853
3854
        // For the following, need to repeat some of the "if (gotNumber)" code above:
3855
        // UDAT_[STANDALONE_]MONTH_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_STANDALONE_DAY_FIELD,
3856
        // UDAT_[STANDALONE_]QUARTER_FIELD
3857
0
        switch (patternCharIndex) {
3858
0
        case UDAT_MONTH_FIELD:
3859
            // See notes under UDAT_MONTH_FIELD case above
3860
0
            if (typeid(cal) == typeid(HebrewCalendar)) {
3861
0
                HebrewCalendar *hc = (HebrewCalendar*)&cal;
3862
0
                if (cal.isSet(UCAL_YEAR)) {
3863
0
                   UErrorCode monthStatus = U_ZERO_ERROR;
3864
0
                   if (!hc->isLeapYear(hc->get(UCAL_YEAR, monthStatus)) && val >= 6) {
3865
0
                       cal.set(UCAL_MONTH, val);
3866
0
                   } else {
3867
0
                       cal.set(UCAL_MONTH, val - 1);
3868
0
                   }
3869
0
                } else {
3870
0
                    saveHebrewMonth = val;
3871
0
                }
3872
0
            } else {
3873
0
                cal.set(UCAL_MONTH, val - 1);
3874
0
            }
3875
0
            break;
3876
0
        case UDAT_STANDALONE_MONTH_FIELD:
3877
0
            cal.set(UCAL_MONTH, val - 1);
3878
0
            break;
3879
0
        case UDAT_DOW_LOCAL_FIELD:
3880
0
        case UDAT_STANDALONE_DAY_FIELD:
3881
0
            cal.set(UCAL_DOW_LOCAL, val);
3882
0
            break;
3883
0
        case UDAT_QUARTER_FIELD:
3884
0
        case UDAT_STANDALONE_QUARTER_FIELD:
3885
0
             cal.set(UCAL_MONTH, (val - 1) * 3);
3886
0
             break;
3887
0
        case UDAT_RELATED_YEAR_FIELD:
3888
0
            cal.setRelatedYear(val);
3889
0
            break;
3890
0
        default:
3891
0
            cal.set(field, val);
3892
0
            break;
3893
0
        }
3894
0
        return pos.getIndex();
3895
0
    }
3896
0
    return -start;
3897
0
}
3898
3899
/**
3900
 * Parse an integer using fNumberFormat.  This method is semantically
3901
 * const, but actually may modify fNumberFormat.
3902
 */
3903
void SimpleDateFormat::parseInt(const UnicodeString& text,
3904
                                Formattable& number,
3905
                                ParsePosition& pos,
3906
                                UBool allowNegative,
3907
0
                                const NumberFormat *fmt) const {
3908
0
    parseInt(text, number, -1, pos, allowNegative,fmt);
3909
0
}
3910
3911
/**
3912
 * Parse an integer using fNumberFormat up to maxDigits.
3913
 */
3914
void SimpleDateFormat::parseInt(const UnicodeString& text,
3915
                                Formattable& number,
3916
                                int32_t maxDigits,
3917
                                ParsePosition& pos,
3918
                                UBool allowNegative,
3919
0
                                const NumberFormat *fmt) const {
3920
0
    UnicodeString oldPrefix;
3921
0
    const auto* fmtAsDF = dynamic_cast<const DecimalFormat*>(fmt);
3922
0
    LocalPointer<DecimalFormat> df;
3923
0
    if (!allowNegative && fmtAsDF != nullptr) {
3924
0
        df.adoptInstead(fmtAsDF->clone());
3925
0
        if (df.isNull()) {
3926
            // Memory allocation error
3927
0
            return;
3928
0
        }
3929
0
        df->setNegativePrefix(UnicodeString(true, SUPPRESS_NEGATIVE_PREFIX, -1));
3930
0
        fmt = df.getAlias();
3931
0
    }
3932
0
    int32_t oldPos = pos.getIndex();
3933
0
    fmt->parse(text, number, pos);
3934
3935
0
    if (maxDigits > 0) {
3936
        // adjust the result to fit into
3937
        // the maxDigits and move the position back
3938
0
        int32_t nDigits = pos.getIndex() - oldPos;
3939
0
        if (nDigits > maxDigits) {
3940
0
            int32_t val = number.getLong();
3941
0
            nDigits -= maxDigits;
3942
0
            while (nDigits > 0) {
3943
0
                val /= 10;
3944
0
                nDigits--;
3945
0
            }
3946
0
            pos.setIndex(oldPos + maxDigits);
3947
0
            number.setLong(val);
3948
0
        }
3949
0
    }
3950
0
}
3951
3952
0
int32_t SimpleDateFormat::countDigits(const UnicodeString& text, int32_t start, int32_t end) const {
3953
0
    int32_t numDigits = 0;
3954
0
    int32_t idx = start;
3955
0
    while (idx < end) {
3956
0
        UChar32 cp = text.char32At(idx);
3957
0
        if (u_isdigit(cp)) {
3958
0
            numDigits++;
3959
0
        }
3960
0
        idx += U16_LENGTH(cp);
3961
0
    }
3962
0
    return numDigits;
3963
0
}
3964
3965
//----------------------------------------------------------------------
3966
3967
void SimpleDateFormat::translatePattern(const UnicodeString& originalPattern,
3968
                                        UnicodeString& translatedPattern,
3969
                                        const UnicodeString& from,
3970
                                        const UnicodeString& to,
3971
                                        UErrorCode& status)
3972
0
{
3973
    // run through the pattern and convert any pattern symbols from the version
3974
    // in "from" to the corresponding character in "to".  This code takes
3975
    // quoted strings into account (it doesn't try to translate them), and it signals
3976
    // an error if a particular "pattern character" doesn't appear in "from".
3977
    // Depending on the values of "from" and "to" this can convert from generic
3978
    // to localized patterns or localized to generic.
3979
0
    if (U_FAILURE(status)) {
3980
0
        return;
3981
0
    }
3982
3983
0
    translatedPattern.remove();
3984
0
    UBool inQuote = false;
3985
0
    for (int32_t i = 0; i < originalPattern.length(); ++i) {
3986
0
        char16_t c = originalPattern[i];
3987
0
        if (inQuote) {
3988
0
            if (c == QUOTE) {
3989
0
                inQuote = false;
3990
0
            }
3991
0
        } else {
3992
0
            if (c == QUOTE) {
3993
0
                inQuote = true;
3994
0
            } else if (isSyntaxChar(c)) {
3995
0
                int32_t ci = from.indexOf(c);
3996
0
                if (ci == -1) {
3997
0
                    status = U_INVALID_FORMAT_ERROR;
3998
0
                    return;
3999
0
                }
4000
0
                c = to[ci];
4001
0
            }
4002
0
        }
4003
0
        translatedPattern += c;
4004
0
    }
4005
0
    if (inQuote) {
4006
0
        status = U_INVALID_FORMAT_ERROR;
4007
0
        return;
4008
0
    }
4009
0
}
4010
4011
//----------------------------------------------------------------------
4012
4013
UnicodeString&
4014
SimpleDateFormat::toPattern(UnicodeString& result) const
4015
9.33k
{
4016
9.33k
    result = fPattern;
4017
9.33k
    return result;
4018
9.33k
}
4019
4020
//----------------------------------------------------------------------
4021
4022
UnicodeString&
4023
SimpleDateFormat::toLocalizedPattern(UnicodeString& result,
4024
                                     UErrorCode& status) const
4025
0
{
4026
0
    translatePattern(fPattern, result,
4027
0
                     UnicodeString(DateFormatSymbols::getPatternUChars()),
4028
0
                     fSymbols->fLocalPatternChars, status);
4029
0
    return result;
4030
0
}
4031
4032
//----------------------------------------------------------------------
4033
4034
void
4035
SimpleDateFormat::applyPattern(const UnicodeString& pattern)
4036
18.4k
{
4037
18.4k
    fPattern = pattern;
4038
18.4k
    parsePattern();
4039
4040
    // Hack to update use of Gannen year numbering for ja@calendar=japanese -
4041
    // use only if format is non-numeric (includes 年) and no other fDateOverride.
4042
18.4k
    if (fCalendar != nullptr && typeid(*fCalendar) == typeid(JapaneseCalendar) &&
4043
18.4k
            uprv_strcmp(fLocale.getLanguage(),"ja") == 0) {
4044
0
        if (fDateOverride==UnicodeString(u"y=jpanyear") && !fHasHanYearChar) {
4045
            // Gannen numbering is set but new pattern should not use it, unset;
4046
            // use procedure from adoptNumberFormat to clear overrides
4047
0
            if (fSharedNumberFormatters) {
4048
0
                freeSharedNumberFormatters(fSharedNumberFormatters);
4049
0
                fSharedNumberFormatters = nullptr;
4050
0
            }
4051
0
            fDateOverride.setToBogus(); // record status
4052
0
        } else if (fDateOverride.isBogus() && fHasHanYearChar) {
4053
            // No current override (=> no Gannen numbering) but new pattern needs it;
4054
            // use procedures from initNUmberFormatters / adoptNumberFormat
4055
0
            umtx_lock(&LOCK);
4056
0
            if (fSharedNumberFormatters == nullptr) {
4057
0
                fSharedNumberFormatters = allocSharedNumberFormatters();
4058
0
            }
4059
0
            umtx_unlock(&LOCK);
4060
0
            if (fSharedNumberFormatters != nullptr) {
4061
0
                Locale ovrLoc(fLocale.getLanguage(),fLocale.getCountry(),fLocale.getVariant(),"numbers=jpanyear");
4062
0
                UErrorCode status = U_ZERO_ERROR;
4063
0
                const SharedNumberFormat *snf = createSharedNumberFormat(ovrLoc, status);
4064
0
                if (U_SUCCESS(status)) {
4065
                    // Now that we have an appropriate number formatter, fill in the
4066
                    // appropriate slot in the number formatters table.
4067
0
                    UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(u'y');
4068
0
                    SharedObject::copyPtr(snf, fSharedNumberFormatters[patternCharIndex]);
4069
0
                    snf->deleteIfZeroRefCount();
4070
0
                    fDateOverride.setTo(u"y=jpanyear", -1); // record status
4071
0
                }
4072
0
            }
4073
0
        }
4074
0
    }
4075
18.4k
}
4076
4077
//----------------------------------------------------------------------
4078
4079
void
4080
SimpleDateFormat::applyLocalizedPattern(const UnicodeString& pattern,
4081
                                        UErrorCode &status)
4082
0
{
4083
0
    translatePattern(pattern, fPattern,
4084
0
                     fSymbols->fLocalPatternChars,
4085
0
                     UnicodeString(DateFormatSymbols::getPatternUChars()), status);
4086
0
}
4087
4088
//----------------------------------------------------------------------
4089
4090
const DateFormatSymbols*
4091
SimpleDateFormat::getDateFormatSymbols() const
4092
0
{
4093
0
    return fSymbols;
4094
0
}
4095
4096
//----------------------------------------------------------------------
4097
4098
void
4099
SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols* newFormatSymbols)
4100
0
{
4101
0
    delete fSymbols;
4102
0
    fSymbols = newFormatSymbols;
4103
0
}
4104
4105
//----------------------------------------------------------------------
4106
void
4107
SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols& newFormatSymbols)
4108
0
{
4109
0
    delete fSymbols;
4110
0
    fSymbols = new DateFormatSymbols(newFormatSymbols);
4111
0
}
4112
4113
//----------------------------------------------------------------------
4114
const TimeZoneFormat*
4115
0
SimpleDateFormat::getTimeZoneFormat() const {
4116
    // TimeZoneFormat initialization might fail when out of memory.
4117
    // If we always initialize TimeZoneFormat instance, we can return
4118
    // such status there. For now, this implementation lazily instantiates
4119
    // a TimeZoneFormat for performance optimization reasons, but cannot
4120
    // propagate such error (probably just out of memory case) to the caller.
4121
0
    UErrorCode status = U_ZERO_ERROR;
4122
0
    return (const TimeZoneFormat*)tzFormat(status);
4123
0
}
4124
4125
//----------------------------------------------------------------------
4126
void
4127
SimpleDateFormat::adoptTimeZoneFormat(TimeZoneFormat* timeZoneFormatToAdopt)
4128
0
{
4129
0
    delete fTimeZoneFormat;
4130
0
    fTimeZoneFormat = timeZoneFormatToAdopt;
4131
0
}
4132
4133
//----------------------------------------------------------------------
4134
void
4135
SimpleDateFormat::setTimeZoneFormat(const TimeZoneFormat& newTimeZoneFormat)
4136
0
{
4137
0
    delete fTimeZoneFormat;
4138
0
    fTimeZoneFormat = new TimeZoneFormat(newTimeZoneFormat);
4139
0
}
4140
4141
//----------------------------------------------------------------------
4142
4143
4144
void SimpleDateFormat::adoptCalendar(Calendar* calendarToAdopt)
4145
0
{
4146
0
  UErrorCode status = U_ZERO_ERROR;
4147
0
  Locale calLocale(fLocale);
4148
0
  calLocale.setKeywordValue("calendar", calendarToAdopt->getType(), status);
4149
0
  DateFormatSymbols *newSymbols =
4150
0
          DateFormatSymbols::createForLocale(calLocale, status);
4151
0
  if (U_FAILURE(status)) {
4152
0
      delete calendarToAdopt;
4153
0
      return;
4154
0
  }
4155
0
  DateFormat::adoptCalendar(calendarToAdopt);
4156
0
  delete fSymbols;
4157
0
  fSymbols = newSymbols;
4158
0
  initializeDefaultCentury();  // we need a new century (possibly)
4159
0
}
4160
4161
4162
//----------------------------------------------------------------------
4163
4164
4165
// override the DateFormat implementation in order to
4166
// lazily initialize fCapitalizationBrkIter
4167
void
4168
SimpleDateFormat::setContext(UDisplayContext value, UErrorCode& status)
4169
2.58k
{
4170
2.58k
    DateFormat::setContext(value, status);
4171
2.58k
#if !UCONFIG_NO_BREAK_ITERATION
4172
2.58k
    if (U_SUCCESS(status)) {
4173
2.58k
        if ( fCapitalizationBrkIter == nullptr && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
4174
2.58k
                value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE) ) {
4175
0
            status = U_ZERO_ERROR;
4176
0
            fCapitalizationBrkIter = BreakIterator::createSentenceInstance(fLocale, status);
4177
0
            if (U_FAILURE(status)) {
4178
0
                delete fCapitalizationBrkIter;
4179
0
                fCapitalizationBrkIter = nullptr;
4180
0
            }
4181
0
        }
4182
2.58k
    }
4183
2.58k
#endif
4184
2.58k
}
4185
4186
4187
//----------------------------------------------------------------------
4188
4189
4190
UBool
4191
0
SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field) const {
4192
0
    return isFieldUnitIgnored(fPattern, field);
4193
0
}
4194
4195
4196
UBool
4197
SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern,
4198
0
                                     UCalendarDateFields field) {
4199
0
    int32_t fieldLevel = fgCalendarFieldToLevel[field];
4200
0
    int32_t level;
4201
0
    char16_t ch;
4202
0
    UBool inQuote = false;
4203
0
    char16_t prevCh = 0;
4204
0
    int32_t count = 0;
4205
4206
0
    for (int32_t i = 0; i < pattern.length(); ++i) {
4207
0
        ch = pattern[i];
4208
0
        if (ch != prevCh && count > 0) {
4209
0
            level = getLevelFromChar(prevCh);
4210
            // the larger the level, the smaller the field unit.
4211
0
            if (fieldLevel <= level) {
4212
0
                return false;
4213
0
            }
4214
0
            count = 0;
4215
0
        }
4216
0
        if (ch == QUOTE) {
4217
0
            if ((i+1) < pattern.length() && pattern[i+1] == QUOTE) {
4218
0
                ++i;
4219
0
            } else {
4220
0
                inQuote = ! inQuote;
4221
0
            }
4222
0
        }
4223
0
        else if (!inQuote && isSyntaxChar(ch)) {
4224
0
            prevCh = ch;
4225
0
            ++count;
4226
0
        }
4227
0
    }
4228
0
    if (count > 0) {
4229
        // last item
4230
0
        level = getLevelFromChar(prevCh);
4231
0
        if (fieldLevel <= level) {
4232
0
            return false;
4233
0
        }
4234
0
    }
4235
0
    return true;
4236
0
}
4237
4238
//----------------------------------------------------------------------
4239
4240
const Locale&
4241
604
SimpleDateFormat::getSmpFmtLocale() const {
4242
604
    return fLocale;
4243
604
}
4244
4245
//----------------------------------------------------------------------
4246
4247
int32_t
4248
SimpleDateFormat::checkIntSuffix(const UnicodeString& text, int32_t start,
4249
0
                                 int32_t patLoc, UBool isNegative) const {
4250
    // local variables
4251
0
    UnicodeString suf;
4252
0
    int32_t patternMatch;
4253
0
    int32_t textPreMatch;
4254
0
    int32_t textPostMatch;
4255
4256
    // check that we are still in range
4257
0
    if ( (start > text.length()) ||
4258
0
         (start < 0) ||
4259
0
         (patLoc < 0) ||
4260
0
         (patLoc > fPattern.length())) {
4261
        // out of range, don't advance location in text
4262
0
        return start;
4263
0
    }
4264
4265
    // get the suffix
4266
0
    DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(fNumberFormat);
4267
0
    if (decfmt != nullptr) {
4268
0
        if (isNegative) {
4269
0
            suf = decfmt->getNegativeSuffix(suf);
4270
0
        }
4271
0
        else {
4272
0
            suf = decfmt->getPositiveSuffix(suf);
4273
0
        }
4274
0
    }
4275
4276
    // check for suffix
4277
0
    if (suf.length() <= 0) {
4278
0
        return start;
4279
0
    }
4280
4281
    // check suffix will be encountered in the pattern
4282
0
    patternMatch = compareSimpleAffix(suf,fPattern,patLoc);
4283
4284
    // check if a suffix will be encountered in the text
4285
0
    textPreMatch = compareSimpleAffix(suf,text,start);
4286
4287
    // check if a suffix was encountered in the text
4288
0
    textPostMatch = compareSimpleAffix(suf,text,start-suf.length());
4289
4290
    // check for suffix match
4291
0
    if ((textPreMatch >= 0) && (patternMatch >= 0) && (textPreMatch == patternMatch)) {
4292
0
        return start;
4293
0
    }
4294
0
    else if ((textPostMatch >= 0) && (patternMatch >= 0) && (textPostMatch == patternMatch)) {
4295
0
        return  start - suf.length();
4296
0
    }
4297
4298
    // should not get here
4299
0
    return start;
4300
0
}
4301
4302
//----------------------------------------------------------------------
4303
4304
int32_t
4305
SimpleDateFormat::compareSimpleAffix(const UnicodeString& affix,
4306
                   const UnicodeString& input,
4307
0
                   int32_t pos) const {
4308
0
    int32_t start = pos;
4309
0
    for (int32_t i=0; i<affix.length(); ) {
4310
0
        UChar32 c = affix.char32At(i);
4311
0
        int32_t len = U16_LENGTH(c);
4312
0
        if (PatternProps::isWhiteSpace(c)) {
4313
            // We may have a pattern like: \u200F \u0020
4314
            //        and input text like: \u200F \u0020
4315
            // Note that U+200F and U+0020 are Pattern_White_Space but only
4316
            // U+0020 is UWhiteSpace.  So we have to first do a direct
4317
            // match of the run of Pattern_White_Space in the pattern,
4318
            // then match any extra characters.
4319
0
            UBool literalMatch = false;
4320
0
            while (pos < input.length() &&
4321
0
                   input.char32At(pos) == c) {
4322
0
                literalMatch = true;
4323
0
                i += len;
4324
0
                pos += len;
4325
0
                if (i == affix.length()) {
4326
0
                    break;
4327
0
                }
4328
0
                c = affix.char32At(i);
4329
0
                len = U16_LENGTH(c);
4330
0
                if (!PatternProps::isWhiteSpace(c)) {
4331
0
                    break;
4332
0
                }
4333
0
            }
4334
4335
            // Advance over run in pattern
4336
0
            i = skipPatternWhiteSpace(affix, i);
4337
4338
            // Advance over run in input text
4339
            // Must see at least one white space char in input,
4340
            // unless we've already matched some characters literally.
4341
0
            int32_t s = pos;
4342
0
            pos = skipUWhiteSpace(input, pos);
4343
0
            if (pos == s && !literalMatch) {
4344
0
                return -1;
4345
0
            }
4346
4347
            // If we skip UWhiteSpace in the input text, we need to skip it in the pattern.
4348
            // Otherwise, the previous lines may have skipped over text (such as U+00A0) that
4349
            // is also in the affix.
4350
0
            i = skipUWhiteSpace(affix, i);
4351
0
        } else {
4352
0
            if (pos < input.length() &&
4353
0
                input.char32At(pos) == c) {
4354
0
                i += len;
4355
0
                pos += len;
4356
0
            } else {
4357
0
                return -1;
4358
0
            }
4359
0
        }
4360
0
    }
4361
0
    return pos - start;
4362
0
}
4363
4364
//----------------------------------------------------------------------
4365
4366
int32_t
4367
0
SimpleDateFormat::skipPatternWhiteSpace(const UnicodeString& text, int32_t pos) const {
4368
0
    const char16_t* s = text.getBuffer();
4369
0
    return static_cast<int32_t>(PatternProps::skipWhiteSpace(s + pos, text.length() - pos) - s);
4370
0
}
4371
4372
//----------------------------------------------------------------------
4373
4374
int32_t
4375
0
SimpleDateFormat::skipUWhiteSpace(const UnicodeString& text, int32_t pos) const {
4376
0
    while (pos < text.length()) {
4377
0
        UChar32 c = text.char32At(pos);
4378
0
        if (!u_isUWhiteSpace(c)) {
4379
0
            break;
4380
0
        }
4381
0
        pos += U16_LENGTH(c);
4382
0
    }
4383
0
    return pos;
4384
0
}
4385
4386
//----------------------------------------------------------------------
4387
4388
// Lazy TimeZoneFormat instantiation, semantically const.
4389
TimeZoneFormat *
4390
7.76k
SimpleDateFormat::tzFormat(UErrorCode &status) const {
4391
7.76k
    Mutex m(&LOCK);
4392
7.76k
    if (fTimeZoneFormat == nullptr && U_SUCCESS(status)) {
4393
7.76k
        const_cast<SimpleDateFormat *>(this)->fTimeZoneFormat =
4394
7.76k
                TimeZoneFormat::createInstance(fLocale, status);
4395
7.76k
    }
4396
7.76k
    return fTimeZoneFormat;
4397
7.76k
}
4398
4399
156k
void SimpleDateFormat::parsePattern() {
4400
156k
    fHasMinute = false;
4401
156k
    fHasSecond = false;
4402
156k
    fHasHanYearChar = false;
4403
4404
156k
    int len = fPattern.length();
4405
156k
    UBool inQuote = false;
4406
176M
    for (int32_t i = 0; i < len; ++i) {
4407
175M
        char16_t ch = fPattern[i];
4408
175M
        if (ch == QUOTE) {
4409
90.0k
            inQuote = !inQuote;
4410
90.0k
        }
4411
175M
        if (ch == 0x5E74) { // don't care whether this is inside quotes
4412
544
            fHasHanYearChar = true;
4413
544
        }
4414
175M
        if (!inQuote) {
4415
124M
            if (ch == 0x6D) {  // 0x6D == 'm'
4416
140k
                fHasMinute = true;
4417
140k
            }
4418
124M
            if (ch == 0x73) {  // 0x73 == 's'
4419
74.3k
                fHasSecond = true;
4420
74.3k
            }
4421
124M
        }
4422
175M
    }
4423
156k
}
4424
4425
U_NAMESPACE_END
4426
4427
#endif /* #if !UCONFIG_NO_FORMATTING */
4428
4429
//eof