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