1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 1997-2016, International Business Machines Corporation and *
6 * others. All Rights Reserved. *
7 *******************************************************************************
11 * Modification History:
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
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 ********************************************************************************
29 #define ZID_KEY_MAX 128
31 #include "unicode/utypes.h"
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/utf16.h"
52 #include "unicode/vtzone.h"
53 #include "unicode/udisplaycontext.h"
54 #include "unicode/brkiter.h"
57 #include "patternprops.h"
66 #include "sharednumberformat.h"
71 #include "dayperiodrules.h"
73 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
77 // *****************************************************************************
78 // class SimpleDateFormat
79 // *****************************************************************************
84 * Last-resort string to use for "GMT" when constructing time zone strings.
86 // For time zones that have no names, use strings GMT+minutes and
87 // GMT-minutes. For instance, in France the time zone is GMT+60.
88 // Also accepted are GMT+H:MM or GMT-H:MM.
89 // Currently not being used
90 //static const UChar gGmt[] = {0x0047, 0x004D, 0x0054, 0x0000}; // "GMT"
91 //static const UChar gGmtPlus[] = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+"
92 //static const UChar gGmtMinus[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-"
93 //static const UChar gDefGmtPat[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
94 //static const UChar gDefGmtNegHmsPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */
95 //static const UChar gDefGmtNegHmPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */
96 //static const UChar gDefGmtPosHmsPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */
97 //static const UChar gDefGmtPosHmPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */
98 //static const UChar gUt[] = {0x0055, 0x0054, 0x0000}; // "UT"
99 //static const UChar gUtc[] = {0x0055, 0x0054, 0x0043, 0x0000}; // "UT"
101 typedef enum GmtPatSize {
112 // Stuff needed for numbering system overrides
114 typedef enum OvrStrType {
120 static const UDateFormatField kDateFields[] = {
124 UDAT_DAY_OF_YEAR_FIELD,
125 UDAT_DAY_OF_WEEK_IN_MONTH_FIELD,
126 UDAT_WEEK_OF_YEAR_FIELD,
127 UDAT_WEEK_OF_MONTH_FIELD,
129 UDAT_EXTENDED_YEAR_FIELD,
130 UDAT_JULIAN_DAY_FIELD,
131 UDAT_STANDALONE_DAY_FIELD,
132 UDAT_STANDALONE_MONTH_FIELD,
134 UDAT_STANDALONE_QUARTER_FIELD,
135 UDAT_YEAR_NAME_FIELD,
136 UDAT_RELATED_YEAR_FIELD };
137 static const int8_t kDateFieldsCount = 16;
139 static const UDateFormatField kTimeFields[] = {
140 UDAT_HOUR_OF_DAY1_FIELD,
141 UDAT_HOUR_OF_DAY0_FIELD,
144 UDAT_FRACTIONAL_SECOND_FIELD,
147 UDAT_MILLISECONDS_IN_DAY_FIELD,
148 UDAT_TIMEZONE_RFC_FIELD,
149 UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD };
150 static const int8_t kTimeFieldsCount = 10;
153 // This is a pattern-of-last-resort used when we can't load a usable pattern out
155 static const UChar gDefaultPattern[] =
157 0x79, 0x79, 0x79, 0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0
158 }; /* "yyyyMMdd hh:mm a" */
160 // This prefix is designed to NEVER MATCH real text, in order to
161 // suppress the parsing of negative numbers. Adjust as needed (if
162 // this becomes valid Unicode).
163 static const UChar SUPPRESS_NEGATIVE_PREFIX[] = {0xAB00, 0};
166 * These are the tags we expect to see in normal resource bundle files associated
169 static const UChar QUOTE = 0x27; // Single quote
172 * The field range check bias for each UDateFormatField.
173 * The bias is added to the minimum and maximum values
174 * before they are compared to the parsed number.
175 * For example, the calendar stores zero-based month numbers
176 * but the parsed month numbers start at 1, so the bias is 1.
178 * A value of -1 means that the value is not checked.
180 static const int32_t gFieldRangeBias[] = {
181 -1, // 'G' - UDAT_ERA_FIELD
182 -1, // 'y' - UDAT_YEAR_FIELD
183 1, // 'M' - UDAT_MONTH_FIELD
184 0, // 'd' - UDAT_DATE_FIELD
185 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
186 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
187 0, // 'm' - UDAT_MINUTE_FIELD
188 0, // 's' - UDAT_SECOND_FIELD
189 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
190 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
191 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
192 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
193 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
194 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
195 -1, // 'a' - UDAT_AM_PM_FIELD
196 -1, // 'h' - UDAT_HOUR1_FIELD
197 -1, // 'K' - UDAT_HOUR0_FIELD
198 -1, // 'z' - UDAT_TIMEZONE_FIELD
199 -1, // 'Y' - UDAT_YEAR_WOY_FIELD
200 -1, // 'e' - UDAT_DOW_LOCAL_FIELD
201 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
202 -1, // 'g' - UDAT_JULIAN_DAY_FIELD
203 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
204 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
205 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
206 0, // 'c' - UDAT_STANDALONE_DAY_FIELD
207 1, // 'L' - UDAT_STANDALONE_MONTH_FIELD
208 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
209 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
210 -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
211 -1, // 'U' - UDAT_YEAR_NAME_FIELD
212 -1, // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
213 -1, // 'X' - UDAT_TIMEZONE_ISO_FIELD
214 -1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
215 -1, // 'r' - UDAT_RELATED_YEAR_FIELD
216 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
217 -1, // ':' - UDAT_TIME_SEPARATOR_FIELD
219 -1, // (no pattern character currently) - UDAT_TIME_SEPARATOR_FIELD
223 // When calendar uses hebr numbering (i.e. he@calendar=hebrew),
224 // offset the years within the current millenium down to 1-999
225 static const int32_t HEBREW_CAL_CUR_MILLENIUM_START_YEAR = 5000;
226 static const int32_t HEBREW_CAL_CUR_MILLENIUM_END_YEAR = 6000;
228 static UMutex LOCK = U_MUTEX_INITIALIZER;
230 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat)
232 SimpleDateFormat::NSOverride::~NSOverride() {
239 void SimpleDateFormat::NSOverride::free() {
240 NSOverride *cur = this;
242 NSOverride *next = cur->next;
248 // no matter what the locale's default number format looked like, we want
249 // to modify it so that it doesn't use thousands separators, doesn't always
250 // show the decimal point, and recognizes integers only when parsing
251 static void fixNumberFormatForDates(NumberFormat &nf) {
252 nf.setGroupingUsed(FALSE);
253 DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(&nf);
254 if (decfmt != NULL) {
255 decfmt->setDecimalSeparatorAlwaysShown(FALSE);
257 nf.setParseIntegerOnly(TRUE);
258 nf.setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
261 static const SharedNumberFormat *createSharedNumberFormat(
262 NumberFormat *nfToAdopt) {
263 fixNumberFormatForDates(*nfToAdopt);
264 const SharedNumberFormat *result = new SharedNumberFormat(nfToAdopt);
265 if (result == NULL) {
271 static const SharedNumberFormat *createSharedNumberFormat(
272 const Locale &loc, UErrorCode &status) {
273 NumberFormat *nf = NumberFormat::createInstance(loc, status);
274 if (U_FAILURE(status)) {
277 const SharedNumberFormat *result = createSharedNumberFormat(nf);
278 if (result == NULL) {
279 status = U_MEMORY_ALLOCATION_ERROR;
284 static const SharedNumberFormat **allocSharedNumberFormatters() {
285 const SharedNumberFormat **result = (const SharedNumberFormat**)
286 uprv_malloc(UDAT_FIELD_COUNT * sizeof(const SharedNumberFormat*));
287 if (result == NULL) {
290 for (int32_t i = 0; i < UDAT_FIELD_COUNT; ++i) {
296 static void freeSharedNumberFormatters(const SharedNumberFormat ** list) {
297 for (int32_t i = 0; i < UDAT_FIELD_COUNT; ++i) {
298 SharedObject::clearPtr(list[i]);
303 const NumberFormat *SimpleDateFormat::getNumberFormatByIndex(
304 UDateFormatField index) const {
305 if (fSharedNumberFormatters == NULL ||
306 fSharedNumberFormatters[index] == NULL) {
307 return fNumberFormat;
309 return &(**fSharedNumberFormatters[index]);
312 class SimpleDateFormatMutableNFNode {
314 const NumberFormat *key;
316 SimpleDateFormatMutableNFNode()
317 : key(NULL), value(NULL) { }
318 ~SimpleDateFormatMutableNFNode() {
322 SimpleDateFormatMutableNFNode(const SimpleDateFormatMutableNFNode &);
323 SimpleDateFormatMutableNFNode &operator=(const SimpleDateFormatMutableNFNode &);
326 // Single threaded cache of non const NumberFormats. Designed to be stack
327 // allocated and used for a single format call.
328 class SimpleDateFormatMutableNFs : public UMemory {
330 SimpleDateFormatMutableNFs() {
333 // Returns a non-const clone of nf which can be safely modified.
334 // Subsequent calls with same nf will return the same non-const clone.
335 // This object maintains ownership of all returned non-const
336 // NumberFormat objects. On memory allocation error returns NULL.
337 // Caller must check for NULL return value.
338 NumberFormat *get(const NumberFormat *nf) {
343 while (nodes[idx].value) {
344 if (nf == nodes[idx].key) {
345 return nodes[idx].value;
349 U_ASSERT(idx < UDAT_FIELD_COUNT);
351 nodes[idx].value = (NumberFormat *) nf->clone();
352 return nodes[idx].value;
355 // +1 extra for sentinel. If each field had its own NumberFormat, this
356 // cache would have to allocate UDAT_FIELD_COUNT mutable versions worst
358 SimpleDateFormatMutableNFNode nodes[UDAT_FIELD_COUNT + 1];
359 SimpleDateFormatMutableNFs(const SimpleDateFormatMutableNFs &);
360 SimpleDateFormatMutableNFs &operator=(const SimpleDateFormatMutableNFs &);
363 //----------------------------------------------------------------------
365 SimpleDateFormat::~SimpleDateFormat()
368 if (fSharedNumberFormatters) {
369 freeSharedNumberFormatters(fSharedNumberFormatters);
371 if (fTimeZoneFormat) {
372 delete fTimeZoneFormat;
375 #if !UCONFIG_NO_BREAK_ITERATION
376 delete fCapitalizationBrkIter;
380 //----------------------------------------------------------------------
382 SimpleDateFormat::SimpleDateFormat(UErrorCode& status)
383 : fLocale(Locale::getDefault()),
385 fTimeZoneFormat(NULL),
386 fSharedNumberFormatters(NULL),
387 fCapitalizationBrkIter(NULL)
389 initializeBooleanAttributes();
390 construct(kShort, (EStyle) (kShort + kDateOffset), fLocale, status);
391 initializeDefaultCentury();
394 //----------------------------------------------------------------------
396 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
399 fLocale(Locale::getDefault()),
401 fTimeZoneFormat(NULL),
402 fSharedNumberFormatters(NULL),
403 fCapitalizationBrkIter(NULL)
405 fDateOverride.setToBogus();
406 fTimeOverride.setToBogus();
407 initializeBooleanAttributes();
408 initializeCalendar(NULL,fLocale,status);
409 fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
410 initialize(fLocale, status);
411 initializeDefaultCentury();
414 //----------------------------------------------------------------------
416 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
417 const UnicodeString& override,
420 fLocale(Locale::getDefault()),
422 fTimeZoneFormat(NULL),
423 fSharedNumberFormatters(NULL),
424 fCapitalizationBrkIter(NULL)
426 fDateOverride.setTo(override);
427 fTimeOverride.setToBogus();
428 initializeBooleanAttributes();
429 initializeCalendar(NULL,fLocale,status);
430 fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
431 initialize(fLocale, status);
432 initializeDefaultCentury();
434 processOverrideString(fLocale,override,kOvrStrBoth,status);
438 //----------------------------------------------------------------------
440 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
441 const Locale& locale,
445 fTimeZoneFormat(NULL),
446 fSharedNumberFormatters(NULL),
447 fCapitalizationBrkIter(NULL)
450 fDateOverride.setToBogus();
451 fTimeOverride.setToBogus();
452 initializeBooleanAttributes();
454 initializeCalendar(NULL,fLocale,status);
455 fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
456 initialize(fLocale, status);
457 initializeDefaultCentury();
460 //----------------------------------------------------------------------
462 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
463 const UnicodeString& override,
464 const Locale& locale,
468 fTimeZoneFormat(NULL),
469 fSharedNumberFormatters(NULL),
470 fCapitalizationBrkIter(NULL)
473 fDateOverride.setTo(override);
474 fTimeOverride.setToBogus();
475 initializeBooleanAttributes();
477 initializeCalendar(NULL,fLocale,status);
478 fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
479 initialize(fLocale, status);
480 initializeDefaultCentury();
482 processOverrideString(locale,override,kOvrStrBoth,status);
486 //----------------------------------------------------------------------
488 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
489 DateFormatSymbols* symbolsToAdopt,
492 fLocale(Locale::getDefault()),
493 fSymbols(symbolsToAdopt),
494 fTimeZoneFormat(NULL),
495 fSharedNumberFormatters(NULL),
496 fCapitalizationBrkIter(NULL)
499 fDateOverride.setToBogus();
500 fTimeOverride.setToBogus();
501 initializeBooleanAttributes();
503 initializeCalendar(NULL,fLocale,status);
504 initialize(fLocale, status);
505 initializeDefaultCentury();
508 //----------------------------------------------------------------------
510 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
511 const DateFormatSymbols& symbols,
514 fLocale(Locale::getDefault()),
515 fSymbols(new DateFormatSymbols(symbols)),
516 fTimeZoneFormat(NULL),
517 fSharedNumberFormatters(NULL),
518 fCapitalizationBrkIter(NULL)
521 fDateOverride.setToBogus();
522 fTimeOverride.setToBogus();
523 initializeBooleanAttributes();
525 initializeCalendar(NULL, fLocale, status);
526 initialize(fLocale, status);
527 initializeDefaultCentury();
530 //----------------------------------------------------------------------
532 // Not for public consumption; used by DateFormat
533 SimpleDateFormat::SimpleDateFormat(EStyle timeStyle,
535 const Locale& locale,
539 fTimeZoneFormat(NULL),
540 fSharedNumberFormatters(NULL),
541 fCapitalizationBrkIter(NULL)
543 initializeBooleanAttributes();
544 construct(timeStyle, dateStyle, fLocale, status);
545 if(U_SUCCESS(status)) {
546 initializeDefaultCentury();
550 //----------------------------------------------------------------------
553 * Not for public consumption; used by DateFormat. This constructor
554 * never fails. If the resource data is not available, it uses the
555 * the last resort symbols.
557 SimpleDateFormat::SimpleDateFormat(const Locale& locale,
559 : fPattern(gDefaultPattern),
562 fTimeZoneFormat(NULL),
563 fSharedNumberFormatters(NULL),
564 fCapitalizationBrkIter(NULL)
566 if (U_FAILURE(status)) return;
567 initializeBooleanAttributes();
568 initializeCalendar(NULL, fLocale, status);
569 fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
570 if (U_FAILURE(status))
572 status = U_ZERO_ERROR;
574 // This constructor doesn't fail; it uses last resort data
575 fSymbols = new DateFormatSymbols(status);
578 status = U_MEMORY_ALLOCATION_ERROR;
583 fDateOverride.setToBogus();
584 fTimeOverride.setToBogus();
586 initialize(fLocale, status);
587 if(U_SUCCESS(status)) {
588 initializeDefaultCentury();
592 //----------------------------------------------------------------------
594 SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other)
596 fLocale(other.fLocale),
598 fTimeZoneFormat(NULL),
599 fSharedNumberFormatters(NULL),
600 fCapitalizationBrkIter(NULL)
602 initializeBooleanAttributes();
606 //----------------------------------------------------------------------
608 SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other)
610 if (this == &other) {
613 DateFormat::operator=(other);
614 fDateOverride = other.fDateOverride;
615 fTimeOverride = other.fTimeOverride;
621 fSymbols = new DateFormatSymbols(*other.fSymbols);
623 fDefaultCenturyStart = other.fDefaultCenturyStart;
624 fDefaultCenturyStartYear = other.fDefaultCenturyStartYear;
625 fHaveDefaultCentury = other.fHaveDefaultCentury;
627 fPattern = other.fPattern;
628 fHasMinute = other.fHasMinute;
629 fHasSecond = other.fHasSecond;
631 // TimeZoneFormat in ICU4C only depends on a locale for now
632 if (fLocale != other.fLocale) {
633 delete fTimeZoneFormat;
634 fTimeZoneFormat = NULL; // forces lazy instantiation with the other locale
635 fLocale = other.fLocale;
638 #if !UCONFIG_NO_BREAK_ITERATION
639 if (other.fCapitalizationBrkIter != NULL) {
640 fCapitalizationBrkIter = (other.fCapitalizationBrkIter)->clone();
644 if (fSharedNumberFormatters != NULL) {
645 freeSharedNumberFormatters(fSharedNumberFormatters);
646 fSharedNumberFormatters = NULL;
648 if (other.fSharedNumberFormatters != NULL) {
649 fSharedNumberFormatters = allocSharedNumberFormatters();
650 if (fSharedNumberFormatters) {
651 for (int32_t i = 0; i < UDAT_FIELD_COUNT; ++i) {
652 SharedObject::copyPtr(
653 other.fSharedNumberFormatters[i],
654 fSharedNumberFormatters[i]);
662 //----------------------------------------------------------------------
665 SimpleDateFormat::clone() const
667 return new SimpleDateFormat(*this);
670 //----------------------------------------------------------------------
673 SimpleDateFormat::operator==(const Format& other) const
675 if (DateFormat::operator==(other)) {
676 // The DateFormat::operator== check for fCapitalizationContext equality above
677 // is sufficient to check equality of all derived context-related data.
678 // DateFormat::operator== guarantees following cast is safe
679 SimpleDateFormat* that = (SimpleDateFormat*)&other;
680 return (fPattern == that->fPattern &&
681 fSymbols != NULL && // Check for pathological object
682 that->fSymbols != NULL && // Check for pathological object
683 *fSymbols == *that->fSymbols &&
684 fHaveDefaultCentury == that->fHaveDefaultCentury &&
685 fDefaultCenturyStart == that->fDefaultCenturyStart);
690 //----------------------------------------------------------------------
692 void SimpleDateFormat::construct(EStyle timeStyle,
694 const Locale& locale,
697 // called by several constructors to load pattern data from the resources
698 if (U_FAILURE(status)) return;
700 // We will need the calendar to know what type of symbols to load.
701 initializeCalendar(NULL, locale, status);
702 if (U_FAILURE(status)) return;
704 // Load date time patterns directly from resources.
705 const char* cType = fCalendar ? fCalendar->getType() : NULL;
706 LocalUResourceBundlePointer bundle(ures_open(NULL, locale.getBaseName(), &status));
707 if (U_FAILURE(status)) return;
709 UBool cTypeIsGregorian = TRUE;
710 LocalUResourceBundlePointer dateTimePatterns;
711 if (cType != NULL && uprv_strcmp(cType, "gregorian") != 0) {
712 CharString resourcePath("calendar/", status);
713 resourcePath.append(cType, status).append("/DateTimePatterns", status);
714 dateTimePatterns.adoptInstead(
715 ures_getByKeyWithFallback(bundle.getAlias(), resourcePath.data(),
716 (UResourceBundle*)NULL, &status));
717 cTypeIsGregorian = FALSE;
720 // Check for "gregorian" fallback.
721 if (cTypeIsGregorian || status == U_MISSING_RESOURCE_ERROR) {
722 status = U_ZERO_ERROR;
723 dateTimePatterns.adoptInstead(
724 ures_getByKeyWithFallback(bundle.getAlias(),
725 "calendar/gregorian/DateTimePatterns",
726 (UResourceBundle*)NULL, &status));
728 if (U_FAILURE(status)) return;
730 LocalUResourceBundlePointer currentBundle;
732 if (ures_getSize(dateTimePatterns.getAlias()) <= kDateTime)
734 status = U_INVALID_FORMAT_ERROR;
738 setLocaleIDs(ures_getLocaleByType(dateTimePatterns.getAlias(), ULOC_VALID_LOCALE, &status),
739 ures_getLocaleByType(dateTimePatterns.getAlias(), ULOC_ACTUAL_LOCALE, &status));
741 // create a symbols object from the locale
742 fSymbols = DateFormatSymbols::createForLocale(locale, status);
743 if (U_FAILURE(status)) return;
746 status = U_MEMORY_ALLOCATION_ERROR;
750 const UChar *resStr,*ovrStr;
751 int32_t resStrLen,ovrStrLen = 0;
752 fDateOverride.setToBogus();
753 fTimeOverride.setToBogus();
755 // if the pattern should include both date and time information, use the date/time
756 // pattern string as a guide to tell use how to glue together the appropriate date
757 // and time pattern strings.
758 if ((timeStyle != kNone) && (dateStyle != kNone))
760 currentBundle.adoptInstead(
761 ures_getByIndex(dateTimePatterns.getAlias(), (int32_t)timeStyle, NULL, &status));
762 if (U_FAILURE(status)) {
763 status = U_INVALID_FORMAT_ERROR;
766 switch (ures_getType(currentBundle.getAlias())) {
768 resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status);
772 resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status);
773 ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status);
774 fTimeOverride.setTo(TRUE, ovrStr, ovrStrLen);
778 status = U_INVALID_FORMAT_ERROR;
783 UnicodeString tempus1(TRUE, resStr, resStrLen);
785 currentBundle.adoptInstead(
786 ures_getByIndex(dateTimePatterns.getAlias(), (int32_t)dateStyle, NULL, &status));
787 if (U_FAILURE(status)) {
788 status = U_INVALID_FORMAT_ERROR;
791 switch (ures_getType(currentBundle.getAlias())) {
793 resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status);
797 resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status);
798 ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status);
799 fDateOverride.setTo(TRUE, ovrStr, ovrStrLen);
803 status = U_INVALID_FORMAT_ERROR;
808 UnicodeString tempus2(TRUE, resStr, resStrLen);
810 int32_t glueIndex = kDateTime;
811 int32_t patternsSize = ures_getSize(dateTimePatterns.getAlias());
812 if (patternsSize >= (kDateTimeOffset + kShort + 1)) {
813 // Get proper date time format
814 glueIndex = (int32_t)(kDateTimeOffset + (dateStyle - kDateOffset));
817 resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), glueIndex, &resStrLen, &status);
818 SimpleFormatter(UnicodeString(TRUE, resStr, resStrLen), 2, 2, status).
819 format(tempus1, tempus2, fPattern, status);
821 // if the pattern includes just time data or just date date, load the appropriate
822 // pattern string from the resources
823 // setTo() - see DateFormatSymbols::assignArray comments
824 else if (timeStyle != kNone) {
825 currentBundle.adoptInstead(
826 ures_getByIndex(dateTimePatterns.getAlias(), (int32_t)timeStyle, NULL, &status));
827 if (U_FAILURE(status)) {
828 status = U_INVALID_FORMAT_ERROR;
831 switch (ures_getType(currentBundle.getAlias())) {
833 resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status);
837 resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status);
838 ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status);
839 fDateOverride.setTo(TRUE, ovrStr, ovrStrLen);
843 status = U_INVALID_FORMAT_ERROR;
847 fPattern.setTo(TRUE, resStr, resStrLen);
849 else if (dateStyle != kNone) {
850 currentBundle.adoptInstead(
851 ures_getByIndex(dateTimePatterns.getAlias(), (int32_t)dateStyle, NULL, &status));
852 if (U_FAILURE(status)) {
853 status = U_INVALID_FORMAT_ERROR;
856 switch (ures_getType(currentBundle.getAlias())) {
858 resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status);
862 resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status);
863 ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status);
864 fDateOverride.setTo(TRUE, ovrStr, ovrStrLen);
868 status = U_INVALID_FORMAT_ERROR;
872 fPattern.setTo(TRUE, resStr, resStrLen);
875 // and if it includes _neither_, that's an error
877 status = U_INVALID_FORMAT_ERROR;
879 // finally, finish initializing by creating a Calendar and a NumberFormat
880 initialize(locale, status);
883 //----------------------------------------------------------------------
886 SimpleDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status)
888 if(!U_FAILURE(status)) {
889 fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status);
895 SimpleDateFormat::initialize(const Locale& locale,
898 if (U_FAILURE(status)) return;
900 // We don't need to check that the row count is >= 1, since all 2d arrays have at
902 fNumberFormat = NumberFormat::createInstance(locale, status);
903 if (fNumberFormat != NULL && U_SUCCESS(status))
905 fixNumberFormatForDates(*fNumberFormat);
906 //fNumberFormat->setLenient(TRUE); // Java uses a custom DateNumberFormat to format/parse
908 initNumberFormatters(locale,status);
911 else if (U_SUCCESS(status))
913 status = U_MISSING_RESOURCE_ERROR;
919 /* Initialize the fields we use to disambiguate ambiguous years. Separate
920 * so we can call it from readObject().
922 void SimpleDateFormat::initializeDefaultCentury()
925 fHaveDefaultCentury = fCalendar->haveDefaultCentury();
926 if(fHaveDefaultCentury) {
927 fDefaultCenturyStart = fCalendar->defaultCenturyStart();
928 fDefaultCenturyStartYear = fCalendar->defaultCenturyStartYear();
930 fDefaultCenturyStart = DBL_MIN;
931 fDefaultCenturyStartYear = -1;
937 * Initialize the boolean attributes. Separate so we can call it from all constructors.
939 void SimpleDateFormat::initializeBooleanAttributes()
941 UErrorCode status = U_ZERO_ERROR;
943 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status);
944 setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
945 setBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH, true, status);
946 setBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, true, status);
949 /* Define one-century window into which to disambiguate dates using
950 * two-digit years. Make public in JDK 1.2.
952 void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& status)
954 if(U_FAILURE(status)) {
958 status = U_ILLEGAL_ARGUMENT_ERROR;
962 fCalendar->setTime(startDate, status);
963 if(U_SUCCESS(status)) {
964 fHaveDefaultCentury = TRUE;
965 fDefaultCenturyStart = startDate;
966 fDefaultCenturyStartYear = fCalendar->get(UCAL_YEAR, status);
970 //----------------------------------------------------------------------
973 SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition& pos) const
975 UErrorCode status = U_ZERO_ERROR;
976 FieldPositionOnlyHandler handler(pos);
977 return _format(cal, appendTo, handler, status);
980 //----------------------------------------------------------------------
983 SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo,
984 FieldPositionIterator* posIter, UErrorCode& status) const
986 FieldPositionIteratorHandler handler(posIter, status);
987 return _format(cal, appendTo, handler, status);
990 //----------------------------------------------------------------------
993 SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo,
994 FieldPositionHandler& handler, UErrorCode& status) const
996 if ( U_FAILURE(status) ) {
999 Calendar* workCal = &cal;
1000 Calendar* calClone = NULL;
1001 if (&cal != fCalendar && uprv_strcmp(cal.getType(), fCalendar->getType()) != 0) {
1002 // Different calendar type
1003 // We use the time and time zone from the input calendar, but
1004 // do not use the input calendar for field calculation.
1005 calClone = fCalendar->clone();
1006 if (calClone != NULL) {
1007 UDate t = cal.getTime(status);
1008 calClone->setTime(t, status);
1009 calClone->setTimeZone(cal.getTimeZone());
1012 status = U_MEMORY_ALLOCATION_ERROR;
1017 UBool inQuote = FALSE;
1020 int32_t fieldNum = 0;
1021 UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status);
1023 // Create temporary cache of mutable number format objects. This way
1024 // subFormat won't have to clone the const NumberFormat for each field.
1025 // if several fields share the same NumberFormat, which will almost
1026 // always be the case, this is a big save.
1027 SimpleDateFormatMutableNFs mutableNFs;
1028 // loop through the pattern string character by character
1029 for (int32_t i = 0; i < fPattern.length() && U_SUCCESS(status); ++i) {
1030 UChar ch = fPattern[i];
1032 // Use subFormat() to format a repeated pattern character
1033 // when a different pattern or non-pattern character is seen
1034 if (ch != prevCh && count > 0) {
1035 subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, mutableNFs, status);
1039 // Consecutive single quotes are a single quote literal,
1040 // either outside of quotes or between quotes
1041 if ((i+1) < fPattern.length() && fPattern[i+1] == QUOTE) {
1042 appendTo += (UChar)QUOTE;
1045 inQuote = ! inQuote;
1048 else if (!inQuote && isSyntaxChar(ch)) {
1049 // ch is a date-time pattern character to be interpreted
1050 // by subFormat(); count the number of times it is repeated
1055 // Append quoted characters and unquoted non-pattern characters
1060 // Format the last item in the pattern, if any
1062 subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, mutableNFs, status);
1065 if (calClone != NULL) {
1072 //----------------------------------------------------------------------
1074 /* Map calendar field into calendar field level.
1075 * the larger the level, the smaller the field unit.
1076 * For example, UCAL_ERA level is 0, UCAL_YEAR level is 10,
1077 * UCAL_MONTH level is 20.
1078 * NOTE: if new fields adds in, the table needs to update.
1081 SimpleDateFormat::fgCalendarFieldToLevel[] =
1085 /*dDEF*/ 30, 20, 30, 30,
1086 /*ahHm*/ 40, 50, 50, 60,
1093 int32_t SimpleDateFormat::getLevelFromChar(UChar ch) {
1094 // Map date field LETTER into calendar field level.
1095 // the larger the level, the smaller the field unit.
1096 // NOTE: if new fields adds in, the table needs to update.
1097 static const int32_t mapCharToLevel[] = {
1098 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1100 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1101 // ! " # $ % & ' ( ) * + , - . /
1102 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1103 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1104 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
1105 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1,
1107 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
1108 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1110 // @ A B C D E F G H I J K L M N O
1111 -1, 40, -1, -1, 20, 30, 30, 0, 50, -1, -1, 50, 20, 20, -1, 0,
1112 // P Q R S T U V W X Y Z [ \ ] ^ _
1113 -1, 20, -1, 80, -1, 10, 0, 30, 0, 10, 0, -1, -1, -1, -1, -1,
1114 // ` a b c d e f g h i j k l m n o
1115 -1, 40, -1, 30, 30, 30, -1, 0, 50, -1, -1, 50, 0, 60, -1, -1,
1116 // p q r s t u v w x y z { | } ~
1117 -1, 20, 10, 70, -1, 10, 0, 20, 0, 10, 0, -1, -1, -1, -1, -1
1120 return ch < UPRV_LENGTHOF(mapCharToLevel) ? mapCharToLevel[ch] : -1;
1123 UBool SimpleDateFormat::isSyntaxChar(UChar ch) {
1124 static const UBool mapCharToIsSyntax[] = {
1126 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1128 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1130 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1132 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1134 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1136 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1138 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1139 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1141 FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE,
1144 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1147 FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
1149 TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
1151 TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
1153 TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE,
1155 FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
1157 TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
1159 TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
1161 TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE
1164 return ch < UPRV_LENGTHOF(mapCharToIsSyntax) ? mapCharToIsSyntax[ch] : FALSE;
1167 // Map index into pattern character string to Calendar field number.
1168 const UCalendarDateFields
1169 SimpleDateFormat::fgPatternIndexToCalendarField[] =
1171 /*GyM*/ UCAL_ERA, UCAL_YEAR, UCAL_MONTH,
1172 /*dkH*/ UCAL_DATE, UCAL_HOUR_OF_DAY, UCAL_HOUR_OF_DAY,
1173 /*msS*/ UCAL_MINUTE, UCAL_SECOND, UCAL_MILLISECOND,
1174 /*EDF*/ UCAL_DAY_OF_WEEK, UCAL_DAY_OF_YEAR, UCAL_DAY_OF_WEEK_IN_MONTH,
1175 /*wWa*/ UCAL_WEEK_OF_YEAR, UCAL_WEEK_OF_MONTH, UCAL_AM_PM,
1176 /*hKz*/ UCAL_HOUR, UCAL_HOUR, UCAL_ZONE_OFFSET,
1177 /*Yeu*/ UCAL_YEAR_WOY, UCAL_DOW_LOCAL, UCAL_EXTENDED_YEAR,
1178 /*gAZ*/ UCAL_JULIAN_DAY, UCAL_MILLISECONDS_IN_DAY, UCAL_ZONE_OFFSET,
1179 /*v*/ UCAL_ZONE_OFFSET,
1180 /*c*/ UCAL_DOW_LOCAL,
1184 /*V*/ UCAL_ZONE_OFFSET,
1186 /*O*/ UCAL_ZONE_OFFSET,
1187 /*Xx*/ UCAL_ZONE_OFFSET, UCAL_ZONE_OFFSET,
1188 /*r*/ UCAL_EXTENDED_YEAR,
1189 /*bB*/ UCAL_FIELD_COUNT, UCAL_FIELD_COUNT, // no mappings to calendar fields
1190 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1191 /*:*/ UCAL_FIELD_COUNT, /* => no useful mapping to any calendar field */
1193 /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/ UCAL_FIELD_COUNT, /* => no useful mapping to any calendar field */
1197 // Map index into pattern character string to DateFormat field number
1198 const UDateFormatField
1199 SimpleDateFormat::fgPatternIndexToDateFormatField[] = {
1200 /*GyM*/ UDAT_ERA_FIELD, UDAT_YEAR_FIELD, UDAT_MONTH_FIELD,
1201 /*dkH*/ UDAT_DATE_FIELD, UDAT_HOUR_OF_DAY1_FIELD, UDAT_HOUR_OF_DAY0_FIELD,
1202 /*msS*/ UDAT_MINUTE_FIELD, UDAT_SECOND_FIELD, UDAT_FRACTIONAL_SECOND_FIELD,
1203 /*EDF*/ UDAT_DAY_OF_WEEK_FIELD, UDAT_DAY_OF_YEAR_FIELD, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD,
1204 /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD, UDAT_WEEK_OF_MONTH_FIELD, UDAT_AM_PM_FIELD,
1205 /*hKz*/ UDAT_HOUR1_FIELD, UDAT_HOUR0_FIELD, UDAT_TIMEZONE_FIELD,
1206 /*Yeu*/ UDAT_YEAR_WOY_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_EXTENDED_YEAR_FIELD,
1207 /*gAZ*/ UDAT_JULIAN_DAY_FIELD, UDAT_MILLISECONDS_IN_DAY_FIELD, UDAT_TIMEZONE_RFC_FIELD,
1208 /*v*/ UDAT_TIMEZONE_GENERIC_FIELD,
1209 /*c*/ UDAT_STANDALONE_DAY_FIELD,
1210 /*L*/ UDAT_STANDALONE_MONTH_FIELD,
1211 /*Q*/ UDAT_QUARTER_FIELD,
1212 /*q*/ UDAT_STANDALONE_QUARTER_FIELD,
1213 /*V*/ UDAT_TIMEZONE_SPECIAL_FIELD,
1214 /*U*/ UDAT_YEAR_NAME_FIELD,
1215 /*O*/ UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD,
1216 /*Xx*/ UDAT_TIMEZONE_ISO_FIELD, UDAT_TIMEZONE_ISO_LOCAL_FIELD,
1217 /*r*/ UDAT_RELATED_YEAR_FIELD,
1218 /*bB*/ UDAT_AM_PM_MIDNIGHT_NOON_FIELD, UDAT_FLEXIBLE_DAY_PERIOD_FIELD,
1219 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1220 /*:*/ UDAT_TIME_SEPARATOR_FIELD,
1222 /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/ UDAT_TIME_SEPARATOR_FIELD,
1226 //----------------------------------------------------------------------
1229 * Append symbols[value] to dst. Make sure the array index is not out
1233 _appendSymbol(UnicodeString& dst,
1235 const UnicodeString* symbols,
1236 int32_t symbolsCount) {
1237 U_ASSERT(0 <= value && value < symbolsCount);
1238 if (0 <= value && value < symbolsCount) {
1239 dst += symbols[value];
1244 _appendSymbolWithMonthPattern(UnicodeString& dst, int32_t value, const UnicodeString* symbols, int32_t symbolsCount,
1245 const UnicodeString* monthPattern, UErrorCode& status) {
1246 U_ASSERT(0 <= value && value < symbolsCount);
1247 if (0 <= value && value < symbolsCount) {
1248 if (monthPattern == NULL) {
1249 dst += symbols[value];
1251 SimpleFormatter(*monthPattern, 1, 1, status).format(symbols[value], dst, status);
1256 //----------------------------------------------------------------------
1258 SimpleDateFormat::initNumberFormatters(const Locale &locale,UErrorCode &status) {
1259 if (U_FAILURE(status)) {
1262 if ( fDateOverride.isBogus() && fTimeOverride.isBogus() ) {
1266 if (fSharedNumberFormatters == NULL) {
1267 fSharedNumberFormatters = allocSharedNumberFormatters();
1268 if (fSharedNumberFormatters == NULL) {
1269 status = U_MEMORY_ALLOCATION_ERROR;
1274 if (U_FAILURE(status)) {
1278 processOverrideString(locale,fDateOverride,kOvrStrDate,status);
1279 processOverrideString(locale,fTimeOverride,kOvrStrTime,status);
1283 SimpleDateFormat::processOverrideString(const Locale &locale, const UnicodeString &str, int8_t type, UErrorCode &status) {
1284 if (str.isBogus() || U_FAILURE(status)) {
1290 UnicodeString nsName;
1291 UnicodeString ovrField;
1292 UBool moreToProcess = TRUE;
1293 NSOverride *overrideList = NULL;
1295 while (moreToProcess) {
1296 int32_t delimiterPosition = str.indexOf((UChar)ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE,start);
1297 if (delimiterPosition == -1) {
1298 moreToProcess = FALSE;
1299 len = str.length() - start;
1301 len = delimiterPosition - start;
1303 UnicodeString currentString(str,start,len);
1304 int32_t equalSignPosition = currentString.indexOf((UChar)ULOC_KEYWORD_ASSIGN_UNICODE,0);
1305 if (equalSignPosition == -1) { // Simple override string such as "hebrew"
1306 nsName.setTo(currentString);
1307 ovrField.setToBogus();
1308 } else { // Field specific override string such as "y=hebrew"
1309 nsName.setTo(currentString,equalSignPosition+1);
1310 ovrField.setTo(currentString,0,1); // We just need the first character.
1313 int32_t nsNameHash = nsName.hashCode();
1314 // See if the numbering system is in the override list, if not, then add it.
1315 NSOverride *cur = overrideList;
1316 const SharedNumberFormat *snf = NULL;
1317 UBool found = FALSE;
1318 while ( cur && !found ) {
1319 if ( cur->hash == nsNameHash ) {
1327 LocalPointer<NSOverride> cur(new NSOverride);
1328 if (!cur.isNull()) {
1329 char kw[ULOC_KEYWORD_AND_VALUES_CAPACITY];
1330 uprv_strcpy(kw,"numbers=");
1331 nsName.extract(0,len,kw+8,ULOC_KEYWORD_AND_VALUES_CAPACITY-8,US_INV);
1333 Locale ovrLoc(locale.getLanguage(),locale.getCountry(),locale.getVariant(),kw);
1334 cur->hash = nsNameHash;
1335 cur->next = overrideList;
1336 SharedObject::copyPtr(
1337 createSharedNumberFormat(ovrLoc, status), cur->snf);
1338 if (U_FAILURE(status)) {
1340 overrideList->free();
1345 overrideList = cur.orphan();
1347 status = U_MEMORY_ALLOCATION_ERROR;
1349 overrideList->free();
1355 // Now that we have an appropriate number formatter, fill in the appropriate spaces in the
1356 // number formatters table.
1357 if (ovrField.isBogus()) {
1361 for ( int8_t i=0 ; i<kDateFieldsCount; i++ ) {
1362 SharedObject::copyPtr(snf, fSharedNumberFormatters[kDateFields[i]]);
1364 if (type==kOvrStrDate) {
1369 case kOvrStrTime : {
1370 for ( int8_t i=0 ; i<kTimeFieldsCount; i++ ) {
1371 SharedObject::copyPtr(snf, fSharedNumberFormatters[kTimeFields[i]]);
1377 // if the pattern character is unrecognized, signal an error and bail out
1378 UDateFormatField patternCharIndex =
1379 DateFormatSymbols::getPatternCharIndex(ovrField.charAt(0));
1380 if (patternCharIndex == UDAT_FIELD_COUNT) {
1381 status = U_INVALID_FORMAT_ERROR;
1383 overrideList->free();
1387 SharedObject::copyPtr(snf, fSharedNumberFormatters[patternCharIndex]);
1390 start = delimiterPosition + 1;
1393 overrideList->free();
1397 //---------------------------------------------------------------------
1399 SimpleDateFormat::subFormat(UnicodeString &appendTo,
1402 UDisplayContext capitalizationContext,
1404 FieldPositionHandler& handler,
1406 SimpleDateFormatMutableNFs &mutableNFs,
1407 UErrorCode& status) const
1409 if (U_FAILURE(status)) {
1413 // this function gets called by format() to produce the appropriate substitution
1414 // text for an individual pattern symbol (e.g., "HH" or "yyyy")
1416 UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch);
1417 const int32_t maxIntCount = 10;
1418 int32_t beginOffset = appendTo.length();
1419 NumberFormat *currentNumberFormat;
1420 DateFormatSymbols::ECapitalizationContextUsageType capContextUsageType = DateFormatSymbols::kCapContextUsageOther;
1422 UBool isHebrewCalendar = (uprv_strcmp(cal.getType(),"hebrew") == 0);
1423 UBool isChineseCalendar = (uprv_strcmp(cal.getType(),"chinese") == 0 || uprv_strcmp(cal.getType(),"dangi") == 0);
1425 // if the pattern character is unrecognized, signal an error and dump out
1426 if (patternCharIndex == UDAT_FIELD_COUNT)
1428 if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
1429 status = U_INVALID_FORMAT_ERROR;
1434 UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex];
1436 // Don't get value unless it is useful
1437 if (field < UCAL_FIELD_COUNT) {
1438 value = (patternCharIndex != UDAT_RELATED_YEAR_FIELD)? cal.get(field, status): cal.getRelatedYear(status);
1440 if (U_FAILURE(status)) {
1444 currentNumberFormat = mutableNFs.get(getNumberFormatByIndex(patternCharIndex));
1445 if (currentNumberFormat == NULL) {
1446 status = U_MEMORY_ALLOCATION_ERROR;
1449 UnicodeString hebr("hebr", 4, US_INV);
1451 switch (patternCharIndex) {
1453 // for any "G" symbol, write out the appropriate era string
1454 // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name
1455 case UDAT_ERA_FIELD:
1456 if (isChineseCalendar) {
1457 zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, 9); // as in ICU4J
1460 _appendSymbol(appendTo, value, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount);
1461 capContextUsageType = DateFormatSymbols::kCapContextUsageEraNarrow;
1462 } else if (count == 4) {
1463 _appendSymbol(appendTo, value, fSymbols->fEraNames, fSymbols->fEraNamesCount);
1464 capContextUsageType = DateFormatSymbols::kCapContextUsageEraWide;
1466 _appendSymbol(appendTo, value, fSymbols->fEras, fSymbols->fErasCount);
1467 capContextUsageType = DateFormatSymbols::kCapContextUsageEraAbbrev;
1472 case UDAT_YEAR_NAME_FIELD:
1473 if (fSymbols->fShortYearNames != NULL && value <= fSymbols->fShortYearNamesCount) {
1474 // the Calendar YEAR field runs 1 through 60 for cyclic years
1475 _appendSymbol(appendTo, value - 1, fSymbols->fShortYearNames, fSymbols->fShortYearNamesCount);
1478 // else fall through to numeric year handling, do not break here
1481 // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits
1483 //Year y yy yyy yyyy yyyyy
1484 //AD 1 1 01 001 0001 00001
1485 //AD 12 12 12 012 0012 00012
1486 //AD 123 123 23 123 0123 00123
1487 //AD 1234 1234 34 1234 1234 01234
1488 //AD 12345 12345 45 12345 12345 12345
1489 case UDAT_YEAR_FIELD:
1490 case UDAT_YEAR_WOY_FIELD:
1491 if (fDateOverride.compare(hebr)==0 && value>HEBREW_CAL_CUR_MILLENIUM_START_YEAR && value<HEBREW_CAL_CUR_MILLENIUM_END_YEAR) {
1492 value-=HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
1495 zeroPaddingNumber(currentNumberFormat, appendTo, value, 2, 2);
1497 zeroPaddingNumber(currentNumberFormat, appendTo, value, count, maxIntCount);
1500 // for "MMMM"/"LLLL", write out the whole month name, for "MMM"/"LLL", write out the month
1501 // abbreviation, for "M"/"L" or "MM"/"LL", write out the month as a number with the
1502 // appropriate number of digits
1503 // for "MMMMM"/"LLLLL", use the narrow form
1504 case UDAT_MONTH_FIELD:
1505 case UDAT_STANDALONE_MONTH_FIELD:
1506 if ( isHebrewCalendar ) {
1507 HebrewCalendar *hc = (HebrewCalendar*)&cal;
1508 if (hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value == 6 && count >= 3 )
1509 value = 13; // Show alternate form for Adar II in leap years in Hebrew calendar.
1510 if (!hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value >= 6 && count < 3 )
1511 value--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7.
1514 int32_t isLeapMonth = (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount)?
1515 cal.get(UCAL_IS_LEAP_MONTH, status): 0;
1516 // should consolidate the next section by using arrays of pointers & counts for the right symbols...
1518 if (patternCharIndex == UDAT_MONTH_FIELD) {
1519 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fNarrowMonths, fSymbols->fNarrowMonthsCount,
1520 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatNarrow]): NULL, status);
1522 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneNarrowMonths, fSymbols->fStandaloneNarrowMonthsCount,
1523 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneNarrow]): NULL, status);
1525 capContextUsageType = DateFormatSymbols::kCapContextUsageMonthNarrow;
1526 } else if (count == 4) {
1527 if (patternCharIndex == UDAT_MONTH_FIELD) {
1528 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fMonths, fSymbols->fMonthsCount,
1529 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatWide]): NULL, status);
1530 capContextUsageType = DateFormatSymbols::kCapContextUsageMonthFormat;
1532 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount,
1533 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneWide]): NULL, status);
1534 capContextUsageType = DateFormatSymbols::kCapContextUsageMonthStandalone;
1536 } else if (count == 3) {
1537 if (patternCharIndex == UDAT_MONTH_FIELD) {
1538 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fShortMonths, fSymbols->fShortMonthsCount,
1539 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatAbbrev]): NULL, status);
1540 capContextUsageType = DateFormatSymbols::kCapContextUsageMonthFormat;
1542 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount,
1543 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev]): NULL, status);
1544 capContextUsageType = DateFormatSymbols::kCapContextUsageMonthStandalone;
1547 UnicodeString monthNumber;
1548 zeroPaddingNumber(currentNumberFormat,monthNumber, value + 1, count, maxIntCount);
1549 _appendSymbolWithMonthPattern(appendTo, 0, &monthNumber, 1,
1550 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric]): NULL, status);
1555 // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
1556 case UDAT_HOUR_OF_DAY1_FIELD:
1558 zeroPaddingNumber(currentNumberFormat,appendTo, cal.getMaximum(UCAL_HOUR_OF_DAY) + 1, count, maxIntCount);
1560 zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
1563 case UDAT_FRACTIONAL_SECOND_FIELD:
1564 // Fractional seconds left-justify
1566 currentNumberFormat->setMinimumIntegerDigits((count > 3) ? 3 : count);
1567 currentNumberFormat->setMaximumIntegerDigits(maxIntCount);
1570 } else if (count == 2) {
1573 FieldPosition p(FieldPosition::DONT_CARE);
1574 currentNumberFormat->format(value, appendTo, p);
1576 currentNumberFormat->setMinimumIntegerDigits(count - 3);
1577 currentNumberFormat->format((int32_t)0, appendTo, p);
1582 // for "ee" or "e", use local numeric day-of-the-week
1583 // for "EEEEEE" or "eeeeee", write out the short day-of-the-week name
1584 // for "EEEEE" or "eeeee", write out the narrow day-of-the-week name
1585 // for "EEEE" or "eeee", write out the wide day-of-the-week name
1586 // for "EEE" or "EE" or "E" or "eee", write out the abbreviated day-of-the-week name
1587 case UDAT_DOW_LOCAL_FIELD:
1589 zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
1592 // fall through to EEEEE-EEE handling, but for that we don't want local day-of-week,
1593 // we want standard day-of-week, so first fix value to work for EEEEE-EEE.
1594 value = cal.get(UCAL_DAY_OF_WEEK, status);
1595 if (U_FAILURE(status)) {
1598 // fall through, do not break here
1600 case UDAT_DAY_OF_WEEK_FIELD:
1602 _appendSymbol(appendTo, value, fSymbols->fNarrowWeekdays,
1603 fSymbols->fNarrowWeekdaysCount);
1604 capContextUsageType = DateFormatSymbols::kCapContextUsageDayNarrow;
1605 } else if (count == 4) {
1606 _appendSymbol(appendTo, value, fSymbols->fWeekdays,
1607 fSymbols->fWeekdaysCount);
1608 capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat;
1609 } else if (count == 6) {
1610 _appendSymbol(appendTo, value, fSymbols->fShorterWeekdays,
1611 fSymbols->fShorterWeekdaysCount);
1612 capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat;
1614 _appendSymbol(appendTo, value, fSymbols->fShortWeekdays,
1615 fSymbols->fShortWeekdaysCount);
1616 capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat;
1620 // for "ccc", write out the abbreviated day-of-the-week name
1621 // for "cccc", write out the wide day-of-the-week name
1622 // for "ccccc", use the narrow day-of-the-week name
1623 // for "ccccc", use the short day-of-the-week name
1624 case UDAT_STANDALONE_DAY_FIELD:
1626 zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, maxIntCount);
1629 // fall through to alpha DOW handling, but for that we don't want local day-of-week,
1630 // we want standard day-of-week, so first fix value.
1631 value = cal.get(UCAL_DAY_OF_WEEK, status);
1632 if (U_FAILURE(status)) {
1636 _appendSymbol(appendTo, value, fSymbols->fStandaloneNarrowWeekdays,
1637 fSymbols->fStandaloneNarrowWeekdaysCount);
1638 capContextUsageType = DateFormatSymbols::kCapContextUsageDayNarrow;
1639 } else if (count == 4) {
1640 _appendSymbol(appendTo, value, fSymbols->fStandaloneWeekdays,
1641 fSymbols->fStandaloneWeekdaysCount);
1642 capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone;
1643 } else if (count == 6) {
1644 _appendSymbol(appendTo, value, fSymbols->fStandaloneShorterWeekdays,
1645 fSymbols->fStandaloneShorterWeekdaysCount);
1646 capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone;
1647 } else { // count == 3
1648 _appendSymbol(appendTo, value, fSymbols->fStandaloneShortWeekdays,
1649 fSymbols->fStandaloneShortWeekdaysCount);
1650 capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone;
1654 // for "a" symbol, write out the whole AM/PM string
1655 case UDAT_AM_PM_FIELD:
1657 _appendSymbol(appendTo, value, fSymbols->fAmPms,
1658 fSymbols->fAmPmsCount);
1660 _appendSymbol(appendTo, value, fSymbols->fNarrowAmPms,
1661 fSymbols->fNarrowAmPmsCount);
1665 // if we see pattern character for UDAT_TIME_SEPARATOR_FIELD (none currently defined),
1666 // write out the time separator string. Leave support in for future definition.
1667 case UDAT_TIME_SEPARATOR_FIELD:
1669 UnicodeString separator;
1670 appendTo += fSymbols->getTimeSeparatorString(separator);
1674 // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
1676 case UDAT_HOUR1_FIELD:
1678 zeroPaddingNumber(currentNumberFormat,appendTo, cal.getLeastMaximum(UCAL_HOUR) + 1, count, maxIntCount);
1680 zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
1683 case UDAT_TIMEZONE_FIELD: // 'z'
1684 case UDAT_TIMEZONE_RFC_FIELD: // 'Z'
1685 case UDAT_TIMEZONE_GENERIC_FIELD: // 'v'
1686 case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V'
1687 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: // 'O'
1688 case UDAT_TIMEZONE_ISO_FIELD: // 'X'
1689 case UDAT_TIMEZONE_ISO_LOCAL_FIELD: // 'x'
1692 UnicodeString zoneString(zsbuf, 0, UPRV_LENGTHOF(zsbuf));
1693 const TimeZone& tz = cal.getTimeZone();
1694 UDate date = cal.getTime(status);
1695 if (U_SUCCESS(status)) {
1696 if (patternCharIndex == UDAT_TIMEZONE_FIELD) {
1699 tzFormat()->format(UTZFMT_STYLE_SPECIFIC_SHORT, tz, date, zoneString);
1700 capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort;
1703 tzFormat()->format(UTZFMT_STYLE_SPECIFIC_LONG, tz, date, zoneString);
1704 capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong;
1707 else if (patternCharIndex == UDAT_TIMEZONE_RFC_FIELD) {
1710 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString);
1711 } else if (count == 5) {
1713 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString);
1715 // "ZZ", "ZZZ", "ZZZZ"
1716 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString);
1719 else if (patternCharIndex == UDAT_TIMEZONE_GENERIC_FIELD) {
1722 tzFormat()->format(UTZFMT_STYLE_GENERIC_SHORT, tz, date, zoneString);
1723 capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort;
1724 } else if (count == 4) {
1726 tzFormat()->format(UTZFMT_STYLE_GENERIC_LONG, tz, date, zoneString);
1727 capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong;
1730 else if (patternCharIndex == UDAT_TIMEZONE_SPECIAL_FIELD) {
1733 tzFormat()->format(UTZFMT_STYLE_ZONE_ID_SHORT, tz, date, zoneString);
1734 } else if (count == 2) {
1736 tzFormat()->format(UTZFMT_STYLE_ZONE_ID, tz, date, zoneString);
1737 } else if (count == 3) {
1739 tzFormat()->format(UTZFMT_STYLE_EXEMPLAR_LOCATION, tz, date, zoneString);
1740 } else if (count == 4) {
1742 tzFormat()->format(UTZFMT_STYLE_GENERIC_LOCATION, tz, date, zoneString);
1743 capContextUsageType = DateFormatSymbols::kCapContextUsageZoneLong;
1746 else if (patternCharIndex == UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD) {
1749 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT_SHORT, tz, date, zoneString);
1750 } else if (count == 4) {
1752 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString);
1755 else if (patternCharIndex == UDAT_TIMEZONE_ISO_FIELD) {
1758 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_SHORT, tz, date, zoneString);
1759 } else if (count == 2) {
1761 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_FIXED, tz, date, zoneString);
1762 } else if (count == 3) {
1764 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FIXED, tz, date, zoneString);
1765 } else if (count == 4) {
1767 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_FULL, tz, date, zoneString);
1768 } else if (count == 5) {
1770 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString);
1773 else if (patternCharIndex == UDAT_TIMEZONE_ISO_LOCAL_FIELD) {
1776 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT, tz, date, zoneString);
1777 } else if (count == 2) {
1779 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED, tz, date, zoneString);
1780 } else if (count == 3) {
1782 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED, tz, date, zoneString);
1783 } else if (count == 4) {
1785 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString);
1786 } else if (count == 5) {
1788 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL, tz, date, zoneString);
1795 appendTo += zoneString;
1799 case UDAT_QUARTER_FIELD:
1801 _appendSymbol(appendTo, value/3, fSymbols->fQuarters,
1802 fSymbols->fQuartersCount);
1803 else if (count == 3)
1804 _appendSymbol(appendTo, value/3, fSymbols->fShortQuarters,
1805 fSymbols->fShortQuartersCount);
1807 zeroPaddingNumber(currentNumberFormat,appendTo, (value/3) + 1, count, maxIntCount);
1810 case UDAT_STANDALONE_QUARTER_FIELD:
1812 _appendSymbol(appendTo, value/3, fSymbols->fStandaloneQuarters,
1813 fSymbols->fStandaloneQuartersCount);
1814 else if (count == 3)
1815 _appendSymbol(appendTo, value/3, fSymbols->fStandaloneShortQuarters,
1816 fSymbols->fStandaloneShortQuartersCount);
1818 zeroPaddingNumber(currentNumberFormat,appendTo, (value/3) + 1, count, maxIntCount);
1821 case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
1823 const UnicodeString *toAppend = NULL;
1824 int32_t hour = cal.get(UCAL_HOUR_OF_DAY, status);
1826 // Note: "midnight" can be ambiguous as to whether it refers to beginning of day or end of day.
1827 // For ICU 57 output of "midnight" is temporarily suppressed.
1829 // For "midnight" and "noon":
1830 // Time, as displayed, must be exactly noon or midnight.
1831 // This means minutes and seconds, if present, must be zero.
1832 if ((/*hour == 0 ||*/ hour == 12) &&
1833 (!fHasMinute || cal.get(UCAL_MINUTE, status) == 0) &&
1834 (!fHasSecond || cal.get(UCAL_SECOND, status) == 0)) {
1835 // Stealing am/pm value to use as our array index.
1836 // It works out: am/midnight are both 0, pm/noon are both 1,
1837 // 12 am is 12 midnight, and 12 pm is 12 noon.
1838 int32_t value = cal.get(UCAL_AM_PM, status);
1841 toAppend = &fSymbols->fAbbreviatedDayPeriods[value];
1842 } else if (count == 4 || count > 5) {
1843 toAppend = &fSymbols->fWideDayPeriods[value];
1844 } else { // count == 5
1845 toAppend = &fSymbols->fNarrowDayPeriods[value];
1849 // toAppend is NULL if time isn't exactly midnight or noon (as displayed).
1850 // toAppend is bogus if time is midnight or noon, but no localized string exists.
1851 // In either case, fall back to am/pm.
1852 if (toAppend == NULL || toAppend->isBogus()) {
1853 // Reformat with identical arguments except ch, now changed to 'a'.
1854 subFormat(appendTo, 0x61, count, capitalizationContext, fieldNum,
1855 handler, cal, mutableNFs, status);
1857 appendTo += *toAppend;
1863 case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
1865 // TODO: Maybe fetch the DayperiodRules during initialization (instead of at the first
1866 // loading of an instance) if a relevant pattern character (b or B) is used.
1867 const DayPeriodRules *ruleSet = DayPeriodRules::getInstance(this->getSmpFmtLocale(), status);
1868 if (U_FAILURE(status)) {
1869 // Data doesn't conform to spec, therefore loading failed.
1872 if (ruleSet == NULL) {
1873 // Data doesn't exist for the locale we're looking for.
1874 // Falling back to am/pm.
1875 subFormat(appendTo, 0x61, count, capitalizationContext, fieldNum,
1876 handler, cal, mutableNFs, status);
1880 // Get current display time.
1881 int32_t hour = cal.get(UCAL_HOUR_OF_DAY, status);
1884 minute = cal.get(UCAL_MINUTE, status);
1888 second = cal.get(UCAL_SECOND, status);
1891 // Determine day period.
1892 DayPeriodRules::DayPeriod periodType;
1893 if (hour == 0 && minute == 0 && second == 0 && ruleSet->hasMidnight()) {
1894 periodType = DayPeriodRules::DAYPERIOD_MIDNIGHT;
1895 } else if (hour == 12 && minute == 0 && second == 0 && ruleSet->hasNoon()) {
1896 periodType = DayPeriodRules::DAYPERIOD_NOON;
1898 periodType = ruleSet->getDayPeriodForHour(hour);
1901 // Rule set exists, therefore periodType can't be UNKNOWN.
1902 // Get localized string.
1903 U_ASSERT(periodType != DayPeriodRules::DAYPERIOD_UNKNOWN);
1904 UnicodeString *toAppend = NULL;
1907 // Note: "midnight" can be ambiguous as to whether it refers to beginning of day or end of day.
1908 // For ICU 57 output of "midnight" is temporarily suppressed.
1910 if (periodType != DayPeriodRules::DAYPERIOD_AM &&
1911 periodType != DayPeriodRules::DAYPERIOD_PM &&
1912 periodType != DayPeriodRules::DAYPERIOD_MIDNIGHT) {
1913 index = (int32_t)periodType;
1915 toAppend = &fSymbols->fAbbreviatedDayPeriods[index]; // i.e. short
1916 } else if (count == 4 || count > 5) {
1917 toAppend = &fSymbols->fWideDayPeriods[index];
1918 } else { // count == 5
1919 toAppend = &fSymbols->fNarrowDayPeriods[index];
1923 // Fallback schedule:
1924 // Midnight/Noon -> General Periods -> AM/PM.
1926 // Midnight/Noon -> General Periods.
1927 if ((toAppend == NULL || toAppend->isBogus()) &&
1928 (periodType == DayPeriodRules::DAYPERIOD_MIDNIGHT ||
1929 periodType == DayPeriodRules::DAYPERIOD_NOON)) {
1930 periodType = ruleSet->getDayPeriodForHour(hour);
1931 index = (int32_t)periodType;
1934 toAppend = &fSymbols->fAbbreviatedDayPeriods[index]; // i.e. short
1935 } else if (count == 4 || count > 5) {
1936 toAppend = &fSymbols->fWideDayPeriods[index];
1937 } else { // count == 5
1938 toAppend = &fSymbols->fNarrowDayPeriods[index];
1942 // General Periods -> AM/PM.
1943 if (periodType == DayPeriodRules::DAYPERIOD_AM ||
1944 periodType == DayPeriodRules::DAYPERIOD_PM ||
1945 toAppend->isBogus()) {
1946 subFormat(appendTo, 0x61, count, capitalizationContext, fieldNum,
1947 handler, cal, mutableNFs, status);
1950 appendTo += *toAppend;
1956 // all of the other pattern symbols can be formatted as simple numbers with
1957 // appropriate zero padding
1959 zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
1962 #if !UCONFIG_NO_BREAK_ITERATION
1963 // if first field, check to see whether we need to and are able to titlecase it
1964 if (fieldNum == 0 && u_islower(appendTo.char32At(beginOffset)) && fCapitalizationBrkIter != NULL) {
1965 UBool titlecase = FALSE;
1966 switch (capitalizationContext) {
1967 case UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE:
1970 case UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU:
1971 titlecase = fSymbols->fCapitalization[capContextUsageType][0];
1973 case UDISPCTX_CAPITALIZATION_FOR_STANDALONE:
1974 titlecase = fSymbols->fCapitalization[capContextUsageType][1];
1977 // titlecase = FALSE;
1981 UnicodeString firstField(appendTo, beginOffset);
1982 firstField.toTitle(fCapitalizationBrkIter, fLocale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
1983 appendTo.replaceBetween(beginOffset, appendTo.length(), firstField);
1988 handler.addAttribute(fgPatternIndexToDateFormatField[patternCharIndex], beginOffset, appendTo.length());
1991 //----------------------------------------------------------------------
1993 void SimpleDateFormat::adoptNumberFormat(NumberFormat *formatToAdopt) {
1994 fixNumberFormatForDates(*formatToAdopt);
1995 delete fNumberFormat;
1996 fNumberFormat = formatToAdopt;
1998 // We successfully set the default number format. Now delete the overrides
2000 if (fSharedNumberFormatters) {
2001 freeSharedNumberFormatters(fSharedNumberFormatters);
2002 fSharedNumberFormatters = NULL;
2006 void SimpleDateFormat::adoptNumberFormat(const UnicodeString& fields, NumberFormat *formatToAdopt, UErrorCode &status){
2007 fixNumberFormatForDates(*formatToAdopt);
2008 LocalPointer<NumberFormat> fmt(formatToAdopt);
2009 if (U_FAILURE(status)) {
2013 // We must ensure fSharedNumberFormatters is allocated.
2014 if (fSharedNumberFormatters == NULL) {
2015 fSharedNumberFormatters = allocSharedNumberFormatters();
2016 if (fSharedNumberFormatters == NULL) {
2017 status = U_MEMORY_ALLOCATION_ERROR;
2021 const SharedNumberFormat *newFormat = createSharedNumberFormat(fmt.orphan());
2022 if (newFormat == NULL) {
2023 status = U_MEMORY_ALLOCATION_ERROR;
2026 for (int i=0; i<fields.length(); i++) {
2027 UChar field = fields.charAt(i);
2028 // if the pattern character is unrecognized, signal an error and bail out
2029 UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(field);
2030 if (patternCharIndex == UDAT_FIELD_COUNT) {
2031 status = U_INVALID_FORMAT_ERROR;
2032 newFormat->deleteIfZeroRefCount();
2036 // Set the number formatter in the table
2037 SharedObject::copyPtr(
2038 newFormat, fSharedNumberFormatters[patternCharIndex]);
2040 newFormat->deleteIfZeroRefCount();
2043 const NumberFormat *
2044 SimpleDateFormat::getNumberFormatForField(UChar field) const {
2045 UDateFormatField index = DateFormatSymbols::getPatternCharIndex(field);
2046 if (index == UDAT_FIELD_COUNT) {
2049 return getNumberFormatByIndex(index);
2052 //----------------------------------------------------------------------
2054 SimpleDateFormat::zeroPaddingNumber(
2055 NumberFormat *currentNumberFormat,
2056 UnicodeString &appendTo,
2057 int32_t value, int32_t minDigits, int32_t maxDigits) const
2059 if (currentNumberFormat!=NULL) {
2060 FieldPosition pos(FieldPosition::DONT_CARE);
2062 currentNumberFormat->setMinimumIntegerDigits(minDigits);
2063 currentNumberFormat->setMaximumIntegerDigits(maxDigits);
2064 currentNumberFormat->format(value, appendTo, pos); // 3rd arg is there to speed up processing
2068 //----------------------------------------------------------------------
2071 * Return true if the given format character, occuring count
2072 * times, represents a numeric field.
2074 UBool SimpleDateFormat::isNumeric(UChar formatChar, int32_t count) {
2075 return DateFormatSymbols::isNumericPatternChar(formatChar, count);
2079 SimpleDateFormat::isAtNumericField(const UnicodeString &pattern, int32_t patternOffset) {
2080 if (patternOffset >= pattern.length()) {
2084 UChar ch = pattern.charAt(patternOffset);
2085 UDateFormatField f = DateFormatSymbols::getPatternCharIndex(ch);
2086 if (f == UDAT_FIELD_COUNT) {
2090 int32_t i = patternOffset;
2091 while (pattern.charAt(++i) == ch) {}
2092 return DateFormatSymbols::isNumericField(f, i - patternOffset);
2096 SimpleDateFormat::isAfterNonNumericField(const UnicodeString &pattern, int32_t patternOffset) {
2097 if (patternOffset <= 0) {
2098 // not after any field
2101 UChar ch = pattern.charAt(--patternOffset);
2102 UDateFormatField f = DateFormatSymbols::getPatternCharIndex(ch);
2103 if (f == UDAT_FIELD_COUNT) {
2104 // not after any field
2107 int32_t i = patternOffset;
2108 while (pattern.charAt(--i) == ch) {}
2109 return !DateFormatSymbols::isNumericField(f, patternOffset - i);
2113 SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& parsePos) const
2115 UErrorCode status = U_ZERO_ERROR;
2116 int32_t pos = parsePos.getIndex();
2117 if(parsePos.getIndex() < 0) {
2118 parsePos.setErrorIndex(0);
2121 int32_t start = pos;
2123 // Hold the day period until everything else is parsed, because we need
2124 // the hour to interpret time correctly.
2125 int32_t dayPeriodInt = -1;
2127 UBool ambiguousYear[] = { FALSE };
2128 int32_t saveHebrewMonth = -1;
2130 UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
2131 SimpleDateFormatMutableNFs mutableNFs;
2133 // For parsing abutting numeric fields. 'abutPat' is the
2134 // offset into 'pattern' of the first of 2 or more abutting
2135 // numeric fields. 'abutStart' is the offset into 'text'
2136 // where parsing the fields begins. 'abutPass' starts off as 0
2137 // and increments each time we try to parse the fields.
2138 int32_t abutPat = -1; // If >=0, we are in a run of abutting numeric fields
2139 int32_t abutStart = 0;
2140 int32_t abutPass = 0;
2141 UBool inQuote = FALSE;
2143 MessageFormat * numericLeapMonthFormatter = NULL;
2145 Calendar* calClone = NULL;
2146 Calendar *workCal = &cal;
2147 if (&cal != fCalendar && uprv_strcmp(cal.getType(), fCalendar->getType()) != 0) {
2148 // Different calendar type
2149 // We use the time/zone from the input calendar, but
2150 // do not use the input calendar for field calculation.
2151 calClone = fCalendar->clone();
2152 if (calClone != NULL) {
2153 calClone->setTime(cal.getTime(status),status);
2154 if (U_FAILURE(status)) {
2157 calClone->setTimeZone(cal.getTimeZone());
2160 status = U_MEMORY_ALLOCATION_ERROR;
2165 if (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) {
2166 numericLeapMonthFormatter = new MessageFormat(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric], fLocale, status);
2167 if (numericLeapMonthFormatter == NULL) {
2168 status = U_MEMORY_ALLOCATION_ERROR;
2170 } else if (U_FAILURE(status)) {
2171 goto ExitParse; // this will delete numericLeapMonthFormatter
2175 for (int32_t i=0; i<fPattern.length(); ++i) {
2176 UChar ch = fPattern.charAt(i);
2178 // Handle alphabetic field characters.
2179 if (!inQuote && isSyntaxChar(ch)) {
2180 int32_t fieldPat = i;
2182 // Count the length of this field specifier
2184 while ((i+1)<fPattern.length() &&
2185 fPattern.charAt(i+1) == ch) {
2190 if (isNumeric(ch, count)) {
2192 // Determine if there is an abutting numeric field.
2193 // Record the start of a set of abutting numeric fields.
2194 if (isAtNumericField(fPattern, i + 1)) {
2201 abutPat = -1; // End of any abutting fields
2204 // Handle fields within a run of abutting numeric fields. Take
2205 // the pattern "HHmmss" as an example. We will try to parse
2206 // 2/2/2 characters of the input text, then if that fails,
2207 // 1/2/2. We only adjust the width of the leftmost field; the
2208 // others remain fixed. This allows "123456" => 12:34:56, but
2209 // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
2210 // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
2212 // If we are at the start of a run of abutting fields, then
2213 // shorten this field in each pass. If we can't shorten
2214 // this field any more, then the parse of this set of
2215 // abutting numeric fields has failed.
2216 if (fieldPat == abutPat) {
2217 count -= abutPass++;
2219 status = U_PARSE_ERROR;
2224 pos = subParse(text, pos, ch, count,
2225 TRUE, FALSE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType, mutableNFs);
2227 // If the parse fails anywhere in the run, back up to the
2228 // start of the run and retry.
2236 // Handle non-numeric fields and non-abutting numeric
2238 else if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
2239 int32_t s = subParse(text, pos, ch, count,
2240 FALSE, TRUE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType, mutableNFs, &dayPeriodInt);
2243 // era not present, in special cases allow this to continue
2244 // from the position where the era was expected
2247 if (i+1 < fPattern.length()) {
2248 // move to next pattern character
2249 UChar ch = fPattern.charAt(i+1);
2251 // check for whitespace
2252 if (PatternProps::isWhiteSpace(ch)) {
2254 // Advance over run in pattern
2255 while ((i+1)<fPattern.length() &&
2256 PatternProps::isWhiteSpace(fPattern.charAt(i+1))) {
2263 status = U_PARSE_ERROR;
2270 // Handle literal pattern characters. These are any
2271 // quoted characters and non-alphabetic unquoted
2275 abutPat = -1; // End of any abutting fields
2277 if (! matchLiterals(fPattern, i, text, pos, getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status), getBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH, status), isLenient())) {
2278 status = U_PARSE_ERROR;
2284 // Special hack for trailing "." after non-numeric field.
2285 if (text.charAt(pos) == 0x2e && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)) {
2286 // only do if the last field is not numeric
2287 if (isAfterNonNumericField(fPattern, fPattern.length())) {
2288 pos++; // skip the extra "."
2292 // If dayPeriod is set, use it in conjunction with hour-of-day to determine am/pm.
2293 if (dayPeriodInt >= 0) {
2294 DayPeriodRules::DayPeriod dayPeriod = (DayPeriodRules::DayPeriod)dayPeriodInt;
2295 const DayPeriodRules *ruleSet = DayPeriodRules::getInstance(this->getSmpFmtLocale(), status);
2297 if (!cal.isSet(UCAL_HOUR) && !cal.isSet(UCAL_HOUR_OF_DAY)) {
2298 // If hour is not set, set time to the midpoint of current day period, overwriting
2299 // minutes if it's set.
2300 double midPoint = ruleSet->getMidPointForDayPeriod(dayPeriod, status);
2302 // If we can't get midPoint we do nothing.
2303 if (U_SUCCESS(status)) {
2304 // Truncate midPoint toward zero to get the hour.
2305 // Any leftover means it was a half-hour.
2306 int32_t midPointHour = (int32_t) midPoint;
2307 int32_t midPointMinute = (midPoint - midPointHour) > 0 ? 30 : 0;
2309 // No need to set am/pm because hour-of-day is set last therefore takes precedence.
2310 cal.set(UCAL_HOUR_OF_DAY, midPointHour);
2311 cal.set(UCAL_MINUTE, midPointMinute);
2316 if (cal.isSet(UCAL_HOUR_OF_DAY)) { // Hour is parsed in 24-hour format.
2317 hourOfDay = cal.get(UCAL_HOUR_OF_DAY, status);
2318 } else { // Hour is parsed in 12-hour format.
2319 hourOfDay = cal.get(UCAL_HOUR, status);
2320 // cal.get() turns 12 to 0 for 12-hour time; change 0 to 12
2321 // so 0 unambiguously means a 24-hour time from above.
2322 if (hourOfDay == 0) { hourOfDay = 12; }
2324 U_ASSERT(0 <= hourOfDay && hourOfDay <= 23);
2327 // If hour-of-day is 0 or 13 thru 23 then input time in unambiguously in 24-hour format.
2328 if (hourOfDay == 0 || (13 <= hourOfDay && hourOfDay <= 23)) {
2329 // Make hour-of-day take precedence over (hour + am/pm) by setting it again.
2330 cal.set(UCAL_HOUR_OF_DAY, hourOfDay);
2332 // We have a 12-hour time and need to choose between am and pm.
2333 // Behave as if dayPeriod spanned 6 hours each way from its center point.
2334 // This will parse correctly for consistent time + period (e.g. 10 at night) as
2335 // well as provide a reasonable recovery for inconsistent time + period (e.g.
2336 // 9 in the afternoon).
2338 // Assume current time is in the AM.
2339 // - Change 12 back to 0 for easier handling of 12am.
2340 // - Append minutes as fractional hours because e.g. 8:15 and 8:45 could be parsed
2341 // into different half-days if center of dayPeriod is at 14:30.
2342 // - cal.get(MINUTE) will return 0 if MINUTE is unset, which works.
2343 if (hourOfDay == 12) { hourOfDay = 0; }
2344 double currentHour = hourOfDay + (cal.get(UCAL_MINUTE, status)) / 60.0;
2345 double midPointHour = ruleSet->getMidPointForDayPeriod(dayPeriod, status);
2347 if (U_SUCCESS(status)) {
2348 double hoursAheadMidPoint = currentHour - midPointHour;
2350 // Assume current time is in the AM.
2351 if (-6 <= hoursAheadMidPoint && hoursAheadMidPoint < 6) {
2352 // Assumption holds; set time as such.
2353 cal.set(UCAL_AM_PM, 0);
2355 cal.set(UCAL_AM_PM, 1);
2362 // At this point the fields of Calendar have been set. Calendar
2363 // will fill in default values for missing fields when the time
2366 parsePos.setIndex(pos);
2368 // This part is a problem: When we call parsedDate.after, we compute the time.
2369 // Take the date April 3 2004 at 2:30 am. When this is first set up, the year
2370 // will be wrong if we're parsing a 2-digit year pattern. It will be 1904.
2371 // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am
2372 // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
2373 // on that day. It is therefore parsed out to fields as 3:30 am. Then we
2374 // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is
2375 // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
2377 UDate parsedDate = calendar.getTime();
2378 if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
2379 calendar.add(Calendar.YEAR, 100);
2380 parsedDate = calendar.getTime();
2383 // Because of the above condition, save off the fields in case we need to readjust.
2384 // The procedure we use here is not particularly efficient, but there is no other
2385 // way to do this given the API restrictions present in Calendar. We minimize
2386 // inefficiency by only performing this computation when it might apply, that is,
2387 // when the two-digit year is equal to the start year, and thus might fall at the
2388 // front or the back of the default century. This only works because we adjust
2389 // the year correctly to start with in other cases -- see subParse().
2390 if (ambiguousYear[0] || tzTimeType != UTZFMT_TIME_TYPE_UNKNOWN) // If this is true then the two-digit year == the default start year
2392 // We need a copy of the fields, and we need to avoid triggering a call to
2393 // complete(), which will recalculate the fields. Since we can't access
2394 // the fields[] array in Calendar, we clone the entire object. This will
2395 // stop working if Calendar.clone() is ever rewritten to call complete().
2397 if (ambiguousYear[0]) {
2399 // Check for failed cloning.
2401 status = U_MEMORY_ALLOCATION_ERROR;
2404 UDate parsedDate = copy->getTime(status);
2405 // {sfb} check internalGetDefaultCenturyStart
2406 if (fHaveDefaultCentury && (parsedDate < fDefaultCenturyStart)) {
2407 // We can't use add here because that does a complete() first.
2408 cal.set(UCAL_YEAR, fDefaultCenturyStartYear + 100);
2413 if (tzTimeType != UTZFMT_TIME_TYPE_UNKNOWN) {
2415 // Check for failed cloning.
2417 status = U_MEMORY_ALLOCATION_ERROR;
2420 const TimeZone & tz = cal.getTimeZone();
2421 BasicTimeZone *btz = NULL;
2423 if (dynamic_cast<const OlsonTimeZone *>(&tz) != NULL
2424 || dynamic_cast<const SimpleTimeZone *>(&tz) != NULL
2425 || dynamic_cast<const RuleBasedTimeZone *>(&tz) != NULL
2426 || dynamic_cast<const VTimeZone *>(&tz) != NULL) {
2427 btz = (BasicTimeZone*)&tz;
2431 copy->set(UCAL_ZONE_OFFSET, 0);
2432 copy->set(UCAL_DST_OFFSET, 0);
2433 UDate localMillis = copy->getTime(status);
2435 // Make sure parsed time zone type (Standard or Daylight)
2436 // matches the rule used by the parsed time zone.
2439 if (tzTimeType == UTZFMT_TIME_TYPE_STANDARD) {
2440 btz->getOffsetFromLocal(localMillis,
2441 BasicTimeZone::kStandard, BasicTimeZone::kStandard, raw, dst, status);
2443 btz->getOffsetFromLocal(localMillis,
2444 BasicTimeZone::kDaylight, BasicTimeZone::kDaylight, raw, dst, status);
2447 // No good way to resolve ambiguous time at transition,
2448 // but following code work in most case.
2449 tz.getOffset(localMillis, TRUE, raw, dst, status);
2452 // Now, compare the results with parsed type, either standard or daylight saving time
2453 int32_t resolvedSavings = dst;
2454 if (tzTimeType == UTZFMT_TIME_TYPE_STANDARD) {
2456 // Override DST_OFFSET = 0 in the result calendar
2457 resolvedSavings = 0;
2459 } else { // tztype == TZTYPE_DST
2462 UDate time = localMillis + raw;
2463 // We use the nearest daylight saving time rule.
2464 TimeZoneTransition beforeTrs, afterTrs;
2465 UDate beforeT = time, afterT = time;
2466 int32_t beforeSav = 0, afterSav = 0;
2467 UBool beforeTrsAvail, afterTrsAvail;
2469 // Search for DST rule before or on the time
2471 beforeTrsAvail = btz->getPreviousTransition(beforeT, TRUE, beforeTrs);
2472 if (!beforeTrsAvail) {
2475 beforeT = beforeTrs.getTime() - 1;
2476 beforeSav = beforeTrs.getFrom()->getDSTSavings();
2477 if (beforeSav != 0) {
2482 // Search for DST rule after the time
2484 afterTrsAvail = btz->getNextTransition(afterT, FALSE, afterTrs);
2485 if (!afterTrsAvail) {
2488 afterT = afterTrs.getTime();
2489 afterSav = afterTrs.getTo()->getDSTSavings();
2490 if (afterSav != 0) {
2495 if (beforeTrsAvail && afterTrsAvail) {
2496 if (time - beforeT > afterT - time) {
2497 resolvedSavings = afterSav;
2499 resolvedSavings = beforeSav;
2501 } else if (beforeTrsAvail && beforeSav != 0) {
2502 resolvedSavings = beforeSav;
2503 } else if (afterTrsAvail && afterSav != 0) {
2504 resolvedSavings = afterSav;
2506 resolvedSavings = btz->getDSTSavings();
2509 resolvedSavings = tz.getDSTSavings();
2511 if (resolvedSavings == 0) {
2513 resolvedSavings = U_MILLIS_PER_HOUR;
2517 cal.set(UCAL_ZONE_OFFSET, raw);
2518 cal.set(UCAL_DST_OFFSET, resolvedSavings);
2523 // Set the parsed result if local calendar is used
2524 // instead of the input calendar
2525 if (U_SUCCESS(status) && workCal != &cal) {
2526 cal.setTimeZone(workCal->getTimeZone());
2527 cal.setTime(workCal->getTime(status), status);
2530 if (numericLeapMonthFormatter != NULL) {
2531 delete numericLeapMonthFormatter;
2533 if (calClone != NULL) {
2537 // If any Calendar calls failed, we pretend that we
2538 // couldn't parse the string, when in reality this isn't quite accurate--
2539 // we did parse it; the Calendar calls just failed.
2540 if (U_FAILURE(status)) {
2541 parsePos.setErrorIndex(pos);
2542 parsePos.setIndex(start);
2546 //----------------------------------------------------------------------
2549 matchStringWithOptionalDot(const UnicodeString &text,
2551 const UnicodeString &data);
2553 int32_t SimpleDateFormat::matchQuarterString(const UnicodeString& text,
2555 UCalendarDateFields field,
2556 const UnicodeString* data,
2558 Calendar& cal) const
2561 int32_t count = dataCount;
2563 // There may be multiple strings in the data[] array which begin with
2564 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2565 // We keep track of the longest match, and return that. Note that this
2566 // unfortunately requires us to test all array elements.
2567 int32_t bestMatchLength = 0, bestMatch = -1;
2568 UnicodeString bestMatchName;
2570 for (; i < count; ++i) {
2571 int32_t matchLength = 0;
2572 if ((matchLength = matchStringWithOptionalDot(text, start, data[i])) > bestMatchLength) {
2573 bestMatchLength = matchLength;
2578 if (bestMatch >= 0) {
2579 cal.set(field, bestMatch * 3);
2580 return start + bestMatchLength;
2586 int32_t SimpleDateFormat::matchDayPeriodStrings(const UnicodeString& text, int32_t start,
2587 const UnicodeString* data, int32_t dataCount,
2588 int32_t &dayPeriod) const
2591 int32_t bestMatchLength = 0, bestMatch = -1;
2593 for (int32_t i = 0; i < dataCount; ++i) {
2594 int32_t matchLength = 0;
2595 if ((matchLength = matchStringWithOptionalDot(text, start, data[i])) > bestMatchLength) {
2596 bestMatchLength = matchLength;
2601 if (bestMatch >= 0) {
2602 dayPeriod = bestMatch;
2603 return start + bestMatchLength;
2609 //----------------------------------------------------------------------
2610 UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern,
2611 int32_t &patternOffset,
2612 const UnicodeString &text,
2613 int32_t &textOffset,
2614 UBool whitespaceLenient,
2615 UBool partialMatchLenient,
2618 UBool inQuote = FALSE;
2619 UnicodeString literal;
2620 int32_t i = patternOffset;
2622 // scan pattern looking for contiguous literal characters
2623 for ( ; i < pattern.length(); i += 1) {
2624 UChar ch = pattern.charAt(i);
2626 if (!inQuote && isSyntaxChar(ch)) {
2631 // Match a quote literal ('') inside OR outside of quotes
2632 if ((i + 1) < pattern.length() && pattern.charAt(i + 1) == QUOTE) {
2643 // at this point, literal contains the literal text
2644 // and i is the index of the next non-literal pattern character.
2646 int32_t t = textOffset;
2648 if (whitespaceLenient) {
2649 // trim leading, trailing whitespace from
2653 // ignore any leading whitespace in the text
2654 while (t < text.length() && u_isWhitespace(text.charAt(t))) {
2659 for (p = 0; p < literal.length() && t < text.length();) {
2660 UBool needWhitespace = FALSE;
2662 while (p < literal.length() && PatternProps::isWhiteSpace(literal.charAt(p))) {
2663 needWhitespace = TRUE;
2667 if (needWhitespace) {
2670 while (t < text.length()) {
2671 UChar tch = text.charAt(t);
2673 if (!u_isUWhiteSpace(tch) && !PatternProps::isWhiteSpace(tch)) {
2680 // TODO: should we require internal spaces
2681 // in lenient mode? (There won't be any
2682 // leading or trailing spaces)
2683 if (!whitespaceLenient && t == tStart) {
2684 // didn't find matching whitespace:
2685 // an error in strict mode
2689 // In strict mode, this run of whitespace
2690 // may have been at the end.
2691 if (p >= literal.length()) {
2695 if (t >= text.length() || literal.charAt(p) != text.charAt(t)) {
2696 // Ran out of text, or found a non-matching character:
2697 // OK in lenient mode, an error in strict mode.
2698 if (whitespaceLenient) {
2699 if (t == textOffset && text.charAt(t) == 0x2e &&
2700 isAfterNonNumericField(pattern, patternOffset)) {
2701 // Lenient mode and the literal input text begins with a "." and
2702 // we are after a non-numeric field: We skip the "."
2704 continue; // Do not update p.
2706 // if it is actual whitespace and we're whitespace lenient it's OK
2708 UChar wsc = text.charAt(t);
2709 if(PatternProps::isWhiteSpace(wsc)) {
2710 // Lenient mode and it's just whitespace we skip it
2712 continue; // Do not update p.
2715 // hack around oldleniency being a bit of a catch-all bucket and we're just adding support specifically for paritial matches
2716 if(partialMatchLenient && oldLeniency) {
2726 // At this point if we're in strict mode we have a complete match.
2727 // If we're in lenient mode we may have a partial match, or no
2730 // no match. Pretend it matched a run of whitespace
2731 // and ignorables in the text.
2732 const UnicodeSet *ignorables = NULL;
2733 UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(pattern.charAt(i));
2734 if (patternCharIndex != UDAT_FIELD_COUNT) {
2735 ignorables = SimpleDateFormatStaticSets::getIgnorables(patternCharIndex);
2738 for (t = textOffset; t < text.length(); t += 1) {
2739 UChar ch = text.charAt(t);
2741 if (ignorables == NULL || !ignorables->contains(ch)) {
2747 // if we get here, we've got a complete match.
2748 patternOffset = i - 1;
2754 //----------------------------------------------------------------------
2756 int32_t SimpleDateFormat::matchString(const UnicodeString& text,
2758 UCalendarDateFields field,
2759 const UnicodeString* data,
2761 const UnicodeString* monthPattern,
2762 Calendar& cal) const
2765 int32_t count = dataCount;
2767 if (field == UCAL_DAY_OF_WEEK) i = 1;
2769 // There may be multiple strings in the data[] array which begin with
2770 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2771 // We keep track of the longest match, and return that. Note that this
2772 // unfortunately requires us to test all array elements.
2773 int32_t bestMatchLength = 0, bestMatch = -1;
2774 UnicodeString bestMatchName;
2775 int32_t isLeapMonth = 0;
2777 for (; i < count; ++i) {
2778 int32_t matchLen = 0;
2779 if ((matchLen = matchStringWithOptionalDot(text, start, data[i])) > bestMatchLength) {
2781 bestMatchLength = matchLen;
2784 if (monthPattern != NULL) {
2785 UErrorCode status = U_ZERO_ERROR;
2786 UnicodeString leapMonthName;
2787 SimpleFormatter(*monthPattern, 1, 1, status).format(data[i], leapMonthName, status);
2788 if (U_SUCCESS(status)) {
2789 if ((matchLen = matchStringWithOptionalDot(text, start, leapMonthName)) > bestMatchLength) {
2791 bestMatchLength = matchLen;
2798 if (bestMatch >= 0) {
2799 if (field < UCAL_FIELD_COUNT) {
2800 // Adjustment for Hebrew Calendar month Adar II
2801 if (!strcmp(cal.getType(),"hebrew") && field==UCAL_MONTH && bestMatch==13) {
2804 if (field == UCAL_YEAR) {
2805 bestMatch++; // only get here for cyclic year names, which match 1-based years 1-60
2807 cal.set(field, bestMatch);
2809 if (monthPattern != NULL) {
2810 cal.set(UCAL_IS_LEAP_MONTH, isLeapMonth);
2814 return start + bestMatchLength;
2821 matchStringWithOptionalDot(const UnicodeString &text,
2823 const UnicodeString &data) {
2824 UErrorCode sts = U_ZERO_ERROR;
2825 int32_t matchLenText = 0;
2826 int32_t matchLenData = 0;
2828 u_caseInsensitivePrefixMatch(text.getBuffer() + index, text.length() - index,
2829 data.getBuffer(), data.length(),
2830 0 /* default case option */,
2831 &matchLenText, &matchLenData,
2833 U_ASSERT (U_SUCCESS(sts));
2835 if (matchLenData == data.length() /* normal match */
2836 || (data.charAt(data.length() - 1) == 0x2e
2837 && matchLenData == data.length() - 1 /* match without trailing dot */)) {
2838 return matchLenText;
2844 //----------------------------------------------------------------------
2847 SimpleDateFormat::set2DigitYearStart(UDate d, UErrorCode& status)
2849 parseAmbiguousDatesAsAfter(d, status);
2853 * Private member function that converts the parsed date strings into
2854 * timeFields. Returns -start (for ParsePosition) if failed.
2856 int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UChar ch, int32_t count,
2857 UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], int32_t& saveHebrewMonth, Calendar& cal,
2858 int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType, SimpleDateFormatMutableNFs &mutableNFs,
2859 int32_t *dayPeriod) const
2865 UErrorCode status = U_ZERO_ERROR;
2866 ParsePosition pos(0);
2867 UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch);
2868 NumberFormat *currentNumberFormat;
2870 UBool gotNumber = FALSE;
2872 #if defined (U_DEBUG_CAL)
2873 //fprintf(stderr, "%s:%d - [%c] st=%d \n", __FILE__, __LINE__, (char) ch, start);
2876 if (patternCharIndex == UDAT_FIELD_COUNT) {
2880 currentNumberFormat = mutableNFs.get(getNumberFormatByIndex(patternCharIndex));
2881 if (currentNumberFormat == NULL) {
2884 UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex]; // UCAL_FIELD_COUNT if irrelevant
2885 UnicodeString hebr("hebr", 4, US_INV);
2887 if (numericLeapMonthFormatter != NULL) {
2888 numericLeapMonthFormatter->setFormats((const Format **)¤tNumberFormat, 1);
2890 UBool isChineseCalendar = (uprv_strcmp(cal.getType(),"chinese") == 0 || uprv_strcmp(cal.getType(),"dangi") == 0);
2892 // If there are any spaces here, skip over them. If we hit the end
2893 // of the string, then fail.
2895 if (start >= text.length()) {
2898 UChar32 c = text.char32At(start);
2899 if (!u_isUWhiteSpace(c) /*||*/ && !PatternProps::isWhiteSpace(c)) {
2902 start += U16_LENGTH(c);
2904 pos.setIndex(start);
2906 // We handle a few special cases here where we need to parse
2907 // a number value. We handle further, more generic cases below. We need
2908 // to handle some of them here because some fields require extra processing on
2909 // the parsed value.
2910 if (patternCharIndex == UDAT_HOUR_OF_DAY1_FIELD || // k
2911 patternCharIndex == UDAT_HOUR_OF_DAY0_FIELD || // H
2912 patternCharIndex == UDAT_HOUR1_FIELD || // h
2913 patternCharIndex == UDAT_HOUR0_FIELD || // K
2914 (patternCharIndex == UDAT_DOW_LOCAL_FIELD && count <= 2) || // e
2915 (patternCharIndex == UDAT_STANDALONE_DAY_FIELD && count <= 2) || // c
2916 (patternCharIndex == UDAT_MONTH_FIELD && count <= 2) || // M
2917 (patternCharIndex == UDAT_STANDALONE_MONTH_FIELD && count <= 2) || // L
2918 (patternCharIndex == UDAT_QUARTER_FIELD && count <= 2) || // Q
2919 (patternCharIndex == UDAT_STANDALONE_QUARTER_FIELD && count <= 2) || // q
2920 patternCharIndex == UDAT_YEAR_FIELD || // y
2921 patternCharIndex == UDAT_YEAR_WOY_FIELD || // Y
2922 patternCharIndex == UDAT_YEAR_NAME_FIELD || // U (falls back to numeric)
2923 (patternCharIndex == UDAT_ERA_FIELD && isChineseCalendar) || // G
2924 patternCharIndex == UDAT_FRACTIONAL_SECOND_FIELD) // S
2926 int32_t parseStart = pos.getIndex();
2927 // It would be good to unify this with the obeyCount logic below,
2928 // but that's going to be difficult.
2929 const UnicodeString* src;
2931 UBool parsedNumericLeapMonth = FALSE;
2932 if (numericLeapMonthFormatter != NULL && (patternCharIndex == UDAT_MONTH_FIELD || patternCharIndex == UDAT_STANDALONE_MONTH_FIELD)) {
2934 Formattable * args = numericLeapMonthFormatter->parse(text, pos, argCount);
2935 if (args != NULL && argCount == 1 && pos.getIndex() > parseStart && args[0].isNumeric()) {
2936 parsedNumericLeapMonth = TRUE;
2937 number.setLong(args[0].getLong());
2938 cal.set(UCAL_IS_LEAP_MONTH, 1);
2941 pos.setIndex(parseStart);
2942 cal.set(UCAL_IS_LEAP_MONTH, 0);
2946 if (!parsedNumericLeapMonth) {
2948 if ((start+count) > text.length()) {
2952 text.extractBetween(0, start + count, temp);
2958 parseInt(*src, number, pos, allowNegative,currentNumberFormat);
2961 int32_t txtLoc = pos.getIndex();
2963 if (txtLoc > parseStart) {
2964 value = number.getLong();
2967 // suffix processing
2969 txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, TRUE);
2970 if (txtLoc != pos.getIndex()) {
2975 txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, FALSE);
2978 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)) {
2979 // Check the range of the value
2980 int32_t bias = gFieldRangeBias[patternCharIndex];
2981 if (bias >= 0 && (value > cal.getMaximum(field) + bias || value < cal.getMinimum(field) + bias)) {
2986 pos.setIndex(txtLoc);
2990 // Make sure that we got a number if
2991 // we want one, and didn't get one
2992 // if we don't want one.
2993 switch (patternCharIndex) {
2994 case UDAT_HOUR_OF_DAY1_FIELD:
2995 case UDAT_HOUR_OF_DAY0_FIELD:
2996 case UDAT_HOUR1_FIELD:
2997 case UDAT_HOUR0_FIELD:
2998 // special range check for hours:
2999 if (value < 0 || value > 24) {
3003 // fall through to gotNumber check
3005 case UDAT_YEAR_FIELD:
3006 case UDAT_YEAR_WOY_FIELD:
3007 case UDAT_FRACTIONAL_SECOND_FIELD:
3008 // these must be a number
3016 // we check the rest of the fields below.
3020 switch (patternCharIndex) {
3021 case UDAT_ERA_FIELD:
3022 if (isChineseCalendar) {
3026 cal.set(UCAL_ERA, value);
3027 return pos.getIndex();
3030 ps = matchString(text, start, UCAL_ERA, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount, NULL, cal);
3031 } else if (count == 4) {
3032 ps = matchString(text, start, UCAL_ERA, fSymbols->fEraNames, fSymbols->fEraNamesCount, NULL, cal);
3034 ps = matchString(text, start, UCAL_ERA, fSymbols->fEras, fSymbols->fErasCount, NULL, cal);
3037 // check return position, if it equals -start, then matchString error
3038 // special case the return code so we don't necessarily fail out until we
3039 // verify no year information also
3045 case UDAT_YEAR_FIELD:
3046 // If there are 3 or more YEAR pattern characters, this indicates
3047 // that the year value is to be treated literally, without any
3048 // two-digit year adjustments (e.g., from "01" to 2001). Otherwise
3049 // we made adjustments to place the 2-digit year in the proper
3050 // century, for parsed strings from "00" to "99". Any other string
3051 // is treated literally: "2250", "-1", "1", "002".
3052 if (fDateOverride.compare(hebr)==0 && value < 1000) {
3053 value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
3054 } else if ((pos.getIndex() - start) == 2 && !isChineseCalendar
3055 && u_isdigit(text.charAt(start))
3056 && u_isdigit(text.charAt(start+1)))
3058 // only adjust year for patterns less than 3.
3060 // Assume for example that the defaultCenturyStart is 6/18/1903.
3061 // This means that two-digit years will be forced into the range
3062 // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
3063 // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
3064 // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
3065 // other fields specify a date before 6/18, or 1903 if they specify a
3066 // date afterwards. As a result, 03 is an ambiguous year. All other
3067 // two-digit years are unambiguous.
3068 if(fHaveDefaultCentury) { // check if this formatter even has a pivot year
3069 int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
3070 ambiguousYear[0] = (value == ambiguousTwoDigitYear);
3071 value += (fDefaultCenturyStartYear/100)*100 +
3072 (value < ambiguousTwoDigitYear ? 100 : 0);
3076 cal.set(UCAL_YEAR, value);
3078 // Delayed checking for adjustment of Hebrew month numbers in non-leap years.
3079 if (saveHebrewMonth >= 0) {
3080 HebrewCalendar *hc = (HebrewCalendar*)&cal;
3081 if (!hc->isLeapYear(value) && saveHebrewMonth >= 6) {
3082 cal.set(UCAL_MONTH,saveHebrewMonth);
3084 cal.set(UCAL_MONTH,saveHebrewMonth-1);
3086 saveHebrewMonth = -1;
3088 return pos.getIndex();
3090 case UDAT_YEAR_WOY_FIELD:
3091 // Comment is the same as for UDAT_Year_FIELDs - look above
3092 if (fDateOverride.compare(hebr)==0 && value < 1000) {
3093 value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
3094 } else if ((pos.getIndex() - start) == 2
3095 && u_isdigit(text.charAt(start))
3096 && u_isdigit(text.charAt(start+1))
3097 && fHaveDefaultCentury )
3099 int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
3100 ambiguousYear[0] = (value == ambiguousTwoDigitYear);
3101 value += (fDefaultCenturyStartYear/100)*100 +
3102 (value < ambiguousTwoDigitYear ? 100 : 0);
3104 cal.set(UCAL_YEAR_WOY, value);
3105 return pos.getIndex();
3107 case UDAT_YEAR_NAME_FIELD:
3108 if (fSymbols->fShortYearNames != NULL) {
3109 int32_t newStart = matchString(text, start, UCAL_YEAR, fSymbols->fShortYearNames, fSymbols->fShortYearNamesCount, NULL, cal);
3114 if (gotNumber && (getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC,status) || value > fSymbols->fShortYearNamesCount)) {
3115 cal.set(UCAL_YEAR, value);
3116 return pos.getIndex();
3120 case UDAT_MONTH_FIELD:
3121 case UDAT_STANDALONE_MONTH_FIELD:
3122 if (gotNumber) // i.e., M or MM.
3124 // When parsing month numbers from the Hebrew Calendar, we might need to adjust the month depending on whether
3125 // 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
3126 // the year is parsed.
3127 if (!strcmp(cal.getType(),"hebrew")) {
3128 HebrewCalendar *hc = (HebrewCalendar*)&cal;
3129 if (cal.isSet(UCAL_YEAR)) {
3130 UErrorCode status = U_ZERO_ERROR;
3131 if (!hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value >= 6) {
3132 cal.set(UCAL_MONTH, value);
3134 cal.set(UCAL_MONTH, value - 1);
3137 saveHebrewMonth = value;
3140 // Don't want to parse the month if it is a string
3141 // while pattern uses numeric style: M/MM, L/LL
3142 // [We computed 'value' above.]
3143 cal.set(UCAL_MONTH, value - 1);
3145 return pos.getIndex();
3147 // count >= 3 // i.e., MMM/MMMM, LLL/LLLL
3148 // Want to be able to parse both short and long forms.
3149 // Try count == 4 first:
3150 UnicodeString * wideMonthPat = NULL;
3151 UnicodeString * shortMonthPat = NULL;
3152 if (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) {
3153 if (patternCharIndex==UDAT_MONTH_FIELD) {
3154 wideMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatWide];
3155 shortMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatAbbrev];
3157 wideMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneWide];
3158 shortMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev];
3161 int32_t newStart = 0;
3162 if (patternCharIndex==UDAT_MONTH_FIELD) {
3163 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3164 newStart = matchString(text, start, UCAL_MONTH, fSymbols->fMonths, fSymbols->fMonthsCount, wideMonthPat, cal); // try MMMM
3169 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3170 newStart = matchString(text, start, UCAL_MONTH, fSymbols->fShortMonths, fSymbols->fShortMonthsCount, shortMonthPat, cal); // try MMM
3173 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3174 newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount, wideMonthPat, cal); // try LLLL
3179 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3180 newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount, shortMonthPat, cal); // try LLL
3183 if (newStart > 0 || !getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) // currently we do not try to parse MMMMM/LLLLL: #8860
3185 // else we allowing parsing as number, below
3189 case UDAT_HOUR_OF_DAY1_FIELD:
3190 // [We computed 'value' above.]
3191 if (value == cal.getMaximum(UCAL_HOUR_OF_DAY) + 1)
3194 // fall through to set field
3196 case UDAT_HOUR_OF_DAY0_FIELD:
3197 cal.set(UCAL_HOUR_OF_DAY, value);
3198 return pos.getIndex();
3200 case UDAT_FRACTIONAL_SECOND_FIELD:
3201 // Fractional seconds left-justify
3202 i = pos.getIndex() - start;
3216 cal.set(UCAL_MILLISECOND, value);
3217 return pos.getIndex();
3219 case UDAT_DOW_LOCAL_FIELD:
3220 if (gotNumber) // i.e., e or ee
3222 // [We computed 'value' above.]
3223 cal.set(UCAL_DOW_LOCAL, value);
3224 return pos.getIndex();
3226 // else for eee-eeeee fall through to handling of EEE-EEEEE
3227 // fall through, do not break here
3229 case UDAT_DAY_OF_WEEK_FIELD:
3231 // Want to be able to parse both short and long forms.
3232 // Try count == 4 (EEEE) wide first:
3233 int32_t newStart = 0;
3234 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3235 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3236 fSymbols->fWeekdays, fSymbols->fWeekdaysCount, NULL, cal)) > 0)
3239 // EEEE wide failed, now try EEE abbreviated
3240 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3241 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3242 fSymbols->fShortWeekdays, fSymbols->fShortWeekdaysCount, NULL, cal)) > 0)
3245 // EEE abbreviated failed, now try EEEEEE short
3246 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 6) {
3247 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3248 fSymbols->fShorterWeekdays, fSymbols->fShorterWeekdaysCount, NULL, cal)) > 0)
3251 // EEEEEE short failed, now try EEEEE narrow
3252 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
3253 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3254 fSymbols->fNarrowWeekdays, fSymbols->fNarrowWeekdaysCount, NULL, cal)) > 0)
3257 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status) || patternCharIndex == UDAT_DAY_OF_WEEK_FIELD)
3259 // else we allowing parsing as number, below
3263 case UDAT_STANDALONE_DAY_FIELD:
3265 if (gotNumber) // c or cc
3267 // [We computed 'value' above.]
3268 cal.set(UCAL_DOW_LOCAL, value);
3269 return pos.getIndex();
3271 // Want to be able to parse both short and long forms.
3272 // Try count == 4 (cccc) first:
3273 int32_t newStart = 0;
3274 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3275 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3276 fSymbols->fStandaloneWeekdays, fSymbols->fStandaloneWeekdaysCount, NULL, cal)) > 0)
3279 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3280 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3281 fSymbols->fStandaloneShortWeekdays, fSymbols->fStandaloneShortWeekdaysCount, NULL, cal)) > 0)
3284 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 6) {
3285 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3286 fSymbols->fStandaloneShorterWeekdays, fSymbols->fStandaloneShorterWeekdaysCount, NULL, cal)) > 0)
3289 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))
3291 // else we allowing parsing as number, below
3295 case UDAT_AM_PM_FIELD:
3297 // optionally try both wide/abbrev and narrow forms
3298 int32_t newStart = 0;
3300 if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count < 5 ) {
3301 if ((newStart = matchString(text, start, UCAL_AM_PM, fSymbols->fAmPms, fSymbols->fAmPmsCount, NULL, cal)) > 0) {
3306 if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count >= 5 ) {
3307 if ((newStart = matchString(text, start, UCAL_AM_PM, fSymbols->fNarrowAmPms, fSymbols->fNarrowAmPmsCount, NULL, cal)) > 0) {
3311 // no matches for given options
3315 case UDAT_HOUR1_FIELD:
3316 // [We computed 'value' above.]
3317 if (value == cal.getLeastMaximum(UCAL_HOUR)+1)
3320 // fall through to set field
3322 case UDAT_HOUR0_FIELD:
3323 cal.set(UCAL_HOUR, value);
3324 return pos.getIndex();
3326 case UDAT_QUARTER_FIELD:
3327 if (gotNumber) // i.e., Q or QQ.
3329 // Don't want to parse the month if it is a string
3330 // while pattern uses numeric style: Q or QQ.
3331 // [We computed 'value' above.]
3332 cal.set(UCAL_MONTH, (value - 1) * 3);
3333 return pos.getIndex();
3335 // count >= 3 // i.e., QQQ or QQQQ
3336 // Want to be able to parse both short and long forms.
3337 // Try count == 4 first:
3338 int32_t newStart = 0;
3340 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3341 if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
3342 fSymbols->fQuarters, fSymbols->fQuartersCount, cal)) > 0)
3345 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3346 if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
3347 fSymbols->fShortQuarters, fSymbols->fShortQuartersCount, cal)) > 0)
3350 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))
3352 // else we allowing parsing as number, below
3353 if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status))
3358 case UDAT_STANDALONE_QUARTER_FIELD:
3359 if (gotNumber) // i.e., q or qq.
3361 // Don't want to parse the month if it is a string
3362 // while pattern uses numeric style: q or q.
3363 // [We computed 'value' above.]
3364 cal.set(UCAL_MONTH, (value - 1) * 3);
3365 return pos.getIndex();
3367 // count >= 3 // i.e., qqq or qqqq
3368 // Want to be able to parse both short and long forms.
3369 // Try count == 4 first:
3370 int32_t newStart = 0;
3372 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3373 if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
3374 fSymbols->fStandaloneQuarters, fSymbols->fStandaloneQuartersCount, cal)) > 0)
3377 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3378 if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
3379 fSymbols->fStandaloneShortQuarters, fSymbols->fStandaloneShortQuartersCount, cal)) > 0)
3382 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))
3384 // else we allowing parsing as number, below
3385 if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status))
3390 case UDAT_TIMEZONE_FIELD: // 'z'
3392 UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_SPECIFIC_SHORT : UTZFMT_STYLE_SPECIFIC_LONG;
3393 TimeZone *tz = tzFormat()->parse(style, text, pos, tzTimeType);
3395 cal.adoptTimeZone(tz);
3396 return pos.getIndex();
3400 case UDAT_TIMEZONE_RFC_FIELD: // 'Z'
3402 UTimeZoneFormatStyle style = (count < 4) ?
3403 UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL : ((count == 5) ? UTZFMT_STYLE_ISO_EXTENDED_FULL: UTZFMT_STYLE_LOCALIZED_GMT);
3404 TimeZone *tz = tzFormat()->parse(style, text, pos, tzTimeType);
3406 cal.adoptTimeZone(tz);
3407 return pos.getIndex();
3411 case UDAT_TIMEZONE_GENERIC_FIELD: // 'v'
3413 UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_GENERIC_SHORT : UTZFMT_STYLE_GENERIC_LONG;
3414 TimeZone *tz = tzFormat()->parse(style, text, pos, tzTimeType);
3416 cal.adoptTimeZone(tz);
3417 return pos.getIndex();
3421 case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V'
3423 UTimeZoneFormatStyle style;
3426 style = UTZFMT_STYLE_ZONE_ID_SHORT;
3429 style = UTZFMT_STYLE_ZONE_ID;
3432 style = UTZFMT_STYLE_EXEMPLAR_LOCATION;
3435 style = UTZFMT_STYLE_GENERIC_LOCATION;
3438 TimeZone *tz = tzFormat()->parse(style, text, pos, tzTimeType);
3440 cal.adoptTimeZone(tz);
3441 return pos.getIndex();
3445 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: // 'O'
3447 UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_LOCALIZED_GMT_SHORT : UTZFMT_STYLE_LOCALIZED_GMT;
3448 TimeZone *tz = tzFormat()->parse(style, text, pos, tzTimeType);
3450 cal.adoptTimeZone(tz);
3451 return pos.getIndex();
3455 case UDAT_TIMEZONE_ISO_FIELD: // 'X'
3457 UTimeZoneFormatStyle style;
3460 style = UTZFMT_STYLE_ISO_BASIC_SHORT;
3463 style = UTZFMT_STYLE_ISO_BASIC_FIXED;
3466 style = UTZFMT_STYLE_ISO_EXTENDED_FIXED;
3469 style = UTZFMT_STYLE_ISO_BASIC_FULL;
3472 style = UTZFMT_STYLE_ISO_EXTENDED_FULL;
3475 TimeZone *tz = tzFormat()->parse(style, text, pos, tzTimeType);
3477 cal.adoptTimeZone(tz);
3478 return pos.getIndex();
3482 case UDAT_TIMEZONE_ISO_LOCAL_FIELD: // 'x'
3484 UTimeZoneFormatStyle style;
3487 style = UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT;
3490 style = UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED;
3493 style = UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED;
3496 style = UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL;
3499 style = UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL;
3502 TimeZone *tz = tzFormat()->parse(style, text, pos, tzTimeType);
3504 cal.adoptTimeZone(tz);
3505 return pos.getIndex();
3509 // currently no pattern character is defined for UDAT_TIME_SEPARATOR_FIELD
3510 // so we should not get here. Leave support in for future definition.
3511 case UDAT_TIME_SEPARATOR_FIELD:
3513 static const UChar def_sep = DateFormatSymbols::DEFAULT_TIME_SEPARATOR;
3514 static const UChar alt_sep = DateFormatSymbols::ALTERNATE_TIME_SEPARATOR;
3516 // Try matching a time separator.
3518 UnicodeString data[3];
3519 fSymbols->getTimeSeparatorString(data[0]);
3521 // Add the default, if different from the locale.
3522 if (data[0].compare(&def_sep, 1) != 0) {
3523 data[count++].setTo(def_sep);
3526 // If lenient, add also the alternate, if different from the locale.
3527 if (isLenient() && data[0].compare(&alt_sep, 1) != 0) {
3528 data[count++].setTo(alt_sep);
3531 return matchString(text, start, UCAL_FIELD_COUNT /* => nothing to set */, data, count, NULL, cal);
3534 case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
3536 U_ASSERT(dayPeriod != NULL);
3537 int32_t ampmStart = subParse(text, start, 0x61, count,
3538 obeyCount, allowNegative, ambiguousYear, saveHebrewMonth, cal,
3539 patLoc, numericLeapMonthFormatter, tzTimeType, mutableNFs);
3541 if (ampmStart > 0) {
3544 int32_t newStart = 0;
3546 // Only match the first two strings from the day period strings array.
3547 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3548 if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fAbbreviatedDayPeriods,
3549 2, *dayPeriod)) > 0) {
3553 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
3554 if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fNarrowDayPeriods,
3555 2, *dayPeriod)) > 0) {
3559 // count == 4, but allow other counts
3560 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status)) {
3561 if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fWideDayPeriods,
3562 2, *dayPeriod)) > 0) {
3571 case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
3573 U_ASSERT(dayPeriod != NULL);
3574 int32_t newStart = 0;
3576 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3577 if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fAbbreviatedDayPeriods,
3578 fSymbols->fAbbreviatedDayPeriodsCount, *dayPeriod)) > 0) {
3582 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
3583 if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fNarrowDayPeriods,
3584 fSymbols->fNarrowDayPeriodsCount, *dayPeriod)) > 0) {
3588 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3589 if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fWideDayPeriods,
3590 fSymbols->fWideDayPeriodsCount, *dayPeriod)) > 0) {
3599 // Handle "generic" fields
3600 // this is now handled below, outside the switch block
3603 // Handle "generic" fields:
3604 // switch default case now handled here (outside switch block) to allow
3605 // parsing of some string fields as digits for lenient case
3607 int32_t parseStart = pos.getIndex();
3608 const UnicodeString* src;
3610 if ((start+count) > text.length()) {
3613 text.extractBetween(0, start + count, temp);
3618 parseInt(*src, number, pos, allowNegative,currentNumberFormat);
3619 if (pos.getIndex() != parseStart) {
3620 int32_t value = number.getLong();
3622 // Don't need suffix processing here (as in number processing at the beginning of the function);
3623 // the new fields being handled as numeric values (month, weekdays, quarters) should not have suffixes.
3625 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) {
3626 // Check the range of the value
3627 int32_t bias = gFieldRangeBias[patternCharIndex];
3628 if (bias >= 0 && (value > cal.getMaximum(field) + bias || value < cal.getMinimum(field) + bias)) {
3633 // For the following, need to repeat some of the "if (gotNumber)" code above:
3634 // UDAT_[STANDALONE_]MONTH_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_STANDALONE_DAY_FIELD,
3635 // UDAT_[STANDALONE_]QUARTER_FIELD
3636 switch (patternCharIndex) {
3637 case UDAT_MONTH_FIELD:
3638 // See notes under UDAT_MONTH_FIELD case above
3639 if (!strcmp(cal.getType(),"hebrew")) {
3640 HebrewCalendar *hc = (HebrewCalendar*)&cal;
3641 if (cal.isSet(UCAL_YEAR)) {
3642 UErrorCode status = U_ZERO_ERROR;
3643 if (!hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value >= 6) {
3644 cal.set(UCAL_MONTH, value);
3646 cal.set(UCAL_MONTH, value - 1);
3649 saveHebrewMonth = value;
3652 cal.set(UCAL_MONTH, value - 1);
3655 case UDAT_STANDALONE_MONTH_FIELD:
3656 cal.set(UCAL_MONTH, value - 1);
3658 case UDAT_DOW_LOCAL_FIELD:
3659 case UDAT_STANDALONE_DAY_FIELD:
3660 cal.set(UCAL_DOW_LOCAL, value);
3662 case UDAT_QUARTER_FIELD:
3663 case UDAT_STANDALONE_QUARTER_FIELD:
3664 cal.set(UCAL_MONTH, (value - 1) * 3);
3666 case UDAT_RELATED_YEAR_FIELD:
3667 cal.setRelatedYear(value);
3670 cal.set(field, value);
3673 return pos.getIndex();
3679 * Parse an integer using fNumberFormat. This method is semantically
3680 * const, but actually may modify fNumberFormat.
3682 void SimpleDateFormat::parseInt(const UnicodeString& text,
3683 Formattable& number,
3685 UBool allowNegative,
3686 NumberFormat *fmt) const {
3687 parseInt(text, number, -1, pos, allowNegative,fmt);
3691 * Parse an integer using fNumberFormat up to maxDigits.
3693 void SimpleDateFormat::parseInt(const UnicodeString& text,
3694 Formattable& number,
3697 UBool allowNegative,
3698 NumberFormat *fmt) const {
3699 UnicodeString oldPrefix;
3700 DecimalFormat* df = NULL;
3701 if (!allowNegative && (df = dynamic_cast<DecimalFormat*>(fmt)) != NULL) {
3702 df->getNegativePrefix(oldPrefix);
3703 df->setNegativePrefix(UnicodeString(TRUE, SUPPRESS_NEGATIVE_PREFIX, -1));
3705 int32_t oldPos = pos.getIndex();
3706 fmt->parse(text, number, pos);
3708 df->setNegativePrefix(oldPrefix);
3711 if (maxDigits > 0) {
3712 // adjust the result to fit into
3713 // the maxDigits and move the position back
3714 int32_t nDigits = pos.getIndex() - oldPos;
3715 if (nDigits > maxDigits) {
3716 int32_t val = number.getLong();
3717 nDigits -= maxDigits;
3718 while (nDigits > 0) {
3722 pos.setIndex(oldPos + maxDigits);
3723 number.setLong(val);
3728 //----------------------------------------------------------------------
3730 void SimpleDateFormat::translatePattern(const UnicodeString& originalPattern,
3731 UnicodeString& translatedPattern,
3732 const UnicodeString& from,
3733 const UnicodeString& to,
3736 // run through the pattern and convert any pattern symbols from the version
3737 // in "from" to the corresponding character in "to". This code takes
3738 // quoted strings into account (it doesn't try to translate them), and it signals
3739 // an error if a particular "pattern character" doesn't appear in "from".
3740 // Depending on the values of "from" and "to" this can convert from generic
3741 // to localized patterns or localized to generic.
3742 if (U_FAILURE(status)) {
3746 translatedPattern.remove();
3747 UBool inQuote = FALSE;
3748 for (int32_t i = 0; i < originalPattern.length(); ++i) {
3749 UChar c = originalPattern[i];
3757 } else if (isSyntaxChar(c)) {
3758 int32_t ci = from.indexOf(c);
3760 status = U_INVALID_FORMAT_ERROR;
3766 translatedPattern += c;
3769 status = U_INVALID_FORMAT_ERROR;
3774 //----------------------------------------------------------------------
3777 SimpleDateFormat::toPattern(UnicodeString& result) const
3783 //----------------------------------------------------------------------
3786 SimpleDateFormat::toLocalizedPattern(UnicodeString& result,
3787 UErrorCode& status) const
3789 translatePattern(fPattern, result,
3790 UnicodeString(DateFormatSymbols::getPatternUChars()),
3791 fSymbols->fLocalPatternChars, status);
3795 //----------------------------------------------------------------------
3798 SimpleDateFormat::applyPattern(const UnicodeString& pattern)
3804 //----------------------------------------------------------------------
3807 SimpleDateFormat::applyLocalizedPattern(const UnicodeString& pattern,
3810 translatePattern(pattern, fPattern,
3811 fSymbols->fLocalPatternChars,
3812 UnicodeString(DateFormatSymbols::getPatternUChars()), status);
3815 //----------------------------------------------------------------------
3817 const DateFormatSymbols*
3818 SimpleDateFormat::getDateFormatSymbols() const
3823 //----------------------------------------------------------------------
3826 SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols* newFormatSymbols)
3829 fSymbols = newFormatSymbols;
3832 //----------------------------------------------------------------------
3834 SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols& newFormatSymbols)
3837 fSymbols = new DateFormatSymbols(newFormatSymbols);
3840 //----------------------------------------------------------------------
3841 const TimeZoneFormat*
3842 SimpleDateFormat::getTimeZoneFormat(void) const {
3843 return (const TimeZoneFormat*)tzFormat();
3846 //----------------------------------------------------------------------
3848 SimpleDateFormat::adoptTimeZoneFormat(TimeZoneFormat* timeZoneFormatToAdopt)
3850 delete fTimeZoneFormat;
3851 fTimeZoneFormat = timeZoneFormatToAdopt;
3854 //----------------------------------------------------------------------
3856 SimpleDateFormat::setTimeZoneFormat(const TimeZoneFormat& newTimeZoneFormat)
3858 delete fTimeZoneFormat;
3859 fTimeZoneFormat = new TimeZoneFormat(newTimeZoneFormat);
3862 //----------------------------------------------------------------------
3865 void SimpleDateFormat::adoptCalendar(Calendar* calendarToAdopt)
3867 UErrorCode status = U_ZERO_ERROR;
3868 Locale calLocale(fLocale);
3869 calLocale.setKeywordValue("calendar", calendarToAdopt->getType(), status);
3870 DateFormatSymbols *newSymbols =
3871 DateFormatSymbols::createForLocale(calLocale, status);
3872 if (U_FAILURE(status)) {
3875 DateFormat::adoptCalendar(calendarToAdopt);
3877 fSymbols = newSymbols;
3878 initializeDefaultCentury(); // we need a new century (possibly)
3882 //----------------------------------------------------------------------
3885 // override the DateFormat implementation in order to
3886 // lazily initialize fCapitalizationBrkIter
3888 SimpleDateFormat::setContext(UDisplayContext value, UErrorCode& status)
3890 DateFormat::setContext(value, status);
3891 #if !UCONFIG_NO_BREAK_ITERATION
3892 if (U_SUCCESS(status)) {
3893 if ( fCapitalizationBrkIter == NULL && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
3894 value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE) ) {
3895 UErrorCode status = U_ZERO_ERROR;
3896 fCapitalizationBrkIter = BreakIterator::createSentenceInstance(fLocale, status);
3897 if (U_FAILURE(status)) {
3898 delete fCapitalizationBrkIter;
3899 fCapitalizationBrkIter = NULL;
3907 //----------------------------------------------------------------------
3911 SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field) const {
3912 return isFieldUnitIgnored(fPattern, field);
3917 SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern,
3918 UCalendarDateFields field) {
3919 int32_t fieldLevel = fgCalendarFieldToLevel[field];
3922 UBool inQuote = FALSE;
3926 for (int32_t i = 0; i < pattern.length(); ++i) {
3928 if (ch != prevCh && count > 0) {
3929 level = getLevelFromChar(prevCh);
3930 // the larger the level, the smaller the field unit.
3931 if (fieldLevel <= level) {
3937 if ((i+1) < pattern.length() && pattern[i+1] == QUOTE) {
3940 inQuote = ! inQuote;
3943 else if (!inQuote && isSyntaxChar(ch)) {
3950 level = getLevelFromChar(prevCh);
3951 if (fieldLevel <= level) {
3958 //----------------------------------------------------------------------
3961 SimpleDateFormat::getSmpFmtLocale(void) const {
3965 //----------------------------------------------------------------------
3968 SimpleDateFormat::checkIntSuffix(const UnicodeString& text, int32_t start,
3969 int32_t patLoc, UBool isNegative) const {
3972 int32_t patternMatch;
3973 int32_t textPreMatch;
3974 int32_t textPostMatch;
3976 // check that we are still in range
3977 if ( (start > text.length()) ||
3980 (patLoc > fPattern.length())) {
3981 // out of range, don't advance location in text
3986 DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(fNumberFormat);
3987 if (decfmt != NULL) {
3989 suf = decfmt->getNegativeSuffix(suf);
3992 suf = decfmt->getPositiveSuffix(suf);
3997 if (suf.length() <= 0) {
4001 // check suffix will be encountered in the pattern
4002 patternMatch = compareSimpleAffix(suf,fPattern,patLoc);
4004 // check if a suffix will be encountered in the text
4005 textPreMatch = compareSimpleAffix(suf,text,start);
4007 // check if a suffix was encountered in the text
4008 textPostMatch = compareSimpleAffix(suf,text,start-suf.length());
4010 // check for suffix match
4011 if ((textPreMatch >= 0) && (patternMatch >= 0) && (textPreMatch == patternMatch)) {
4014 else if ((textPostMatch >= 0) && (patternMatch >= 0) && (textPostMatch == patternMatch)) {
4015 return start - suf.length();
4018 // should not get here
4022 //----------------------------------------------------------------------
4025 SimpleDateFormat::compareSimpleAffix(const UnicodeString& affix,
4026 const UnicodeString& input,
4027 int32_t pos) const {
4028 int32_t start = pos;
4029 for (int32_t i=0; i<affix.length(); ) {
4030 UChar32 c = affix.char32At(i);
4031 int32_t len = U16_LENGTH(c);
4032 if (PatternProps::isWhiteSpace(c)) {
4033 // We may have a pattern like: \u200F \u0020
4034 // and input text like: \u200F \u0020
4035 // Note that U+200F and U+0020 are Pattern_White_Space but only
4036 // U+0020 is UWhiteSpace. So we have to first do a direct
4037 // match of the run of Pattern_White_Space in the pattern,
4038 // then match any extra characters.
4039 UBool literalMatch = FALSE;
4040 while (pos < input.length() &&
4041 input.char32At(pos) == c) {
4042 literalMatch = TRUE;
4045 if (i == affix.length()) {
4048 c = affix.char32At(i);
4049 len = U16_LENGTH(c);
4050 if (!PatternProps::isWhiteSpace(c)) {
4055 // Advance over run in pattern
4056 i = skipPatternWhiteSpace(affix, i);
4058 // Advance over run in input text
4059 // Must see at least one white space char in input,
4060 // unless we've already matched some characters literally.
4062 pos = skipUWhiteSpace(input, pos);
4063 if (pos == s && !literalMatch) {
4067 // If we skip UWhiteSpace in the input text, we need to skip it in the pattern.
4068 // Otherwise, the previous lines may have skipped over text (such as U+00A0) that
4069 // is also in the affix.
4070 i = skipUWhiteSpace(affix, i);
4072 if (pos < input.length() &&
4073 input.char32At(pos) == c) {
4084 //----------------------------------------------------------------------
4087 SimpleDateFormat::skipPatternWhiteSpace(const UnicodeString& text, int32_t pos) const {
4088 const UChar* s = text.getBuffer();
4089 return (int32_t)(PatternProps::skipWhiteSpace(s + pos, text.length() - pos) - s);
4092 //----------------------------------------------------------------------
4095 SimpleDateFormat::skipUWhiteSpace(const UnicodeString& text, int32_t pos) const {
4096 while (pos < text.length()) {
4097 UChar32 c = text.char32At(pos);
4098 if (!u_isUWhiteSpace(c)) {
4101 pos += U16_LENGTH(c);
4106 //----------------------------------------------------------------------
4108 // Lazy TimeZoneFormat instantiation, semantically const.
4110 SimpleDateFormat::tzFormat() const {
4111 if (fTimeZoneFormat == NULL) {
4114 if (fTimeZoneFormat == NULL) {
4115 UErrorCode status = U_ZERO_ERROR;
4116 TimeZoneFormat *tzfmt = TimeZoneFormat::createInstance(fLocale, status);
4117 if (U_FAILURE(status)) {
4121 const_cast<SimpleDateFormat *>(this)->fTimeZoneFormat = tzfmt;
4126 return fTimeZoneFormat;
4129 void SimpleDateFormat::parsePattern() {
4133 int len = fPattern.length();
4134 UBool inQuote = FALSE;
4135 for (int32_t i = 0; i < len; ++i) {
4136 UChar ch = fPattern[i];
4141 if (ch == 0x6D) { // 0x6D == 'm'
4144 if (ch == 0x73) { // 0x73 == 's'
4153 #endif /* #if !UCONFIG_NO_FORMATTING */