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