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