2 *******************************************************************************
3 * Copyright (C) 1997-2013, 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/tzfmt.h"
49 #include "unicode/utf16.h"
50 #include "unicode/vtzone.h"
51 #include "unicode/udisplaycontext.h"
53 #include "patternprops.h"
64 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
68 // *****************************************************************************
69 // class SimpleDateFormat
70 // *****************************************************************************
74 static const UChar PATTERN_CHAR_BASE
= 0x40;
77 * Last-resort string to use for "GMT" when constructing time zone strings.
79 // For time zones that have no names, use strings GMT+minutes and
80 // GMT-minutes. For instance, in France the time zone is GMT+60.
81 // Also accepted are GMT+H:MM or GMT-H:MM.
82 // Currently not being used
83 //static const UChar gGmt[] = {0x0047, 0x004D, 0x0054, 0x0000}; // "GMT"
84 //static const UChar gGmtPlus[] = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+"
85 //static const UChar gGmtMinus[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-"
86 //static const UChar gDefGmtPat[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
87 //static const UChar gDefGmtNegHmsPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */
88 //static const UChar gDefGmtNegHmPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */
89 //static const UChar gDefGmtPosHmsPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */
90 //static const UChar gDefGmtPosHmPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */
91 //static const UChar gUt[] = {0x0055, 0x0054, 0x0000}; // "UT"
92 //static const UChar gUtc[] = {0x0055, 0x0054, 0x0043, 0x0000}; // "UT"
94 typedef enum GmtPatSize
{
105 // Stuff needed for numbering system overrides
107 typedef enum OvrStrType
{
113 static const UDateFormatField kDateFields
[] = {
117 UDAT_DAY_OF_YEAR_FIELD
,
118 UDAT_DAY_OF_WEEK_IN_MONTH_FIELD
,
119 UDAT_WEEK_OF_YEAR_FIELD
,
120 UDAT_WEEK_OF_MONTH_FIELD
,
122 UDAT_EXTENDED_YEAR_FIELD
,
123 UDAT_JULIAN_DAY_FIELD
,
124 UDAT_STANDALONE_DAY_FIELD
,
125 UDAT_STANDALONE_MONTH_FIELD
,
127 UDAT_STANDALONE_QUARTER_FIELD
,
128 UDAT_YEAR_NAME_FIELD
};
129 static const int8_t kDateFieldsCount
= 15;
131 static const UDateFormatField kTimeFields
[] = {
132 UDAT_HOUR_OF_DAY1_FIELD
,
133 UDAT_HOUR_OF_DAY0_FIELD
,
136 UDAT_FRACTIONAL_SECOND_FIELD
,
139 UDAT_MILLISECONDS_IN_DAY_FIELD
,
140 UDAT_TIMEZONE_RFC_FIELD
,
141 UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
};
142 static const int8_t kTimeFieldsCount
= 10;
145 // This is a pattern-of-last-resort used when we can't load a usable pattern out
147 static const UChar gDefaultPattern
[] =
149 0x79, 0x79, 0x79, 0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0
150 }; /* "yyyyMMdd hh:mm a" */
152 // This prefix is designed to NEVER MATCH real text, in order to
153 // suppress the parsing of negative numbers. Adjust as needed (if
154 // this becomes valid Unicode).
155 static const UChar SUPPRESS_NEGATIVE_PREFIX
[] = {0xAB00, 0};
158 * These are the tags we expect to see in normal resource bundle files associated
161 static const char gDateTimePatternsTag
[]="DateTimePatterns";
163 //static const UChar gEtcUTC[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x54, 0x43, 0x00}; // "Etc/UTC"
164 static const UChar QUOTE
= 0x27; // Single quote
167 * The field range check bias for each UDateFormatField.
168 * The bias is added to the minimum and maximum values
169 * before they are compared to the parsed number.
170 * For example, the calendar stores zero-based month numbers
171 * but the parsed month numbers start at 1, so the bias is 1.
173 * A value of -1 means that the value is not checked.
175 static const int32_t gFieldRangeBias
[] = {
176 -1, // 'G' - UDAT_ERA_FIELD
177 -1, // 'y' - UDAT_YEAR_FIELD
178 1, // 'M' - UDAT_MONTH_FIELD
179 0, // 'd' - UDAT_DATE_FIELD
180 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
181 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
182 0, // 'm' - UDAT_MINUTE_FIELD
183 0, // 's' - UDAT_SECOND_FIELD
184 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
185 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
186 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
187 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
188 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
189 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
190 -1, // 'a' - UDAT_AM_PM_FIELD
191 -1, // 'h' - UDAT_HOUR1_FIELD
192 -1, // 'K' - UDAT_HOUR0_FIELD
193 -1, // 'z' - UDAT_TIMEZONE_FIELD
194 -1, // 'Y' - UDAT_YEAR_WOY_FIELD
195 -1, // 'e' - UDAT_DOW_LOCAL_FIELD
196 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
197 -1, // 'g' - UDAT_JULIAN_DAY_FIELD
198 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
199 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
200 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
201 0, // 'c' - UDAT_STANDALONE_DAY_FIELD
202 1, // 'L' - UDAT_STANDALONE_MONTH_FIELD
203 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
204 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
205 -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
206 -1, // 'U' - UDAT_YEAR_NAME_FIELD
207 -1, // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
208 -1, // 'X' - UDAT_TIMEZONE_ISO_FIELD
209 -1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
211 // A slightly looser range check for lenient parsing
212 static const int32_t gFieldRangeBiasLenient
[] = {
213 -1, // 'G' - UDAT_ERA_FIELD
214 -1, // 'y' - UDAT_YEAR_FIELD
215 8, // 'M' - UDAT_MONTH_FIELD (allow calendar max + 7, e.g. 19 for grego 1-based month)
216 18, // 'd' - UDAT_DATE_FIELD (allow calendar max + 18, e.g. 49 for grego; tests require at least 40 for grego)
217 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
218 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
219 40, // 'm' - UDAT_MINUTE_FIELD (allow calendar max + 40, e.g. 99)
220 40, // 's' - UDAT_SECOND_FIELD (allow calendar max + 40, e.g. 99)
221 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
222 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
223 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
224 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
225 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
226 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
227 -1, // 'a' - UDAT_AM_PM_FIELD
228 -1, // 'h' - UDAT_HOUR1_FIELD
229 -1, // 'K' - UDAT_HOUR0_FIELD
230 -1, // 'z' - UDAT_TIMEZONE_FIELD
231 -1, // 'Y' - UDAT_YEAR_WOY_FIELD
232 -1, // 'e' - UDAT_DOW_LOCAL_FIELD
233 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
234 -1, // 'g' - UDAT_JULIAN_DAY_FIELD
235 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
236 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
237 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
238 18, // 'c' - UDAT_STANDALONE_DAY_FIELD (allow calendar max + 18, e.g. 49 for grego)
239 8, // 'L' - UDAT_STANDALONE_MONTH_FIELD (allow calendar max + 7, e.g. 19 for grego 1-based month)
240 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
241 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
242 -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
243 -1, // 'U' - UDAT_YEAR_NAME_FIELD
244 -1, // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
245 -1, // 'X' - UDAT_TIMEZONE_ISO_FIELD
246 -1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
249 // When calendar uses hebr numbering (i.e. he@calendar=hebrew),
250 // offset the years within the current millenium down to 1-999
251 static const int32_t HEBREW_CAL_CUR_MILLENIUM_START_YEAR
= 5000;
252 static const int32_t HEBREW_CAL_CUR_MILLENIUM_END_YEAR
= 6000;
254 static UMutex LOCK
= U_MUTEX_INITIALIZER
;
256 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat
)
258 //----------------------------------------------------------------------
260 SimpleDateFormat::~SimpleDateFormat()
263 if (fNumberFormatters
) {
264 uprv_free(fNumberFormatters
);
266 if (fTimeZoneFormat
) {
267 delete fTimeZoneFormat
;
270 while (fOverrideList
) {
271 NSOverride
*cur
= fOverrideList
;
272 fOverrideList
= cur
->next
;
278 //----------------------------------------------------------------------
280 SimpleDateFormat::SimpleDateFormat(UErrorCode
& status
)
281 : fLocale(Locale::getDefault()),
283 fTimeZoneFormat(NULL
),
284 fNumberFormatters(NULL
),
286 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE
)
288 construct(kShort
, (EStyle
) (kShort
+ kDateOffset
), fLocale
, status
);
289 initializeDefaultCentury();
292 //----------------------------------------------------------------------
294 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
297 fLocale(Locale::getDefault()),
299 fTimeZoneFormat(NULL
),
300 fNumberFormatters(NULL
),
302 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE
)
304 fDateOverride
.setToBogus();
305 fTimeOverride
.setToBogus();
306 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
307 initialize(fLocale
, status
);
308 initializeDefaultCentury();
311 //----------------------------------------------------------------------
313 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
314 const UnicodeString
& override
,
317 fLocale(Locale::getDefault()),
319 fTimeZoneFormat(NULL
),
320 fNumberFormatters(NULL
),
322 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE
)
324 fDateOverride
.setTo(override
);
325 fTimeOverride
.setToBogus();
326 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
327 initialize(fLocale
, status
);
328 initializeDefaultCentury();
330 processOverrideString(fLocale
,override
,kOvrStrBoth
,status
);
334 //----------------------------------------------------------------------
336 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
337 const Locale
& locale
,
341 fTimeZoneFormat(NULL
),
342 fNumberFormatters(NULL
),
344 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE
)
347 fDateOverride
.setToBogus();
348 fTimeOverride
.setToBogus();
350 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
351 initialize(fLocale
, status
);
352 initializeDefaultCentury();
355 //----------------------------------------------------------------------
357 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
358 const UnicodeString
& override
,
359 const Locale
& locale
,
363 fTimeZoneFormat(NULL
),
364 fNumberFormatters(NULL
),
366 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE
)
369 fDateOverride
.setTo(override
);
370 fTimeOverride
.setToBogus();
372 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
373 initialize(fLocale
, status
);
374 initializeDefaultCentury();
376 processOverrideString(locale
,override
,kOvrStrBoth
,status
);
380 //----------------------------------------------------------------------
382 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
383 DateFormatSymbols
* symbolsToAdopt
,
386 fLocale(Locale::getDefault()),
387 fSymbols(symbolsToAdopt
),
388 fTimeZoneFormat(NULL
),
389 fNumberFormatters(NULL
),
391 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE
)
394 fDateOverride
.setToBogus();
395 fTimeOverride
.setToBogus();
397 initializeCalendar(NULL
,fLocale
,status
);
398 initialize(fLocale
, status
);
399 initializeDefaultCentury();
402 //----------------------------------------------------------------------
404 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
405 const DateFormatSymbols
& symbols
,
408 fLocale(Locale::getDefault()),
409 fSymbols(new DateFormatSymbols(symbols
)),
410 fTimeZoneFormat(NULL
),
411 fNumberFormatters(NULL
),
413 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE
)
416 fDateOverride
.setToBogus();
417 fTimeOverride
.setToBogus();
419 initializeCalendar(NULL
, fLocale
, status
);
420 initialize(fLocale
, status
);
421 initializeDefaultCentury();
424 //----------------------------------------------------------------------
426 // Not for public consumption; used by DateFormat
427 SimpleDateFormat::SimpleDateFormat(EStyle timeStyle
,
429 const Locale
& locale
,
433 fTimeZoneFormat(NULL
),
434 fNumberFormatters(NULL
),
436 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE
)
438 construct(timeStyle
, dateStyle
, fLocale
, status
);
439 if(U_SUCCESS(status
)) {
440 initializeDefaultCentury();
444 //----------------------------------------------------------------------
447 * Not for public consumption; used by DateFormat. This constructor
448 * never fails. If the resource data is not available, it uses the
449 * the last resort symbols.
451 SimpleDateFormat::SimpleDateFormat(const Locale
& locale
,
453 : fPattern(gDefaultPattern
),
456 fTimeZoneFormat(NULL
),
457 fNumberFormatters(NULL
),
459 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE
)
461 if (U_FAILURE(status
)) return;
462 initializeSymbols(fLocale
, initializeCalendar(NULL
, fLocale
, status
),status
);
463 if (U_FAILURE(status
))
465 status
= U_ZERO_ERROR
;
467 // This constructor doesn't fail; it uses last resort data
468 fSymbols
= new DateFormatSymbols(status
);
471 status
= U_MEMORY_ALLOCATION_ERROR
;
476 fDateOverride
.setToBogus();
477 fTimeOverride
.setToBogus();
479 initialize(fLocale
, status
);
480 if(U_SUCCESS(status
)) {
481 initializeDefaultCentury();
485 //----------------------------------------------------------------------
487 SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat
& other
)
489 fLocale(other
.fLocale
),
491 fTimeZoneFormat(NULL
),
492 fNumberFormatters(NULL
),
494 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE
)
499 //----------------------------------------------------------------------
501 SimpleDateFormat
& SimpleDateFormat::operator=(const SimpleDateFormat
& other
)
503 if (this == &other
) {
506 DateFormat::operator=(other
);
512 fSymbols
= new DateFormatSymbols(*other
.fSymbols
);
514 fDefaultCenturyStart
= other
.fDefaultCenturyStart
;
515 fDefaultCenturyStartYear
= other
.fDefaultCenturyStartYear
;
516 fHaveDefaultCentury
= other
.fHaveDefaultCentury
;
518 fPattern
= other
.fPattern
;
520 // TimeZoneFormat in ICU4C only depends on a locale for now
521 if (fLocale
!= other
.fLocale
) {
522 delete fTimeZoneFormat
;
523 fTimeZoneFormat
= NULL
; // forces lazy instantiation with the other locale
524 fLocale
= other
.fLocale
;
527 fCapitalizationContext
= other
.fCapitalizationContext
;
532 //----------------------------------------------------------------------
535 SimpleDateFormat::clone() const
537 return new SimpleDateFormat(*this);
540 //----------------------------------------------------------------------
543 SimpleDateFormat::operator==(const Format
& other
) const
545 if (DateFormat::operator==(other
)) {
546 // DateFormat::operator== guarantees following cast is safe
547 SimpleDateFormat
* that
= (SimpleDateFormat
*)&other
;
548 return (fPattern
== that
->fPattern
&&
549 fSymbols
!= NULL
&& // Check for pathological object
550 that
->fSymbols
!= NULL
&& // Check for pathological object
551 *fSymbols
== *that
->fSymbols
&&
552 fHaveDefaultCentury
== that
->fHaveDefaultCentury
&&
553 fDefaultCenturyStart
== that
->fDefaultCenturyStart
&&
554 fCapitalizationContext
== that
->fCapitalizationContext
);
559 //----------------------------------------------------------------------
561 void SimpleDateFormat::construct(EStyle timeStyle
,
563 const Locale
& locale
,
566 // called by several constructors to load pattern data from the resources
567 if (U_FAILURE(status
)) return;
569 // We will need the calendar to know what type of symbols to load.
570 initializeCalendar(NULL
, locale
, status
);
571 if (U_FAILURE(status
)) return;
573 CalendarData
calData(locale
, fCalendar
?fCalendar
->getType():NULL
, status
);
574 UResourceBundle
*dateTimePatterns
= calData
.getByKey(gDateTimePatternsTag
, status
);
575 UResourceBundle
*currentBundle
;
577 if (U_FAILURE(status
)) return;
579 if (ures_getSize(dateTimePatterns
) <= kDateTime
)
581 status
= U_INVALID_FORMAT_ERROR
;
585 setLocaleIDs(ures_getLocaleByType(dateTimePatterns
, ULOC_VALID_LOCALE
, &status
),
586 ures_getLocaleByType(dateTimePatterns
, ULOC_ACTUAL_LOCALE
, &status
));
588 // create a symbols object from the locale
589 initializeSymbols(locale
,fCalendar
, status
);
590 if (U_FAILURE(status
)) return;
593 status
= U_MEMORY_ALLOCATION_ERROR
;
597 const UChar
*resStr
,*ovrStr
;
598 int32_t resStrLen
,ovrStrLen
= 0;
599 fDateOverride
.setToBogus();
600 fTimeOverride
.setToBogus();
602 // if the pattern should include both date and time information, use the date/time
603 // pattern string as a guide to tell use how to glue together the appropriate date
604 // and time pattern strings. The actual gluing-together is handled by a convenience
605 // method on MessageFormat.
606 if ((timeStyle
!= kNone
) && (dateStyle
!= kNone
))
608 Formattable timeDateArray
[2];
610 // use Formattable::adoptString() so that we can use fastCopyFrom()
611 // instead of Formattable::setString()'s unaware, safe, deep string clone
612 // see Jitterbug 2296
614 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)timeStyle
, NULL
, &status
);
615 if (U_FAILURE(status
)) {
616 status
= U_INVALID_FORMAT_ERROR
;
619 switch (ures_getType(currentBundle
)) {
621 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
625 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
626 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
627 fTimeOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
631 status
= U_INVALID_FORMAT_ERROR
;
632 ures_close(currentBundle
);
636 ures_close(currentBundle
);
638 UnicodeString
*tempus1
= new UnicodeString(TRUE
, resStr
, resStrLen
);
639 // NULL pointer check
640 if (tempus1
== NULL
) {
641 status
= U_MEMORY_ALLOCATION_ERROR
;
644 timeDateArray
[0].adoptString(tempus1
);
646 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)dateStyle
, NULL
, &status
);
647 if (U_FAILURE(status
)) {
648 status
= U_INVALID_FORMAT_ERROR
;
651 switch (ures_getType(currentBundle
)) {
653 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
657 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
658 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
659 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
663 status
= U_INVALID_FORMAT_ERROR
;
664 ures_close(currentBundle
);
668 ures_close(currentBundle
);
670 UnicodeString
*tempus2
= new UnicodeString(TRUE
, resStr
, resStrLen
);
671 // Null pointer check
672 if (tempus2
== NULL
) {
673 status
= U_MEMORY_ALLOCATION_ERROR
;
676 timeDateArray
[1].adoptString(tempus2
);
678 int32_t glueIndex
= kDateTime
;
679 int32_t patternsSize
= ures_getSize(dateTimePatterns
);
680 if (patternsSize
>= (kDateTimeOffset
+ kShort
+ 1)) {
681 // Get proper date time format
682 glueIndex
= (int32_t)(kDateTimeOffset
+ (dateStyle
- kDateOffset
));
685 resStr
= ures_getStringByIndex(dateTimePatterns
, glueIndex
, &resStrLen
, &status
);
686 MessageFormat::format(UnicodeString(TRUE
, resStr
, resStrLen
), timeDateArray
, 2, fPattern
, status
);
688 // if the pattern includes just time data or just date date, load the appropriate
689 // pattern string from the resources
690 // setTo() - see DateFormatSymbols::assignArray comments
691 else if (timeStyle
!= kNone
) {
692 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)timeStyle
, NULL
, &status
);
693 if (U_FAILURE(status
)) {
694 status
= U_INVALID_FORMAT_ERROR
;
697 switch (ures_getType(currentBundle
)) {
699 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
703 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
704 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
705 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
709 status
= U_INVALID_FORMAT_ERROR
;
710 ures_close(currentBundle
);
714 fPattern
.setTo(TRUE
, resStr
, resStrLen
);
715 ures_close(currentBundle
);
717 else if (dateStyle
!= kNone
) {
718 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)dateStyle
, NULL
, &status
);
719 if (U_FAILURE(status
)) {
720 status
= U_INVALID_FORMAT_ERROR
;
723 switch (ures_getType(currentBundle
)) {
725 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
729 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
730 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
731 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
735 status
= U_INVALID_FORMAT_ERROR
;
736 ures_close(currentBundle
);
740 fPattern
.setTo(TRUE
, resStr
, resStrLen
);
741 ures_close(currentBundle
);
744 // and if it includes _neither_, that's an error
746 status
= U_INVALID_FORMAT_ERROR
;
748 // finally, finish initializing by creating a Calendar and a NumberFormat
749 initialize(locale
, status
);
752 //----------------------------------------------------------------------
755 SimpleDateFormat::initializeCalendar(TimeZone
* adoptZone
, const Locale
& locale
, UErrorCode
& status
)
757 if(!U_FAILURE(status
)) {
758 fCalendar
= Calendar::createInstance(adoptZone
?adoptZone
:TimeZone::createDefault(), locale
, status
);
760 if (U_SUCCESS(status
) && fCalendar
== NULL
) {
761 status
= U_MEMORY_ALLOCATION_ERROR
;
767 SimpleDateFormat::initializeSymbols(const Locale
& locale
, Calendar
* calendar
, UErrorCode
& status
)
769 if(U_FAILURE(status
)) {
772 // pass in calendar type - use NULL (default) if no calendar set (or err).
773 fSymbols
= new DateFormatSymbols(locale
, calendar
?calendar
->getType() :NULL
, status
);
774 // Null pointer check
775 if (fSymbols
== NULL
) {
776 status
= U_MEMORY_ALLOCATION_ERROR
;
783 SimpleDateFormat::initialize(const Locale
& locale
,
786 if (U_FAILURE(status
)) return;
788 // We don't need to check that the row count is >= 1, since all 2d arrays have at
790 fNumberFormat
= NumberFormat::createInstance(locale
, status
);
791 if (fNumberFormat
!= NULL
&& U_SUCCESS(status
))
793 // no matter what the locale's default number format looked like, we want
794 // to modify it so that it doesn't use thousands separators, doesn't always
795 // show the decimal point, and recognizes integers only when parsing
797 fNumberFormat
->setGroupingUsed(FALSE
);
798 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(fNumberFormat
);
799 if (decfmt
!= NULL
) {
800 decfmt
->setDecimalSeparatorAlwaysShown(FALSE
);
802 fNumberFormat
->setParseIntegerOnly(TRUE
);
803 fNumberFormat
->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
805 //fNumberFormat->setLenient(TRUE); // Java uses a custom DateNumberFormat to format/parse
807 initNumberFormatters(locale
,status
);
810 else if (U_SUCCESS(status
))
812 status
= U_MISSING_RESOURCE_ERROR
;
816 /* Initialize the fields we use to disambiguate ambiguous years. Separate
817 * so we can call it from readObject().
819 void SimpleDateFormat::initializeDefaultCentury()
822 fHaveDefaultCentury
= fCalendar
->haveDefaultCentury();
823 if(fHaveDefaultCentury
) {
824 fDefaultCenturyStart
= fCalendar
->defaultCenturyStart();
825 fDefaultCenturyStartYear
= fCalendar
->defaultCenturyStartYear();
827 fDefaultCenturyStart
= DBL_MIN
;
828 fDefaultCenturyStartYear
= -1;
833 /* Define one-century window into which to disambiguate dates using
834 * two-digit years. Make public in JDK 1.2.
836 void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate
, UErrorCode
& status
)
838 if(U_FAILURE(status
)) {
842 status
= U_ILLEGAL_ARGUMENT_ERROR
;
846 fCalendar
->setTime(startDate
, status
);
847 if(U_SUCCESS(status
)) {
848 fHaveDefaultCentury
= TRUE
;
849 fDefaultCenturyStart
= startDate
;
850 fDefaultCenturyStartYear
= fCalendar
->get(UCAL_YEAR
, status
);
854 //----------------------------------------------------------------------
857 SimpleDateFormat::format(Calendar
& cal
, UnicodeString
& appendTo
, FieldPosition
& pos
) const
859 UErrorCode status
= U_ZERO_ERROR
;
860 FieldPositionOnlyHandler
handler(pos
);
861 return _format(cal
, appendTo
, handler
, status
);
864 //----------------------------------------------------------------------
867 SimpleDateFormat::format(Calendar
& cal
, UnicodeString
& appendTo
,
868 FieldPositionIterator
* posIter
, UErrorCode
& status
) const
870 FieldPositionIteratorHandler
handler(posIter
, status
);
871 return _format(cal
, appendTo
, handler
, status
);
874 //----------------------------------------------------------------------
877 SimpleDateFormat::_format(Calendar
& cal
, UnicodeString
& appendTo
,
878 FieldPositionHandler
& handler
, UErrorCode
& status
) const
880 if ( U_FAILURE(status
) ) {
883 Calendar
* workCal
= &cal
;
884 Calendar
* calClone
= NULL
;
885 if (&cal
!= fCalendar
&& uprv_strcmp(cal
.getType(), fCalendar
->getType()) != 0) {
886 // Different calendar type
887 // We use the time and time zone from the input calendar, but
888 // do not use the input calendar for field calculation.
889 calClone
= fCalendar
->clone();
890 if (calClone
!= NULL
) {
891 UDate t
= cal
.getTime(status
);
892 calClone
->setTime(t
, status
);
893 calClone
->setTimeZone(cal
.getTimeZone());
896 status
= U_MEMORY_ALLOCATION_ERROR
;
901 UBool inQuote
= FALSE
;
904 int32_t fieldNum
= 0;
906 // loop through the pattern string character by character
907 for (int32_t i
= 0; i
< fPattern
.length() && U_SUCCESS(status
); ++i
) {
908 UChar ch
= fPattern
[i
];
910 // Use subFormat() to format a repeated pattern character
911 // when a different pattern or non-pattern character is seen
912 if (ch
!= prevCh
&& count
> 0) {
913 subFormat(appendTo
, prevCh
, count
, fCapitalizationContext
, fieldNum
++, handler
, *workCal
, status
);
917 // Consecutive single quotes are a single quote literal,
918 // either outside of quotes or between quotes
919 if ((i
+1) < fPattern
.length() && fPattern
[i
+1] == QUOTE
) {
920 appendTo
+= (UChar
)QUOTE
;
926 else if ( ! inQuote
&& ((ch
>= 0x0061 /*'a'*/ && ch
<= 0x007A /*'z'*/)
927 || (ch
>= 0x0041 /*'A'*/ && ch
<= 0x005A /*'Z'*/))) {
928 // ch is a date-time pattern character to be interpreted
929 // by subFormat(); count the number of times it is repeated
934 // Append quoted characters and unquoted non-pattern characters
939 // Format the last item in the pattern, if any
941 subFormat(appendTo
, prevCh
, count
, fCapitalizationContext
, fieldNum
++, handler
, *workCal
, status
);
944 if (calClone
!= NULL
) {
951 //----------------------------------------------------------------------
953 /* Map calendar field into calendar field level.
954 * the larger the level, the smaller the field unit.
955 * For example, UCAL_ERA level is 0, UCAL_YEAR level is 10,
956 * UCAL_MONTH level is 20.
957 * NOTE: if new fields adds in, the table needs to update.
960 SimpleDateFormat::fgCalendarFieldToLevel
[] =
964 /*dDEF*/ 30, 20, 30, 30,
965 /*ahHm*/ 40, 50, 50, 60,
973 /* Map calendar field LETTER into calendar field level.
974 * the larger the level, the smaller the field unit.
975 * NOTE: if new fields adds in, the table needs to update.
978 SimpleDateFormat::fgPatternCharToLevel
[] = {
979 // A B C D E F G H I J K L M N O
980 -1, 40, -1, -1, 20, 30, 30, 0, 50, -1, -1, 50, 20, 20, -1, 0,
981 // P Q R S T U V W X Y Z
982 -1, 20, -1, 80, -1, 10, 0, 30, 0, 10, 0, -1, -1, -1, -1, -1,
983 // a b c d e f g h i j k l m n o
984 -1, 40, -1, 30, 30, 30, -1, 0, 50, -1, -1, 50, -1, 60, -1, -1,
985 // p q r s t u v w x y z
986 -1, 20, -1, 70, -1, 10, 0, 20, 0, 10, 0, -1, -1, -1, -1, -1
990 // Map index into pattern character string to Calendar field number.
991 const UCalendarDateFields
992 SimpleDateFormat::fgPatternIndexToCalendarField
[] =
994 /*GyM*/ UCAL_ERA
, UCAL_YEAR
, UCAL_MONTH
,
995 /*dkH*/ UCAL_DATE
, UCAL_HOUR_OF_DAY
, UCAL_HOUR_OF_DAY
,
996 /*msS*/ UCAL_MINUTE
, UCAL_SECOND
, UCAL_MILLISECOND
,
997 /*EDF*/ UCAL_DAY_OF_WEEK
, UCAL_DAY_OF_YEAR
, UCAL_DAY_OF_WEEK_IN_MONTH
,
998 /*wWa*/ UCAL_WEEK_OF_YEAR
, UCAL_WEEK_OF_MONTH
, UCAL_AM_PM
,
999 /*hKz*/ UCAL_HOUR
, UCAL_HOUR
, UCAL_ZONE_OFFSET
,
1000 /*Yeu*/ UCAL_YEAR_WOY
, UCAL_DOW_LOCAL
, UCAL_EXTENDED_YEAR
,
1001 /*gAZ*/ UCAL_JULIAN_DAY
, UCAL_MILLISECONDS_IN_DAY
, UCAL_ZONE_OFFSET
,
1002 /*v*/ UCAL_ZONE_OFFSET
,
1003 /*c*/ UCAL_DOW_LOCAL
,
1007 /*V*/ UCAL_ZONE_OFFSET
,
1009 /*O*/ UCAL_ZONE_OFFSET
,
1010 /*Xx*/ UCAL_ZONE_OFFSET
, UCAL_ZONE_OFFSET
,
1013 // Map index into pattern character string to DateFormat field number
1014 const UDateFormatField
1015 SimpleDateFormat::fgPatternIndexToDateFormatField
[] = {
1016 /*GyM*/ UDAT_ERA_FIELD
, UDAT_YEAR_FIELD
, UDAT_MONTH_FIELD
,
1017 /*dkH*/ UDAT_DATE_FIELD
, UDAT_HOUR_OF_DAY1_FIELD
, UDAT_HOUR_OF_DAY0_FIELD
,
1018 /*msS*/ UDAT_MINUTE_FIELD
, UDAT_SECOND_FIELD
, UDAT_FRACTIONAL_SECOND_FIELD
,
1019 /*EDF*/ UDAT_DAY_OF_WEEK_FIELD
, UDAT_DAY_OF_YEAR_FIELD
, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD
,
1020 /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD
, UDAT_WEEK_OF_MONTH_FIELD
, UDAT_AM_PM_FIELD
,
1021 /*hKz*/ UDAT_HOUR1_FIELD
, UDAT_HOUR0_FIELD
, UDAT_TIMEZONE_FIELD
,
1022 /*Yeu*/ UDAT_YEAR_WOY_FIELD
, UDAT_DOW_LOCAL_FIELD
, UDAT_EXTENDED_YEAR_FIELD
,
1023 /*gAZ*/ UDAT_JULIAN_DAY_FIELD
, UDAT_MILLISECONDS_IN_DAY_FIELD
, UDAT_TIMEZONE_RFC_FIELD
,
1024 /*v*/ UDAT_TIMEZONE_GENERIC_FIELD
,
1025 /*c*/ UDAT_STANDALONE_DAY_FIELD
,
1026 /*L*/ UDAT_STANDALONE_MONTH_FIELD
,
1027 /*Q*/ UDAT_QUARTER_FIELD
,
1028 /*q*/ UDAT_STANDALONE_QUARTER_FIELD
,
1029 /*V*/ UDAT_TIMEZONE_SPECIAL_FIELD
,
1030 /*U*/ UDAT_YEAR_NAME_FIELD
,
1031 /*O*/ UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
,
1032 /*Xx*/ UDAT_TIMEZONE_ISO_FIELD
, UDAT_TIMEZONE_ISO_LOCAL_FIELD
,
1035 //----------------------------------------------------------------------
1038 * Append symbols[value] to dst. Make sure the array index is not out
1042 _appendSymbol(UnicodeString
& dst
,
1044 const UnicodeString
* symbols
,
1045 int32_t symbolsCount
) {
1046 U_ASSERT(0 <= value
&& value
< symbolsCount
);
1047 if (0 <= value
&& value
< symbolsCount
) {
1048 dst
+= symbols
[value
];
1053 _appendSymbolWithMonthPattern(UnicodeString
& dst
, int32_t value
, const UnicodeString
* symbols
, int32_t symbolsCount
,
1054 const UnicodeString
* monthPattern
, UErrorCode
& status
) {
1055 U_ASSERT(0 <= value
&& value
< symbolsCount
);
1056 if (0 <= value
&& value
< symbolsCount
) {
1057 if (monthPattern
== NULL
) {
1058 dst
+= symbols
[value
];
1060 Formattable
monthName((const UnicodeString
&)(symbols
[value
]));
1061 MessageFormat::format(*monthPattern
, &monthName
, 1, dst
, status
);
1066 //----------------------------------------------------------------------
1068 SimpleDateFormat::initNumberFormatters(const Locale
&locale
,UErrorCode
&status
) {
1069 if (U_FAILURE(status
)) {
1072 if ( fDateOverride
.isBogus() && fTimeOverride
.isBogus() ) {
1076 if (fNumberFormatters
== NULL
) {
1077 fNumberFormatters
= (NumberFormat
**)uprv_malloc(UDAT_FIELD_COUNT
* sizeof(NumberFormat
*));
1078 if (fNumberFormatters
) {
1079 for (int32_t i
= 0; i
< UDAT_FIELD_COUNT
; i
++) {
1080 fNumberFormatters
[i
] = fNumberFormat
;
1083 status
= U_MEMORY_ALLOCATION_ERROR
;
1088 processOverrideString(locale
,fDateOverride
,kOvrStrDate
,status
);
1089 processOverrideString(locale
,fTimeOverride
,kOvrStrTime
,status
);
1094 SimpleDateFormat::processOverrideString(const Locale
&locale
, const UnicodeString
&str
, int8_t type
, UErrorCode
&status
) {
1095 if (str
.isBogus()) {
1100 UnicodeString nsName
;
1101 UnicodeString ovrField
;
1102 UBool moreToProcess
= TRUE
;
1104 while (moreToProcess
) {
1105 int32_t delimiterPosition
= str
.indexOf((UChar
)ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE
,start
);
1106 if (delimiterPosition
== -1) {
1107 moreToProcess
= FALSE
;
1108 len
= str
.length() - start
;
1110 len
= delimiterPosition
- start
;
1112 UnicodeString
currentString(str
,start
,len
);
1113 int32_t equalSignPosition
= currentString
.indexOf((UChar
)ULOC_KEYWORD_ASSIGN_UNICODE
,0);
1114 if (equalSignPosition
== -1) { // Simple override string such as "hebrew"
1115 nsName
.setTo(currentString
);
1116 ovrField
.setToBogus();
1117 } else { // Field specific override string such as "y=hebrew"
1118 nsName
.setTo(currentString
,equalSignPosition
+1);
1119 ovrField
.setTo(currentString
,0,1); // We just need the first character.
1122 int32_t nsNameHash
= nsName
.hashCode();
1123 // See if the numbering system is in the override list, if not, then add it.
1124 NSOverride
*cur
= fOverrideList
;
1125 NumberFormat
*nf
= NULL
;
1126 UBool found
= FALSE
;
1127 while ( cur
&& !found
) {
1128 if ( cur
->hash
== nsNameHash
) {
1136 cur
= (NSOverride
*)uprv_malloc(sizeof(NSOverride
));
1138 char kw
[ULOC_KEYWORD_AND_VALUES_CAPACITY
];
1139 uprv_strcpy(kw
,"numbers=");
1140 nsName
.extract(0,len
,kw
+8,ULOC_KEYWORD_AND_VALUES_CAPACITY
-8,US_INV
);
1142 Locale
ovrLoc(locale
.getLanguage(),locale
.getCountry(),locale
.getVariant(),kw
);
1143 nf
= NumberFormat::createInstance(ovrLoc
,status
);
1145 // no matter what the locale's default number format looked like, we want
1146 // to modify it so that it doesn't use thousands separators, doesn't always
1147 // show the decimal point, and recognizes integers only when parsing
1149 if (U_SUCCESS(status
)) {
1150 nf
->setGroupingUsed(FALSE
);
1151 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(nf
);
1152 if (decfmt
!= NULL
) {
1153 decfmt
->setDecimalSeparatorAlwaysShown(FALSE
);
1155 nf
->setParseIntegerOnly(TRUE
);
1156 nf
->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
1159 cur
->hash
= nsNameHash
;
1160 cur
->next
= fOverrideList
;
1161 fOverrideList
= cur
;
1164 // clean up before returning
1172 status
= U_MEMORY_ALLOCATION_ERROR
;
1177 // Now that we have an appropriate number formatter, fill in the appropriate spaces in the
1178 // number formatters table.
1180 if (ovrField
.isBogus()) {
1184 for ( int8_t i
=0 ; i
<kDateFieldsCount
; i
++ ) {
1185 fNumberFormatters
[kDateFields
[i
]] = nf
;
1187 if (type
==kOvrStrDate
) {
1191 case kOvrStrTime
: {
1192 for ( int8_t i
=0 ; i
<kTimeFieldsCount
; i
++ ) {
1193 fNumberFormatters
[kTimeFields
[i
]] = nf
;
1199 // if the pattern character is unrecognized, signal an error and bail out
1200 UDateFormatField patternCharIndex
=
1201 DateFormatSymbols::getPatternCharIndex(ovrField
.charAt(0));
1202 if (patternCharIndex
== UDAT_FIELD_COUNT
) {
1203 status
= U_INVALID_FORMAT_ERROR
;
1207 // Set the number formatter in the table
1208 fNumberFormatters
[patternCharIndex
] = nf
;
1211 start
= delimiterPosition
+ 1;
1215 //---------------------------------------------------------------------
1217 kUnicodeLatnDigitZero
= 0x0030,
1218 kUnicodeLatnDigitNine
= 0x0039,
1222 SimpleDateFormat::subFormat(UnicodeString
&appendTo
,
1225 UDisplayContext capitalizationContext
,
1227 FieldPositionHandler
& handler
,
1229 UErrorCode
& status
) const
1231 if (U_FAILURE(status
)) {
1235 // this function gets called by format() to produce the appropriate substitution
1236 // text for an individual pattern symbol (e.g., "HH" or "yyyy")
1238 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(ch
);
1239 const int32_t maxIntCount
= 10;
1240 int32_t beginOffset
= appendTo
.length();
1241 NumberFormat
*currentNumberFormat
;
1242 DateFormatSymbols::ECapitalizationContextUsageType capContextUsageType
= DateFormatSymbols::kCapContextUsageOther
;
1244 UBool isHebrewCalendar
= (uprv_strcmp(cal
.getType(),"hebrew") == 0);
1245 UBool isChineseCalendar
= (uprv_strcmp(cal
.getType(),"chinese") == 0);
1247 // if the pattern character is unrecognized, signal an error and dump out
1248 if (patternCharIndex
== UDAT_FIELD_COUNT
)
1250 if (ch
!= 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
1251 status
= U_INVALID_FORMAT_ERROR
;
1256 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
];
1257 int32_t value
= cal
.get(field
, status
);
1258 if (U_FAILURE(status
)) {
1262 currentNumberFormat
= getNumberFormatByIndex(patternCharIndex
);
1263 UBool basicNumFmt
= FALSE
;
1264 DecimalFormat
* df
= NULL
;
1265 if ( (df
= dynamic_cast<DecimalFormat
*>(currentNumberFormat
)) != NULL
) {
1266 const DecimalFormatSymbols
* dfSymbols
= df
->getDecimalFormatSymbols();
1267 const UnicodeString
*zeroDigit
= &dfSymbols
->getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol
);
1268 const UnicodeString
*nineDigit
= &dfSymbols
->getConstSymbol(DecimalFormatSymbols::kNineDigitSymbol
);
1269 basicNumFmt
= ( zeroDigit
!=NULL
&& zeroDigit
->length()==1 && zeroDigit
->charAt(0)==kUnicodeLatnDigitZero
&&
1270 nineDigit
!=NULL
&& nineDigit
->length()==1 && nineDigit
->charAt(0)==kUnicodeLatnDigitNine
);
1272 UnicodeString
hebr("hebr", 4, US_INV
);
1274 switch (patternCharIndex
) {
1276 // for any "G" symbol, write out the appropriate era string
1277 // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name
1278 case UDAT_ERA_FIELD
:
1279 if (isChineseCalendar
) {
1280 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, 1, 9, basicNumFmt
); // as in ICU4J
1283 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowEras
, fSymbols
->fNarrowErasCount
);
1284 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraNarrow
;
1285 } else if (count
== 4) {
1286 _appendSymbol(appendTo
, value
, fSymbols
->fEraNames
, fSymbols
->fEraNamesCount
);
1287 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraWide
;
1289 _appendSymbol(appendTo
, value
, fSymbols
->fEras
, fSymbols
->fErasCount
);
1290 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraAbbrev
;
1295 case UDAT_YEAR_NAME_FIELD
:
1296 if (fSymbols
->fShortYearNames
!= NULL
&& value
<= fSymbols
->fShortYearNamesCount
) {
1297 // the Calendar YEAR field runs 1 through 60 for cyclic years
1298 _appendSymbol(appendTo
, value
- 1, fSymbols
->fShortYearNames
, fSymbols
->fShortYearNamesCount
);
1301 // else fall through to numeric year handling, do not break here
1303 // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits
1305 //Year y yy yyy yyyy yyyyy
1306 //AD 1 1 01 001 0001 00001
1307 //AD 12 12 12 012 0012 00012
1308 //AD 123 123 23 123 0123 00123
1309 //AD 1234 1234 34 1234 1234 01234
1310 //AD 12345 12345 45 12345 12345 12345
1311 case UDAT_YEAR_FIELD
:
1312 case UDAT_YEAR_WOY_FIELD
:
1313 if (fDateOverride
.compare(hebr
)==0 && value
>HEBREW_CAL_CUR_MILLENIUM_START_YEAR
&& value
<HEBREW_CAL_CUR_MILLENIUM_END_YEAR
) {
1314 value
-=HEBREW_CAL_CUR_MILLENIUM_START_YEAR
;
1317 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, 2, 2, basicNumFmt
);
1319 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, count
, maxIntCount
, basicNumFmt
);
1322 // for "MMMM"/"LLLL", write out the whole month name, for "MMM"/"LLL", write out the month
1323 // abbreviation, for "M"/"L" or "MM"/"LL", write out the month as a number with the
1324 // appropriate number of digits
1325 // for "MMMMM"/"LLLLL", use the narrow form
1326 case UDAT_MONTH_FIELD
:
1327 case UDAT_STANDALONE_MONTH_FIELD
:
1328 if ( isHebrewCalendar
) {
1329 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
1330 if (hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
== 6 && count
>= 3 )
1331 value
= 13; // Show alternate form for Adar II in leap years in Hebrew calendar.
1332 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6 && count
< 3 )
1333 value
--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7.
1336 int32_t isLeapMonth
= (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
)?
1337 cal
.get(UCAL_IS_LEAP_MONTH
, status
): 0;
1338 // should consolidate the next section by using arrays of pointers & counts for the right symbols...
1340 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1341 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fNarrowMonths
, fSymbols
->fNarrowMonthsCount
,
1342 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatNarrow
]): NULL
, status
);
1344 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneNarrowMonths
, fSymbols
->fStandaloneNarrowMonthsCount
,
1345 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneNarrow
]): NULL
, status
);
1347 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthNarrow
;
1348 } else if (count
== 4) {
1349 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1350 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fMonths
, fSymbols
->fMonthsCount
,
1351 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatWide
]): NULL
, status
);
1352 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthFormat
;
1354 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneMonths
, fSymbols
->fStandaloneMonthsCount
,
1355 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneWide
]): NULL
, status
);
1356 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthStandalone
;
1358 } else if (count
== 3) {
1359 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1360 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fShortMonths
, fSymbols
->fShortMonthsCount
,
1361 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatAbbrev
]): NULL
, status
);
1362 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthFormat
;
1364 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneShortMonths
, fSymbols
->fStandaloneShortMonthsCount
,
1365 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev
]): NULL
, status
);
1366 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthStandalone
;
1369 UnicodeString monthNumber
;
1370 zeroPaddingNumber(currentNumberFormat
,monthNumber
, value
+ 1, count
, maxIntCount
, basicNumFmt
);
1371 _appendSymbolWithMonthPattern(appendTo
, 0, &monthNumber
, 1,
1372 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternNumeric
]): NULL
, status
);
1377 // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
1378 case UDAT_HOUR_OF_DAY1_FIELD
:
1380 zeroPaddingNumber(currentNumberFormat
,appendTo
, cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1, count
, maxIntCount
, basicNumFmt
);
1382 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
, basicNumFmt
);
1385 case UDAT_FRACTIONAL_SECOND_FIELD
:
1386 // Fractional seconds left-justify
1388 currentNumberFormat
->setMinimumIntegerDigits((count
> 3) ? 3 : count
);
1389 currentNumberFormat
->setMaximumIntegerDigits(maxIntCount
);
1392 } else if (count
== 2) {
1396 currentNumberFormat
->format(value
, appendTo
, p
);
1398 currentNumberFormat
->setMinimumIntegerDigits(count
- 3);
1399 currentNumberFormat
->format((int32_t)0, appendTo
, p
);
1404 // for "ee" or "e", use local numeric day-of-the-week
1405 // for "EEEEEE" or "eeeeee", write out the short day-of-the-week name
1406 // for "EEEEE" or "eeeee", write out the narrow day-of-the-week name
1407 // for "EEEE" or "eeee", write out the wide day-of-the-week name
1408 // for "EEE" or "EE" or "E" or "eee", write out the abbreviated day-of-the-week name
1409 case UDAT_DOW_LOCAL_FIELD
:
1411 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
, basicNumFmt
);
1414 // fall through to EEEEE-EEE handling, but for that we don't want local day-of-week,
1415 // we want standard day-of-week, so first fix value to work for EEEEE-EEE.
1416 value
= cal
.get(UCAL_DAY_OF_WEEK
, status
);
1417 if (U_FAILURE(status
)) {
1420 // fall through, do not break here
1421 case UDAT_DAY_OF_WEEK_FIELD
:
1423 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowWeekdays
,
1424 fSymbols
->fNarrowWeekdaysCount
);
1425 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayNarrow
;
1426 } else if (count
== 4) {
1427 _appendSymbol(appendTo
, value
, fSymbols
->fWeekdays
,
1428 fSymbols
->fWeekdaysCount
);
1429 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1430 } else if (count
== 6) {
1431 _appendSymbol(appendTo
, value
, fSymbols
->fShorterWeekdays
,
1432 fSymbols
->fShorterWeekdaysCount
);
1433 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1435 _appendSymbol(appendTo
, value
, fSymbols
->fShortWeekdays
,
1436 fSymbols
->fShortWeekdaysCount
);
1437 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1441 // for "ccc", write out the abbreviated day-of-the-week name
1442 // for "cccc", write out the wide day-of-the-week name
1443 // for "ccccc", use the narrow day-of-the-week name
1444 // for "ccccc", use the short day-of-the-week name
1445 case UDAT_STANDALONE_DAY_FIELD
:
1447 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, 1, maxIntCount
, basicNumFmt
);
1450 // fall through to alpha DOW handling, but for that we don't want local day-of-week,
1451 // we want standard day-of-week, so first fix value.
1452 value
= cal
.get(UCAL_DAY_OF_WEEK
, status
);
1453 if (U_FAILURE(status
)) {
1457 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneNarrowWeekdays
,
1458 fSymbols
->fStandaloneNarrowWeekdaysCount
);
1459 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayNarrow
;
1460 } else if (count
== 4) {
1461 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneWeekdays
,
1462 fSymbols
->fStandaloneWeekdaysCount
);
1463 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1464 } else if (count
== 6) {
1465 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneShorterWeekdays
,
1466 fSymbols
->fStandaloneShorterWeekdaysCount
);
1467 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1468 } else { // count == 3
1469 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneShortWeekdays
,
1470 fSymbols
->fStandaloneShortWeekdaysCount
);
1471 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1475 // for and "a" symbol, write out the whole AM/PM string
1476 case UDAT_AM_PM_FIELD
:
1477 _appendSymbol(appendTo
, value
, fSymbols
->fAmPms
,
1478 fSymbols
->fAmPmsCount
);
1481 // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
1483 case UDAT_HOUR1_FIELD
:
1485 zeroPaddingNumber(currentNumberFormat
,appendTo
, cal
.getLeastMaximum(UCAL_HOUR
) + 1, count
, maxIntCount
, basicNumFmt
);
1487 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
, basicNumFmt
);
1490 case UDAT_TIMEZONE_FIELD
: // 'z'
1491 case UDAT_TIMEZONE_RFC_FIELD
: // 'Z'
1492 case UDAT_TIMEZONE_GENERIC_FIELD
: // 'v'
1493 case UDAT_TIMEZONE_SPECIAL_FIELD
: // 'V'
1494 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
: // 'O'
1495 case UDAT_TIMEZONE_ISO_FIELD
: // 'X'
1496 case UDAT_TIMEZONE_ISO_LOCAL_FIELD
: // 'x'
1498 UnicodeString zoneString
;
1499 const TimeZone
& tz
= cal
.getTimeZone();
1500 UDate date
= cal
.getTime(status
);
1501 if (U_SUCCESS(status
)) {
1502 if (patternCharIndex
== UDAT_TIMEZONE_FIELD
) {
1505 tzFormat()->format(UTZFMT_STYLE_SPECIFIC_SHORT
, tz
, date
, zoneString
);
1506 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneShort
;
1509 tzFormat()->format(UTZFMT_STYLE_SPECIFIC_LONG
, tz
, date
, zoneString
);
1510 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneLong
;
1513 else if (patternCharIndex
== UDAT_TIMEZONE_RFC_FIELD
) {
1516 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
, tz
, date
, zoneString
);
1517 } else if (count
== 5) {
1519 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FULL
, tz
, date
, zoneString
);
1521 // "ZZ", "ZZZ", "ZZZZ"
1522 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT
, tz
, date
, zoneString
);
1525 else if (patternCharIndex
== UDAT_TIMEZONE_GENERIC_FIELD
) {
1528 tzFormat()->format(UTZFMT_STYLE_GENERIC_SHORT
, tz
, date
, zoneString
);
1529 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneShort
;
1530 } else if (count
== 4) {
1532 tzFormat()->format(UTZFMT_STYLE_GENERIC_LONG
, tz
, date
, zoneString
);
1533 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneLong
;
1536 else if (patternCharIndex
== UDAT_TIMEZONE_SPECIAL_FIELD
) {
1539 tzFormat()->format(UTZFMT_STYLE_ZONE_ID_SHORT
, tz
, date
, zoneString
);
1540 } else if (count
== 2) {
1542 tzFormat()->format(UTZFMT_STYLE_ZONE_ID
, tz
, date
, zoneString
);
1543 } else if (count
== 3) {
1545 tzFormat()->format(UTZFMT_STYLE_EXEMPLAR_LOCATION
, tz
, date
, zoneString
);
1546 } else if (count
== 4) {
1548 tzFormat()->format(UTZFMT_STYLE_GENERIC_LOCATION
, tz
, date
, zoneString
);
1549 capContextUsageType
= DateFormatSymbols::kCapContextUsageZoneLong
;
1552 else if (patternCharIndex
== UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
) {
1555 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT_SHORT
, tz
, date
, zoneString
);
1556 } else if (count
== 4) {
1558 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT
, tz
, date
, zoneString
);
1561 else if (patternCharIndex
== UDAT_TIMEZONE_ISO_FIELD
) {
1564 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_SHORT
, tz
, date
, zoneString
);
1565 } else if (count
== 2) {
1567 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_FIXED
, tz
, date
, zoneString
);
1568 } else if (count
== 3) {
1570 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FIXED
, tz
, date
, zoneString
);
1571 } else if (count
== 4) {
1573 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_FULL
, tz
, date
, zoneString
);
1574 } else if (count
== 5) {
1576 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FULL
, tz
, date
, zoneString
);
1579 else if (patternCharIndex
== UDAT_TIMEZONE_ISO_LOCAL_FIELD
) {
1582 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
, tz
, date
, zoneString
);
1583 } else if (count
== 2) {
1585 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
, tz
, date
, zoneString
);
1586 } else if (count
== 3) {
1588 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
, tz
, date
, zoneString
);
1589 } else if (count
== 4) {
1591 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
, tz
, date
, zoneString
);
1592 } else if (count
== 5) {
1594 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
, tz
, date
, zoneString
);
1601 appendTo
+= zoneString
;
1605 case UDAT_QUARTER_FIELD
:
1607 _appendSymbol(appendTo
, value
/3, fSymbols
->fQuarters
,
1608 fSymbols
->fQuartersCount
);
1609 else if (count
== 3)
1610 _appendSymbol(appendTo
, value
/3, fSymbols
->fShortQuarters
,
1611 fSymbols
->fShortQuartersCount
);
1613 zeroPaddingNumber(currentNumberFormat
,appendTo
, (value
/3) + 1, count
, maxIntCount
, basicNumFmt
);
1616 case UDAT_STANDALONE_QUARTER_FIELD
:
1618 _appendSymbol(appendTo
, value
/3, fSymbols
->fStandaloneQuarters
,
1619 fSymbols
->fStandaloneQuartersCount
);
1620 else if (count
== 3)
1621 _appendSymbol(appendTo
, value
/3, fSymbols
->fStandaloneShortQuarters
,
1622 fSymbols
->fStandaloneShortQuartersCount
);
1624 zeroPaddingNumber(currentNumberFormat
,appendTo
, (value
/3) + 1, count
, maxIntCount
, basicNumFmt
);
1628 // all of the other pattern symbols can be formatted as simple numbers with
1629 // appropriate zero padding
1631 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
, basicNumFmt
);
1634 #if !UCONFIG_NO_BREAK_ITERATION
1635 if (fieldNum
== 0) {
1636 // first field, check to see whether we need to titlecase it
1637 UBool titlecase
= FALSE
;
1638 switch (capitalizationContext
) {
1639 case UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
:
1642 case UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
:
1643 titlecase
= fSymbols
->fCapitalization
[capContextUsageType
][0];
1645 case UDISPCTX_CAPITALIZATION_FOR_STANDALONE
:
1646 titlecase
= fSymbols
->fCapitalization
[capContextUsageType
][1];
1649 // titlecase = FALSE;
1653 UnicodeString
firstField(appendTo
, beginOffset
);
1654 firstField
.toTitle(NULL
, fLocale
, U_TITLECASE_NO_LOWERCASE
| U_TITLECASE_NO_BREAK_ADJUSTMENT
);
1655 appendTo
.replaceBetween(beginOffset
, appendTo
.length(), firstField
);
1660 handler
.addAttribute(fgPatternIndexToDateFormatField
[patternCharIndex
], beginOffset
, appendTo
.length());
1663 //----------------------------------------------------------------------
1666 SimpleDateFormat::getNumberFormatByIndex(UDateFormatField index
) const {
1667 if (fNumberFormatters
!= NULL
) {
1668 return fNumberFormatters
[index
];
1670 return fNumberFormat
;
1674 //----------------------------------------------------------------------
1675 enum { kBasicNumMaxDigits
= 16 };
1678 SimpleDateFormat::zeroPaddingNumber(NumberFormat
*currentNumberFormat
,UnicodeString
&appendTo
,
1679 int32_t value
, int32_t minDigits
, int32_t maxDigits
, UBool basicNumFmt
) const
1681 if (basicNumFmt
&& maxDigits
<= kBasicNumMaxDigits
&& minDigits
<= maxDigits
&& value
>= 0) {
1682 UChar basicNum
[kBasicNumMaxDigits
];
1683 UChar
* basicNumPtr
= basicNum
+ kBasicNumMaxDigits
;
1684 int32_t remainingValue
= value
;
1685 int32_t currentDigits
= 0;
1687 *--basicNumPtr
= kUnicodeLatnDigitZero
+ (remainingValue
% 10);
1688 remainingValue
/= 10;
1690 } while (currentDigits
< minDigits
|| (remainingValue
> 0 && currentDigits
< maxDigits
));
1691 if (remainingValue
== 0) {
1692 appendTo
.append(basicNumPtr
, currentDigits
);
1696 if (currentNumberFormat
!=NULL
) {
1697 FieldPosition
pos(0);
1699 currentNumberFormat
->setMinimumIntegerDigits(minDigits
);
1700 currentNumberFormat
->setMaximumIntegerDigits(maxDigits
);
1701 currentNumberFormat
->format(value
, appendTo
, pos
); // 3rd arg is there to speed up processing
1705 //----------------------------------------------------------------------
1708 * Return true if the given format character, occuring count
1709 * times, represents a numeric field.
1711 UBool
SimpleDateFormat::isNumeric(UChar formatChar
, int32_t count
) {
1712 return DateFormatSymbols::isNumericPatternChar(formatChar
, count
);
1716 SimpleDateFormat::isAtNumericField(const UnicodeString
&pattern
, int32_t patternOffset
) {
1717 if (patternOffset
>= pattern
.length()) {
1721 UChar ch
= pattern
.charAt(patternOffset
);
1722 UDateFormatField f
= DateFormatSymbols::getPatternCharIndex(ch
);
1723 if (f
== UDAT_FIELD_COUNT
) {
1727 int32_t i
= patternOffset
;
1728 while (pattern
.charAt(++i
) == ch
) {}
1729 return DateFormatSymbols::isNumericField(f
, i
- patternOffset
);
1733 SimpleDateFormat::isAfterNonNumericField(const UnicodeString
&pattern
, int32_t patternOffset
) {
1734 if (patternOffset
<= 0) {
1735 // not after any field
1738 UChar ch
= pattern
.charAt(--patternOffset
);
1739 UDateFormatField f
= DateFormatSymbols::getPatternCharIndex(ch
);
1740 if (f
== UDAT_FIELD_COUNT
) {
1741 // not after any field
1744 int32_t i
= patternOffset
;
1745 while (pattern
.charAt(--i
) == ch
) {}
1746 return !DateFormatSymbols::isNumericField(f
, patternOffset
- i
);
1750 SimpleDateFormat::parse(const UnicodeString
& text
, Calendar
& cal
, ParsePosition
& parsePos
) const
1752 UErrorCode status
= U_ZERO_ERROR
;
1753 int32_t pos
= parsePos
.getIndex();
1754 int32_t start
= pos
;
1756 UBool ambiguousYear
[] = { FALSE
};
1757 int32_t saveHebrewMonth
= -1;
1760 UBool lenient
= isLenient();
1762 // hack, reset tztype, cast away const
1763 ((SimpleDateFormat
*)this)->tztype
= UTZFMT_TIME_TYPE_UNKNOWN
;
1765 // For parsing abutting numeric fields. 'abutPat' is the
1766 // offset into 'pattern' of the first of 2 or more abutting
1767 // numeric fields. 'abutStart' is the offset into 'text'
1768 // where parsing the fields begins. 'abutPass' starts off as 0
1769 // and increments each time we try to parse the fields.
1770 int32_t abutPat
= -1; // If >=0, we are in a run of abutting numeric fields
1771 int32_t abutStart
= 0;
1772 int32_t abutPass
= 0;
1773 UBool inQuote
= FALSE
;
1775 MessageFormat
* numericLeapMonthFormatter
= NULL
;
1777 Calendar
* calClone
= NULL
;
1778 Calendar
*workCal
= &cal
;
1779 if (&cal
!= fCalendar
&& uprv_strcmp(cal
.getType(), fCalendar
->getType()) != 0) {
1780 // Different calendar type
1781 // We use the time/zone from the input calendar, but
1782 // do not use the input calendar for field calculation.
1783 calClone
= fCalendar
->clone();
1784 if (calClone
!= NULL
) {
1785 calClone
->setTime(cal
.getTime(status
),status
);
1786 if (U_FAILURE(status
)) {
1789 calClone
->setTimeZone(cal
.getTimeZone());
1792 status
= U_MEMORY_ALLOCATION_ERROR
;
1797 if (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
) {
1798 numericLeapMonthFormatter
= new MessageFormat(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternNumeric
], fLocale
, status
);
1799 if (numericLeapMonthFormatter
== NULL
) {
1800 status
= U_MEMORY_ALLOCATION_ERROR
;
1802 } else if (U_FAILURE(status
)) {
1803 goto ExitParse
; // this will delete numericLeapMonthFormatter
1807 for (int32_t i
=0; i
<fPattern
.length(); ++i
) {
1808 UChar ch
= fPattern
.charAt(i
);
1810 // Handle alphabetic field characters.
1811 if (!inQuote
&& ((ch
>= 0x41 && ch
<= 0x5A) || (ch
>= 0x61 && ch
<= 0x7A))) { // [A-Za-z]
1812 int32_t fieldPat
= i
;
1814 // Count the length of this field specifier
1816 while ((i
+1)<fPattern
.length() &&
1817 fPattern
.charAt(i
+1) == ch
) {
1822 if (isNumeric(ch
, count
)) {
1824 // Determine if there is an abutting numeric field.
1825 // Record the start of a set of abutting numeric fields.
1826 if (isAtNumericField(fPattern
, i
+ 1)) {
1833 abutPat
= -1; // End of any abutting fields
1836 // Handle fields within a run of abutting numeric fields. Take
1837 // the pattern "HHmmss" as an example. We will try to parse
1838 // 2/2/2 characters of the input text, then if that fails,
1839 // 1/2/2. We only adjust the width of the leftmost field; the
1840 // others remain fixed. This allows "123456" => 12:34:56, but
1841 // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
1842 // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
1844 // If we are at the start of a run of abutting fields, then
1845 // shorten this field in each pass. If we can't shorten
1846 // this field any more, then the parse of this set of
1847 // abutting numeric fields has failed.
1848 if (fieldPat
== abutPat
) {
1849 count
-= abutPass
++;
1851 status
= U_PARSE_ERROR
;
1856 pos
= subParse(text
, pos
, ch
, count
,
1857 TRUE
, FALSE
, ambiguousYear
, saveHebrewMonth
, *workCal
, i
, numericLeapMonthFormatter
);
1859 // If the parse fails anywhere in the run, back up to the
1860 // start of the run and retry.
1868 // Handle non-numeric fields and non-abutting numeric
1870 else if (ch
!= 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
1871 int32_t s
= subParse(text
, pos
, ch
, count
,
1872 FALSE
, TRUE
, ambiguousYear
, saveHebrewMonth
, *workCal
, i
, numericLeapMonthFormatter
);
1875 // era not present, in special cases allow this to continue
1876 // from the position where the era was expected
1879 if (i
+1 < fPattern
.length()) {
1880 // move to next pattern character
1881 UChar ch
= fPattern
.charAt(i
+1);
1883 // check for whitespace
1884 if (PatternProps::isWhiteSpace(ch
)) {
1886 // Advance over run in pattern
1887 while ((i
+1)<fPattern
.length() &&
1888 PatternProps::isWhiteSpace(fPattern
.charAt(i
+1))) {
1895 status
= U_PARSE_ERROR
;
1902 // Handle literal pattern characters. These are any
1903 // quoted characters and non-alphabetic unquoted
1907 abutPat
= -1; // End of any abutting fields
1909 if (! matchLiterals(fPattern
, i
, text
, pos
, lenient
)) {
1910 status
= U_PARSE_ERROR
;
1916 // Special hack for trailing "." after non-numeric field.
1917 if (text
.charAt(pos
) == 0x2e && lenient
) {
1918 // only do if the last field is not numeric
1919 if (isAfterNonNumericField(fPattern
, fPattern
.length())) {
1920 pos
++; // skip the extra "."
1924 // At this point the fields of Calendar have been set. Calendar
1925 // will fill in default values for missing fields when the time
1928 parsePos
.setIndex(pos
);
1930 // This part is a problem: When we call parsedDate.after, we compute the time.
1931 // Take the date April 3 2004 at 2:30 am. When this is first set up, the year
1932 // will be wrong if we're parsing a 2-digit year pattern. It will be 1904.
1933 // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am
1934 // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
1935 // on that day. It is therefore parsed out to fields as 3:30 am. Then we
1936 // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is
1937 // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
1939 UDate parsedDate = calendar.getTime();
1940 if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
1941 calendar.add(Calendar.YEAR, 100);
1942 parsedDate = calendar.getTime();
1945 // Because of the above condition, save off the fields in case we need to readjust.
1946 // The procedure we use here is not particularly efficient, but there is no other
1947 // way to do this given the API restrictions present in Calendar. We minimize
1948 // inefficiency by only performing this computation when it might apply, that is,
1949 // when the two-digit year is equal to the start year, and thus might fall at the
1950 // front or the back of the default century. This only works because we adjust
1951 // the year correctly to start with in other cases -- see subParse().
1952 if (ambiguousYear
[0] || tztype
!= UTZFMT_TIME_TYPE_UNKNOWN
) // If this is true then the two-digit year == the default start year
1954 // We need a copy of the fields, and we need to avoid triggering a call to
1955 // complete(), which will recalculate the fields. Since we can't access
1956 // the fields[] array in Calendar, we clone the entire object. This will
1957 // stop working if Calendar.clone() is ever rewritten to call complete().
1959 if (ambiguousYear
[0]) {
1961 // Check for failed cloning.
1963 status
= U_MEMORY_ALLOCATION_ERROR
;
1966 UDate parsedDate
= copy
->getTime(status
);
1967 // {sfb} check internalGetDefaultCenturyStart
1968 if (fHaveDefaultCentury
&& (parsedDate
< fDefaultCenturyStart
)) {
1969 // We can't use add here because that does a complete() first.
1970 cal
.set(UCAL_YEAR
, fDefaultCenturyStartYear
+ 100);
1975 if (tztype
!= UTZFMT_TIME_TYPE_UNKNOWN
) {
1977 // Check for failed cloning.
1979 status
= U_MEMORY_ALLOCATION_ERROR
;
1982 const TimeZone
& tz
= cal
.getTimeZone();
1983 BasicTimeZone
*btz
= NULL
;
1985 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
1986 || dynamic_cast<const SimpleTimeZone
*>(&tz
) != NULL
1987 || dynamic_cast<const RuleBasedTimeZone
*>(&tz
) != NULL
1988 || dynamic_cast<const VTimeZone
*>(&tz
) != NULL
) {
1989 btz
= (BasicTimeZone
*)&tz
;
1993 copy
->set(UCAL_ZONE_OFFSET
, 0);
1994 copy
->set(UCAL_DST_OFFSET
, 0);
1995 UDate localMillis
= copy
->getTime(status
);
1997 // Make sure parsed time zone type (Standard or Daylight)
1998 // matches the rule used by the parsed time zone.
2001 if (tztype
== UTZFMT_TIME_TYPE_STANDARD
) {
2002 btz
->getOffsetFromLocal(localMillis
,
2003 BasicTimeZone::kStandard
, BasicTimeZone::kStandard
, raw
, dst
, status
);
2005 btz
->getOffsetFromLocal(localMillis
,
2006 BasicTimeZone::kDaylight
, BasicTimeZone::kDaylight
, raw
, dst
, status
);
2009 // No good way to resolve ambiguous time at transition,
2010 // but following code work in most case.
2011 tz
.getOffset(localMillis
, TRUE
, raw
, dst
, status
);
2014 // Now, compare the results with parsed type, either standard or daylight saving time
2015 int32_t resolvedSavings
= dst
;
2016 if (tztype
== UTZFMT_TIME_TYPE_STANDARD
) {
2018 // Override DST_OFFSET = 0 in the result calendar
2019 resolvedSavings
= 0;
2021 } else { // tztype == TZTYPE_DST
2024 UDate time
= localMillis
+ raw
;
2025 // We use the nearest daylight saving time rule.
2026 TimeZoneTransition beforeTrs
, afterTrs
;
2027 UDate beforeT
= time
, afterT
= time
;
2028 int32_t beforeSav
= 0, afterSav
= 0;
2029 UBool beforeTrsAvail
, afterTrsAvail
;
2031 // Search for DST rule before or on the time
2033 beforeTrsAvail
= btz
->getPreviousTransition(beforeT
, TRUE
, beforeTrs
);
2034 if (!beforeTrsAvail
) {
2037 beforeT
= beforeTrs
.getTime() - 1;
2038 beforeSav
= beforeTrs
.getFrom()->getDSTSavings();
2039 if (beforeSav
!= 0) {
2044 // Search for DST rule after the time
2046 afterTrsAvail
= btz
->getNextTransition(afterT
, FALSE
, afterTrs
);
2047 if (!afterTrsAvail
) {
2050 afterT
= afterTrs
.getTime();
2051 afterSav
= afterTrs
.getTo()->getDSTSavings();
2052 if (afterSav
!= 0) {
2057 if (beforeTrsAvail
&& afterTrsAvail
) {
2058 if (time
- beforeT
> afterT
- time
) {
2059 resolvedSavings
= afterSav
;
2061 resolvedSavings
= beforeSav
;
2063 } else if (beforeTrsAvail
&& beforeSav
!= 0) {
2064 resolvedSavings
= beforeSav
;
2065 } else if (afterTrsAvail
&& afterSav
!= 0) {
2066 resolvedSavings
= afterSav
;
2068 resolvedSavings
= btz
->getDSTSavings();
2071 resolvedSavings
= tz
.getDSTSavings();
2073 if (resolvedSavings
== 0) {
2075 resolvedSavings
= U_MILLIS_PER_HOUR
;
2079 cal
.set(UCAL_ZONE_OFFSET
, raw
);
2080 cal
.set(UCAL_DST_OFFSET
, resolvedSavings
);
2085 // Set the parsed result if local calendar is used
2086 // instead of the input calendar
2087 if (U_SUCCESS(status
) && workCal
!= &cal
) {
2088 cal
.setTimeZone(workCal
->getTimeZone());
2089 cal
.setTime(workCal
->getTime(status
), status
);
2092 if (numericLeapMonthFormatter
!= NULL
) {
2093 delete numericLeapMonthFormatter
;
2095 if (calClone
!= NULL
) {
2099 // If any Calendar calls failed, we pretend that we
2100 // couldn't parse the string, when in reality this isn't quite accurate--
2101 // we did parse it; the Calendar calls just failed.
2102 if (U_FAILURE(status
)) {
2103 parsePos
.setErrorIndex(pos
);
2104 parsePos
.setIndex(start
);
2109 SimpleDateFormat::parse( const UnicodeString
& text
,
2110 ParsePosition
& pos
) const {
2111 // redefined here because the other parse() function hides this function's
2112 // cunterpart on DateFormat
2113 return DateFormat::parse(text
, pos
);
2117 SimpleDateFormat::parse(const UnicodeString
& text
, UErrorCode
& status
) const
2119 // redefined here because the other parse() function hides this function's
2120 // counterpart on DateFormat
2121 return DateFormat::parse(text
, status
);
2123 //----------------------------------------------------------------------
2126 newBestMatchWithOptionalDot(const UnicodeString
&lcaseText
,
2127 const UnicodeString
&data
,
2128 UnicodeString
&bestMatchName
,
2129 int32_t &bestMatchLength
);
2131 int32_t SimpleDateFormat::matchQuarterString(const UnicodeString
& text
,
2133 UCalendarDateFields field
,
2134 const UnicodeString
* data
,
2136 Calendar
& cal
) const
2139 int32_t count
= dataCount
;
2141 // There may be multiple strings in the data[] array which begin with
2142 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2143 // We keep track of the longest match, and return that. Note that this
2144 // unfortunately requires us to test all array elements.
2145 int32_t bestMatchLength
= 0, bestMatch
= -1;
2146 UnicodeString bestMatchName
;
2148 // {sfb} kludge to support case-insensitive comparison
2149 // {markus 2002oct11} do not just use caseCompareBetween because we do not know
2150 // the length of the match after case folding
2151 // {alan 20040607} don't case change the whole string, since the length
2153 // TODO we need a case-insensitive startsWith function
2154 UnicodeString lcaseText
;
2155 text
.extract(start
, INT32_MAX
, lcaseText
);
2156 lcaseText
.foldCase();
2158 for (; i
< count
; ++i
)
2160 // Always compare if we have no match yet; otherwise only compare
2161 // against potentially better matches (longer strings).
2163 if (newBestMatchWithOptionalDot(lcaseText
, data
[i
], bestMatchName
, bestMatchLength
)) {
2169 cal
.set(field
, bestMatch
* 3);
2171 // Once we have a match, we have to determine the length of the
2172 // original source string. This will usually be == the length of
2173 // the case folded string, but it may differ (e.g. sharp s).
2175 // Most of the time, the length will be the same as the length
2176 // of the string from the locale data. Sometimes it will be
2177 // different, in which case we will have to figure it out by
2178 // adding a character at a time, until we have a match. We do
2179 // this all in one loop, where we try 'len' first (at index
2181 int32_t len
= bestMatchName
.length(); // 99+% of the time
2182 int32_t n
= text
.length() - start
;
2183 for (i
=0; i
<=n
; ++i
) {
2187 } else if (i
== len
) {
2188 continue; // already tried this when i was 0
2190 text
.extract(start
, j
, lcaseText
);
2191 lcaseText
.foldCase();
2192 if (bestMatchName
== lcaseText
) {
2201 //----------------------------------------------------------------------
2202 UBool
SimpleDateFormat::matchLiterals(const UnicodeString
&pattern
,
2203 int32_t &patternOffset
,
2204 const UnicodeString
&text
,
2205 int32_t &textOffset
,
2208 UBool inQuote
= FALSE
;
2209 UnicodeString literal
;
2210 int32_t i
= patternOffset
;
2212 // scan pattern looking for contiguous literal characters
2213 for ( ; i
< pattern
.length(); i
+= 1) {
2214 UChar ch
= pattern
.charAt(i
);
2216 if (!inQuote
&& ((ch
>= 0x41 && ch
<= 0x5A) || (ch
>= 0x61 && ch
<= 0x7A))) { // unquoted [A-Za-z]
2221 // Match a quote literal ('') inside OR outside of quotes
2222 if ((i
+ 1) < pattern
.length() && pattern
.charAt(i
+ 1) == QUOTE
) {
2233 // at this point, literal contains the literal text
2234 // and i is the index of the next non-literal pattern character.
2236 int32_t t
= textOffset
;
2239 // trim leading, trailing whitespace from
2243 // ignore any leading whitespace in the text
2244 while (t
< text
.length() && u_isWhitespace(text
.charAt(t
))) {
2249 for (p
= 0; p
< literal
.length() && t
< text
.length();) {
2250 UBool needWhitespace
= FALSE
;
2252 while (p
< literal
.length() && PatternProps::isWhiteSpace(literal
.charAt(p
))) {
2253 needWhitespace
= TRUE
;
2257 if (needWhitespace
) {
2260 while (t
< text
.length()) {
2261 UChar tch
= text
.charAt(t
);
2263 if (!u_isUWhiteSpace(tch
) && !PatternProps::isWhiteSpace(tch
)) {
2270 // TODO: should we require internal spaces
2271 // in lenient mode? (There won't be any
2272 // leading or trailing spaces)
2273 if (!lenient
&& t
== tStart
) {
2274 // didn't find matching whitespace:
2275 // an error in strict mode
2279 // In strict mode, this run of whitespace
2280 // may have been at the end.
2281 if (p
>= literal
.length()) {
2286 if (t
>= text
.length() || literal
.charAt(p
) != text
.charAt(t
)) {
2287 // Ran out of text, or found a non-matching character:
2288 // OK in lenient mode, an error in strict mode.
2290 if (t
== textOffset
&& text
.charAt(t
) == 0x2e &&
2291 isAfterNonNumericField(pattern
, patternOffset
)) {
2292 // Lenient mode and the literal input text begins with a "." and
2293 // we are after a non-numeric field: We skip the "."
2295 continue; // Do not update p.
2306 // At this point if we're in strict mode we have a complete match.
2307 // If we're in lenient mode we may have a partial match, or no
2310 // no match. Pretend it matched a run of whitespace
2311 // and ignorables in the text.
2312 const UnicodeSet
*ignorables
= NULL
;
2313 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(pattern
.charAt(i
));
2314 if (patternCharIndex
!= UDAT_FIELD_COUNT
) {
2315 ignorables
= SimpleDateFormatStaticSets::getIgnorables(patternCharIndex
);
2318 for (t
= textOffset
; t
< text
.length(); t
+= 1) {
2319 UChar ch
= text
.charAt(t
);
2321 if (ignorables
== NULL
|| !ignorables
->contains(ch
)) {
2327 // if we get here, we've got a complete match.
2328 patternOffset
= i
- 1;
2334 //----------------------------------------------------------------------
2336 int32_t SimpleDateFormat::matchString(const UnicodeString
& text
,
2338 UCalendarDateFields field
,
2339 const UnicodeString
* data
,
2341 const UnicodeString
* monthPattern
,
2342 Calendar
& cal
) const
2345 int32_t count
= dataCount
;
2347 if (field
== UCAL_DAY_OF_WEEK
) i
= 1;
2349 // There may be multiple strings in the data[] array which begin with
2350 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2351 // We keep track of the longest match, and return that. Note that this
2352 // unfortunately requires us to test all array elements.
2353 int32_t bestMatchLength
= 0, bestMatch
= -1;
2354 UnicodeString bestMatchName
;
2355 int32_t isLeapMonth
= 0;
2357 // {sfb} kludge to support case-insensitive comparison
2358 // {markus 2002oct11} do not just use caseCompareBetween because we do not know
2359 // the length of the match after case folding
2360 // {alan 20040607} don't case change the whole string, since the length
2362 // TODO we need a case-insensitive startsWith function
2363 UnicodeString lcaseText
;
2364 text
.extract(start
, INT32_MAX
, lcaseText
);
2365 lcaseText
.foldCase();
2367 for (; i
< count
; ++i
)
2369 // Always compare if we have no match yet; otherwise only compare
2370 // against potentially better matches (longer strings).
2372 if (newBestMatchWithOptionalDot(lcaseText
, data
[i
], bestMatchName
, bestMatchLength
)) {
2377 if (monthPattern
!= NULL
) {
2378 UErrorCode status
= U_ZERO_ERROR
;
2379 UnicodeString leapMonthName
;
2380 Formattable
monthName((const UnicodeString
&)(data
[i
]));
2381 MessageFormat::format(*monthPattern
, &monthName
, 1, leapMonthName
, status
);
2382 if (U_SUCCESS(status
)) {
2383 if (newBestMatchWithOptionalDot(lcaseText
, leapMonthName
, bestMatchName
, bestMatchLength
)) {
2392 // Adjustment for Hebrew Calendar month Adar II
2393 if (!strcmp(cal
.getType(),"hebrew") && field
==UCAL_MONTH
&& bestMatch
==13) {
2397 if (field
== UCAL_YEAR
) {
2398 bestMatch
++; // only get here for cyclic year names, which match 1-based years 1-60
2400 cal
.set(field
, bestMatch
);
2402 if (monthPattern
!= NULL
) {
2403 cal
.set(UCAL_IS_LEAP_MONTH
, isLeapMonth
);
2406 // Once we have a match, we have to determine the length of the
2407 // original source string. This will usually be == the length of
2408 // the case folded string, but it may differ (e.g. sharp s).
2410 // Most of the time, the length will be the same as the length
2411 // of the string from the locale data. Sometimes it will be
2412 // different, in which case we will have to figure it out by
2413 // adding a character at a time, until we have a match. We do
2414 // this all in one loop, where we try 'len' first (at index
2416 int32_t len
= bestMatchName
.length(); // 99+% of the time
2417 int32_t n
= text
.length() - start
;
2418 for (i
=0; i
<=n
; ++i
) {
2422 } else if (i
== len
) {
2423 continue; // already tried this when i was 0
2425 text
.extract(start
, j
, lcaseText
);
2426 lcaseText
.foldCase();
2427 if (bestMatchName
== lcaseText
) {
2437 newBestMatchWithOptionalDot(const UnicodeString
&lcaseText
,
2438 const UnicodeString
&data
,
2439 UnicodeString
&bestMatchName
,
2440 int32_t &bestMatchLength
) {
2441 UnicodeString lcase
;
2442 lcase
.fastCopyFrom(data
).foldCase();
2443 int32_t length
= lcase
.length();
2444 if (length
<= bestMatchLength
) {
2445 // data cannot provide a better match.
2449 if (lcaseText
.compareBetween(0, length
, lcase
, 0, length
) == 0) {
2451 bestMatchName
= lcase
;
2452 bestMatchLength
= length
;
2455 if (lcase
.charAt(--length
) == 0x2e) {
2456 if (lcaseText
.compareBetween(0, length
, lcase
, 0, length
) == 0) {
2457 // The input text matches the data except for data's trailing dot.
2458 bestMatchName
= lcase
;
2459 bestMatchName
.truncate(length
);
2460 bestMatchLength
= length
;
2467 //----------------------------------------------------------------------
2470 SimpleDateFormat::set2DigitYearStart(UDate d
, UErrorCode
& status
)
2472 parseAmbiguousDatesAsAfter(d
, status
);
2476 * Private member function that converts the parsed date strings into
2477 * timeFields. Returns -start (for ParsePosition) if failed.
2478 * @param text the time text to be parsed.
2479 * @param start where to start parsing.
2480 * @param ch the pattern character for the date field text to be parsed.
2481 * @param count the count of a pattern character.
2482 * @return the new start position if matching succeeded; a negative number
2483 * indicating matching failure, otherwise.
2485 int32_t SimpleDateFormat::subParse(const UnicodeString
& text
, int32_t& start
, UChar ch
, int32_t count
,
2486 UBool obeyCount
, UBool allowNegative
, UBool ambiguousYear
[], int32_t& saveHebrewMonth
, Calendar
& cal
,
2487 int32_t patLoc
, MessageFormat
* numericLeapMonthFormatter
) const
2493 ParsePosition
pos(0);
2494 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(ch
);
2495 NumberFormat
*currentNumberFormat
;
2497 UBool lenient
= isLenient();
2498 int32_t tzParseOptions
= (lenient
)? UTZFMT_PARSE_OPTION_ALL_STYLES
: UTZFMT_PARSE_OPTION_NONE
;
2499 UBool gotNumber
= FALSE
;
2501 #if defined (U_DEBUG_CAL)
2502 //fprintf(stderr, "%s:%d - [%c] st=%d \n", __FILE__, __LINE__, (char) ch, start);
2505 if (patternCharIndex
== UDAT_FIELD_COUNT
) {
2509 currentNumberFormat
= getNumberFormatByIndex(patternCharIndex
);
2510 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
];
2511 UnicodeString
hebr("hebr", 4, US_INV
);
2513 if (numericLeapMonthFormatter
!= NULL
) {
2514 numericLeapMonthFormatter
->setFormats((const Format
**)¤tNumberFormat
, 1);
2516 UBool isChineseCalendar
= (uprv_strcmp(cal
.getType(),"chinese") == 0);
2518 // If there are any spaces here, skip over them. If we hit the end
2519 // of the string, then fail.
2521 if (start
>= text
.length()) {
2524 UChar32 c
= text
.char32At(start
);
2525 if (!u_isUWhiteSpace(c
) /*||*/ && !PatternProps::isWhiteSpace(c
)) {
2528 start
+= U16_LENGTH(c
);
2530 pos
.setIndex(start
);
2532 // We handle a few special cases here where we need to parse
2533 // a number value. We handle further, more generic cases below. We need
2534 // to handle some of them here because some fields require extra processing on
2535 // the parsed value.
2536 if (patternCharIndex
== UDAT_HOUR_OF_DAY1_FIELD
|| // k
2537 patternCharIndex
== UDAT_HOUR_OF_DAY0_FIELD
|| // H
2538 patternCharIndex
== UDAT_HOUR1_FIELD
|| // h
2539 patternCharIndex
== UDAT_HOUR0_FIELD
|| // K
2540 (patternCharIndex
== UDAT_DOW_LOCAL_FIELD
&& count
<= 2) || // e
2541 (patternCharIndex
== UDAT_STANDALONE_DAY_FIELD
&& count
<= 2) || // c
2542 (patternCharIndex
== UDAT_MONTH_FIELD
&& count
<= 2) || // M
2543 (patternCharIndex
== UDAT_STANDALONE_MONTH_FIELD
&& count
<= 2) || // L
2544 (patternCharIndex
== UDAT_QUARTER_FIELD
&& count
<= 2) || // Q
2545 (patternCharIndex
== UDAT_STANDALONE_QUARTER_FIELD
&& count
<= 2) || // q
2546 patternCharIndex
== UDAT_YEAR_FIELD
|| // y
2547 patternCharIndex
== UDAT_YEAR_WOY_FIELD
|| // Y
2548 patternCharIndex
== UDAT_YEAR_NAME_FIELD
|| // U (falls back to numeric)
2549 (patternCharIndex
== UDAT_ERA_FIELD
&& isChineseCalendar
) || // G
2550 patternCharIndex
== UDAT_FRACTIONAL_SECOND_FIELD
) // S
2552 int32_t parseStart
= pos
.getIndex();
2553 // It would be good to unify this with the obeyCount logic below,
2554 // but that's going to be difficult.
2555 const UnicodeString
* src
;
2557 UBool parsedNumericLeapMonth
= FALSE
;
2558 if (numericLeapMonthFormatter
!= NULL
&& (patternCharIndex
== UDAT_MONTH_FIELD
|| patternCharIndex
== UDAT_STANDALONE_MONTH_FIELD
)) {
2560 Formattable
* args
= numericLeapMonthFormatter
->parse(text
, pos
, argCount
);
2561 if (args
!= NULL
&& argCount
== 1 && pos
.getIndex() > parseStart
&& args
[0].isNumeric()) {
2562 parsedNumericLeapMonth
= TRUE
;
2563 number
.setLong(args
[0].getLong());
2564 cal
.set(UCAL_IS_LEAP_MONTH
, 1);
2567 pos
.setIndex(parseStart
);
2568 cal
.set(UCAL_IS_LEAP_MONTH
, 0);
2572 if (!parsedNumericLeapMonth
) {
2574 if ((start
+count
) > text
.length()) {
2578 text
.extractBetween(0, start
+ count
, temp
);
2584 parseInt(*src
, number
, pos
, allowNegative
,currentNumberFormat
);
2587 int32_t txtLoc
= pos
.getIndex();
2589 if (txtLoc
> parseStart
) {
2590 value
= number
.getLong();
2593 // suffix processing
2595 txtLoc
= checkIntSuffix(text
, txtLoc
, patLoc
+1, TRUE
);
2596 if (txtLoc
!= pos
.getIndex()) {
2601 txtLoc
= checkIntSuffix(text
, txtLoc
, patLoc
+1, FALSE
);
2604 // Check the range of the value
2606 int32_t bias
= gFieldRangeBias
[patternCharIndex
];
2607 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
|| value
< cal
.getMinimum(field
) + bias
)) {
2611 int32_t bias
= gFieldRangeBiasLenient
[patternCharIndex
];
2612 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
)) {
2617 pos
.setIndex(txtLoc
);
2621 // Make sure that we got a number if
2622 // we want one, and didn't get one
2623 // if we don't want one.
2624 switch (patternCharIndex
) {
2625 case UDAT_HOUR_OF_DAY1_FIELD
:
2626 case UDAT_HOUR_OF_DAY0_FIELD
:
2627 case UDAT_HOUR1_FIELD
:
2628 case UDAT_HOUR0_FIELD
:
2629 // special range check for hours:
2630 if (value
< 0 || value
> 24) {
2634 // fall through to gotNumber check
2636 case UDAT_YEAR_FIELD
:
2637 case UDAT_YEAR_WOY_FIELD
:
2638 case UDAT_FRACTIONAL_SECOND_FIELD
:
2639 // these must be a number
2647 // we check the rest of the fields below.
2651 switch (patternCharIndex
) {
2652 case UDAT_ERA_FIELD
:
2653 if (isChineseCalendar
) {
2657 cal
.set(UCAL_ERA
, value
);
2658 return pos
.getIndex();
2661 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fNarrowEras
, fSymbols
->fNarrowErasCount
, NULL
, cal
);
2662 } else if (count
== 4) {
2663 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fEraNames
, fSymbols
->fEraNamesCount
, NULL
, cal
);
2665 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fEras
, fSymbols
->fErasCount
, NULL
, cal
);
2668 // check return position, if it equals -start, then matchString error
2669 // special case the return code so we don't necessarily fail out until we
2670 // verify no year information also
2676 case UDAT_YEAR_FIELD
:
2677 // If there are 3 or more YEAR pattern characters, this indicates
2678 // that the year value is to be treated literally, without any
2679 // two-digit year adjustments (e.g., from "01" to 2001). Otherwise
2680 // we made adjustments to place the 2-digit year in the proper
2681 // century, for parsed strings from "00" to "99". Any other string
2682 // is treated literally: "2250", "-1", "1", "002".
2683 if (fDateOverride
.compare(hebr
)==0 && value
< 1000) {
2684 value
+= HEBREW_CAL_CUR_MILLENIUM_START_YEAR
;
2685 } else if ((pos
.getIndex() - start
) == 2 && !isChineseCalendar
2686 && u_isdigit(text
.charAt(start
))
2687 && u_isdigit(text
.charAt(start
+1)))
2689 // only adjust year for patterns less than 3.
2691 // Assume for example that the defaultCenturyStart is 6/18/1903.
2692 // This means that two-digit years will be forced into the range
2693 // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
2694 // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
2695 // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
2696 // other fields specify a date before 6/18, or 1903 if they specify a
2697 // date afterwards. As a result, 03 is an ambiguous year. All other
2698 // two-digit years are unambiguous.
2699 if(fHaveDefaultCentury
) { // check if this formatter even has a pivot year
2700 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
2701 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
2702 value
+= (fDefaultCenturyStartYear
/100)*100 +
2703 (value
< ambiguousTwoDigitYear
? 100 : 0);
2707 cal
.set(UCAL_YEAR
, value
);
2709 // Delayed checking for adjustment of Hebrew month numbers in non-leap years.
2710 if (saveHebrewMonth
>= 0) {
2711 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
2712 if (!hc
->isLeapYear(value
) && saveHebrewMonth
>= 6) {
2713 cal
.set(UCAL_MONTH
,saveHebrewMonth
);
2715 cal
.set(UCAL_MONTH
,saveHebrewMonth
-1);
2717 saveHebrewMonth
= -1;
2719 return pos
.getIndex();
2721 case UDAT_YEAR_WOY_FIELD
:
2722 // Comment is the same as for UDAT_Year_FIELDs - look above
2723 if (fDateOverride
.compare(hebr
)==0 && value
< 1000) {
2724 value
+= HEBREW_CAL_CUR_MILLENIUM_START_YEAR
;
2725 } else if ((pos
.getIndex() - start
) == 2
2726 && u_isdigit(text
.charAt(start
))
2727 && u_isdigit(text
.charAt(start
+1))
2728 && fHaveDefaultCentury
)
2730 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
2731 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
2732 value
+= (fDefaultCenturyStartYear
/100)*100 +
2733 (value
< ambiguousTwoDigitYear
? 100 : 0);
2735 cal
.set(UCAL_YEAR_WOY
, value
);
2736 return pos
.getIndex();
2738 case UDAT_YEAR_NAME_FIELD
:
2739 if (fSymbols
->fShortYearNames
!= NULL
) {
2740 int32_t newStart
= matchString(text
, start
, UCAL_YEAR
, fSymbols
->fShortYearNames
, fSymbols
->fShortYearNamesCount
, NULL
, cal
);
2745 if (gotNumber
&& (lenient
|| value
> fSymbols
->fShortYearNamesCount
)) {
2746 cal
.set(UCAL_YEAR
, value
);
2747 return pos
.getIndex();
2751 case UDAT_MONTH_FIELD
:
2752 case UDAT_STANDALONE_MONTH_FIELD
:
2753 if (gotNumber
) // i.e., M or MM.
2755 // When parsing month numbers from the Hebrew Calendar, we might need to adjust the month depending on whether
2756 // 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
2757 // the year is parsed.
2758 if (!strcmp(cal
.getType(),"hebrew")) {
2759 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
2760 if (cal
.isSet(UCAL_YEAR
)) {
2761 UErrorCode status
= U_ZERO_ERROR
;
2762 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6) {
2763 cal
.set(UCAL_MONTH
, value
);
2765 cal
.set(UCAL_MONTH
, value
- 1);
2768 saveHebrewMonth
= value
;
2771 // Don't want to parse the month if it is a string
2772 // while pattern uses numeric style: M/MM, L/LL
2773 // [We computed 'value' above.]
2774 cal
.set(UCAL_MONTH
, value
- 1);
2776 return pos
.getIndex();
2778 // count >= 3 // i.e., MMM/MMMM, LLL/LLLL
2779 // Want to be able to parse both short and long forms.
2780 // Try count == 4 first:
2781 UnicodeString
* wideMonthPat
= NULL
;
2782 UnicodeString
* shortMonthPat
= NULL
;
2783 if (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
) {
2784 if (patternCharIndex
==UDAT_MONTH_FIELD
) {
2785 wideMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatWide
];
2786 shortMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatAbbrev
];
2788 wideMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneWide
];
2789 shortMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev
];
2792 int32_t newStart
= 0;
2793 if (patternCharIndex
==UDAT_MONTH_FIELD
) {
2794 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fMonths
, fSymbols
->fMonthsCount
, wideMonthPat
, cal
); // try MMMM
2798 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fShortMonths
, fSymbols
->fShortMonthsCount
, shortMonthPat
, cal
); // try MMM
2800 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fStandaloneMonths
, fSymbols
->fStandaloneMonthsCount
, wideMonthPat
, cal
); // try LLLL
2804 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fStandaloneShortMonths
, fSymbols
->fStandaloneShortMonthsCount
, shortMonthPat
, cal
); // try LLL
2806 if (newStart
> 0 || !lenient
) // currently we do not try to parse MMMMM/LLLLL: #8860
2808 // else we allowing parsing as number, below
2812 case UDAT_HOUR_OF_DAY1_FIELD
:
2813 // [We computed 'value' above.]
2814 if (value
== cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1)
2817 // fall through to set field
2819 case UDAT_HOUR_OF_DAY0_FIELD
:
2820 cal
.set(UCAL_HOUR_OF_DAY
, value
);
2821 return pos
.getIndex();
2823 case UDAT_FRACTIONAL_SECOND_FIELD
:
2824 // Fractional seconds left-justify
2825 i
= pos
.getIndex() - start
;
2839 cal
.set(UCAL_MILLISECOND
, value
);
2840 return pos
.getIndex();
2842 case UDAT_DOW_LOCAL_FIELD
:
2843 if (gotNumber
) // i.e., e or ee
2845 // [We computed 'value' above.]
2846 cal
.set(UCAL_DOW_LOCAL
, value
);
2847 return pos
.getIndex();
2849 // else for eee-eeeee fall through to handling of EEE-EEEEE
2850 // fall through, do not break here
2851 case UDAT_DAY_OF_WEEK_FIELD
:
2853 // Want to be able to parse both short and long forms.
2854 // Try count == 4 (EEEE) wide first:
2855 int32_t newStart
= 0;
2856 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2857 fSymbols
->fWeekdays
, fSymbols
->fWeekdaysCount
, NULL
, cal
)) > 0)
2859 // EEEE wide failed, now try EEE abbreviated
2860 else if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2861 fSymbols
->fShortWeekdays
, fSymbols
->fShortWeekdaysCount
, NULL
, cal
)) > 0)
2863 // EEE abbreviated failed, now try EEEEEE short
2864 else if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2865 fSymbols
->fShorterWeekdays
, fSymbols
->fShorterWeekdaysCount
, NULL
, cal
)) > 0)
2867 // EEEEEE short failed, now try EEEEE narrow
2868 else if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2869 fSymbols
->fNarrowWeekdays
, fSymbols
->fNarrowWeekdaysCount
, NULL
, cal
)) > 0)
2871 else if (!lenient
|| patternCharIndex
== UDAT_DAY_OF_WEEK_FIELD
)
2873 // else we allowing parsing as number, below
2877 case UDAT_STANDALONE_DAY_FIELD
:
2879 if (gotNumber
) // c or cc
2881 // [We computed 'value' above.]
2882 cal
.set(UCAL_DOW_LOCAL
, value
);
2883 return pos
.getIndex();
2885 // Want to be able to parse both short and long forms.
2886 // Try count == 4 (cccc) first:
2887 int32_t newStart
= 0;
2888 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2889 fSymbols
->fStandaloneWeekdays
, fSymbols
->fStandaloneWeekdaysCount
, NULL
, cal
)) > 0)
2891 else if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2892 fSymbols
->fStandaloneShortWeekdays
, fSymbols
->fStandaloneShortWeekdaysCount
, NULL
, cal
)) > 0)
2894 else if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2895 fSymbols
->fStandaloneShorterWeekdays
, fSymbols
->fStandaloneShorterWeekdaysCount
, NULL
, cal
)) > 0)
2899 // else we allowing parsing as number, below
2903 case UDAT_AM_PM_FIELD
:
2904 return matchString(text
, start
, UCAL_AM_PM
, fSymbols
->fAmPms
, fSymbols
->fAmPmsCount
, NULL
, cal
);
2906 case UDAT_HOUR1_FIELD
:
2907 // [We computed 'value' above.]
2908 if (value
== cal
.getLeastMaximum(UCAL_HOUR
)+1)
2911 // fall through to set field
2913 case UDAT_HOUR0_FIELD
:
2914 cal
.set(UCAL_HOUR
, value
);
2915 return pos
.getIndex();
2917 case UDAT_QUARTER_FIELD
:
2918 if (gotNumber
) // i.e., Q or QQ.
2920 // Don't want to parse the month if it is a string
2921 // while pattern uses numeric style: Q or QQ.
2922 // [We computed 'value' above.]
2923 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
2924 return pos
.getIndex();
2926 // count >= 3 // i.e., QQQ or QQQQ
2927 // Want to be able to parse both short and long forms.
2928 // Try count == 4 first:
2929 int32_t newStart
= 0;
2931 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
2932 fSymbols
->fQuarters
, fSymbols
->fQuartersCount
, cal
)) > 0)
2934 else if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
2935 fSymbols
->fShortQuarters
, fSymbols
->fShortQuartersCount
, cal
)) > 0)
2939 // else we allowing parsing as number, below
2943 case UDAT_STANDALONE_QUARTER_FIELD
:
2944 if (gotNumber
) // i.e., q or qq.
2946 // Don't want to parse the month if it is a string
2947 // while pattern uses numeric style: q or q.
2948 // [We computed 'value' above.]
2949 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
2950 return pos
.getIndex();
2952 // count >= 3 // i.e., qqq or qqqq
2953 // Want to be able to parse both short and long forms.
2954 // Try count == 4 first:
2955 int32_t newStart
= 0;
2957 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
2958 fSymbols
->fStandaloneQuarters
, fSymbols
->fStandaloneQuartersCount
, cal
)) > 0)
2960 else if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
2961 fSymbols
->fStandaloneShortQuarters
, fSymbols
->fStandaloneShortQuartersCount
, cal
)) > 0)
2965 // else we allowing parsing as number, below
2969 case UDAT_TIMEZONE_FIELD
: // 'z'
2971 UTimeZoneFormatTimeType tzTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
2972 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_SPECIFIC_SHORT
: UTZFMT_STYLE_SPECIFIC_LONG
;
2973 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzParseOptions
, &tzTimeType
);
2975 ((SimpleDateFormat
*)this)->tztype
= tzTimeType
;
2976 cal
.adoptTimeZone(tz
);
2977 return pos
.getIndex();
2981 case UDAT_TIMEZONE_RFC_FIELD
: // 'Z'
2983 UTimeZoneFormatTimeType tzTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
2984 UTimeZoneFormatStyle style
= (count
< 4) ?
2985 UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
: ((count
== 5) ? UTZFMT_STYLE_ISO_EXTENDED_FULL
: UTZFMT_STYLE_LOCALIZED_GMT
);
2986 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, &tzTimeType
);
2988 ((SimpleDateFormat
*)this)->tztype
= tzTimeType
;
2989 cal
.adoptTimeZone(tz
);
2990 return pos
.getIndex();
2994 case UDAT_TIMEZONE_GENERIC_FIELD
: // 'v'
2996 UTimeZoneFormatTimeType tzTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
2997 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_GENERIC_SHORT
: UTZFMT_STYLE_GENERIC_LONG
;
2998 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzParseOptions
, &tzTimeType
);
3000 ((SimpleDateFormat
*)this)->tztype
= tzTimeType
;
3001 cal
.adoptTimeZone(tz
);
3002 return pos
.getIndex();
3006 case UDAT_TIMEZONE_SPECIAL_FIELD
: // 'V'
3008 UTimeZoneFormatTimeType tzTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
3009 UTimeZoneFormatStyle style
;
3012 style
= UTZFMT_STYLE_ZONE_ID_SHORT
;
3015 style
= UTZFMT_STYLE_ZONE_ID
;
3018 style
= UTZFMT_STYLE_EXEMPLAR_LOCATION
;
3021 style
= UTZFMT_STYLE_GENERIC_LOCATION
;
3024 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, &tzTimeType
);
3026 ((SimpleDateFormat
*)this)->tztype
= tzTimeType
;
3027 cal
.adoptTimeZone(tz
);
3028 return pos
.getIndex();
3032 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
: // 'O'
3034 UTimeZoneFormatTimeType tzTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
3035 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_LOCALIZED_GMT_SHORT
: UTZFMT_STYLE_LOCALIZED_GMT
;
3036 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, &tzTimeType
);
3038 ((SimpleDateFormat
*)this)->tztype
= tzTimeType
;
3039 cal
.adoptTimeZone(tz
);
3040 return pos
.getIndex();
3044 case UDAT_TIMEZONE_ISO_FIELD
: // 'X'
3046 UTimeZoneFormatTimeType tzTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
3047 UTimeZoneFormatStyle style
;
3050 style
= UTZFMT_STYLE_ISO_BASIC_SHORT
;
3053 style
= UTZFMT_STYLE_ISO_BASIC_FIXED
;
3056 style
= UTZFMT_STYLE_ISO_EXTENDED_FIXED
;
3059 style
= UTZFMT_STYLE_ISO_BASIC_FULL
;
3062 style
= UTZFMT_STYLE_ISO_EXTENDED_FULL
;
3065 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, &tzTimeType
);
3067 ((SimpleDateFormat
*)this)->tztype
= tzTimeType
;
3068 cal
.adoptTimeZone(tz
);
3069 return pos
.getIndex();
3073 case UDAT_TIMEZONE_ISO_LOCAL_FIELD
: // 'x'
3075 UTimeZoneFormatTimeType tzTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
3076 UTimeZoneFormatStyle style
;
3079 style
= UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
;
3082 style
= UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
;
3085 style
= UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
;
3088 style
= UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
;
3091 style
= UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
;
3094 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, &tzTimeType
);
3096 ((SimpleDateFormat
*)this)->tztype
= tzTimeType
;
3097 cal
.adoptTimeZone(tz
);
3098 return pos
.getIndex();
3104 // Handle "generic" fields
3105 // this is now handled below, outside the switch block
3108 // Handle "generic" fields:
3109 // switch default case now handled here (outside switch block) to allow
3110 // parsing of some string fields as digits for lenient case
3112 int32_t parseStart
= pos
.getIndex();
3113 const UnicodeString
* src
;
3115 if ((start
+count
) > text
.length()) {
3118 text
.extractBetween(0, start
+ count
, temp
);
3123 parseInt(*src
, number
, pos
, allowNegative
,currentNumberFormat
);
3124 if (pos
.getIndex() != parseStart
) {
3125 int32_t value
= number
.getLong();
3127 // Don't need suffix processing here (as in number processing at the beginning of the function);
3128 // the new fields being handled as numeric values (month, weekdays, quarters) should not have suffixes.
3130 // Check the range of the value
3132 int32_t bias
= gFieldRangeBias
[patternCharIndex
];
3133 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
|| value
< cal
.getMinimum(field
) + bias
)) {
3137 int32_t bias
= gFieldRangeBiasLenient
[patternCharIndex
];
3138 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
)) {
3143 // For the following, need to repeat some of the "if (gotNumber)" code above:
3144 // UDAT_[STANDALONE_]MONTH_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_STANDALONE_DAY_FIELD,
3145 // UDAT_[STANDALONE_]QUARTER_FIELD
3146 switch (patternCharIndex
) {
3147 case UDAT_MONTH_FIELD
:
3148 // See notes under UDAT_MONTH_FIELD case above
3149 if (!strcmp(cal
.getType(),"hebrew")) {
3150 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
3151 if (cal
.isSet(UCAL_YEAR
)) {
3152 UErrorCode status
= U_ZERO_ERROR
;
3153 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6) {
3154 cal
.set(UCAL_MONTH
, value
);
3156 cal
.set(UCAL_MONTH
, value
- 1);
3159 saveHebrewMonth
= value
;
3162 cal
.set(UCAL_MONTH
, value
- 1);
3165 case UDAT_STANDALONE_MONTH_FIELD
:
3166 cal
.set(UCAL_MONTH
, value
- 1);
3168 case UDAT_DOW_LOCAL_FIELD
:
3169 case UDAT_STANDALONE_DAY_FIELD
:
3170 cal
.set(UCAL_DOW_LOCAL
, value
);
3172 case UDAT_QUARTER_FIELD
:
3173 case UDAT_STANDALONE_QUARTER_FIELD
:
3174 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
3177 cal
.set(field
, value
);
3180 return pos
.getIndex();
3186 * Parse an integer using fNumberFormat. This method is semantically
3187 * const, but actually may modify fNumberFormat.
3189 void SimpleDateFormat::parseInt(const UnicodeString
& text
,
3190 Formattable
& number
,
3192 UBool allowNegative
,
3193 NumberFormat
*fmt
) const {
3194 parseInt(text
, number
, -1, pos
, allowNegative
,fmt
);
3198 * Parse an integer using fNumberFormat up to maxDigits.
3200 void SimpleDateFormat::parseInt(const UnicodeString
& text
,
3201 Formattable
& number
,
3204 UBool allowNegative
,
3205 NumberFormat
*fmt
) const {
3206 UnicodeString oldPrefix
;
3207 DecimalFormat
* df
= NULL
;
3208 if (!allowNegative
&& (df
= dynamic_cast<DecimalFormat
*>(fmt
)) != NULL
) {
3209 df
->getNegativePrefix(oldPrefix
);
3210 df
->setNegativePrefix(UnicodeString(TRUE
, SUPPRESS_NEGATIVE_PREFIX
, -1));
3212 int32_t oldPos
= pos
.getIndex();
3213 fmt
->parse(text
, number
, pos
);
3215 df
->setNegativePrefix(oldPrefix
);
3218 if (maxDigits
> 0) {
3219 // adjust the result to fit into
3220 // the maxDigits and move the position back
3221 int32_t nDigits
= pos
.getIndex() - oldPos
;
3222 if (nDigits
> maxDigits
) {
3223 int32_t val
= number
.getLong();
3224 nDigits
-= maxDigits
;
3225 while (nDigits
> 0) {
3229 pos
.setIndex(oldPos
+ maxDigits
);
3230 number
.setLong(val
);
3235 //----------------------------------------------------------------------
3237 void SimpleDateFormat::translatePattern(const UnicodeString
& originalPattern
,
3238 UnicodeString
& translatedPattern
,
3239 const UnicodeString
& from
,
3240 const UnicodeString
& to
,
3243 // run through the pattern and convert any pattern symbols from the version
3244 // in "from" to the corresponding character ion "to". This code takes
3245 // quoted strings into account (it doesn't try to translate them), and it signals
3246 // an error if a particular "pattern character" doesn't appear in "from".
3247 // Depending on the values of "from" and "to" this can convert from generic
3248 // to localized patterns or localized to generic.
3249 if (U_FAILURE(status
))
3252 translatedPattern
.remove();
3253 UBool inQuote
= FALSE
;
3254 for (int32_t i
= 0; i
< originalPattern
.length(); ++i
) {
3255 UChar c
= originalPattern
[i
];
3263 else if ((c
>= 0x0061 /*'a'*/ && c
<= 0x007A) /*'z'*/
3264 || (c
>= 0x0041 /*'A'*/ && c
<= 0x005A /*'Z'*/)) {
3265 int32_t ci
= from
.indexOf(c
);
3267 status
= U_INVALID_FORMAT_ERROR
;
3273 translatedPattern
+= c
;
3276 status
= U_INVALID_FORMAT_ERROR
;
3281 //----------------------------------------------------------------------
3284 SimpleDateFormat::toPattern(UnicodeString
& result
) const
3290 //----------------------------------------------------------------------
3293 SimpleDateFormat::toLocalizedPattern(UnicodeString
& result
,
3294 UErrorCode
& status
) const
3296 translatePattern(fPattern
, result
,
3297 UnicodeString(DateFormatSymbols::getPatternUChars()),
3298 fSymbols
->fLocalPatternChars
, status
);
3302 //----------------------------------------------------------------------
3305 SimpleDateFormat::applyPattern(const UnicodeString
& pattern
)
3310 //----------------------------------------------------------------------
3313 SimpleDateFormat::applyLocalizedPattern(const UnicodeString
& pattern
,
3316 translatePattern(pattern
, fPattern
,
3317 fSymbols
->fLocalPatternChars
,
3318 UnicodeString(DateFormatSymbols::getPatternUChars()), status
);
3321 //----------------------------------------------------------------------
3323 const DateFormatSymbols
*
3324 SimpleDateFormat::getDateFormatSymbols() const
3329 //----------------------------------------------------------------------
3332 SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols
* newFormatSymbols
)
3335 fSymbols
= newFormatSymbols
;
3338 //----------------------------------------------------------------------
3340 SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols
& newFormatSymbols
)
3343 fSymbols
= new DateFormatSymbols(newFormatSymbols
);
3346 //----------------------------------------------------------------------
3347 const TimeZoneFormat
*
3348 SimpleDateFormat::getTimeZoneFormat(void) const {
3349 return (const TimeZoneFormat
*)tzFormat();
3352 //----------------------------------------------------------------------
3354 SimpleDateFormat::adoptTimeZoneFormat(TimeZoneFormat
* timeZoneFormatToAdopt
)
3356 delete fTimeZoneFormat
;
3357 fTimeZoneFormat
= timeZoneFormatToAdopt
;
3360 //----------------------------------------------------------------------
3362 SimpleDateFormat::setTimeZoneFormat(const TimeZoneFormat
& newTimeZoneFormat
)
3364 delete fTimeZoneFormat
;
3365 fTimeZoneFormat
= new TimeZoneFormat(newTimeZoneFormat
);
3368 //----------------------------------------------------------------------
3371 void SimpleDateFormat::adoptCalendar(Calendar
* calendarToAdopt
)
3373 UErrorCode status
= U_ZERO_ERROR
;
3374 DateFormat::adoptCalendar(calendarToAdopt
);
3377 initializeSymbols(fLocale
, fCalendar
, status
); // we need new symbols
3378 initializeDefaultCentury(); // we need a new century (possibly)
3382 //----------------------------------------------------------------------
3385 void SimpleDateFormat::setContext(UDisplayContext value
, UErrorCode
& status
)
3387 if (U_FAILURE(status
))
3389 if ( (UDisplayContextType
)((uint32_t)value
>> 8) == UDISPCTX_TYPE_CAPITALIZATION
) {
3390 fCapitalizationContext
= value
;
3392 status
= U_ILLEGAL_ARGUMENT_ERROR
;
3397 //----------------------------------------------------------------------
3400 UDisplayContext
SimpleDateFormat::getContext(UDisplayContextType type
, UErrorCode
& status
) const
3402 if (U_FAILURE(status
))
3403 return (UDisplayContext
)0;
3404 if (type
!= UDISPCTX_TYPE_CAPITALIZATION
) {
3405 status
= U_ILLEGAL_ARGUMENT_ERROR
;
3406 return (UDisplayContext
)0;
3408 return fCapitalizationContext
;
3412 //----------------------------------------------------------------------
3416 SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field
) const {
3417 return isFieldUnitIgnored(fPattern
, field
);
3422 SimpleDateFormat::isFieldUnitIgnored(const UnicodeString
& pattern
,
3423 UCalendarDateFields field
) {
3424 int32_t fieldLevel
= fgCalendarFieldToLevel
[field
];
3427 UBool inQuote
= FALSE
;
3431 for (int32_t i
= 0; i
< pattern
.length(); ++i
) {
3433 if (ch
!= prevCh
&& count
> 0) {
3434 level
= fgPatternCharToLevel
[prevCh
- PATTERN_CHAR_BASE
];
3435 // the larger the level, the smaller the field unit.
3436 if ( fieldLevel
<= level
) {
3442 if ((i
+1) < pattern
.length() && pattern
[i
+1] == QUOTE
) {
3445 inQuote
= ! inQuote
;
3448 else if ( ! inQuote
&& ((ch
>= 0x0061 /*'a'*/ && ch
<= 0x007A /*'z'*/)
3449 || (ch
>= 0x0041 /*'A'*/ && ch
<= 0x005A /*'Z'*/))) {
3456 level
= fgPatternCharToLevel
[prevCh
- PATTERN_CHAR_BASE
];
3457 if ( fieldLevel
<= level
) {
3464 //----------------------------------------------------------------------
3467 SimpleDateFormat::getSmpFmtLocale(void) const {
3471 //----------------------------------------------------------------------
3474 SimpleDateFormat::checkIntSuffix(const UnicodeString
& text
, int32_t start
,
3475 int32_t patLoc
, UBool isNegative
) const {
3478 int32_t patternMatch
;
3479 int32_t textPreMatch
;
3480 int32_t textPostMatch
;
3482 // check that we are still in range
3483 if ( (start
> text
.length()) ||
3486 (patLoc
> fPattern
.length())) {
3487 // out of range, don't advance location in text
3492 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(fNumberFormat
);
3493 if (decfmt
!= NULL
) {
3495 suf
= decfmt
->getNegativeSuffix(suf
);
3498 suf
= decfmt
->getPositiveSuffix(suf
);
3503 if (suf
.length() <= 0) {
3507 // check suffix will be encountered in the pattern
3508 patternMatch
= compareSimpleAffix(suf
,fPattern
,patLoc
);
3510 // check if a suffix will be encountered in the text
3511 textPreMatch
= compareSimpleAffix(suf
,text
,start
);
3513 // check if a suffix was encountered in the text
3514 textPostMatch
= compareSimpleAffix(suf
,text
,start
-suf
.length());
3516 // check for suffix match
3517 if ((textPreMatch
>= 0) && (patternMatch
>= 0) && (textPreMatch
== patternMatch
)) {
3520 else if ((textPostMatch
>= 0) && (patternMatch
>= 0) && (textPostMatch
== patternMatch
)) {
3521 return start
- suf
.length();
3524 // should not get here
3528 //----------------------------------------------------------------------
3531 SimpleDateFormat::compareSimpleAffix(const UnicodeString
& affix
,
3532 const UnicodeString
& input
,
3533 int32_t pos
) const {
3534 int32_t start
= pos
;
3535 for (int32_t i
=0; i
<affix
.length(); ) {
3536 UChar32 c
= affix
.char32At(i
);
3537 int32_t len
= U16_LENGTH(c
);
3538 if (PatternProps::isWhiteSpace(c
)) {
3539 // We may have a pattern like: \u200F \u0020
3540 // and input text like: \u200F \u0020
3541 // Note that U+200F and U+0020 are Pattern_White_Space but only
3542 // U+0020 is UWhiteSpace. So we have to first do a direct
3543 // match of the run of Pattern_White_Space in the pattern,
3544 // then match any extra characters.
3545 UBool literalMatch
= FALSE
;
3546 while (pos
< input
.length() &&
3547 input
.char32At(pos
) == c
) {
3548 literalMatch
= TRUE
;
3551 if (i
== affix
.length()) {
3554 c
= affix
.char32At(i
);
3555 len
= U16_LENGTH(c
);
3556 if (!PatternProps::isWhiteSpace(c
)) {
3561 // Advance over run in pattern
3562 i
= skipPatternWhiteSpace(affix
, i
);
3564 // Advance over run in input text
3565 // Must see at least one white space char in input,
3566 // unless we've already matched some characters literally.
3568 pos
= skipUWhiteSpace(input
, pos
);
3569 if (pos
== s
&& !literalMatch
) {
3573 // If we skip UWhiteSpace in the input text, we need to skip it in the pattern.
3574 // Otherwise, the previous lines may have skipped over text (such as U+00A0) that
3575 // is also in the affix.
3576 i
= skipUWhiteSpace(affix
, i
);
3578 if (pos
< input
.length() &&
3579 input
.char32At(pos
) == c
) {
3590 //----------------------------------------------------------------------
3593 SimpleDateFormat::skipPatternWhiteSpace(const UnicodeString
& text
, int32_t pos
) const {
3594 const UChar
* s
= text
.getBuffer();
3595 return (int32_t)(PatternProps::skipWhiteSpace(s
+ pos
, text
.length() - pos
) - s
);
3598 //----------------------------------------------------------------------
3601 SimpleDateFormat::skipUWhiteSpace(const UnicodeString
& text
, int32_t pos
) const {
3602 while (pos
< text
.length()) {
3603 UChar32 c
= text
.char32At(pos
);
3604 if (!u_isUWhiteSpace(c
)) {
3607 pos
+= U16_LENGTH(c
);
3612 //----------------------------------------------------------------------
3614 // Lazy TimeZoneFormat instantiation, semantically const.
3616 SimpleDateFormat::tzFormat() const {
3617 if (fTimeZoneFormat
== NULL
) {
3620 if (fTimeZoneFormat
== NULL
) {
3621 UErrorCode status
= U_ZERO_ERROR
;
3622 TimeZoneFormat
*tzfmt
= TimeZoneFormat::createInstance(fLocale
, status
);
3623 if (U_FAILURE(status
)) {
3627 const_cast<SimpleDateFormat
*>(this)->fTimeZoneFormat
= tzfmt
;
3632 return fTimeZoneFormat
;
3637 #endif /* #if !UCONFIG_NO_FORMATTING */