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

source code of dart_sdk/third_party/icu/source/i18n/smpdtfmt.cpp