2 *******************************************************************************
3 * Copyright (C) 1997-2010, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
9 * Modification History:
11 * Date Name Description
12 * 02/19/97 aliu Converted from java.
13 * 03/31/97 aliu Modified extensively to work with 50 locales.
14 * 04/01/97 aliu Added support for centuries.
15 * 07/09/97 helena Made ParsePosition into a class.
16 * 07/21/98 stephen Added initializeDefaultCentury.
17 * Removed getZoneIndex (added in DateFormatSymbols)
18 * Removed subParseLong
20 * 02/22/99 stephen Removed character literals for EBCDIC safety
21 * 10/14/99 aliu Updated 2-digit year parsing so that only "00" thru
22 * "99" are recognized. {j28 4182066}
23 * 11/15/99 weiv Added support for week of year/day of week format
24 ********************************************************************************
27 #define ZID_KEY_MAX 128
29 #include "unicode/utypes.h"
31 #if !UCONFIG_NO_FORMATTING
33 #include "unicode/smpdtfmt.h"
34 #include "unicode/dtfmtsym.h"
35 #include "unicode/ures.h"
36 #include "unicode/msgfmt.h"
37 #include "unicode/calendar.h"
38 #include "unicode/gregocal.h"
39 #include "unicode/timezone.h"
40 #include "unicode/decimfmt.h"
41 #include "unicode/dcfmtsym.h"
42 #include "unicode/uchar.h"
43 #include "unicode/uniset.h"
44 #include "unicode/ustring.h"
45 #include "unicode/basictz.h"
46 #include "unicode/simpletz.h"
47 #include "unicode/rbtz.h"
48 #include "unicode/vtzone.h"
62 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
66 // *****************************************************************************
67 // class SimpleDateFormat
68 // *****************************************************************************
72 static const UChar PATTERN_CHAR_BASE
= 0x40;
75 * Last-resort string to use for "GMT" when constructing time zone strings.
77 // For time zones that have no names, use strings GMT+minutes and
78 // GMT-minutes. For instance, in France the time zone is GMT+60.
79 // Also accepted are GMT+H:MM or GMT-H:MM.
80 static const UChar gGmt
[] = {0x0047, 0x004D, 0x0054, 0x0000}; // "GMT"
81 static const UChar gGmtPlus
[] = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+"
82 static const UChar gGmtMinus
[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-"
83 static const UChar gDefGmtPat
[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
84 static const UChar gDefGmtNegHmsPat
[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */
85 static const UChar gDefGmtNegHmPat
[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */
86 static const UChar gDefGmtPosHmsPat
[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */
87 static const UChar gDefGmtPosHmPat
[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */
88 static const UChar gUt
[] = {0x0055, 0x0054, 0x0000}; // "UT"
89 static const UChar gUtc
[] = {0x0055, 0x0054, 0x0043, 0x0000}; // "UT"
91 typedef enum GmtPatSize
{
102 // Stuff needed for numbering system overrides
104 typedef enum OvrStrType
{
110 static const UDateFormatField kDateFields
[] = {
114 UDAT_DAY_OF_YEAR_FIELD
,
115 UDAT_DAY_OF_WEEK_IN_MONTH_FIELD
,
116 UDAT_WEEK_OF_YEAR_FIELD
,
117 UDAT_WEEK_OF_MONTH_FIELD
,
119 UDAT_EXTENDED_YEAR_FIELD
,
120 UDAT_JULIAN_DAY_FIELD
,
121 UDAT_STANDALONE_DAY_FIELD
,
122 UDAT_STANDALONE_MONTH_FIELD
,
124 UDAT_STANDALONE_QUARTER_FIELD
};
125 static const int8_t kDateFieldsCount
= 13;
127 static const UDateFormatField kTimeFields
[] = {
128 UDAT_HOUR_OF_DAY1_FIELD
,
129 UDAT_HOUR_OF_DAY0_FIELD
,
132 UDAT_FRACTIONAL_SECOND_FIELD
,
135 UDAT_MILLISECONDS_IN_DAY_FIELD
,
136 UDAT_TIMEZONE_RFC_FIELD
};
137 static const int8_t kTimeFieldsCount
= 9;
140 // This is a pattern-of-last-resort used when we can't load a usable pattern out
142 static const UChar gDefaultPattern
[] =
144 0x79, 0x79, 0x79, 0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0
145 }; /* "yyyyMMdd hh:mm a" */
147 // This prefix is designed to NEVER MATCH real text, in order to
148 // suppress the parsing of negative numbers. Adjust as needed (if
149 // this becomes valid Unicode).
150 static const UChar SUPPRESS_NEGATIVE_PREFIX
[] = {0xAB00, 0};
153 * These are the tags we expect to see in normal resource bundle files associated
156 static const char gDateTimePatternsTag
[]="DateTimePatterns";
158 static const UChar gEtcUTC
[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x54, 0x43, 0x00}; // "Etc/UTC"
159 static const UChar QUOTE
= 0x27; // Single quote
162 * The field range check bias for each UDateFormatField.
163 * The bias is added to the minimum and maximum values
164 * before they are compared to the parsed number.
165 * For example, the calendar stores zero-based month numbers
166 * but the parsed month numbers start at 1, so the bias is 1.
168 * A value of -1 means that the value is not checked.
170 static const int32_t gFieldRangeBias
[] = {
171 -1, // 'G' - UDAT_ERA_FIELD
172 -1, // 'y' - UDAT_YEAR_FIELD
173 1, // 'M' - UDAT_MONTH_FIELD
174 0, // 'd' - UDAT_DATE_FIELD
175 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
176 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
177 0, // 'm' - UDAT_MINUTE_FIELD
178 0, // 's' - UDAT_SEOND_FIELD
179 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
180 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
181 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
182 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
183 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
184 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
185 -1, // 'a' - UDAT_AM_PM_FIELD
186 -1, // 'h' - UDAT_HOUR1_FIELD
187 -1, // 'K' - UDAT_HOUR0_FIELD
188 -1, // 'z' - UDAT_TIMEZONE_FIELD
189 -1, // 'Y' - UDAT_YEAR_WOY_FIELD
190 -1, // 'e' - UDAT_DOW_LOCAL_FIELD
191 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
192 -1, // 'g' - UDAT_JULIAN_DAY_FIELD
193 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
194 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
195 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
196 0, // 'c' - UDAT_STANDALONE_DAY_FIELD
197 1, // 'L' - UDAT_STANDALONE_MONTH_FIELD
198 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
199 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
200 -1 // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
205 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat
)
207 //----------------------------------------------------------------------
209 SimpleDateFormat::~SimpleDateFormat()
212 if (fGMTFormatters
) {
213 for (int32_t i
= 0; i
< kNumGMTFormatters
; i
++) {
214 if (fGMTFormatters
[i
]) {
215 delete fGMTFormatters
[i
];
218 uprv_free(fGMTFormatters
);
221 if (fNumberFormatters
) {
222 uprv_free(fNumberFormatters
);
225 while (fOverrideList
) {
226 NSOverride
*cur
= fOverrideList
;
227 fOverrideList
= cur
->next
;
233 //----------------------------------------------------------------------
235 SimpleDateFormat::SimpleDateFormat(UErrorCode
& status
)
236 : fLocale(Locale::getDefault()),
238 fGMTFormatters(NULL
),
239 fNumberFormatters(NULL
),
242 construct(kShort
, (EStyle
) (kShort
+ kDateOffset
), fLocale
, status
);
243 initializeDefaultCentury();
246 //----------------------------------------------------------------------
248 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
251 fLocale(Locale::getDefault()),
253 fGMTFormatters(NULL
),
254 fNumberFormatters(NULL
),
257 fDateOverride
.setToBogus();
258 fTimeOverride
.setToBogus();
259 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
260 initialize(fLocale
, status
);
261 initializeDefaultCentury();
264 //----------------------------------------------------------------------
266 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
267 const UnicodeString
& override
,
270 fLocale(Locale::getDefault()),
272 fGMTFormatters(NULL
),
273 fNumberFormatters(NULL
),
276 fDateOverride
.setTo(override
);
277 fTimeOverride
.setToBogus();
278 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
279 initialize(fLocale
, status
);
280 initializeDefaultCentury();
282 processOverrideString(fLocale
,override
,kOvrStrBoth
,status
);
286 //----------------------------------------------------------------------
288 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
289 const Locale
& locale
,
293 fGMTFormatters(NULL
),
294 fNumberFormatters(NULL
),
298 fDateOverride
.setToBogus();
299 fTimeOverride
.setToBogus();
301 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
302 initialize(fLocale
, status
);
303 initializeDefaultCentury();
306 //----------------------------------------------------------------------
308 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
309 const UnicodeString
& override
,
310 const Locale
& locale
,
314 fGMTFormatters(NULL
),
315 fNumberFormatters(NULL
),
319 fDateOverride
.setTo(override
);
320 fTimeOverride
.setToBogus();
322 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
323 initialize(fLocale
, status
);
324 initializeDefaultCentury();
326 processOverrideString(locale
,override
,kOvrStrBoth
,status
);
330 //----------------------------------------------------------------------
332 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
333 DateFormatSymbols
* symbolsToAdopt
,
336 fLocale(Locale::getDefault()),
337 fSymbols(symbolsToAdopt
),
338 fGMTFormatters(NULL
),
339 fNumberFormatters(NULL
),
343 fDateOverride
.setToBogus();
344 fTimeOverride
.setToBogus();
346 initializeCalendar(NULL
,fLocale
,status
);
347 initialize(fLocale
, status
);
348 initializeDefaultCentury();
351 //----------------------------------------------------------------------
353 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
354 const DateFormatSymbols
& symbols
,
357 fLocale(Locale::getDefault()),
358 fSymbols(new DateFormatSymbols(symbols
)),
359 fGMTFormatters(NULL
),
360 fNumberFormatters(NULL
),
364 fDateOverride
.setToBogus();
365 fTimeOverride
.setToBogus();
367 initializeCalendar(NULL
, fLocale
, status
);
368 initialize(fLocale
, status
);
369 initializeDefaultCentury();
372 //----------------------------------------------------------------------
374 // Not for public consumption; used by DateFormat
375 SimpleDateFormat::SimpleDateFormat(EStyle timeStyle
,
377 const Locale
& locale
,
381 fGMTFormatters(NULL
),
382 fNumberFormatters(NULL
),
385 construct(timeStyle
, dateStyle
, fLocale
, status
);
386 if(U_SUCCESS(status
)) {
387 initializeDefaultCentury();
391 //----------------------------------------------------------------------
394 * Not for public consumption; used by DateFormat. This constructor
395 * never fails. If the resource data is not available, it uses the
396 * the last resort symbols.
398 SimpleDateFormat::SimpleDateFormat(const Locale
& locale
,
400 : fPattern(gDefaultPattern
),
403 fGMTFormatters(NULL
),
404 fNumberFormatters(NULL
),
407 if (U_FAILURE(status
)) return;
408 initializeSymbols(fLocale
, initializeCalendar(NULL
, fLocale
, status
),status
);
409 if (U_FAILURE(status
))
411 status
= U_ZERO_ERROR
;
413 // This constructor doesn't fail; it uses last resort data
414 fSymbols
= new DateFormatSymbols(status
);
417 status
= U_MEMORY_ALLOCATION_ERROR
;
422 fDateOverride
.setToBogus();
423 fTimeOverride
.setToBogus();
425 initialize(fLocale
, status
);
426 if(U_SUCCESS(status
)) {
427 initializeDefaultCentury();
431 //----------------------------------------------------------------------
433 SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat
& other
)
436 fGMTFormatters(NULL
),
437 fNumberFormatters(NULL
),
443 //----------------------------------------------------------------------
445 SimpleDateFormat
& SimpleDateFormat::operator=(const SimpleDateFormat
& other
)
447 if (this == &other
) {
450 DateFormat::operator=(other
);
456 fSymbols
= new DateFormatSymbols(*other
.fSymbols
);
458 fDefaultCenturyStart
= other
.fDefaultCenturyStart
;
459 fDefaultCenturyStartYear
= other
.fDefaultCenturyStartYear
;
460 fHaveDefaultCentury
= other
.fHaveDefaultCentury
;
462 fPattern
= other
.fPattern
;
467 //----------------------------------------------------------------------
470 SimpleDateFormat::clone() const
472 return new SimpleDateFormat(*this);
475 //----------------------------------------------------------------------
478 SimpleDateFormat::operator==(const Format
& other
) const
480 if (DateFormat::operator==(other
)) {
481 // DateFormat::operator== guarantees following cast is safe
482 SimpleDateFormat
* that
= (SimpleDateFormat
*)&other
;
483 return (fPattern
== that
->fPattern
&&
484 fSymbols
!= NULL
&& // Check for pathological object
485 that
->fSymbols
!= NULL
&& // Check for pathological object
486 *fSymbols
== *that
->fSymbols
&&
487 fHaveDefaultCentury
== that
->fHaveDefaultCentury
&&
488 fDefaultCenturyStart
== that
->fDefaultCenturyStart
);
493 //----------------------------------------------------------------------
495 void SimpleDateFormat::construct(EStyle timeStyle
,
497 const Locale
& locale
,
500 // called by several constructors to load pattern data from the resources
501 if (U_FAILURE(status
)) return;
503 // We will need the calendar to know what type of symbols to load.
504 initializeCalendar(NULL
, locale
, status
);
505 if (U_FAILURE(status
)) return;
507 CalendarData
calData(locale
, fCalendar
?fCalendar
->getType():NULL
, status
);
508 UResourceBundle
*dateTimePatterns
= calData
.getByKey(gDateTimePatternsTag
, status
);
509 UResourceBundle
*currentBundle
;
511 if (U_FAILURE(status
)) return;
513 if (ures_getSize(dateTimePatterns
) <= kDateTime
)
515 status
= U_INVALID_FORMAT_ERROR
;
519 setLocaleIDs(ures_getLocaleByType(dateTimePatterns
, ULOC_VALID_LOCALE
, &status
),
520 ures_getLocaleByType(dateTimePatterns
, ULOC_ACTUAL_LOCALE
, &status
));
522 // create a symbols object from the locale
523 initializeSymbols(locale
,fCalendar
, status
);
524 if (U_FAILURE(status
)) return;
527 status
= U_MEMORY_ALLOCATION_ERROR
;
531 const UChar
*resStr
,*ovrStr
;
532 int32_t resStrLen
,ovrStrLen
= 0;
533 fDateOverride
.setToBogus();
534 fTimeOverride
.setToBogus();
536 // if the pattern should include both date and time information, use the date/time
537 // pattern string as a guide to tell use how to glue together the appropriate date
538 // and time pattern strings. The actual gluing-together is handled by a convenience
539 // method on MessageFormat.
540 if ((timeStyle
!= kNone
) && (dateStyle
!= kNone
))
542 Formattable timeDateArray
[2];
544 // use Formattable::adoptString() so that we can use fastCopyFrom()
545 // instead of Formattable::setString()'s unaware, safe, deep string clone
546 // see Jitterbug 2296
548 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)timeStyle
, NULL
, &status
);
549 if (U_FAILURE(status
)) {
550 status
= U_INVALID_FORMAT_ERROR
;
553 switch (ures_getType(currentBundle
)) {
555 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
559 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
560 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
561 fTimeOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
565 status
= U_INVALID_FORMAT_ERROR
;
566 ures_close(currentBundle
);
570 ures_close(currentBundle
);
572 UnicodeString
*tempus1
= new UnicodeString(TRUE
, resStr
, resStrLen
);
573 // NULL pointer check
574 if (tempus1
== NULL
) {
575 status
= U_MEMORY_ALLOCATION_ERROR
;
578 timeDateArray
[0].adoptString(tempus1
);
580 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)dateStyle
, NULL
, &status
);
581 if (U_FAILURE(status
)) {
582 status
= U_INVALID_FORMAT_ERROR
;
585 switch (ures_getType(currentBundle
)) {
587 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
591 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
592 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
593 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
597 status
= U_INVALID_FORMAT_ERROR
;
598 ures_close(currentBundle
);
602 ures_close(currentBundle
);
604 UnicodeString
*tempus2
= new UnicodeString(TRUE
, resStr
, resStrLen
);
605 // Null pointer check
606 if (tempus2
== NULL
) {
607 status
= U_MEMORY_ALLOCATION_ERROR
;
610 timeDateArray
[1].adoptString(tempus2
);
612 int32_t glueIndex
= kDateTime
;
613 int32_t patternsSize
= ures_getSize(dateTimePatterns
);
614 if (patternsSize
>= (kDateTimeOffset
+ kShort
+ 1)) {
615 // Get proper date time format
616 glueIndex
= (int32_t)(kDateTimeOffset
+ (dateStyle
- kDateOffset
));
619 resStr
= ures_getStringByIndex(dateTimePatterns
, glueIndex
, &resStrLen
, &status
);
620 MessageFormat::format(UnicodeString(TRUE
, resStr
, resStrLen
), timeDateArray
, 2, fPattern
, status
);
622 // if the pattern includes just time data or just date date, load the appropriate
623 // pattern string from the resources
624 // setTo() - see DateFormatSymbols::assignArray comments
625 else if (timeStyle
!= kNone
) {
626 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)timeStyle
, NULL
, &status
);
627 if (U_FAILURE(status
)) {
628 status
= U_INVALID_FORMAT_ERROR
;
631 switch (ures_getType(currentBundle
)) {
633 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
637 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
638 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
639 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
643 status
= U_INVALID_FORMAT_ERROR
;
644 ures_close(currentBundle
);
648 fPattern
.setTo(TRUE
, resStr
, resStrLen
);
649 ures_close(currentBundle
);
651 else if (dateStyle
!= kNone
) {
652 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)dateStyle
, NULL
, &status
);
653 if (U_FAILURE(status
)) {
654 status
= U_INVALID_FORMAT_ERROR
;
657 switch (ures_getType(currentBundle
)) {
659 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
663 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
664 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
665 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
669 status
= U_INVALID_FORMAT_ERROR
;
670 ures_close(currentBundle
);
674 fPattern
.setTo(TRUE
, resStr
, resStrLen
);
675 ures_close(currentBundle
);
678 // and if it includes _neither_, that's an error
680 status
= U_INVALID_FORMAT_ERROR
;
682 // finally, finish initializing by creating a Calendar and a NumberFormat
683 initialize(locale
, status
);
686 //----------------------------------------------------------------------
689 SimpleDateFormat::initializeCalendar(TimeZone
* adoptZone
, const Locale
& locale
, UErrorCode
& status
)
691 if(!U_FAILURE(status
)) {
692 fCalendar
= Calendar::createInstance(adoptZone
?adoptZone
:TimeZone::createDefault(), locale
, status
);
694 if (U_SUCCESS(status
) && fCalendar
== NULL
) {
695 status
= U_MEMORY_ALLOCATION_ERROR
;
701 SimpleDateFormat::initializeSymbols(const Locale
& locale
, Calendar
* calendar
, UErrorCode
& status
)
703 if(U_FAILURE(status
)) {
706 // pass in calendar type - use NULL (default) if no calendar set (or err).
707 fSymbols
= new DateFormatSymbols(locale
, calendar
?calendar
->getType() :NULL
, status
);
708 // Null pointer check
709 if (fSymbols
== NULL
) {
710 status
= U_MEMORY_ALLOCATION_ERROR
;
717 SimpleDateFormat::initialize(const Locale
& locale
,
720 if (U_FAILURE(status
)) return;
722 // We don't need to check that the row count is >= 1, since all 2d arrays have at
724 fNumberFormat
= NumberFormat::createInstance(locale
, status
);
725 if (fNumberFormat
!= NULL
&& U_SUCCESS(status
))
727 // no matter what the locale's default number format looked like, we want
728 // to modify it so that it doesn't use thousands separators, doesn't always
729 // show the decimal point, and recognizes integers only when parsing
731 fNumberFormat
->setGroupingUsed(FALSE
);
732 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(fNumberFormat
);
733 if (decfmt
!= NULL
) {
734 decfmt
->setDecimalSeparatorAlwaysShown(FALSE
);
736 fNumberFormat
->setParseIntegerOnly(TRUE
);
737 fNumberFormat
->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
739 // TODO: Really, the default should be lenient...
740 fNumberFormat
->setParseStrict(FALSE
);
742 initNumberFormatters(locale
,status
);
745 else if (U_SUCCESS(status
))
747 status
= U_MISSING_RESOURCE_ERROR
;
751 /* Initialize the fields we use to disambiguate ambiguous years. Separate
752 * so we can call it from readObject().
754 void SimpleDateFormat::initializeDefaultCentury()
757 fHaveDefaultCentury
= fCalendar
->haveDefaultCentury();
758 if(fHaveDefaultCentury
) {
759 fDefaultCenturyStart
= fCalendar
->defaultCenturyStart();
760 fDefaultCenturyStartYear
= fCalendar
->defaultCenturyStartYear();
762 fDefaultCenturyStart
= DBL_MIN
;
763 fDefaultCenturyStartYear
= -1;
768 /* Define one-century window into which to disambiguate dates using
769 * two-digit years. Make public in JDK 1.2.
771 void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate
, UErrorCode
& status
)
773 if(U_FAILURE(status
)) {
777 status
= U_ILLEGAL_ARGUMENT_ERROR
;
781 fCalendar
->setTime(startDate
, status
);
782 if(U_SUCCESS(status
)) {
783 fHaveDefaultCentury
= TRUE
;
784 fDefaultCenturyStart
= startDate
;
785 fDefaultCenturyStartYear
= fCalendar
->get(UCAL_YEAR
, status
);
789 //----------------------------------------------------------------------
792 SimpleDateFormat::format(Calendar
& cal
, UnicodeString
& appendTo
, FieldPosition
& pos
) const
794 UErrorCode status
= U_ZERO_ERROR
;
795 FieldPositionOnlyHandler
handler(pos
);
796 return _format(cal
, appendTo
, handler
, status
);
799 //----------------------------------------------------------------------
802 SimpleDateFormat::format(Calendar
& cal
, UnicodeString
& appendTo
,
803 FieldPositionIterator
* posIter
, UErrorCode
& status
) const
805 FieldPositionIteratorHandler
handler(posIter
, status
);
806 return _format(cal
, appendTo
, handler
, status
);
809 //----------------------------------------------------------------------
812 SimpleDateFormat::_format(Calendar
& cal
, UnicodeString
& appendTo
, FieldPositionHandler
& handler
,
813 UErrorCode
& status
) const
815 Calendar
*workCal
= &cal
;
816 TimeZone
*backupTZ
= NULL
;
817 if (&cal
!= fCalendar
&& uprv_strcmp(cal
.getType(), fCalendar
->getType()) != 0) {
818 // Different calendar type
819 // We use the time and time zone from the input calendar, but
820 // do not use the input calendar for field calculation.
821 UDate t
= cal
.getTime(status
);
822 fCalendar
->setTime(t
, status
);
823 backupTZ
= fCalendar
->getTimeZone().clone();
824 fCalendar
->setTimeZone(cal
.getTimeZone());
828 UBool inQuote
= FALSE
;
832 // loop through the pattern string character by character
833 for (int32_t i
= 0; i
< fPattern
.length() && U_SUCCESS(status
); ++i
) {
834 UChar ch
= fPattern
[i
];
836 // Use subFormat() to format a repeated pattern character
837 // when a different pattern or non-pattern character is seen
838 if (ch
!= prevCh
&& count
> 0) {
839 subFormat(appendTo
, prevCh
, count
, handler
, *workCal
, status
);
843 // Consecutive single quotes are a single quote literal,
844 // either outside of quotes or between quotes
845 if ((i
+1) < fPattern
.length() && fPattern
[i
+1] == QUOTE
) {
846 appendTo
+= (UChar
)QUOTE
;
852 else if ( ! inQuote
&& ((ch
>= 0x0061 /*'a'*/ && ch
<= 0x007A /*'z'*/)
853 || (ch
>= 0x0041 /*'A'*/ && ch
<= 0x005A /*'Z'*/))) {
854 // ch is a date-time pattern character to be interpreted
855 // by subFormat(); count the number of times it is repeated
860 // Append quoted characters and unquoted non-pattern characters
865 // Format the last item in the pattern, if any
867 subFormat(appendTo
, prevCh
, count
, handler
, *workCal
, status
);
870 if (backupTZ
!= NULL
) {
871 // Restore the original time zone
872 fCalendar
->adoptTimeZone(backupTZ
);
878 //----------------------------------------------------------------------
880 /* Map calendar field into calendar field level.
881 * the larger the level, the smaller the field unit.
882 * For example, UCAL_ERA level is 0, UCAL_YEAR level is 10,
883 * UCAL_MONTH level is 20.
884 * NOTE: if new fields adds in, the table needs to update.
887 SimpleDateFormat::fgCalendarFieldToLevel
[] =
891 /*dDEF*/ 30, 20, 30, 30,
892 /*ahHm*/ 40, 50, 50, 60,
900 /* Map calendar field LETTER into calendar field level.
901 * the larger the level, the smaller the field unit.
902 * NOTE: if new fields adds in, the table needs to update.
905 SimpleDateFormat::fgPatternCharToLevel
[] = {
906 // A B C D E F G H I J K L M N O
907 -1, 40, -1, -1, 20, 30, 30, 0, 50, -1, -1, 50, 20, 20, -1, -1,
908 // P Q R S T U V W X Y Z
909 -1, 20, -1, 80, -1, -1, 0, 30, -1, 10, 0, -1, -1, -1, -1, -1,
910 // a b c d e f g h i j k l m n o
911 -1, 40, -1, 30, 30, 30, -1, 0, 50, -1, -1, 50, -1, 60, -1, -1,
912 // p q r s t u v w x y z
913 -1, 20, -1, 70, -1, 10, 0, 20, -1, 10, 0, -1, -1, -1, -1, -1
917 // Map index into pattern character string to Calendar field number.
918 const UCalendarDateFields
919 SimpleDateFormat::fgPatternIndexToCalendarField
[] =
921 /*GyM*/ UCAL_ERA
, UCAL_YEAR
, UCAL_MONTH
,
922 /*dkH*/ UCAL_DATE
, UCAL_HOUR_OF_DAY
, UCAL_HOUR_OF_DAY
,
923 /*msS*/ UCAL_MINUTE
, UCAL_SECOND
, UCAL_MILLISECOND
,
924 /*EDF*/ UCAL_DAY_OF_WEEK
, UCAL_DAY_OF_YEAR
, UCAL_DAY_OF_WEEK_IN_MONTH
,
925 /*wWa*/ UCAL_WEEK_OF_YEAR
, UCAL_WEEK_OF_MONTH
, UCAL_AM_PM
,
926 /*hKz*/ UCAL_HOUR
, UCAL_HOUR
, UCAL_ZONE_OFFSET
,
927 /*Yeu*/ UCAL_YEAR_WOY
, UCAL_DOW_LOCAL
, UCAL_EXTENDED_YEAR
,
928 /*gAZ*/ UCAL_JULIAN_DAY
, UCAL_MILLISECONDS_IN_DAY
, UCAL_ZONE_OFFSET
,
929 /*v*/ UCAL_ZONE_OFFSET
,
930 /*c*/ UCAL_DOW_LOCAL
,
934 /*V*/ UCAL_ZONE_OFFSET
,
937 // Map index into pattern character string to DateFormat field number
938 const UDateFormatField
939 SimpleDateFormat::fgPatternIndexToDateFormatField
[] = {
940 /*GyM*/ UDAT_ERA_FIELD
, UDAT_YEAR_FIELD
, UDAT_MONTH_FIELD
,
941 /*dkH*/ UDAT_DATE_FIELD
, UDAT_HOUR_OF_DAY1_FIELD
, UDAT_HOUR_OF_DAY0_FIELD
,
942 /*msS*/ UDAT_MINUTE_FIELD
, UDAT_SECOND_FIELD
, UDAT_FRACTIONAL_SECOND_FIELD
,
943 /*EDF*/ UDAT_DAY_OF_WEEK_FIELD
, UDAT_DAY_OF_YEAR_FIELD
, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD
,
944 /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD
, UDAT_WEEK_OF_MONTH_FIELD
, UDAT_AM_PM_FIELD
,
945 /*hKz*/ UDAT_HOUR1_FIELD
, UDAT_HOUR0_FIELD
, UDAT_TIMEZONE_FIELD
,
946 /*Yeu*/ UDAT_YEAR_WOY_FIELD
, UDAT_DOW_LOCAL_FIELD
, UDAT_EXTENDED_YEAR_FIELD
,
947 /*gAZ*/ UDAT_JULIAN_DAY_FIELD
, UDAT_MILLISECONDS_IN_DAY_FIELD
, UDAT_TIMEZONE_RFC_FIELD
,
948 /*v*/ UDAT_TIMEZONE_GENERIC_FIELD
,
949 /*c*/ UDAT_STANDALONE_DAY_FIELD
,
950 /*L*/ UDAT_STANDALONE_MONTH_FIELD
,
951 /*Q*/ UDAT_QUARTER_FIELD
,
952 /*q*/ UDAT_STANDALONE_QUARTER_FIELD
,
953 /*V*/ UDAT_TIMEZONE_SPECIAL_FIELD
,
956 //----------------------------------------------------------------------
959 * Append symbols[value] to dst. Make sure the array index is not out
963 _appendSymbol(UnicodeString
& dst
,
965 const UnicodeString
* symbols
,
966 int32_t symbolsCount
) {
967 U_ASSERT(0 <= value
&& value
< symbolsCount
);
968 if (0 <= value
&& value
< symbolsCount
) {
969 dst
+= symbols
[value
];
973 //---------------------------------------------------------------------
975 SimpleDateFormat::appendGMT(NumberFormat
*currentNumberFormat
,UnicodeString
&appendTo
, Calendar
& cal
, UErrorCode
& status
) const{
976 int32_t offset
= cal
.get(UCAL_ZONE_OFFSET
, status
) + cal
.get(UCAL_DST_OFFSET
, status
);
977 if (U_FAILURE(status
)) {
980 if (isDefaultGMTFormat()) {
981 formatGMTDefault(currentNumberFormat
,appendTo
, offset
);
983 ((SimpleDateFormat
*)this)->initGMTFormatters(status
);
984 if (U_SUCCESS(status
)) {
988 type
= (offset
% U_MILLIS_PER_MINUTE
) == 0 ? kGMTNegativeHM
: kGMTNegativeHMS
;
990 type
= (offset
% U_MILLIS_PER_MINUTE
) == 0 ? kGMTPositiveHM
: kGMTPositiveHMS
;
992 Formattable
param(offset
, Formattable::kIsDate
);
993 FieldPosition
fpos(0);
994 fGMTFormatters
[type
]->format(¶m
, 1, appendTo
, fpos
, status
);
1000 SimpleDateFormat::parseGMT(const UnicodeString
&text
, ParsePosition
&pos
) const {
1001 if (!isDefaultGMTFormat()) {
1002 int32_t start
= pos
.getIndex();
1005 UBool prefixMatch
= FALSE
;
1006 int32_t prefixLen
= fSymbols
->fGmtFormat
.indexOf((UChar
)0x007B /* '{' */);
1007 if (prefixLen
> 0 && text
.compare(start
, prefixLen
, fSymbols
->fGmtFormat
, 0, prefixLen
) == 0) {
1012 UErrorCode status
= U_ZERO_ERROR
;
1013 ((SimpleDateFormat
*)this)->initGMTFormatters(status
);
1014 if (U_SUCCESS(status
)) {
1016 int32_t parsedCount
;
1019 fGMTFormatters
[kGMTNegativeHMS
]->parseObject(text
, parsed
, pos
);
1020 if (pos
.getErrorIndex() == -1 &&
1021 (pos
.getIndex() - start
) >= fGMTFormatHmsMinLen
[kGMTNegativeHMSMinLenIdx
]) {
1022 parsed
.getArray(parsedCount
);
1023 if (parsedCount
== 1 && parsed
[0].getType() == Formattable::kDate
) {
1024 return (int32_t)(-1 * (int64_t)parsed
[0].getDate());
1028 // Reset ParsePosition
1029 pos
.setIndex(start
);
1030 pos
.setErrorIndex(-1);
1033 fGMTFormatters
[kGMTPositiveHMS
]->parseObject(text
, parsed
, pos
);
1034 if (pos
.getErrorIndex() == -1 &&
1035 (pos
.getIndex() - start
) >= fGMTFormatHmsMinLen
[kGMTPositiveHMSMinLenIdx
]) {
1036 parsed
.getArray(parsedCount
);
1037 if (parsedCount
== 1 && parsed
[0].getType() == Formattable::kDate
) {
1038 return (int32_t)((int64_t)parsed
[0].getDate());
1042 // Reset ParsePosition
1043 pos
.setIndex(start
);
1044 pos
.setErrorIndex(-1);
1047 fGMTFormatters
[kGMTNegativeHM
]->parseObject(text
, parsed
, pos
);
1048 if (pos
.getErrorIndex() == -1 && pos
.getIndex() > start
) {
1049 parsed
.getArray(parsedCount
);
1050 if (parsedCount
== 1 && parsed
[0].getType() == Formattable::kDate
) {
1051 return (int32_t)(-1 * (int64_t)parsed
[0].getDate());
1055 // Reset ParsePosition
1056 pos
.setIndex(start
);
1057 pos
.setErrorIndex(-1);
1060 fGMTFormatters
[kGMTPositiveHM
]->parseObject(text
, parsed
, pos
);
1061 if (pos
.getErrorIndex() == -1 && pos
.getIndex() > start
) {
1062 parsed
.getArray(parsedCount
);
1063 if (parsedCount
== 1 && parsed
[0].getType() == Formattable::kDate
) {
1064 return (int32_t)((int64_t)parsed
[0].getDate());
1068 // Reset ParsePosition
1069 pos
.setIndex(start
);
1070 pos
.setErrorIndex(-1);
1072 // fall through to the default GMT parsing method
1075 return parseGMTDefault(text
, pos
);
1079 SimpleDateFormat::formatGMTDefault(NumberFormat
*currentNumberFormat
,UnicodeString
&appendTo
, int32_t offset
) const {
1081 appendTo
+= gGmtMinus
;
1082 offset
= -offset
; // suppress the '-' sign for text display.
1084 appendTo
+= gGmtPlus
;
1087 offset
/= U_MILLIS_PER_SECOND
; // now in seconds
1088 int32_t sec
= offset
% 60;
1090 int32_t min
= offset
% 60;
1091 int32_t hour
= offset
/ 60;
1094 zeroPaddingNumber(currentNumberFormat
,appendTo
, hour
, 2, 2);
1095 appendTo
+= (UChar
)0x003A /*':'*/;
1096 zeroPaddingNumber(currentNumberFormat
,appendTo
, min
, 2, 2);
1098 appendTo
+= (UChar
)0x003A /*':'*/;
1099 zeroPaddingNumber(currentNumberFormat
,appendTo
, sec
, 2, 2);
1104 SimpleDateFormat::parseGMTDefault(const UnicodeString
&text
, ParsePosition
&pos
) const {
1105 int32_t start
= pos
.getIndex();
1106 NumberFormat
*currentNumberFormat
= getNumberFormatByIndex(UDAT_TIMEZONE_RFC_FIELD
);
1108 if (start
+ kUtLen
+ 1 >= text
.length()) {
1109 pos
.setErrorIndex(start
);
1113 int32_t cur
= start
;
1115 if (text
.compare(start
, kGmtLen
, gGmt
) == 0) {
1117 } else if (text
.compare(start
, kUtLen
, gUt
) == 0) {
1120 pos
.setErrorIndex(start
);
1124 UBool negative
= FALSE
;
1125 if (text
.charAt(cur
) == (UChar
)0x002D /* minus */) {
1127 } else if (text
.charAt(cur
) != (UChar
)0x002B /* plus */) {
1128 pos
.setErrorIndex(cur
);
1138 parseInt(text
, number
, 6, pos
, FALSE
,currentNumberFormat
);
1139 numLen
= pos
.getIndex() - cur
;
1142 pos
.setIndex(start
);
1143 pos
.setErrorIndex(cur
);
1147 int32_t numVal
= number
.getLong();
1157 if (cur
+ 2 < text
.length() && text
.charAt(cur
) == (UChar
)0x003A /* colon */) {
1160 parseInt(text
, number
, 2, pos
, FALSE
,currentNumberFormat
);
1161 numLen
= pos
.getIndex() - cur
;
1164 min
= number
.getLong();
1166 if (cur
+ 2 < text
.length() && text
.charAt(cur
) == (UChar
)0x003A /* colon */) {
1169 parseInt(text
, number
, 2, pos
, FALSE
,currentNumberFormat
);
1170 numLen
= pos
.getIndex() - cur
;
1173 sec
= number
.getLong();
1176 pos
.setIndex(cur
- 1);
1177 pos
.setErrorIndex(-1);
1182 pos
.setIndex(cur
- 1);
1183 pos
.setErrorIndex(-1);
1186 } else if (numLen
== 3 || numLen
== 4) {
1188 hour
= numVal
/ 100;
1190 } else if (numLen
== 5 || numLen
== 6) {
1192 hour
= numVal
/ 10000;
1193 min
= (numVal
% 10000) / 100;
1196 // HHmmss followed by bogus numbers
1197 pos
.setIndex(cur
+ 6);
1199 int32_t shift
= numLen
- 6;
1204 hour
= numVal
/ 10000;
1205 min
= (numVal
% 10000) / 100;
1209 int32_t offset
= ((hour
*60 + min
)*60 + sec
)*1000;
1217 SimpleDateFormat::isDefaultGMTFormat() const {
1219 if (fSymbols
->fGmtFormat
.length() == 0) {
1220 // No GMT pattern is set
1222 } else if (fSymbols
->fGmtFormat
.compare(gDefGmtPat
, kGmtPatLen
) != 0) {
1226 if (fSymbols
->fGmtHourFormats
== NULL
|| fSymbols
->fGmtHourFormatsCount
!= DateFormatSymbols::GMT_HOUR_COUNT
) {
1227 // No Hour pattern is set
1229 } else if ((fSymbols
->fGmtHourFormats
[DateFormatSymbols::GMT_NEGATIVE_HMS
].compare(gDefGmtNegHmsPat
, kNegHmsLen
) != 0)
1230 || (fSymbols
->fGmtHourFormats
[DateFormatSymbols::GMT_NEGATIVE_HM
].compare(gDefGmtNegHmPat
, kNegHmLen
) != 0)
1231 || (fSymbols
->fGmtHourFormats
[DateFormatSymbols::GMT_POSITIVE_HMS
].compare(gDefGmtPosHmsPat
, kPosHmsLen
) != 0)
1232 || (fSymbols
->fGmtHourFormats
[DateFormatSymbols::GMT_POSITIVE_HM
].compare(gDefGmtPosHmPat
, kPosHmLen
) != 0)) {
1239 SimpleDateFormat::formatRFC822TZ(UnicodeString
&appendTo
, int32_t offset
) const {
1240 UChar sign
= 0x002B /* '+' */;
1243 sign
= 0x002D /* '-' */;
1245 appendTo
.append(sign
);
1247 int32_t offsetH
= offset
/ U_MILLIS_PER_HOUR
;
1248 offset
= offset
% U_MILLIS_PER_HOUR
;
1249 int32_t offsetM
= offset
/ U_MILLIS_PER_MINUTE
;
1250 offset
= offset
% U_MILLIS_PER_MINUTE
;
1251 int32_t offsetS
= offset
/ U_MILLIS_PER_SECOND
;
1253 int32_t num
= 0, denom
= 0;
1255 offset
= offsetH
*100 + offsetM
; // HHmm
1256 num
= offset
% 10000;
1259 offset
= offsetH
*10000 + offsetM
*100 + offsetS
; // HHmmss
1260 num
= offset
% 1000000;
1263 while (denom
>= 1) {
1264 UChar digit
= (UChar
)0x0030 + (num
/ denom
);
1265 appendTo
.append(digit
);
1272 SimpleDateFormat::initGMTFormatters(UErrorCode
&status
) {
1273 if (U_FAILURE(status
)) {
1277 if (fGMTFormatters
== NULL
) {
1278 fGMTFormatters
= (MessageFormat
**)uprv_malloc(kNumGMTFormatters
* sizeof(MessageFormat
*));
1279 if (fGMTFormatters
) {
1280 for (int32_t i
= 0; i
< kNumGMTFormatters
; i
++) {
1281 const UnicodeString
*hourPattern
= NULL
; //initialized it to avoid warning
1283 case kGMTNegativeHMS
:
1284 hourPattern
= &(fSymbols
->fGmtHourFormats
[DateFormatSymbols::GMT_NEGATIVE_HMS
]);
1286 case kGMTNegativeHM
:
1287 hourPattern
= &(fSymbols
->fGmtHourFormats
[DateFormatSymbols::GMT_NEGATIVE_HM
]);
1289 case kGMTPositiveHMS
:
1290 hourPattern
= &(fSymbols
->fGmtHourFormats
[DateFormatSymbols::GMT_POSITIVE_HMS
]);
1292 case kGMTPositiveHM
:
1293 hourPattern
= &(fSymbols
->fGmtHourFormats
[DateFormatSymbols::GMT_POSITIVE_HM
]);
1296 fGMTFormatters
[i
] = new MessageFormat(fSymbols
->fGmtFormat
, status
);
1297 GregorianCalendar
*gcal
= new GregorianCalendar(TimeZone::createTimeZone(UnicodeString(gEtcUTC
)), status
);
1298 if (U_FAILURE(status
)) {
1301 SimpleDateFormat
*sdf
= (SimpleDateFormat
*)this->clone();
1302 sdf
->adoptCalendar(gcal
);
1303 sdf
->applyPattern(*hourPattern
);
1305 // This prevents a hours format pattern like "-HH:mm:ss" from matching
1306 // in a string like "GMT-07:00 10:08:11 PM"
1307 sdf
->setLenient(FALSE
);
1309 fGMTFormatters
[i
]->adoptFormat(0, sdf
);
1311 // For parsing, we only allow Hms patterns to be equal or longer
1312 // than its length with fixed minutes/seconds digits.
1314 if (i
== kGMTNegativeHMS
|| i
== kGMTPositiveHMS
) {
1316 Formattable
tmpParam(60*60*1000, Formattable::kIsDate
);
1317 FieldPosition
fpos(0);
1318 fGMTFormatters
[i
]->format(&tmpParam
, 1, tmp
, fpos
, status
);
1319 if (U_FAILURE(status
)) {
1322 if (i
== kGMTNegativeHMS
) {
1323 fGMTFormatHmsMinLen
[kGMTNegativeHMSMinLenIdx
] = tmp
.length();
1325 fGMTFormatHmsMinLen
[kGMTPositiveHMSMinLenIdx
] = tmp
.length();
1330 status
= U_MEMORY_ALLOCATION_ERROR
;
1337 SimpleDateFormat::initNumberFormatters(const Locale
&locale
,UErrorCode
&status
) {
1338 if (U_FAILURE(status
)) {
1341 if ( fDateOverride
.isBogus() && fTimeOverride
.isBogus() ) {
1345 if (fNumberFormatters
== NULL
) {
1346 fNumberFormatters
= (NumberFormat
**)uprv_malloc(UDAT_FIELD_COUNT
* sizeof(NumberFormat
*));
1347 if (fNumberFormatters
) {
1348 for (int32_t i
= 0; i
< UDAT_FIELD_COUNT
; i
++) {
1349 fNumberFormatters
[i
] = fNumberFormat
;
1352 status
= U_MEMORY_ALLOCATION_ERROR
;
1357 processOverrideString(locale
,fDateOverride
,kOvrStrDate
,status
);
1358 processOverrideString(locale
,fTimeOverride
,kOvrStrTime
,status
);
1363 SimpleDateFormat::processOverrideString(const Locale
&locale
, const UnicodeString
&str
, int8_t type
, UErrorCode
&status
) {
1364 if (str
.isBogus()) {
1369 UnicodeString nsName
;
1370 UnicodeString ovrField
;
1371 UBool moreToProcess
= TRUE
;
1373 while (moreToProcess
) {
1374 int32_t delimiterPosition
= str
.indexOf(ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE
,start
);
1375 if (delimiterPosition
== -1) {
1376 moreToProcess
= FALSE
;
1377 len
= str
.length() - start
;
1379 len
= delimiterPosition
- start
;
1381 UnicodeString
currentString(str
,start
,len
);
1382 int32_t equalSignPosition
= currentString
.indexOf(ULOC_KEYWORD_ASSIGN_UNICODE
,0);
1383 if (equalSignPosition
== -1) { // Simple override string such as "hebrew"
1384 nsName
.setTo(currentString
);
1385 ovrField
.setToBogus();
1386 } else { // Field specific override string such as "y=hebrew"
1387 nsName
.setTo(currentString
,equalSignPosition
+1);
1388 ovrField
.setTo(currentString
,0,1); // We just need the first character.
1391 int32_t nsNameHash
= nsName
.hashCode();
1392 // See if the numbering system is in the override list, if not, then add it.
1393 NSOverride
*cur
= fOverrideList
;
1394 NumberFormat
*nf
= NULL
;
1395 UBool found
= FALSE
;
1396 while ( cur
&& !found
) {
1397 if ( cur
->hash
== nsNameHash
) {
1405 cur
= (NSOverride
*)uprv_malloc(sizeof(NSOverride
));
1407 char kw
[ULOC_KEYWORD_AND_VALUES_CAPACITY
];
1408 uprv_strcpy(kw
,"numbers=");
1409 nsName
.extract(0,len
,kw
+8,ULOC_KEYWORD_AND_VALUES_CAPACITY
-8,US_INV
);
1411 Locale
ovrLoc(locale
.getLanguage(),locale
.getCountry(),locale
.getVariant(),kw
);
1412 nf
= NumberFormat::createInstance(ovrLoc
,status
);
1414 // no matter what the locale's default number format looked like, we want
1415 // to modify it so that it doesn't use thousands separators, doesn't always
1416 // show the decimal point, and recognizes integers only when parsing
1418 if (U_SUCCESS(status
)) {
1419 nf
->setGroupingUsed(FALSE
);
1420 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(nf
);
1421 if (decfmt
!= NULL
) {
1422 decfmt
->setDecimalSeparatorAlwaysShown(FALSE
);
1424 nf
->setParseIntegerOnly(TRUE
);
1425 nf
->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
1428 cur
->hash
= nsNameHash
;
1429 cur
->next
= fOverrideList
;
1430 fOverrideList
= cur
;
1433 // clean up before returning
1441 status
= U_MEMORY_ALLOCATION_ERROR
;
1446 // Now that we have an appropriate number formatter, fill in the appropriate spaces in the
1447 // number formatters table.
1449 if (ovrField
.isBogus()) {
1453 for ( int8_t i
=0 ; i
<kDateFieldsCount
; i
++ ) {
1454 fNumberFormatters
[kDateFields
[i
]] = nf
;
1456 if (type
==kOvrStrDate
) {
1460 case kOvrStrTime
: {
1461 for ( int8_t i
=0 ; i
<kTimeFieldsCount
; i
++ ) {
1462 fNumberFormatters
[kTimeFields
[i
]] = nf
;
1468 UChar ch
= ovrField
.charAt(0);
1469 UChar
*patternCharPtr
= u_strchr(DateFormatSymbols::getPatternUChars(), ch
);
1470 UDateFormatField patternCharIndex
;
1472 // if the pattern character is unrecognized, signal an error and bail out
1473 if (patternCharPtr
== NULL
) {
1474 status
= U_INVALID_FORMAT_ERROR
;
1477 patternCharIndex
= (UDateFormatField
)(patternCharPtr
- DateFormatSymbols::getPatternUChars());
1479 // Set the number formatter in the table
1480 fNumberFormatters
[patternCharIndex
] = nf
;
1483 start
= delimiterPosition
+ 1;
1486 //---------------------------------------------------------------------
1488 SimpleDateFormat::subFormat(UnicodeString
&appendTo
,
1491 FieldPositionHandler
& handler
,
1493 UErrorCode
& status
) const
1495 if (U_FAILURE(status
)) {
1499 // this function gets called by format() to produce the appropriate substitution
1500 // text for an individual pattern symbol (e.g., "HH" or "yyyy")
1502 UChar
*patternCharPtr
= u_strchr(DateFormatSymbols::getPatternUChars(), ch
);
1503 UDateFormatField patternCharIndex
;
1504 const int32_t maxIntCount
= 10;
1505 int32_t beginOffset
= appendTo
.length();
1506 NumberFormat
*currentNumberFormat
;
1508 UBool isHebrewCalendar
= !strcmp(cal
.getType(),"hebrew");
1510 // if the pattern character is unrecognized, signal an error and dump out
1511 if (patternCharPtr
== NULL
)
1513 status
= U_INVALID_FORMAT_ERROR
;
1517 patternCharIndex
= (UDateFormatField
)(patternCharPtr
- DateFormatSymbols::getPatternUChars());
1518 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
];
1519 int32_t value
= cal
.get(field
, status
);
1520 if (U_FAILURE(status
)) {
1524 currentNumberFormat
= getNumberFormatByIndex(patternCharIndex
);
1525 switch (patternCharIndex
) {
1527 // for any "G" symbol, write out the appropriate era string
1528 // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name
1529 case UDAT_ERA_FIELD
:
1531 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowEras
, fSymbols
->fNarrowErasCount
);
1532 else if (count
== 4)
1533 _appendSymbol(appendTo
, value
, fSymbols
->fEraNames
, fSymbols
->fEraNamesCount
);
1535 _appendSymbol(appendTo
, value
, fSymbols
->fEras
, fSymbols
->fErasCount
);
1538 // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits
1540 //Year y yy yyy yyyy yyyyy
1541 //AD 1 1 01 001 0001 00001
1542 //AD 12 12 12 012 0012 00012
1543 //AD 123 123 23 123 0123 00123
1544 //AD 1234 1234 34 1234 1234 01234
1545 //AD 12345 12345 45 12345 12345 12345
1546 case UDAT_YEAR_FIELD
:
1547 case UDAT_YEAR_WOY_FIELD
:
1549 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, 2, 2);
1551 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, count
, maxIntCount
);
1554 // for "MMMM", write out the whole month name, for "MMM", write out the month
1555 // abbreviation, for "M" or "MM", write out the month as a number with the
1556 // appropriate number of digits
1557 // for "MMMMM", use the narrow form
1558 case UDAT_MONTH_FIELD
:
1559 if ( isHebrewCalendar
) {
1560 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
1561 if (hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
== 6 && count
>= 3 )
1562 value
= 13; // Show alternate form for Adar II in leap years in Hebrew calendar.
1563 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6 && count
< 3 )
1564 value
--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7.
1567 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowMonths
,
1568 fSymbols
->fNarrowMonthsCount
);
1569 else if (count
== 4)
1570 _appendSymbol(appendTo
, value
, fSymbols
->fMonths
,
1571 fSymbols
->fMonthsCount
);
1572 else if (count
== 3)
1573 _appendSymbol(appendTo
, value
, fSymbols
->fShortMonths
,
1574 fSymbols
->fShortMonthsCount
);
1576 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
+ 1, count
, maxIntCount
);
1579 // for "LLLL", write out the whole month name, for "LLL", write out the month
1580 // abbreviation, for "L" or "LL", write out the month as a number with the
1581 // appropriate number of digits
1582 // for "LLLLL", use the narrow form
1583 case UDAT_STANDALONE_MONTH_FIELD
:
1585 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneNarrowMonths
,
1586 fSymbols
->fStandaloneNarrowMonthsCount
);
1587 else if (count
== 4)
1588 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneMonths
,
1589 fSymbols
->fStandaloneMonthsCount
);
1590 else if (count
== 3)
1591 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneShortMonths
,
1592 fSymbols
->fStandaloneShortMonthsCount
);
1594 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
+ 1, count
, maxIntCount
);
1597 // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
1598 case UDAT_HOUR_OF_DAY1_FIELD
:
1600 zeroPaddingNumber(currentNumberFormat
,appendTo
, cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1, count
, maxIntCount
);
1602 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1605 case UDAT_FRACTIONAL_SECOND_FIELD
:
1606 // Fractional seconds left-justify
1608 currentNumberFormat
->setMinimumIntegerDigits((count
> 3) ? 3 : count
);
1609 currentNumberFormat
->setMaximumIntegerDigits(maxIntCount
);
1612 } else if (count
== 2) {
1616 currentNumberFormat
->format(value
, appendTo
, p
);
1618 currentNumberFormat
->setMinimumIntegerDigits(count
- 3);
1619 currentNumberFormat
->format((int32_t)0, appendTo
, p
);
1624 // for "ee" or "e", use local numeric day-of-the-week
1625 // for "EEEEE" or "eeeee", write out the narrow day-of-the-week name
1626 // for "EEEE" or "eeee", write out the wide day-of-the-week name
1627 // for "EEE" or "EE" or "E" or "eee", write out the abbreviated day-of-the-week name
1628 case UDAT_DOW_LOCAL_FIELD
:
1630 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1633 // fall through to EEEEE-EEE handling, but for that we don't want local day-of-week,
1634 // we want standard day-of-week, so first fix value to work for EEEEE-EEE.
1635 value
= cal
.get(UCAL_DAY_OF_WEEK
, status
);
1636 if (U_FAILURE(status
)) {
1639 // fall through, do not break here
1640 case UDAT_DAY_OF_WEEK_FIELD
:
1642 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowWeekdays
,
1643 fSymbols
->fNarrowWeekdaysCount
);
1644 else if (count
== 4)
1645 _appendSymbol(appendTo
, value
, fSymbols
->fWeekdays
,
1646 fSymbols
->fWeekdaysCount
);
1648 _appendSymbol(appendTo
, value
, fSymbols
->fShortWeekdays
,
1649 fSymbols
->fShortWeekdaysCount
);
1652 // for "ccc", write out the abbreviated day-of-the-week name
1653 // for "cccc", write out the wide day-of-the-week name
1654 // for "ccccc", use the narrow day-of-the-week name
1655 case UDAT_STANDALONE_DAY_FIELD
:
1657 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, 1, maxIntCount
);
1660 // fall through to alpha DOW handling, but for that we don't want local day-of-week,
1661 // we want standard day-of-week, so first fix value.
1662 value
= cal
.get(UCAL_DAY_OF_WEEK
, status
);
1663 if (U_FAILURE(status
)) {
1667 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneNarrowWeekdays
,
1668 fSymbols
->fStandaloneNarrowWeekdaysCount
);
1669 else if (count
== 4)
1670 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneWeekdays
,
1671 fSymbols
->fStandaloneWeekdaysCount
);
1673 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneShortWeekdays
,
1674 fSymbols
->fStandaloneShortWeekdaysCount
);
1677 // for and "a" symbol, write out the whole AM/PM string
1678 case UDAT_AM_PM_FIELD
:
1679 _appendSymbol(appendTo
, value
, fSymbols
->fAmPms
,
1680 fSymbols
->fAmPmsCount
);
1683 // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
1685 case UDAT_HOUR1_FIELD
:
1687 zeroPaddingNumber(currentNumberFormat
,appendTo
, cal
.getLeastMaximum(UCAL_HOUR
) + 1, count
, maxIntCount
);
1689 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1692 // for the "z" symbols, we have to check our time zone data first. If we have a
1693 // localized name for the time zone, then "zzzz" / "zzz" indicate whether
1694 // daylight time is in effect (long/short) and "zz" / "z" do not (long/short).
1695 // If we don't have a localized time zone name,
1696 // then the time zone shows up as "GMT+hh:mm" or "GMT-hh:mm" (where "hh:mm" is the
1697 // offset from GMT) regardless of how many z's were in the pattern symbol
1698 case UDAT_TIMEZONE_FIELD
:
1699 case UDAT_TIMEZONE_GENERIC_FIELD
:
1700 case UDAT_TIMEZONE_SPECIAL_FIELD
:
1702 UnicodeString zoneString
;
1703 const ZoneStringFormat
*zsf
= fSymbols
->getZoneStringFormat();
1705 if (patternCharIndex
== UDAT_TIMEZONE_FIELD
) {
1708 zsf
->getSpecificShortString(cal
, TRUE
/*commonly used only*/,
1709 zoneString
, status
);
1712 zsf
->getSpecificLongString(cal
, zoneString
, status
);
1714 } else if (patternCharIndex
== UDAT_TIMEZONE_GENERIC_FIELD
) {
1717 zsf
->getGenericShortString(cal
, TRUE
/*commonly used only*/,
1718 zoneString
, status
);
1719 } else if (count
== 4) {
1721 zsf
->getGenericLongString(cal
, zoneString
, status
);
1723 } else { // patternCharIndex == UDAT_TIMEZONE_SPECIAL_FIELD
1726 zsf
->getSpecificShortString(cal
, FALSE
/*ignore commonly used*/,
1727 zoneString
, status
);
1728 } else if (count
== 4) {
1730 zsf
->getGenericLocationString(cal
, zoneString
, status
);
1734 if (zoneString
.isEmpty()) {
1735 appendGMT(currentNumberFormat
,appendTo
, cal
, status
);
1737 appendTo
+= zoneString
;
1742 case UDAT_TIMEZONE_RFC_FIELD
: // 'Z' - TIMEZONE_RFC
1744 // RFC822 format, must use ASCII digits
1745 value
= (cal
.get(UCAL_ZONE_OFFSET
, status
) + cal
.get(UCAL_DST_OFFSET
, status
));
1746 formatRFC822TZ(appendTo
, value
);
1748 // long form, localized GMT pattern
1749 appendGMT(currentNumberFormat
,appendTo
, cal
, status
);
1753 case UDAT_QUARTER_FIELD
:
1755 _appendSymbol(appendTo
, value
/3, fSymbols
->fQuarters
,
1756 fSymbols
->fQuartersCount
);
1757 else if (count
== 3)
1758 _appendSymbol(appendTo
, value
/3, fSymbols
->fShortQuarters
,
1759 fSymbols
->fShortQuartersCount
);
1761 zeroPaddingNumber(currentNumberFormat
,appendTo
, (value
/3) + 1, count
, maxIntCount
);
1764 case UDAT_STANDALONE_QUARTER_FIELD
:
1766 _appendSymbol(appendTo
, value
/3, fSymbols
->fStandaloneQuarters
,
1767 fSymbols
->fStandaloneQuartersCount
);
1768 else if (count
== 3)
1769 _appendSymbol(appendTo
, value
/3, fSymbols
->fStandaloneShortQuarters
,
1770 fSymbols
->fStandaloneShortQuartersCount
);
1772 zeroPaddingNumber(currentNumberFormat
,appendTo
, (value
/3) + 1, count
, maxIntCount
);
1776 // all of the other pattern symbols can be formatted as simple numbers with
1777 // appropriate zero padding
1779 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1783 handler
.addAttribute(fgPatternIndexToDateFormatField
[patternCharIndex
], beginOffset
, appendTo
.length());
1786 //----------------------------------------------------------------------
1789 SimpleDateFormat::getNumberFormatByIndex(UDateFormatField index
) const {
1790 if (fNumberFormatters
!= NULL
) {
1791 return fNumberFormatters
[index
];
1793 return fNumberFormat
;
1797 //----------------------------------------------------------------------
1799 SimpleDateFormat::zeroPaddingNumber(NumberFormat
*currentNumberFormat
,UnicodeString
&appendTo
,
1800 int32_t value
, int32_t minDigits
, int32_t maxDigits
) const
1802 if (currentNumberFormat
!=NULL
) {
1803 FieldPosition
pos(0);
1805 currentNumberFormat
->setMinimumIntegerDigits(minDigits
);
1806 currentNumberFormat
->setMaximumIntegerDigits(maxDigits
);
1807 currentNumberFormat
->format(value
, appendTo
, pos
); // 3rd arg is there to speed up processing
1811 //----------------------------------------------------------------------
1814 * Format characters that indicate numeric fields. The character
1815 * at index 0 is treated specially.
1817 static const UChar NUMERIC_FORMAT_CHARS
[] = {0x4D, 0x59, 0x79, 0x75, 0x64, 0x65, 0x68, 0x48, 0x6D, 0x73, 0x53, 0x44, 0x46, 0x77, 0x57, 0x6B, 0x4B, 0x00}; /* "MYyudehHmsSDFwWkK" */
1820 * Return true if the given format character, occuring count
1821 * times, represents a numeric field.
1823 UBool
SimpleDateFormat::isNumeric(UChar formatChar
, int32_t count
) {
1824 UnicodeString
s(NUMERIC_FORMAT_CHARS
);
1825 int32_t i
= s
.indexOf(formatChar
);
1826 return (i
> 0 || (i
== 0 && count
< 3));
1830 SimpleDateFormat::parse(const UnicodeString
& text
, Calendar
& cal
, ParsePosition
& parsePos
) const
1832 UErrorCode status
= U_ZERO_ERROR
;
1833 int32_t pos
= parsePos
.getIndex();
1834 int32_t start
= pos
;
1836 UBool ambiguousYear
[] = { FALSE
};
1837 int32_t saveHebrewMonth
= -1;
1840 UBool lenient
= isLenient();
1842 // hack, reset tztype, cast away const
1843 ((SimpleDateFormat
*)this)->tztype
= TZTYPE_UNK
;
1845 // For parsing abutting numeric fields. 'abutPat' is the
1846 // offset into 'pattern' of the first of 2 or more abutting
1847 // numeric fields. 'abutStart' is the offset into 'text'
1848 // where parsing the fields begins. 'abutPass' starts off as 0
1849 // and increments each time we try to parse the fields.
1850 int32_t abutPat
= -1; // If >=0, we are in a run of abutting numeric fields
1851 int32_t abutStart
= 0;
1852 int32_t abutPass
= 0;
1853 UBool inQuote
= FALSE
;
1855 const UnicodeString
numericFormatChars(NUMERIC_FORMAT_CHARS
);
1857 TimeZone
*backupTZ
= NULL
;
1858 Calendar
*workCal
= &cal
;
1859 if (&cal
!= fCalendar
&& uprv_strcmp(cal
.getType(), fCalendar
->getType()) != 0) {
1860 // Different calendar type
1861 // We use the time/zone from the input calendar, but
1862 // do not use the input calendar for field calculation.
1863 fCalendar
->setTime(cal
.getTime(status
),status
);
1864 if (U_FAILURE(status
)) {
1867 backupTZ
= fCalendar
->getTimeZone().clone();
1868 fCalendar
->setTimeZone(cal
.getTimeZone());
1869 workCal
= fCalendar
;
1872 for (int32_t i
=0; i
<fPattern
.length(); ++i
) {
1873 UChar ch
= fPattern
.charAt(i
);
1875 // Handle alphabetic field characters.
1876 if (!inQuote
&& ((ch
>= 0x41 && ch
<= 0x5A) || (ch
>= 0x61 && ch
<= 0x7A))) { // [A-Za-z]
1877 int32_t fieldPat
= i
;
1879 // Count the length of this field specifier
1881 while ((i
+1)<fPattern
.length() &&
1882 fPattern
.charAt(i
+1) == ch
) {
1887 if (isNumeric(ch
, count
)) {
1889 // Determine if there is an abutting numeric field. For
1890 // most fields we can just look at the next characters,
1891 // but the 'm' field is either numeric or text,
1892 // depending on the count, so we have to look ahead for
1894 if ((i
+1)<fPattern
.length()) {
1896 UChar nextCh
= fPattern
.charAt(i
+1);
1897 int32_t k
= numericFormatChars
.indexOf(nextCh
);
1900 while (j
<fPattern
.length() &&
1901 fPattern
.charAt(j
) == nextCh
) {
1904 abutting
= (j
-i
) < 4; // nextCount < 3
1909 // Record the start of a set of abutting numeric
1919 abutPat
= -1; // End of any abutting fields
1922 // Handle fields within a run of abutting numeric fields. Take
1923 // the pattern "HHmmss" as an example. We will try to parse
1924 // 2/2/2 characters of the input text, then if that fails,
1925 // 1/2/2. We only adjust the width of the leftmost field; the
1926 // others remain fixed. This allows "123456" => 12:34:56, but
1927 // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
1928 // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
1930 // If we are at the start of a run of abutting fields, then
1931 // shorten this field in each pass. If we can't shorten
1932 // this field any more, then the parse of this set of
1933 // abutting numeric fields has failed.
1934 if (fieldPat
== abutPat
) {
1935 count
-= abutPass
++;
1937 status
= U_PARSE_ERROR
;
1942 pos
= subParse(text
, pos
, ch
, count
,
1943 TRUE
, FALSE
, ambiguousYear
, saveHebrewMonth
, *workCal
, i
);
1945 // If the parse fails anywhere in the run, back up to the
1946 // start of the run and retry.
1954 // Handle non-numeric fields and non-abutting numeric
1957 int32_t s
= subParse(text
, pos
, ch
, count
,
1958 FALSE
, TRUE
, ambiguousYear
, saveHebrewMonth
, *workCal
, i
);
1961 // era not present, in special cases allow this to continue
1964 if (i
+1 < fPattern
.length()) {
1965 // move to next pattern character
1966 UChar ch
= fPattern
.charAt(i
+1);
1968 // check for whitespace
1969 if (uprv_isRuleWhiteSpace(ch
)) {
1971 // Advance over run in pattern
1972 while ((i
+1)<fPattern
.length() &&
1973 uprv_isRuleWhiteSpace(fPattern
.charAt(i
+1))) {
1980 status
= U_PARSE_ERROR
;
1987 // Handle literal pattern characters. These are any
1988 // quoted characters and non-alphabetic unquoted
1992 abutPat
= -1; // End of any abutting fields
1994 if (! matchLiterals(fPattern
, i
, text
, pos
, lenient
)) {
1995 status
= U_PARSE_ERROR
;
2001 // At this point the fields of Calendar have been set. Calendar
2002 // will fill in default values for missing fields when the time
2005 parsePos
.setIndex(pos
);
2007 // This part is a problem: When we call parsedDate.after, we compute the time.
2008 // Take the date April 3 2004 at 2:30 am. When this is first set up, the year
2009 // will be wrong if we're parsing a 2-digit year pattern. It will be 1904.
2010 // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am
2011 // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
2012 // on that day. It is therefore parsed out to fields as 3:30 am. Then we
2013 // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is
2014 // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
2016 UDate parsedDate = calendar.getTime();
2017 if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
2018 calendar.add(Calendar.YEAR, 100);
2019 parsedDate = calendar.getTime();
2022 // Because of the above condition, save off the fields in case we need to readjust.
2023 // The procedure we use here is not particularly efficient, but there is no other
2024 // way to do this given the API restrictions present in Calendar. We minimize
2025 // inefficiency by only performing this computation when it might apply, that is,
2026 // when the two-digit year is equal to the start year, and thus might fall at the
2027 // front or the back of the default century. This only works because we adjust
2028 // the year correctly to start with in other cases -- see subParse().
2029 if (ambiguousYear
[0] || tztype
!= TZTYPE_UNK
) // If this is true then the two-digit year == the default start year
2031 // We need a copy of the fields, and we need to avoid triggering a call to
2032 // complete(), which will recalculate the fields. Since we can't access
2033 // the fields[] array in Calendar, we clone the entire object. This will
2034 // stop working if Calendar.clone() is ever rewritten to call complete().
2036 if (ambiguousYear
[0]) {
2038 // Check for failed cloning.
2040 status
= U_MEMORY_ALLOCATION_ERROR
;
2043 UDate parsedDate
= copy
->getTime(status
);
2044 // {sfb} check internalGetDefaultCenturyStart
2045 if (fHaveDefaultCentury
&& (parsedDate
< fDefaultCenturyStart
)) {
2046 // We can't use add here because that does a complete() first.
2047 cal
.set(UCAL_YEAR
, fDefaultCenturyStartYear
+ 100);
2052 if (tztype
!= TZTYPE_UNK
) {
2054 // Check for failed cloning.
2056 status
= U_MEMORY_ALLOCATION_ERROR
;
2059 const TimeZone
& tz
= cal
.getTimeZone();
2060 BasicTimeZone
*btz
= NULL
;
2062 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
2063 || dynamic_cast<const SimpleTimeZone
*>(&tz
) != NULL
2064 || dynamic_cast<const RuleBasedTimeZone
*>(&tz
) != NULL
2065 || dynamic_cast<const VTimeZone
*>(&tz
) != NULL
) {
2066 btz
= (BasicTimeZone
*)&tz
;
2070 copy
->set(UCAL_ZONE_OFFSET
, 0);
2071 copy
->set(UCAL_DST_OFFSET
, 0);
2072 UDate localMillis
= copy
->getTime(status
);
2074 // Make sure parsed time zone type (Standard or Daylight)
2075 // matches the rule used by the parsed time zone.
2078 if (tztype
== TZTYPE_STD
) {
2079 btz
->getOffsetFromLocal(localMillis
,
2080 BasicTimeZone::kStandard
, BasicTimeZone::kStandard
, raw
, dst
, status
);
2082 btz
->getOffsetFromLocal(localMillis
,
2083 BasicTimeZone::kDaylight
, BasicTimeZone::kDaylight
, raw
, dst
, status
);
2086 // No good way to resolve ambiguous time at transition,
2087 // but following code work in most case.
2088 tz
.getOffset(localMillis
, TRUE
, raw
, dst
, status
);
2091 // Now, compare the results with parsed type, either standard or daylight saving time
2092 int32_t resolvedSavings
= dst
;
2093 if (tztype
== TZTYPE_STD
) {
2095 // Override DST_OFFSET = 0 in the result calendar
2096 resolvedSavings
= 0;
2098 } else { // tztype == TZTYPE_DST
2101 UDate time
= localMillis
+ raw
;
2102 // We use the nearest daylight saving time rule.
2103 TimeZoneTransition beforeTrs
, afterTrs
;
2104 UDate beforeT
= time
, afterT
= time
;
2105 int32_t beforeSav
= 0, afterSav
= 0;
2106 UBool beforeTrsAvail
, afterTrsAvail
;
2108 // Search for DST rule before or on the time
2110 beforeTrsAvail
= btz
->getPreviousTransition(beforeT
, TRUE
, beforeTrs
);
2111 if (!beforeTrsAvail
) {
2114 beforeT
= beforeTrs
.getTime() - 1;
2115 beforeSav
= beforeTrs
.getFrom()->getDSTSavings();
2116 if (beforeSav
!= 0) {
2121 // Search for DST rule after the time
2123 afterTrsAvail
= btz
->getNextTransition(afterT
, FALSE
, afterTrs
);
2124 if (!afterTrsAvail
) {
2127 afterT
= afterTrs
.getTime();
2128 afterSav
= afterTrs
.getTo()->getDSTSavings();
2129 if (afterSav
!= 0) {
2134 if (beforeTrsAvail
&& afterTrsAvail
) {
2135 if (time
- beforeT
> afterT
- time
) {
2136 resolvedSavings
= afterSav
;
2138 resolvedSavings
= beforeSav
;
2140 } else if (beforeTrsAvail
&& beforeSav
!= 0) {
2141 resolvedSavings
= beforeSav
;
2142 } else if (afterTrsAvail
&& afterSav
!= 0) {
2143 resolvedSavings
= afterSav
;
2145 resolvedSavings
= btz
->getDSTSavings();
2148 resolvedSavings
= tz
.getDSTSavings();
2150 if (resolvedSavings
== 0) {
2152 resolvedSavings
= U_MILLIS_PER_HOUR
;
2156 cal
.set(UCAL_ZONE_OFFSET
, raw
);
2157 cal
.set(UCAL_DST_OFFSET
, resolvedSavings
);
2162 // Set the parsed result if local calendar is used
2163 // instead of the input calendar
2164 if (U_SUCCESS(status
) && workCal
!= &cal
) {
2165 cal
.setTimeZone(workCal
->getTimeZone());
2166 cal
.setTime(workCal
->getTime(status
), status
);
2169 // Restore the original time zone if required
2170 if (backupTZ
!= NULL
) {
2171 fCalendar
->adoptTimeZone(backupTZ
);
2174 // If any Calendar calls failed, we pretend that we
2175 // couldn't parse the string, when in reality this isn't quite accurate--
2176 // we did parse it; the Calendar calls just failed.
2177 if (U_FAILURE(status
)) {
2178 parsePos
.setErrorIndex(pos
);
2179 parsePos
.setIndex(start
);
2184 SimpleDateFormat::parse( const UnicodeString
& text
,
2185 ParsePosition
& pos
) const {
2186 // redefined here because the other parse() function hides this function's
2187 // cunterpart on DateFormat
2188 return DateFormat::parse(text
, pos
);
2192 SimpleDateFormat::parse(const UnicodeString
& text
, UErrorCode
& status
) const
2194 // redefined here because the other parse() function hides this function's
2195 // counterpart on DateFormat
2196 return DateFormat::parse(text
, status
);
2198 //----------------------------------------------------------------------
2200 int32_t SimpleDateFormat::matchQuarterString(const UnicodeString
& text
,
2202 UCalendarDateFields field
,
2203 const UnicodeString
* data
,
2205 Calendar
& cal
) const
2208 int32_t count
= dataCount
;
2210 // There may be multiple strings in the data[] array which begin with
2211 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2212 // We keep track of the longest match, and return that. Note that this
2213 // unfortunately requires us to test all array elements.
2214 int32_t bestMatchLength
= 0, bestMatch
= -1;
2216 // {sfb} kludge to support case-insensitive comparison
2217 // {markus 2002oct11} do not just use caseCompareBetween because we do not know
2218 // the length of the match after case folding
2219 // {alan 20040607} don't case change the whole string, since the length
2221 // TODO we need a case-insensitive startsWith function
2222 UnicodeString lcase
, lcaseText
;
2223 text
.extract(start
, INT32_MAX
, lcaseText
);
2224 lcaseText
.foldCase();
2226 for (; i
< count
; ++i
)
2228 // Always compare if we have no match yet; otherwise only compare
2229 // against potentially better matches (longer strings).
2231 lcase
.fastCopyFrom(data
[i
]).foldCase();
2232 int32_t length
= lcase
.length();
2234 if (length
> bestMatchLength
&&
2235 lcaseText
.compareBetween(0, length
, lcase
, 0, length
) == 0)
2238 bestMatchLength
= length
;
2243 cal
.set(field
, bestMatch
* 3);
2245 // Once we have a match, we have to determine the length of the
2246 // original source string. This will usually be == the length of
2247 // the case folded string, but it may differ (e.g. sharp s).
2248 lcase
.fastCopyFrom(data
[bestMatch
]).foldCase();
2250 // Most of the time, the length will be the same as the length
2251 // of the string from the locale data. Sometimes it will be
2252 // different, in which case we will have to figure it out by
2253 // adding a character at a time, until we have a match. We do
2254 // this all in one loop, where we try 'len' first (at index
2256 int32_t len
= data
[bestMatch
].length(); // 99+% of the time
2257 int32_t n
= text
.length() - start
;
2258 for (i
=0; i
<=n
; ++i
) {
2262 } else if (i
== len
) {
2263 continue; // already tried this when i was 0
2265 text
.extract(start
, j
, lcaseText
);
2266 lcaseText
.foldCase();
2267 if (lcase
== lcaseText
) {
2276 //----------------------------------------------------------------------
2277 UBool
SimpleDateFormat::matchLiterals(const UnicodeString
&pattern
,
2278 int32_t &patternOffset
,
2279 const UnicodeString
&text
,
2280 int32_t &textOffset
,
2283 UBool inQuote
= FALSE
;
2284 UnicodeString literal
;
2285 int32_t i
= patternOffset
;
2287 // scan pattern looking for contiguous literal characters
2288 for ( ; i
< pattern
.length(); i
+= 1) {
2289 UChar ch
= pattern
.charAt(i
);
2291 if (!inQuote
&& ((ch
>= 0x41 && ch
<= 0x5A) || (ch
>= 0x61 && ch
<= 0x7A))) { // unquoted [A-Za-z]
2296 // Match a quote literal ('') inside OR outside of quotes
2297 if ((i
+ 1) < pattern
.length() && pattern
.charAt(i
+ 1) == QUOTE
) {
2308 // at this point, literal contains the literal text
2309 // and i is the index of the next non-literal pattern character.
2311 int32_t t
= textOffset
;
2314 // trim leading, trailing whitespace from
2318 // ignore any leading whitespace in the text
2319 while (t
< text
.length() && u_isWhitespace(text
.charAt(t
))) {
2324 for (p
= 0; p
< literal
.length() && t
< text
.length(); p
+= 1, t
+= 1) {
2325 UBool needWhitespace
= FALSE
;
2327 while (p
< literal
.length() && uprv_isRuleWhiteSpace(literal
.charAt(p
))) {
2328 needWhitespace
= TRUE
;
2332 if (needWhitespace
) {
2335 while (t
< text
.length()) {
2336 UChar tch
= text
.charAt(t
);
2338 if (!u_isUWhiteSpace(tch
) && !uprv_isRuleWhiteSpace(tch
)) {
2345 // TODO: should we require internal spaces
2346 // in lenient mode? (There won't be any
2347 // leading or trailing spaces)
2348 if (!lenient
&& t
== tStart
) {
2349 // didn't find matching whitespace:
2350 // an error in strict mode
2354 // In strict mode, this run of whitespace
2355 // may have been at the end.
2356 if (p
>= literal
.length()) {
2361 if (t
>= text
.length() || literal
.charAt(p
) != text
.charAt(t
)) {
2362 // Ran out of text, or found a non-matching character:
2363 // OK in lenient mode, an error in strict mode.
2372 // At this point if we're in strict mode we have a complete match.
2373 // If we're in lenient mode we may have a partial match, or no
2376 // no match. Pretend it matched a run of whitespace
2377 // and ignorables in the text.
2378 const UnicodeSet
*ignorables
= NULL
;
2379 UChar
*patternCharPtr
= u_strchr(DateFormatSymbols::getPatternUChars(), pattern
.charAt(i
));
2381 if (patternCharPtr
!= NULL
) {
2382 UDateFormatField patternCharIndex
= (UDateFormatField
) (patternCharPtr
- DateFormatSymbols::getPatternUChars());
2384 ignorables
= SimpleDateFormatStaticSets::getIgnorables(patternCharIndex
);
2387 for (t
= textOffset
; t
< text
.length(); t
+= 1) {
2388 UChar ch
= text
.charAt(t
);
2390 if (ignorables
== NULL
|| !ignorables
->contains(ch
)) {
2396 // if we get here, we've got a complete match.
2397 patternOffset
= i
- 1;
2403 //----------------------------------------------------------------------
2405 int32_t SimpleDateFormat::matchString(const UnicodeString
& text
,
2407 UCalendarDateFields field
,
2408 const UnicodeString
* data
,
2410 Calendar
& cal
) const
2413 int32_t count
= dataCount
;
2415 if (field
== UCAL_DAY_OF_WEEK
) i
= 1;
2417 // There may be multiple strings in the data[] array which begin with
2418 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2419 // We keep track of the longest match, and return that. Note that this
2420 // unfortunately requires us to test all array elements.
2421 int32_t bestMatchLength
= 0, bestMatch
= -1;
2423 // {sfb} kludge to support case-insensitive comparison
2424 // {markus 2002oct11} do not just use caseCompareBetween because we do not know
2425 // the length of the match after case folding
2426 // {alan 20040607} don't case change the whole string, since the length
2428 // TODO we need a case-insensitive startsWith function
2429 UnicodeString lcase
, lcaseText
;
2430 text
.extract(start
, INT32_MAX
, lcaseText
);
2431 lcaseText
.foldCase();
2433 for (; i
< count
; ++i
)
2435 // Always compare if we have no match yet; otherwise only compare
2436 // against potentially better matches (longer strings).
2438 lcase
.fastCopyFrom(data
[i
]).foldCase();
2439 int32_t length
= lcase
.length();
2441 if (length
> bestMatchLength
&&
2442 lcaseText
.compareBetween(0, length
, lcase
, 0, length
) == 0)
2445 bestMatchLength
= length
;
2450 // Adjustment for Hebrew Calendar month Adar II
2451 if (!strcmp(cal
.getType(),"hebrew") && field
==UCAL_MONTH
&& bestMatch
==13) {
2455 cal
.set(field
, bestMatch
);
2458 // Once we have a match, we have to determine the length of the
2459 // original source string. This will usually be == the length of
2460 // the case folded string, but it may differ (e.g. sharp s).
2461 lcase
.fastCopyFrom(data
[bestMatch
]).foldCase();
2463 // Most of the time, the length will be the same as the length
2464 // of the string from the locale data. Sometimes it will be
2465 // different, in which case we will have to figure it out by
2466 // adding a character at a time, until we have a match. We do
2467 // this all in one loop, where we try 'len' first (at index
2469 int32_t len
= data
[bestMatch
].length(); // 99+% of the time
2470 int32_t n
= text
.length() - start
;
2471 for (i
=0; i
<=n
; ++i
) {
2475 } else if (i
== len
) {
2476 continue; // already tried this when i was 0
2478 text
.extract(start
, j
, lcaseText
);
2479 lcaseText
.foldCase();
2480 if (lcase
== lcaseText
) {
2489 //----------------------------------------------------------------------
2492 SimpleDateFormat::set2DigitYearStart(UDate d
, UErrorCode
& status
)
2494 parseAmbiguousDatesAsAfter(d
, status
);
2498 * Private member function that converts the parsed date strings into
2499 * timeFields. Returns -start (for ParsePosition) if failed.
2500 * @param text the time text to be parsed.
2501 * @param start where to start parsing.
2502 * @param ch the pattern character for the date field text to be parsed.
2503 * @param count the count of a pattern character.
2504 * @return the new start position if matching succeeded; a negative number
2505 * indicating matching failure, otherwise.
2507 int32_t SimpleDateFormat::subParse(const UnicodeString
& text
, int32_t& start
, UChar ch
, int32_t count
,
2508 UBool obeyCount
, UBool allowNegative
, UBool ambiguousYear
[], int32_t& saveHebrewMonth
, Calendar
& cal
,
2509 int32_t patLoc
) const
2515 ParsePosition
pos(0);
2516 UDateFormatField patternCharIndex
;
2517 NumberFormat
*currentNumberFormat
;
2519 UChar
*patternCharPtr
= u_strchr(DateFormatSymbols::getPatternUChars(), ch
);
2520 UBool lenient
= isLenient();
2521 UBool gotNumber
= FALSE
;
2523 #if defined (U_DEBUG_CAL)
2524 //fprintf(stderr, "%s:%d - [%c] st=%d \n", __FILE__, __LINE__, (char) ch, start);
2527 if (patternCharPtr
== NULL
) {
2531 patternCharIndex
= (UDateFormatField
)(patternCharPtr
- DateFormatSymbols::getPatternUChars());
2532 currentNumberFormat
= getNumberFormatByIndex(patternCharIndex
);
2533 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
];
2535 // If there are any spaces here, skip over them. If we hit the end
2536 // of the string, then fail.
2538 if (start
>= text
.length()) {
2542 UChar32 c
= text
.char32At(start
);
2543 if (!u_isUWhiteSpace(c
) /*||*/ && !uprv_isRuleWhiteSpace(c
)) {
2547 start
+= UTF_CHAR_LENGTH(c
);
2550 pos
.setIndex(start
);
2552 // We handle a few special cases here where we need to parse
2553 // a number value. We handle further, more generic cases below. We need
2554 // to handle some of them here because some fields require extra processing on
2555 // the parsed value.
2556 if (patternCharIndex
== UDAT_HOUR_OF_DAY1_FIELD
||
2557 patternCharIndex
== UDAT_HOUR_OF_DAY0_FIELD
||
2558 patternCharIndex
== UDAT_HOUR1_FIELD
||
2559 patternCharIndex
== UDAT_HOUR0_FIELD
||
2560 patternCharIndex
== UDAT_DOW_LOCAL_FIELD
||
2561 patternCharIndex
== UDAT_STANDALONE_DAY_FIELD
||
2562 patternCharIndex
== UDAT_MONTH_FIELD
||
2563 patternCharIndex
== UDAT_STANDALONE_MONTH_FIELD
||
2564 patternCharIndex
== UDAT_QUARTER_FIELD
||
2565 patternCharIndex
== UDAT_STANDALONE_QUARTER_FIELD
||
2566 patternCharIndex
== UDAT_YEAR_FIELD
||
2567 patternCharIndex
== UDAT_YEAR_WOY_FIELD
||
2568 patternCharIndex
== UDAT_FRACTIONAL_SECOND_FIELD
)
2570 int32_t parseStart
= pos
.getIndex();
2571 // It would be good to unify this with the obeyCount logic below,
2572 // but that's going to be difficult.
2573 const UnicodeString
* src
;
2576 if ((start
+count
) > text
.length()) {
2580 text
.extractBetween(0, start
+ count
, temp
);
2586 parseInt(*src
, number
, pos
, allowNegative
,currentNumberFormat
);
2588 int32_t txtLoc
= pos
.getIndex();
2590 if (txtLoc
> parseStart
) {
2591 value
= number
.getLong();
2594 // suffix processing
2596 txtLoc
= checkIntSuffix(text
, txtLoc
, patLoc
+1, TRUE
);
2597 if (txtLoc
!= pos
.getIndex()) {
2602 txtLoc
= checkIntSuffix(text
, txtLoc
, patLoc
+1, FALSE
);
2605 // Check the range of the value
2606 int32_t bias
= gFieldRangeBias
[patternCharIndex
];
2608 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
|| value
< cal
.getMinimum(field
) + bias
)) {
2612 pos
.setIndex(txtLoc
);
2616 // Make sure that we got a number if
2617 // we want one, and didn't get one
2618 // if we don't want one.
2619 switch (patternCharIndex
) {
2620 case UDAT_HOUR_OF_DAY1_FIELD
:
2621 case UDAT_HOUR_OF_DAY0_FIELD
:
2622 case UDAT_HOUR1_FIELD
:
2623 case UDAT_HOUR0_FIELD
:
2624 // special range check for hours:
2625 if (value
< 0 || value
> 24) {
2629 // fall through to gotNumber check
2631 case UDAT_YEAR_FIELD
:
2632 case UDAT_YEAR_WOY_FIELD
:
2633 case UDAT_FRACTIONAL_SECOND_FIELD
:
2634 // these must be a number
2641 case UDAT_DOW_LOCAL_FIELD
:
2642 case UDAT_STANDALONE_DAY_FIELD
:
2643 case UDAT_MONTH_FIELD
:
2644 case UDAT_STANDALONE_MONTH_FIELD
:
2645 case UDAT_QUARTER_FIELD
:
2646 case UDAT_STANDALONE_QUARTER_FIELD
:
2647 // in strict mode, these can only
2648 // be a number if count <= 2
2649 if (!lenient
&& gotNumber
&& count
> 2) {
2650 // We have a string pattern in strict mode
2651 // but the input parsed as a number. Ignore
2652 // the fact that the input parsed as a number
2653 // and try to match it as a string. (Some
2654 // locales have numbers for the month names.)
2656 pos
.setIndex(start
);
2662 // we check the rest of the fields below.
2666 switch (patternCharIndex
) {
2667 case UDAT_ERA_FIELD
:
2669 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fNarrowEras
, fSymbols
->fNarrowErasCount
, cal
);
2670 } else if (count
== 4) {
2671 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fEraNames
, fSymbols
->fEraNamesCount
, cal
);
2673 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fEras
, fSymbols
->fErasCount
, cal
);
2676 // check return position, if it equals -start, then matchString error
2677 // special case the return code so we don't necessarily fail out until we
2678 // verify no year information also
2684 case UDAT_YEAR_FIELD
:
2685 // If there are 3 or more YEAR pattern characters, this indicates
2686 // that the year value is to be treated literally, without any
2687 // two-digit year adjustments (e.g., from "01" to 2001). Otherwise
2688 // we made adjustments to place the 2-digit year in the proper
2689 // century, for parsed strings from "00" to "99". Any other string
2690 // is treated literally: "2250", "-1", "1", "002".
2691 if ((pos
.getIndex() - start
) == 2
2692 && u_isdigit(text
.charAt(start
))
2693 && u_isdigit(text
.charAt(start
+1)))
2695 // Assume for example that the defaultCenturyStart is 6/18/1903.
2696 // This means that two-digit years will be forced into the range
2697 // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
2698 // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
2699 // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
2700 // other fields specify a date before 6/18, or 1903 if they specify a
2701 // date afterwards. As a result, 03 is an ambiguous year. All other
2702 // two-digit years are unambiguous.
2703 if(fHaveDefaultCentury
) { // check if this formatter even has a pivot year
2704 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
2705 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
2706 value
+= (fDefaultCenturyStartYear
/100)*100 +
2707 (value
< ambiguousTwoDigitYear
? 100 : 0);
2710 cal
.set(UCAL_YEAR
, value
);
2712 // Delayed checking for adjustment of Hebrew month numbers in non-leap years.
2713 if (saveHebrewMonth
>= 0) {
2714 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
2715 if (!hc
->isLeapYear(value
) && saveHebrewMonth
>= 6) {
2716 cal
.set(UCAL_MONTH
,saveHebrewMonth
);
2718 cal
.set(UCAL_MONTH
,saveHebrewMonth
-1);
2720 saveHebrewMonth
= -1;
2722 return pos
.getIndex();
2724 case UDAT_YEAR_WOY_FIELD
:
2725 // Comment is the same as for UDAT_Year_FIELDs - look above
2726 if ((pos
.getIndex() - start
) == 2
2727 && u_isdigit(text
.charAt(start
))
2728 && u_isdigit(text
.charAt(start
+1))
2729 && fHaveDefaultCentury
)
2731 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
2732 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
2733 value
+= (fDefaultCenturyStartYear
/100)*100 +
2734 (value
< ambiguousTwoDigitYear
? 100 : 0);
2736 cal
.set(UCAL_YEAR_WOY
, value
);
2737 return pos
.getIndex();
2739 case UDAT_MONTH_FIELD
:
2740 if (gotNumber
) // i.e., M or MM.
2742 // When parsing month numbers from the Hebrew Calendar, we might need to adjust the month depending on whether
2743 // 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
2744 // the year is parsed.
2745 if (!strcmp(cal
.getType(),"hebrew")) {
2746 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
2747 if (cal
.isSet(UCAL_YEAR
)) {
2748 UErrorCode status
= U_ZERO_ERROR
;
2749 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6) {
2750 cal
.set(UCAL_MONTH
, value
);
2752 cal
.set(UCAL_MONTH
, value
- 1);
2755 saveHebrewMonth
= value
;
2758 // Don't want to parse the month if it is a string
2759 // while pattern uses numeric style: M or MM.
2760 // [We computed 'value' above.]
2761 cal
.set(UCAL_MONTH
, value
- 1);
2763 return pos
.getIndex();
2765 // count >= 3 // i.e., MMM or MMMM
2766 // Want to be able to parse both short and long forms.
2767 // Try count == 4 first:
2768 int32_t newStart
= 0;
2770 if ((newStart
= matchString(text
, start
, UCAL_MONTH
,
2771 fSymbols
->fMonths
, fSymbols
->fMonthsCount
, cal
)) > 0)
2773 else // count == 4 failed, now try count == 3
2774 return matchString(text
, start
, UCAL_MONTH
,
2775 fSymbols
->fShortMonths
, fSymbols
->fShortMonthsCount
, cal
);
2778 case UDAT_STANDALONE_MONTH_FIELD
:
2779 if (gotNumber
) // i.e., L or LL.
2781 // Don't want to parse the month if it is a string
2782 // while pattern uses numeric style: M or MM.
2783 // [We computed 'value' above.]
2784 cal
.set(UCAL_MONTH
, value
- 1);
2785 return pos
.getIndex();
2787 // count >= 3 // i.e., LLL or LLLL
2788 // Want to be able to parse both short and long forms.
2789 // Try count == 4 first:
2790 int32_t newStart
= 0;
2792 if ((newStart
= matchString(text
, start
, UCAL_MONTH
,
2793 fSymbols
->fStandaloneMonths
, fSymbols
->fStandaloneMonthsCount
, cal
)) > 0)
2795 else // count == 4 failed, now try count == 3
2796 return matchString(text
, start
, UCAL_MONTH
,
2797 fSymbols
->fStandaloneShortMonths
, fSymbols
->fStandaloneShortMonthsCount
, cal
);
2800 case UDAT_HOUR_OF_DAY1_FIELD
:
2801 // [We computed 'value' above.]
2802 if (value
== cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1)
2805 // fall through to set field
2807 case UDAT_HOUR_OF_DAY0_FIELD
:
2808 cal
.set(UCAL_HOUR_OF_DAY
, value
);
2809 return pos
.getIndex();
2811 case UDAT_FRACTIONAL_SECOND_FIELD
:
2812 // Fractional seconds left-justify
2813 i
= pos
.getIndex() - start
;
2825 value
= (value
+ (a
>>1)) / a
;
2827 cal
.set(UCAL_MILLISECOND
, value
);
2828 return pos
.getIndex();
2830 case UDAT_DOW_LOCAL_FIELD
:
2831 if (gotNumber
) // i.e., e or ee
2833 // [We computed 'value' above.]
2834 cal
.set(UCAL_DOW_LOCAL
, value
);
2835 return pos
.getIndex();
2837 // else for eee-eeeee fall through to handling of EEE-EEEEE
2838 // fall through, do not break here
2839 case UDAT_DAY_OF_WEEK_FIELD
:
2841 // Want to be able to parse both short and long forms.
2842 // Try count == 4 (EEEE) first:
2843 int32_t newStart
= 0;
2844 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2845 fSymbols
->fWeekdays
, fSymbols
->fWeekdaysCount
, cal
)) > 0)
2847 // EEEE failed, now try EEE
2848 else if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2849 fSymbols
->fShortWeekdays
, fSymbols
->fShortWeekdaysCount
, cal
)) > 0)
2851 // EEE failed, now try EEEEE
2853 return matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2854 fSymbols
->fNarrowWeekdays
, fSymbols
->fNarrowWeekdaysCount
, cal
);
2857 case UDAT_STANDALONE_DAY_FIELD
:
2859 if (gotNumber
) // c or cc
2861 // [We computed 'value' above.]
2862 cal
.set(UCAL_DOW_LOCAL
, value
);
2863 return pos
.getIndex();
2865 // Want to be able to parse both short and long forms.
2866 // Try count == 4 (cccc) first:
2867 int32_t newStart
= 0;
2868 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2869 fSymbols
->fStandaloneWeekdays
, fSymbols
->fStandaloneWeekdaysCount
, cal
)) > 0)
2871 else // cccc failed, now try ccc
2872 return matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2873 fSymbols
->fStandaloneShortWeekdays
, fSymbols
->fStandaloneShortWeekdaysCount
, cal
);
2876 case UDAT_AM_PM_FIELD
:
2877 return matchString(text
, start
, UCAL_AM_PM
, fSymbols
->fAmPms
, fSymbols
->fAmPmsCount
, cal
);
2879 case UDAT_HOUR1_FIELD
:
2880 // [We computed 'value' above.]
2881 if (value
== cal
.getLeastMaximum(UCAL_HOUR
)+1)
2884 // fall through to set field
2886 case UDAT_HOUR0_FIELD
:
2887 cal
.set(UCAL_HOUR
, value
);
2888 return pos
.getIndex();
2890 case UDAT_QUARTER_FIELD
:
2891 if (gotNumber
) // i.e., Q or QQ.
2893 // Don't want to parse the month if it is a string
2894 // while pattern uses numeric style: Q or QQ.
2895 // [We computed 'value' above.]
2896 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
2897 return pos
.getIndex();
2899 // count >= 3 // i.e., QQQ or QQQQ
2900 // Want to be able to parse both short and long forms.
2901 // Try count == 4 first:
2902 int32_t newStart
= 0;
2904 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
2905 fSymbols
->fQuarters
, fSymbols
->fQuartersCount
, cal
)) > 0)
2907 else // count == 4 failed, now try count == 3
2908 return matchQuarterString(text
, start
, UCAL_MONTH
,
2909 fSymbols
->fShortQuarters
, fSymbols
->fShortQuartersCount
, cal
);
2912 case UDAT_STANDALONE_QUARTER_FIELD
:
2913 if (gotNumber
) // i.e., q or qq.
2915 // Don't want to parse the month if it is a string
2916 // while pattern uses numeric style: q or q.
2917 // [We computed 'value' above.]
2918 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
2919 return pos
.getIndex();
2921 // count >= 3 // i.e., qqq or qqqq
2922 // Want to be able to parse both short and long forms.
2923 // Try count == 4 first:
2924 int32_t newStart
= 0;
2926 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
2927 fSymbols
->fStandaloneQuarters
, fSymbols
->fStandaloneQuartersCount
, cal
)) > 0)
2929 else // count == 4 failed, now try count == 3
2930 return matchQuarterString(text
, start
, UCAL_MONTH
,
2931 fSymbols
->fStandaloneShortQuarters
, fSymbols
->fStandaloneShortQuartersCount
, cal
);
2934 case UDAT_TIMEZONE_FIELD
:
2935 case UDAT_TIMEZONE_RFC_FIELD
:
2936 case UDAT_TIMEZONE_GENERIC_FIELD
:
2937 case UDAT_TIMEZONE_SPECIAL_FIELD
:
2940 UBool parsed
= FALSE
;
2943 // Check if this is a long GMT offset string (either localized or default)
2944 offset
= parseGMT(text
, pos
);
2945 if (pos
.getIndex() - start
> 0) {
2950 // Check if this is an RFC822 time zone offset.
2951 // ICU supports the standard RFC822 format [+|-]HHmm
2952 // and its extended form [+|-]HHmmSS.
2955 UChar signChar
= text
.charAt(start
);
2956 if (signChar
== (UChar
)0x002B /* '+' */) {
2958 } else if (signChar
== (UChar
)0x002D /* '-' */) {
2961 // Not an RFC822 offset string
2966 int32_t orgPos
= start
+ 1;
2967 pos
.setIndex(orgPos
);
2968 parseInt(text
, number
, 6, pos
, FALSE
,currentNumberFormat
);
2969 int32_t numLen
= pos
.getIndex() - orgPos
;
2974 // Followings are possible format (excluding sign char)
2981 int32_t val
= number
.getLong();
2982 int32_t hour
= 0, min
= 0, sec
= 0;
2996 min
= (val
% 10000) / 100;
3000 if (hour
> 23 || min
> 59 || sec
> 59) {
3001 // Invalid value range
3004 offset
= (((hour
* 60) + min
) * 60 + sec
) * 1000 * sign
;
3009 // Failed to parse. Reset the position.
3010 pos
.setIndex(start
);
3015 // offset was successfully parsed as either a long GMT string or RFC822 zone offset
3016 // string. Create normalized zone ID for the offset.
3018 UnicodeString
tzID(gGmt
);
3019 formatRFC822TZ(tzID
, offset
);
3020 //TimeZone *customTZ = TimeZone::createTimeZone(tzID);
3021 TimeZone
*customTZ
= new SimpleTimeZone(offset
, tzID
); // faster than TimeZone::createTimeZone
3022 cal
.adoptTimeZone(customTZ
);
3024 return pos
.getIndex();
3028 // At this point, check for named time zones by looking through
3029 // the locale data from the DateFormatZoneData strings.
3030 // Want to be able to parse both short and long forms.
3031 // optimize for calendar's current time zone
3032 const ZoneStringFormat
*zsf
= fSymbols
->getZoneStringFormat();
3034 UErrorCode status
= U_ZERO_ERROR
;
3035 const ZoneStringInfo
*zsinfo
= NULL
;
3038 switch (patternCharIndex
) {
3039 case UDAT_TIMEZONE_FIELD
: // 'z'
3041 zsinfo
= zsf
->findSpecificShort(text
, start
, matchLen
, status
);
3043 zsinfo
= zsf
->findSpecificLong(text
, start
, matchLen
, status
);
3046 case UDAT_TIMEZONE_GENERIC_FIELD
: // 'v'
3048 zsinfo
= zsf
->findGenericShort(text
, start
, matchLen
, status
);
3049 } else if (count
== 4) {
3050 zsinfo
= zsf
->findGenericLong(text
, start
, matchLen
, status
);
3053 case UDAT_TIMEZONE_SPECIAL_FIELD
: // 'V'
3055 zsinfo
= zsf
->findSpecificShort(text
, start
, matchLen
, status
);
3056 } else if (count
== 4) {
3057 zsinfo
= zsf
->findGenericLocation(text
, start
, matchLen
, status
);
3064 if (U_SUCCESS(status
) && zsinfo
!= NULL
) {
3065 if (zsinfo
->isStandard()) {
3066 ((SimpleDateFormat
*)this)->tztype
= TZTYPE_STD
;
3067 } else if (zsinfo
->isDaylight()) {
3068 ((SimpleDateFormat
*)this)->tztype
= TZTYPE_DST
;
3071 zsinfo
->getID(tzid
);
3073 UnicodeString current
;
3074 cal
.getTimeZone().getID(current
);
3075 if (tzid
!= current
) {
3076 TimeZone
*tz
= TimeZone::createTimeZone(tzid
);
3077 cal
.adoptTimeZone(tz
);
3079 return start
+ matchLen
;
3083 // Final attempt - is this standalone GMT/UT/UTC?
3085 if (text
.compare(start
, kGmtLen
, gGmt
) == 0) {
3087 } else if (text
.compare(start
, kUtcLen
, gUtc
) == 0) {
3089 } else if (text
.compare(start
, kUtLen
, gUt
) == 0) {
3093 TimeZone
*tz
= TimeZone::createTimeZone(UnicodeString("Etc/GMT"));
3094 cal
.adoptTimeZone(tz
);
3095 return start
+ gmtLen
;
3103 // Handle "generic" fields
3104 int32_t parseStart
= pos
.getIndex();
3105 const UnicodeString
* src
;
3107 if ((start
+count
) > text
.length()) {
3110 text
.extractBetween(0, start
+ count
, temp
);
3115 parseInt(*src
, number
, pos
, allowNegative
,currentNumberFormat
);
3116 if (pos
.getIndex() != parseStart
) {
3117 int32_t value
= number
.getLong();
3119 // Check the range of the value
3120 int32_t bias
= gFieldRangeBias
[patternCharIndex
];
3122 if (bias
< 0 || (value
>= cal
.getMinimum(field
) + bias
&& value
<= cal
.getMaximum(field
) + bias
)) {
3123 cal
.set(field
, value
);
3124 return pos
.getIndex();
3133 * Parse an integer using fNumberFormat. This method is semantically
3134 * const, but actually may modify fNumberFormat.
3136 void SimpleDateFormat::parseInt(const UnicodeString
& text
,
3137 Formattable
& number
,
3139 UBool allowNegative
,
3140 NumberFormat
*fmt
) const {
3141 parseInt(text
, number
, -1, pos
, allowNegative
,fmt
);
3145 * Parse an integer using fNumberFormat up to maxDigits.
3147 void SimpleDateFormat::parseInt(const UnicodeString
& text
,
3148 Formattable
& number
,
3151 UBool allowNegative
,
3152 NumberFormat
*fmt
) const {
3153 UnicodeString oldPrefix
;
3154 DecimalFormat
* df
= NULL
;
3155 if (!allowNegative
&& (df
= dynamic_cast<DecimalFormat
*>(fmt
)) != NULL
) {
3156 df
->getNegativePrefix(oldPrefix
);
3157 df
->setNegativePrefix(SUPPRESS_NEGATIVE_PREFIX
);
3159 int32_t oldPos
= pos
.getIndex();
3160 fmt
->parse(text
, number
, pos
);
3162 df
->setNegativePrefix(oldPrefix
);
3165 if (maxDigits
> 0) {
3166 // adjust the result to fit into
3167 // the maxDigits and move the position back
3168 int32_t nDigits
= pos
.getIndex() - oldPos
;
3169 if (nDigits
> maxDigits
) {
3170 int32_t val
= number
.getLong();
3171 nDigits
-= maxDigits
;
3172 while (nDigits
> 0) {
3176 pos
.setIndex(oldPos
+ maxDigits
);
3177 number
.setLong(val
);
3182 //----------------------------------------------------------------------
3184 void SimpleDateFormat::translatePattern(const UnicodeString
& originalPattern
,
3185 UnicodeString
& translatedPattern
,
3186 const UnicodeString
& from
,
3187 const UnicodeString
& to
,
3190 // run through the pattern and convert any pattern symbols from the version
3191 // in "from" to the corresponding character ion "to". This code takes
3192 // quoted strings into account (it doesn't try to translate them), and it signals
3193 // an error if a particular "pattern character" doesn't appear in "from".
3194 // Depending on the values of "from" and "to" this can convert from generic
3195 // to localized patterns or localized to generic.
3196 if (U_FAILURE(status
))
3199 translatedPattern
.remove();
3200 UBool inQuote
= FALSE
;
3201 for (int32_t i
= 0; i
< originalPattern
.length(); ++i
) {
3202 UChar c
= originalPattern
[i
];
3210 else if ((c
>= 0x0061 /*'a'*/ && c
<= 0x007A) /*'z'*/
3211 || (c
>= 0x0041 /*'A'*/ && c
<= 0x005A /*'Z'*/)) {
3212 int32_t ci
= from
.indexOf(c
);
3214 status
= U_INVALID_FORMAT_ERROR
;
3220 translatedPattern
+= c
;
3223 status
= U_INVALID_FORMAT_ERROR
;
3228 //----------------------------------------------------------------------
3231 SimpleDateFormat::toPattern(UnicodeString
& result
) const
3237 //----------------------------------------------------------------------
3240 SimpleDateFormat::toLocalizedPattern(UnicodeString
& result
,
3241 UErrorCode
& status
) const
3243 translatePattern(fPattern
, result
, DateFormatSymbols::getPatternUChars(), fSymbols
->fLocalPatternChars
, status
);
3247 //----------------------------------------------------------------------
3250 SimpleDateFormat::applyPattern(const UnicodeString
& pattern
)
3255 //----------------------------------------------------------------------
3258 SimpleDateFormat::applyLocalizedPattern(const UnicodeString
& pattern
,
3261 translatePattern(pattern
, fPattern
, fSymbols
->fLocalPatternChars
, DateFormatSymbols::getPatternUChars(), status
);
3264 //----------------------------------------------------------------------
3266 const DateFormatSymbols
*
3267 SimpleDateFormat::getDateFormatSymbols() const
3272 //----------------------------------------------------------------------
3275 SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols
* newFormatSymbols
)
3278 fSymbols
= newFormatSymbols
;
3281 //----------------------------------------------------------------------
3283 SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols
& newFormatSymbols
)
3286 fSymbols
= new DateFormatSymbols(newFormatSymbols
);
3290 //----------------------------------------------------------------------
3293 void SimpleDateFormat::adoptCalendar(Calendar
* calendarToAdopt
)
3295 UErrorCode status
= U_ZERO_ERROR
;
3296 DateFormat::adoptCalendar(calendarToAdopt
);
3299 initializeSymbols(fLocale
, fCalendar
, status
); // we need new symbols
3300 initializeDefaultCentury(); // we need a new century (possibly)
3304 //----------------------------------------------------------------------
3308 SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field
) const {
3309 return isFieldUnitIgnored(fPattern
, field
);
3314 SimpleDateFormat::isFieldUnitIgnored(const UnicodeString
& pattern
,
3315 UCalendarDateFields field
) {
3316 int32_t fieldLevel
= fgCalendarFieldToLevel
[field
];
3319 UBool inQuote
= FALSE
;
3323 for (int32_t i
= 0; i
< pattern
.length(); ++i
) {
3325 if (ch
!= prevCh
&& count
> 0) {
3326 level
= fgPatternCharToLevel
[prevCh
- PATTERN_CHAR_BASE
];
3327 // the larger the level, the smaller the field unit.
3328 if ( fieldLevel
<= level
) {
3334 if ((i
+1) < pattern
.length() && pattern
[i
+1] == QUOTE
) {
3337 inQuote
= ! inQuote
;
3340 else if ( ! inQuote
&& ((ch
>= 0x0061 /*'a'*/ && ch
<= 0x007A /*'z'*/)
3341 || (ch
>= 0x0041 /*'A'*/ && ch
<= 0x005A /*'Z'*/))) {
3348 level
= fgPatternCharToLevel
[prevCh
- PATTERN_CHAR_BASE
];
3349 if ( fieldLevel
<= level
) {
3356 //----------------------------------------------------------------------
3359 SimpleDateFormat::getSmpFmtLocale(void) const {
3363 //----------------------------------------------------------------------
3366 SimpleDateFormat::checkIntSuffix(const UnicodeString
& text
, int32_t start
,
3367 int32_t patLoc
, UBool isNegative
) const {
3370 int32_t patternMatch
;
3371 int32_t textPreMatch
;
3372 int32_t textPostMatch
;
3374 // check that we are still in range
3375 if ( (start
> text
.length()) ||
3378 (patLoc
> fPattern
.length())) {
3379 // out of range, don't advance location in text
3384 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(fNumberFormat
);
3385 if (decfmt
!= NULL
) {
3387 suf
= decfmt
->getNegativeSuffix(suf
);
3390 suf
= decfmt
->getPositiveSuffix(suf
);
3395 if (suf
.length() <= 0) {
3399 // check suffix will be encountered in the pattern
3400 patternMatch
= compareSimpleAffix(suf
,fPattern
,patLoc
);
3402 // check if a suffix will be encountered in the text
3403 textPreMatch
= compareSimpleAffix(suf
,text
,start
);
3405 // check if a suffix was encountered in the text
3406 textPostMatch
= compareSimpleAffix(suf
,text
,start
-suf
.length());
3408 // check for suffix match
3409 if ((textPreMatch
>= 0) && (patternMatch
>= 0) && (textPreMatch
== patternMatch
)) {
3412 else if ((textPostMatch
>= 0) && (patternMatch
>= 0) && (textPostMatch
== patternMatch
)) {
3413 return start
- suf
.length();
3416 // should not get here
3420 //----------------------------------------------------------------------
3423 SimpleDateFormat::compareSimpleAffix(const UnicodeString
& affix
,
3424 const UnicodeString
& input
,
3425 int32_t pos
) const {
3426 int32_t start
= pos
;
3427 for (int32_t i
=0; i
<affix
.length(); ) {
3428 UChar32 c
= affix
.char32At(i
);
3429 int32_t len
= U16_LENGTH(c
);
3430 if (uprv_isRuleWhiteSpace(c
)) {
3431 // We may have a pattern like: \u200F \u0020
3432 // and input text like: \u200F \u0020
3433 // Note that U+200F and U+0020 are RuleWhiteSpace but only
3434 // U+0020 is UWhiteSpace. So we have to first do a direct
3435 // match of the run of RULE whitespace in the pattern,
3436 // then match any extra characters.
3437 UBool literalMatch
= FALSE
;
3438 while (pos
< input
.length() &&
3439 input
.char32At(pos
) == c
) {
3440 literalMatch
= TRUE
;
3443 if (i
== affix
.length()) {
3446 c
= affix
.char32At(i
);
3447 len
= U16_LENGTH(c
);
3448 if (!uprv_isRuleWhiteSpace(c
)) {
3453 // Advance over run in pattern
3454 i
= skipRuleWhiteSpace(affix
, i
);
3456 // Advance over run in input text
3457 // Must see at least one white space char in input,
3458 // unless we've already matched some characters literally.
3460 pos
= skipUWhiteSpace(input
, pos
);
3461 if (pos
== s
&& !literalMatch
) {
3465 // If we skip UWhiteSpace in the input text, we need to skip it in the pattern.
3466 // Otherwise, the previous lines may have skipped over text (such as U+00A0) that
3467 // is also in the affix.
3468 i
= skipUWhiteSpace(affix
, i
);
3470 if (pos
< input
.length() &&
3471 input
.char32At(pos
) == c
) {
3482 //----------------------------------------------------------------------
3485 SimpleDateFormat::skipRuleWhiteSpace(const UnicodeString
& text
, int32_t pos
) const {
3486 while (pos
< text
.length()) {
3487 UChar32 c
= text
.char32At(pos
);
3488 if (!uprv_isRuleWhiteSpace(c
)) {
3491 pos
+= U16_LENGTH(c
);
3496 //----------------------------------------------------------------------
3499 SimpleDateFormat::skipUWhiteSpace(const UnicodeString
& text
, int32_t pos
) const {
3500 while (pos
< text
.length()) {
3501 UChar32 c
= text
.char32At(pos
);
3502 if (!u_isUWhiteSpace(c
)) {
3505 pos
+= U16_LENGTH(c
);
3512 #endif /* #if !UCONFIG_NO_FORMATTING */