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