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