2 *******************************************************************************
3 * Copyright (C) 1997-2012, 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"
52 #include "patternprops.h"
63 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
67 // *****************************************************************************
68 // class SimpleDateFormat
69 // *****************************************************************************
73 static const UChar PATTERN_CHAR_BASE
= 0x40;
76 * Last-resort string to use for "GMT" when constructing time zone strings.
78 // For time zones that have no names, use strings GMT+minutes and
79 // GMT-minutes. For instance, in France the time zone is GMT+60.
80 // Also accepted are GMT+H:MM or GMT-H:MM.
81 static const UChar gGmt
[] = {0x0047, 0x004D, 0x0054, 0x0000}; // "GMT"
82 static const UChar gGmtPlus
[] = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+"
83 static const UChar gGmtMinus
[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-"
84 static const UChar gDefGmtPat
[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
85 static const UChar gDefGmtNegHmsPat
[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */
86 static const UChar gDefGmtNegHmPat
[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */
87 static const UChar gDefGmtPosHmsPat
[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */
88 static const UChar gDefGmtPosHmPat
[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */
89 static const UChar gUt
[] = {0x0055, 0x0054, 0x0000}; // "UT"
90 static const UChar gUtc
[] = {0x0055, 0x0054, 0x0043, 0x0000}; // "UT"
92 typedef enum GmtPatSize
{
103 // Stuff needed for numbering system overrides
105 typedef enum OvrStrType
{
111 static const UDateFormatField kDateFields
[] = {
115 UDAT_DAY_OF_YEAR_FIELD
,
116 UDAT_DAY_OF_WEEK_IN_MONTH_FIELD
,
117 UDAT_WEEK_OF_YEAR_FIELD
,
118 UDAT_WEEK_OF_MONTH_FIELD
,
120 UDAT_EXTENDED_YEAR_FIELD
,
121 UDAT_JULIAN_DAY_FIELD
,
122 UDAT_STANDALONE_DAY_FIELD
,
123 UDAT_STANDALONE_MONTH_FIELD
,
125 UDAT_STANDALONE_QUARTER_FIELD
,
126 UDAT_YEAR_NAME_FIELD
};
127 static const int8_t kDateFieldsCount
= 15;
129 static const UDateFormatField kTimeFields
[] = {
130 UDAT_HOUR_OF_DAY1_FIELD
,
131 UDAT_HOUR_OF_DAY0_FIELD
,
134 UDAT_FRACTIONAL_SECOND_FIELD
,
137 UDAT_MILLISECONDS_IN_DAY_FIELD
,
138 UDAT_TIMEZONE_RFC_FIELD
};
139 static const int8_t kTimeFieldsCount
= 9;
142 // This is a pattern-of-last-resort used when we can't load a usable pattern out
144 static const UChar gDefaultPattern
[] =
146 0x79, 0x79, 0x79, 0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0
147 }; /* "yyyyMMdd hh:mm a" */
149 // This prefix is designed to NEVER MATCH real text, in order to
150 // suppress the parsing of negative numbers. Adjust as needed (if
151 // this becomes valid Unicode).
152 static const UChar SUPPRESS_NEGATIVE_PREFIX
[] = {0xAB00, 0};
155 * These are the tags we expect to see in normal resource bundle files associated
158 static const char gDateTimePatternsTag
[]="DateTimePatterns";
160 static const UChar gEtcUTC
[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x54, 0x43, 0x00}; // "Etc/UTC"
161 static const UChar QUOTE
= 0x27; // Single quote
164 * The field range check bias for each UDateFormatField.
165 * The bias is added to the minimum and maximum values
166 * before they are compared to the parsed number.
167 * For example, the calendar stores zero-based month numbers
168 * but the parsed month numbers start at 1, so the bias is 1.
170 * A value of -1 means that the value is not checked.
172 static const int32_t gFieldRangeBias
[] = {
173 -1, // 'G' - UDAT_ERA_FIELD
174 -1, // 'y' - UDAT_YEAR_FIELD
175 1, // 'M' - UDAT_MONTH_FIELD
176 0, // 'd' - UDAT_DATE_FIELD
177 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
178 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
179 0, // 'm' - UDAT_MINUTE_FIELD
180 0, // 's' - UDAT_SECOND_FIELD
181 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
182 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
183 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
184 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
185 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
186 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
187 -1, // 'a' - UDAT_AM_PM_FIELD
188 -1, // 'h' - UDAT_HOUR1_FIELD
189 -1, // 'K' - UDAT_HOUR0_FIELD
190 -1, // 'z' - UDAT_TIMEZONE_FIELD
191 -1, // 'Y' - UDAT_YEAR_WOY_FIELD
192 -1, // 'e' - UDAT_DOW_LOCAL_FIELD
193 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
194 -1, // 'g' - UDAT_JULIAN_DAY_FIELD
195 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
196 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
197 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
198 0, // 'c' - UDAT_STANDALONE_DAY_FIELD
199 1, // 'L' - UDAT_STANDALONE_MONTH_FIELD
200 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
201 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
202 -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
203 -1 // 'U' - UDAT_YEAR_NAME_FIELD
205 // A slightly looser range check for lenient parsing
206 static const int32_t gFieldRangeBiasLenient
[] = {
207 -1, // 'G' - UDAT_ERA_FIELD
208 -1, // 'y' - UDAT_YEAR_FIELD
209 8, // 'M' - UDAT_MONTH_FIELD (allow calendar max + 7, e.g. 19 for grego 1-based month)
210 18, // 'd' - UDAT_DATE_FIELD (allow calendar max + 18, e.g. 49 for grego; tests require at least 40 for grego)
211 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
212 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
213 40, // 'm' - UDAT_MINUTE_FIELD (allow calendar max + 40, e.g. 99)
214 40, // 's' - UDAT_SECOND_FIELD (allow calendar max + 40, e.g. 99)
215 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
216 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
217 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
218 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
219 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
220 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
221 -1, // 'a' - UDAT_AM_PM_FIELD
222 -1, // 'h' - UDAT_HOUR1_FIELD
223 -1, // 'K' - UDAT_HOUR0_FIELD
224 -1, // 'z' - UDAT_TIMEZONE_FIELD
225 -1, // 'Y' - UDAT_YEAR_WOY_FIELD
226 -1, // 'e' - UDAT_DOW_LOCAL_FIELD
227 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
228 -1, // 'g' - UDAT_JULIAN_DAY_FIELD
229 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
230 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
231 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
232 18, // 'c' - UDAT_STANDALONE_DAY_FIELD (allow calendar max + 18, e.g. 49 for grego)
233 8, // 'L' - UDAT_STANDALONE_MONTH_FIELD (allow calendar max + 7, e.g. 19 for grego 1-based month)
234 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
235 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
236 -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
237 -1 // 'U' - UDAT_YEAR_NAME_FIELD
242 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat
)
244 //----------------------------------------------------------------------
246 SimpleDateFormat::~SimpleDateFormat()
249 if (fNumberFormatters
) {
250 uprv_free(fNumberFormatters
);
252 if (fTimeZoneFormat
) {
253 delete fTimeZoneFormat
;
256 while (fOverrideList
) {
257 NSOverride
*cur
= fOverrideList
;
258 fOverrideList
= cur
->next
;
264 //----------------------------------------------------------------------
266 SimpleDateFormat::SimpleDateFormat(UErrorCode
& status
)
267 : fLocale(Locale::getDefault()),
269 fTimeZoneFormat(NULL
),
270 fNumberFormatters(NULL
),
272 fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN
)
274 construct(kShort
, (EStyle
) (kShort
+ kDateOffset
), fLocale
, status
);
275 initializeDefaultCentury();
278 //----------------------------------------------------------------------
280 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
283 fLocale(Locale::getDefault()),
285 fTimeZoneFormat(NULL
),
286 fNumberFormatters(NULL
),
288 fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN
)
290 fDateOverride
.setToBogus();
291 fTimeOverride
.setToBogus();
292 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
293 initialize(fLocale
, status
);
294 initializeDefaultCentury();
297 //----------------------------------------------------------------------
299 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
300 const UnicodeString
& override
,
303 fLocale(Locale::getDefault()),
305 fTimeZoneFormat(NULL
),
306 fNumberFormatters(NULL
),
308 fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN
)
310 fDateOverride
.setTo(override
);
311 fTimeOverride
.setToBogus();
312 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
313 initialize(fLocale
, status
);
314 initializeDefaultCentury();
316 processOverrideString(fLocale
,override
,kOvrStrBoth
,status
);
320 //----------------------------------------------------------------------
322 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
323 const Locale
& locale
,
327 fTimeZoneFormat(NULL
),
328 fNumberFormatters(NULL
),
330 fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN
)
333 fDateOverride
.setToBogus();
334 fTimeOverride
.setToBogus();
336 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
337 initialize(fLocale
, status
);
338 initializeDefaultCentury();
341 //----------------------------------------------------------------------
343 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
344 const UnicodeString
& override
,
345 const Locale
& locale
,
349 fTimeZoneFormat(NULL
),
350 fNumberFormatters(NULL
),
352 fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN
)
355 fDateOverride
.setTo(override
);
356 fTimeOverride
.setToBogus();
358 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
359 initialize(fLocale
, status
);
360 initializeDefaultCentury();
362 processOverrideString(locale
,override
,kOvrStrBoth
,status
);
366 //----------------------------------------------------------------------
368 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
369 DateFormatSymbols
* symbolsToAdopt
,
372 fLocale(Locale::getDefault()),
373 fSymbols(symbolsToAdopt
),
374 fTimeZoneFormat(NULL
),
375 fNumberFormatters(NULL
),
377 fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN
)
380 fDateOverride
.setToBogus();
381 fTimeOverride
.setToBogus();
383 initializeCalendar(NULL
,fLocale
,status
);
384 initialize(fLocale
, status
);
385 initializeDefaultCentury();
388 //----------------------------------------------------------------------
390 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
391 const DateFormatSymbols
& symbols
,
394 fLocale(Locale::getDefault()),
395 fSymbols(new DateFormatSymbols(symbols
)),
396 fTimeZoneFormat(NULL
),
397 fNumberFormatters(NULL
),
399 fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN
)
402 fDateOverride
.setToBogus();
403 fTimeOverride
.setToBogus();
405 initializeCalendar(NULL
, fLocale
, status
);
406 initialize(fLocale
, status
);
407 initializeDefaultCentury();
410 //----------------------------------------------------------------------
412 // Not for public consumption; used by DateFormat
413 SimpleDateFormat::SimpleDateFormat(EStyle timeStyle
,
415 const Locale
& locale
,
419 fTimeZoneFormat(NULL
),
420 fNumberFormatters(NULL
),
422 fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN
)
424 construct(timeStyle
, dateStyle
, fLocale
, status
);
425 if(U_SUCCESS(status
)) {
426 initializeDefaultCentury();
430 //----------------------------------------------------------------------
433 * Not for public consumption; used by DateFormat. This constructor
434 * never fails. If the resource data is not available, it uses the
435 * the last resort symbols.
437 SimpleDateFormat::SimpleDateFormat(const Locale
& locale
,
439 : fPattern(gDefaultPattern
),
442 fTimeZoneFormat(NULL
),
443 fNumberFormatters(NULL
),
445 fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN
)
447 if (U_FAILURE(status
)) return;
448 initializeSymbols(fLocale
, initializeCalendar(NULL
, fLocale
, status
),status
);
449 if (U_FAILURE(status
))
451 status
= U_ZERO_ERROR
;
453 // This constructor doesn't fail; it uses last resort data
454 fSymbols
= new DateFormatSymbols(status
);
457 status
= U_MEMORY_ALLOCATION_ERROR
;
462 fDateOverride
.setToBogus();
463 fTimeOverride
.setToBogus();
465 initialize(fLocale
, status
);
466 if(U_SUCCESS(status
)) {
467 initializeDefaultCentury();
471 //----------------------------------------------------------------------
473 SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat
& other
)
475 fLocale(other
.fLocale
),
477 fTimeZoneFormat(NULL
),
478 fNumberFormatters(NULL
),
480 fDefaultCapitalizationContext(UDAT_CONTEXT_UNKNOWN
)
485 //----------------------------------------------------------------------
487 SimpleDateFormat
& SimpleDateFormat::operator=(const SimpleDateFormat
& other
)
489 if (this == &other
) {
492 DateFormat::operator=(other
);
498 fSymbols
= new DateFormatSymbols(*other
.fSymbols
);
500 fDefaultCenturyStart
= other
.fDefaultCenturyStart
;
501 fDefaultCenturyStartYear
= other
.fDefaultCenturyStartYear
;
502 fHaveDefaultCentury
= other
.fHaveDefaultCentury
;
504 fPattern
= other
.fPattern
;
506 // TimeZoneFormat in ICU4C only depends on a locale for now
507 if (fLocale
!= other
.fLocale
) {
508 delete fTimeZoneFormat
;
509 fTimeZoneFormat
= NULL
; // forces lazy instantiation with the other locale
510 fLocale
= other
.fLocale
;
513 fDefaultCapitalizationContext
= other
.fDefaultCapitalizationContext
;
518 //----------------------------------------------------------------------
521 SimpleDateFormat::clone() const
523 return new SimpleDateFormat(*this);
526 //----------------------------------------------------------------------
529 SimpleDateFormat::operator==(const Format
& other
) const
531 if (DateFormat::operator==(other
)) {
532 // DateFormat::operator== guarantees following cast is safe
533 SimpleDateFormat
* that
= (SimpleDateFormat
*)&other
;
534 return (fPattern
== that
->fPattern
&&
535 fSymbols
!= NULL
&& // Check for pathological object
536 that
->fSymbols
!= NULL
&& // Check for pathological object
537 *fSymbols
== *that
->fSymbols
&&
538 fHaveDefaultCentury
== that
->fHaveDefaultCentury
&&
539 fDefaultCenturyStart
== that
->fDefaultCenturyStart
&&
540 fDefaultCapitalizationContext
== that
->fDefaultCapitalizationContext
);
545 //----------------------------------------------------------------------
547 void SimpleDateFormat::construct(EStyle timeStyle
,
549 const Locale
& locale
,
552 // called by several constructors to load pattern data from the resources
553 if (U_FAILURE(status
)) return;
555 // We will need the calendar to know what type of symbols to load.
556 initializeCalendar(NULL
, locale
, status
);
557 if (U_FAILURE(status
)) return;
559 CalendarData
calData(locale
, fCalendar
?fCalendar
->getType():NULL
, status
);
560 UResourceBundle
*dateTimePatterns
= calData
.getByKey(gDateTimePatternsTag
, status
);
561 UResourceBundle
*currentBundle
;
563 if (U_FAILURE(status
)) return;
565 if (ures_getSize(dateTimePatterns
) <= kDateTime
)
567 status
= U_INVALID_FORMAT_ERROR
;
571 setLocaleIDs(ures_getLocaleByType(dateTimePatterns
, ULOC_VALID_LOCALE
, &status
),
572 ures_getLocaleByType(dateTimePatterns
, ULOC_ACTUAL_LOCALE
, &status
));
574 // create a symbols object from the locale
575 initializeSymbols(locale
,fCalendar
, status
);
576 if (U_FAILURE(status
)) return;
579 status
= U_MEMORY_ALLOCATION_ERROR
;
583 const UChar
*resStr
,*ovrStr
;
584 int32_t resStrLen
,ovrStrLen
= 0;
585 fDateOverride
.setToBogus();
586 fTimeOverride
.setToBogus();
588 // if the pattern should include both date and time information, use the date/time
589 // pattern string as a guide to tell use how to glue together the appropriate date
590 // and time pattern strings. The actual gluing-together is handled by a convenience
591 // method on MessageFormat.
592 if ((timeStyle
!= kNone
) && (dateStyle
!= kNone
))
594 Formattable timeDateArray
[2];
596 // use Formattable::adoptString() so that we can use fastCopyFrom()
597 // instead of Formattable::setString()'s unaware, safe, deep string clone
598 // see Jitterbug 2296
600 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)timeStyle
, NULL
, &status
);
601 if (U_FAILURE(status
)) {
602 status
= U_INVALID_FORMAT_ERROR
;
605 switch (ures_getType(currentBundle
)) {
607 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
611 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
612 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
613 fTimeOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
617 status
= U_INVALID_FORMAT_ERROR
;
618 ures_close(currentBundle
);
622 ures_close(currentBundle
);
624 UnicodeString
*tempus1
= new UnicodeString(TRUE
, resStr
, resStrLen
);
625 // NULL pointer check
626 if (tempus1
== NULL
) {
627 status
= U_MEMORY_ALLOCATION_ERROR
;
630 timeDateArray
[0].adoptString(tempus1
);
632 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)dateStyle
, NULL
, &status
);
633 if (U_FAILURE(status
)) {
634 status
= U_INVALID_FORMAT_ERROR
;
637 switch (ures_getType(currentBundle
)) {
639 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
643 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
644 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
645 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
649 status
= U_INVALID_FORMAT_ERROR
;
650 ures_close(currentBundle
);
654 ures_close(currentBundle
);
656 UnicodeString
*tempus2
= new UnicodeString(TRUE
, resStr
, resStrLen
);
657 // Null pointer check
658 if (tempus2
== NULL
) {
659 status
= U_MEMORY_ALLOCATION_ERROR
;
662 timeDateArray
[1].adoptString(tempus2
);
664 int32_t glueIndex
= kDateTime
;
665 int32_t patternsSize
= ures_getSize(dateTimePatterns
);
666 if (patternsSize
>= (kDateTimeOffset
+ kShort
+ 1)) {
667 // Get proper date time format
668 glueIndex
= (int32_t)(kDateTimeOffset
+ (dateStyle
- kDateOffset
));
671 resStr
= ures_getStringByIndex(dateTimePatterns
, glueIndex
, &resStrLen
, &status
);
672 MessageFormat::format(UnicodeString(TRUE
, resStr
, resStrLen
), timeDateArray
, 2, fPattern
, status
);
674 // if the pattern includes just time data or just date date, load the appropriate
675 // pattern string from the resources
676 // setTo() - see DateFormatSymbols::assignArray comments
677 else if (timeStyle
!= kNone
) {
678 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)timeStyle
, NULL
, &status
);
679 if (U_FAILURE(status
)) {
680 status
= U_INVALID_FORMAT_ERROR
;
683 switch (ures_getType(currentBundle
)) {
685 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
689 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
690 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
691 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
695 status
= U_INVALID_FORMAT_ERROR
;
696 ures_close(currentBundle
);
700 fPattern
.setTo(TRUE
, resStr
, resStrLen
);
701 ures_close(currentBundle
);
703 else if (dateStyle
!= kNone
) {
704 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)dateStyle
, NULL
, &status
);
705 if (U_FAILURE(status
)) {
706 status
= U_INVALID_FORMAT_ERROR
;
709 switch (ures_getType(currentBundle
)) {
711 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
715 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
716 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
717 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
721 status
= U_INVALID_FORMAT_ERROR
;
722 ures_close(currentBundle
);
726 fPattern
.setTo(TRUE
, resStr
, resStrLen
);
727 ures_close(currentBundle
);
730 // and if it includes _neither_, that's an error
732 status
= U_INVALID_FORMAT_ERROR
;
734 // finally, finish initializing by creating a Calendar and a NumberFormat
735 initialize(locale
, status
);
738 //----------------------------------------------------------------------
741 SimpleDateFormat::initializeCalendar(TimeZone
* adoptZone
, const Locale
& locale
, UErrorCode
& status
)
743 if(!U_FAILURE(status
)) {
744 fCalendar
= Calendar::createInstance(adoptZone
?adoptZone
:TimeZone::createDefault(), locale
, status
);
746 if (U_SUCCESS(status
) && fCalendar
== NULL
) {
747 status
= U_MEMORY_ALLOCATION_ERROR
;
753 SimpleDateFormat::initializeSymbols(const Locale
& locale
, Calendar
* calendar
, UErrorCode
& status
)
755 if(U_FAILURE(status
)) {
758 // pass in calendar type - use NULL (default) if no calendar set (or err).
759 fSymbols
= new DateFormatSymbols(locale
, calendar
?calendar
->getType() :NULL
, status
);
760 // Null pointer check
761 if (fSymbols
== NULL
) {
762 status
= U_MEMORY_ALLOCATION_ERROR
;
769 SimpleDateFormat::initialize(const Locale
& locale
,
772 if (U_FAILURE(status
)) return;
774 // We don't need to check that the row count is >= 1, since all 2d arrays have at
776 fNumberFormat
= NumberFormat::createInstance(locale
, status
);
777 if (fNumberFormat
!= NULL
&& U_SUCCESS(status
))
779 // no matter what the locale's default number format looked like, we want
780 // to modify it so that it doesn't use thousands separators, doesn't always
781 // show the decimal point, and recognizes integers only when parsing
783 fNumberFormat
->setGroupingUsed(FALSE
);
784 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(fNumberFormat
);
785 if (decfmt
!= NULL
) {
786 decfmt
->setDecimalSeparatorAlwaysShown(FALSE
);
788 fNumberFormat
->setParseIntegerOnly(TRUE
);
789 fNumberFormat
->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
791 //fNumberFormat->setLenient(TRUE); // Java uses a custom DateNumberFormat to format/parse
793 initNumberFormatters(locale
,status
);
796 else if (U_SUCCESS(status
))
798 status
= U_MISSING_RESOURCE_ERROR
;
802 /* Initialize the fields we use to disambiguate ambiguous years. Separate
803 * so we can call it from readObject().
805 void SimpleDateFormat::initializeDefaultCentury()
808 fHaveDefaultCentury
= fCalendar
->haveDefaultCentury();
809 if(fHaveDefaultCentury
) {
810 fDefaultCenturyStart
= fCalendar
->defaultCenturyStart();
811 fDefaultCenturyStartYear
= fCalendar
->defaultCenturyStartYear();
813 fDefaultCenturyStart
= DBL_MIN
;
814 fDefaultCenturyStartYear
= -1;
819 /* Define one-century window into which to disambiguate dates using
820 * two-digit years. Make public in JDK 1.2.
822 void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate
, UErrorCode
& status
)
824 if(U_FAILURE(status
)) {
828 status
= U_ILLEGAL_ARGUMENT_ERROR
;
832 fCalendar
->setTime(startDate
, status
);
833 if(U_SUCCESS(status
)) {
834 fHaveDefaultCentury
= TRUE
;
835 fDefaultCenturyStart
= startDate
;
836 fDefaultCenturyStartYear
= fCalendar
->get(UCAL_YEAR
, status
);
840 //----------------------------------------------------------------------
843 SimpleDateFormat::format(Calendar
& cal
, UnicodeString
& appendTo
, FieldPosition
& pos
) const
845 UErrorCode status
= U_ZERO_ERROR
;
846 FieldPositionOnlyHandler
handler(pos
);
847 return _format(cal
, fDefaultCapitalizationContext
, appendTo
, handler
, status
);
850 //----------------------------------------------------------------------
853 SimpleDateFormat::format(Calendar
& cal
, const UDateFormatContextType
* types
, const UDateFormatContextValue
* values
,
854 int32_t typesAndValuesCount
, UnicodeString
& appendTo
, FieldPosition
& pos
) const
856 UErrorCode status
= U_ZERO_ERROR
;
857 FieldPositionOnlyHandler
handler(pos
);
858 UDateFormatContextValue capitalizationContext
= fDefaultCapitalizationContext
;
859 if (types
!= NULL
&& values
!= NULL
&& typesAndValuesCount
==1 && types
[0]==UDAT_CAPITALIZATION
) {
860 capitalizationContext
= values
[0];
862 return _format(cal
, capitalizationContext
, appendTo
, handler
, status
);
865 //----------------------------------------------------------------------
868 SimpleDateFormat::format(Calendar
& cal
, UnicodeString
& appendTo
,
869 FieldPositionIterator
* posIter
, UErrorCode
& status
) const
871 FieldPositionIteratorHandler
handler(posIter
, status
);
872 return _format(cal
, fDefaultCapitalizationContext
, appendTo
, handler
, status
);
875 //----------------------------------------------------------------------
878 SimpleDateFormat::_format(Calendar
& cal
, UDateFormatContextValue capitalizationContext
,
879 UnicodeString
& appendTo
, FieldPositionHandler
& handler
, UErrorCode
& status
) const
881 if ( U_FAILURE(status
) ) {
884 Calendar
* workCal
= &cal
;
885 Calendar
* calClone
= NULL
;
886 if (&cal
!= fCalendar
&& uprv_strcmp(cal
.getType(), fCalendar
->getType()) != 0) {
887 // Different calendar type
888 // We use the time and time zone from the input calendar, but
889 // do not use the input calendar for field calculation.
890 calClone
= fCalendar
->clone();
891 if (calClone
!= NULL
) {
892 UDate t
= cal
.getTime(status
);
893 calClone
->setTime(t
, status
);
894 calClone
->setTimeZone(cal
.getTimeZone());
897 status
= U_MEMORY_ALLOCATION_ERROR
;
902 UBool inQuote
= FALSE
;
905 int32_t fieldNum
= 0;
907 // loop through the pattern string character by character
908 for (int32_t i
= 0; i
< fPattern
.length() && U_SUCCESS(status
); ++i
) {
909 UChar ch
= fPattern
[i
];
911 // Use subFormat() to format a repeated pattern character
912 // when a different pattern or non-pattern character is seen
913 if (ch
!= prevCh
&& count
> 0) {
914 subFormat(appendTo
, prevCh
, count
, capitalizationContext
, fieldNum
++, handler
, *workCal
, status
);
918 // Consecutive single quotes are a single quote literal,
919 // either outside of quotes or between quotes
920 if ((i
+1) < fPattern
.length() && fPattern
[i
+1] == QUOTE
) {
921 appendTo
+= (UChar
)QUOTE
;
927 else if ( ! inQuote
&& ((ch
>= 0x0061 /*'a'*/ && ch
<= 0x007A /*'z'*/)
928 || (ch
>= 0x0041 /*'A'*/ && ch
<= 0x005A /*'Z'*/))) {
929 // ch is a date-time pattern character to be interpreted
930 // by subFormat(); count the number of times it is repeated
935 // Append quoted characters and unquoted non-pattern characters
940 // Format the last item in the pattern, if any
942 subFormat(appendTo
, prevCh
, count
, capitalizationContext
, fieldNum
++, handler
, *workCal
, status
);
945 if (calClone
!= NULL
) {
952 //----------------------------------------------------------------------
954 /* Map calendar field into calendar field level.
955 * the larger the level, the smaller the field unit.
956 * For example, UCAL_ERA level is 0, UCAL_YEAR level is 10,
957 * UCAL_MONTH level is 20.
958 * NOTE: if new fields adds in, the table needs to update.
961 SimpleDateFormat::fgCalendarFieldToLevel
[] =
965 /*dDEF*/ 30, 20, 30, 30,
966 /*ahHm*/ 40, 50, 50, 60,
974 /* Map calendar field LETTER into calendar field level.
975 * the larger the level, the smaller the field unit.
976 * NOTE: if new fields adds in, the table needs to update.
979 SimpleDateFormat::fgPatternCharToLevel
[] = {
980 // A B C D E F G H I J K L M N O
981 -1, 40, -1, -1, 20, 30, 30, 0, 50, -1, -1, 50, 20, 20, -1, -1,
982 // P Q R S T U V W X Y Z
983 -1, 20, -1, 80, -1, 10, 0, 30, -1, 10, 0, -1, -1, -1, -1, -1,
984 // a b c d e f g h i j k l m n o
985 -1, 40, -1, 30, 30, 30, -1, 0, 50, -1, -1, 50, -1, 60, -1, -1,
986 // p q r s t u v w x y z
987 -1, 20, -1, 70, -1, 10, 0, 20, -1, 10, 0, -1, -1, -1, -1, -1
991 // Map index into pattern character string to Calendar field number.
992 const UCalendarDateFields
993 SimpleDateFormat::fgPatternIndexToCalendarField
[] =
995 /*GyM*/ UCAL_ERA
, UCAL_YEAR
, UCAL_MONTH
,
996 /*dkH*/ UCAL_DATE
, UCAL_HOUR_OF_DAY
, UCAL_HOUR_OF_DAY
,
997 /*msS*/ UCAL_MINUTE
, UCAL_SECOND
, UCAL_MILLISECOND
,
998 /*EDF*/ UCAL_DAY_OF_WEEK
, UCAL_DAY_OF_YEAR
, UCAL_DAY_OF_WEEK_IN_MONTH
,
999 /*wWa*/ UCAL_WEEK_OF_YEAR
, UCAL_WEEK_OF_MONTH
, UCAL_AM_PM
,
1000 /*hKz*/ UCAL_HOUR
, UCAL_HOUR
, UCAL_ZONE_OFFSET
,
1001 /*Yeu*/ UCAL_YEAR_WOY
, UCAL_DOW_LOCAL
, UCAL_EXTENDED_YEAR
,
1002 /*gAZ*/ UCAL_JULIAN_DAY
, UCAL_MILLISECONDS_IN_DAY
, UCAL_ZONE_OFFSET
,
1003 /*v*/ UCAL_ZONE_OFFSET
,
1004 /*c*/ UCAL_DOW_LOCAL
,
1008 /*V*/ UCAL_ZONE_OFFSET
,
1012 // Map index into pattern character string to DateFormat field number
1013 const UDateFormatField
1014 SimpleDateFormat::fgPatternIndexToDateFormatField
[] = {
1015 /*GyM*/ UDAT_ERA_FIELD
, UDAT_YEAR_FIELD
, UDAT_MONTH_FIELD
,
1016 /*dkH*/ UDAT_DATE_FIELD
, UDAT_HOUR_OF_DAY1_FIELD
, UDAT_HOUR_OF_DAY0_FIELD
,
1017 /*msS*/ UDAT_MINUTE_FIELD
, UDAT_SECOND_FIELD
, UDAT_FRACTIONAL_SECOND_FIELD
,
1018 /*EDF*/ UDAT_DAY_OF_WEEK_FIELD
, UDAT_DAY_OF_YEAR_FIELD
, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD
,
1019 /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD
, UDAT_WEEK_OF_MONTH_FIELD
, UDAT_AM_PM_FIELD
,
1020 /*hKz*/ UDAT_HOUR1_FIELD
, UDAT_HOUR0_FIELD
, UDAT_TIMEZONE_FIELD
,
1021 /*Yeu*/ UDAT_YEAR_WOY_FIELD
, UDAT_DOW_LOCAL_FIELD
, UDAT_EXTENDED_YEAR_FIELD
,
1022 /*gAZ*/ UDAT_JULIAN_DAY_FIELD
, UDAT_MILLISECONDS_IN_DAY_FIELD
, UDAT_TIMEZONE_RFC_FIELD
,
1023 /*v*/ UDAT_TIMEZONE_GENERIC_FIELD
,
1024 /*c*/ UDAT_STANDALONE_DAY_FIELD
,
1025 /*L*/ UDAT_STANDALONE_MONTH_FIELD
,
1026 /*Q*/ UDAT_QUARTER_FIELD
,
1027 /*q*/ UDAT_STANDALONE_QUARTER_FIELD
,
1028 /*V*/ UDAT_TIMEZONE_SPECIAL_FIELD
,
1029 /*U*/ UDAT_YEAR_NAME_FIELD
,
1032 //----------------------------------------------------------------------
1035 * Append symbols[value] to dst. Make sure the array index is not out
1039 _appendSymbol(UnicodeString
& dst
,
1041 const UnicodeString
* symbols
,
1042 int32_t symbolsCount
) {
1043 U_ASSERT(0 <= value
&& value
< symbolsCount
);
1044 if (0 <= value
&& value
< symbolsCount
) {
1045 dst
+= symbols
[value
];
1050 _appendSymbolWithMonthPattern(UnicodeString
& dst
, int32_t value
, const UnicodeString
* symbols
, int32_t symbolsCount
,
1051 const UnicodeString
* monthPattern
, UErrorCode
& status
) {
1052 U_ASSERT(0 <= value
&& value
< symbolsCount
);
1053 if (0 <= value
&& value
< symbolsCount
) {
1054 if (monthPattern
== NULL
) {
1055 dst
+= symbols
[value
];
1057 Formattable
monthName((const UnicodeString
&)(symbols
[value
]));
1058 MessageFormat::format(*monthPattern
, &monthName
, 1, dst
, status
);
1063 //----------------------------------------------------------------------
1065 SimpleDateFormat::initNumberFormatters(const Locale
&locale
,UErrorCode
&status
) {
1066 if (U_FAILURE(status
)) {
1069 if ( fDateOverride
.isBogus() && fTimeOverride
.isBogus() ) {
1073 if (fNumberFormatters
== NULL
) {
1074 fNumberFormatters
= (NumberFormat
**)uprv_malloc(UDAT_FIELD_COUNT
* sizeof(NumberFormat
*));
1075 if (fNumberFormatters
) {
1076 for (int32_t i
= 0; i
< UDAT_FIELD_COUNT
; i
++) {
1077 fNumberFormatters
[i
] = fNumberFormat
;
1080 status
= U_MEMORY_ALLOCATION_ERROR
;
1085 processOverrideString(locale
,fDateOverride
,kOvrStrDate
,status
);
1086 processOverrideString(locale
,fTimeOverride
,kOvrStrTime
,status
);
1091 SimpleDateFormat::processOverrideString(const Locale
&locale
, const UnicodeString
&str
, int8_t type
, UErrorCode
&status
) {
1092 if (str
.isBogus()) {
1097 UnicodeString nsName
;
1098 UnicodeString ovrField
;
1099 UBool moreToProcess
= TRUE
;
1101 while (moreToProcess
) {
1102 int32_t delimiterPosition
= str
.indexOf((UChar
)ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE
,start
);
1103 if (delimiterPosition
== -1) {
1104 moreToProcess
= FALSE
;
1105 len
= str
.length() - start
;
1107 len
= delimiterPosition
- start
;
1109 UnicodeString
currentString(str
,start
,len
);
1110 int32_t equalSignPosition
= currentString
.indexOf((UChar
)ULOC_KEYWORD_ASSIGN_UNICODE
,0);
1111 if (equalSignPosition
== -1) { // Simple override string such as "hebrew"
1112 nsName
.setTo(currentString
);
1113 ovrField
.setToBogus();
1114 } else { // Field specific override string such as "y=hebrew"
1115 nsName
.setTo(currentString
,equalSignPosition
+1);
1116 ovrField
.setTo(currentString
,0,1); // We just need the first character.
1119 int32_t nsNameHash
= nsName
.hashCode();
1120 // See if the numbering system is in the override list, if not, then add it.
1121 NSOverride
*cur
= fOverrideList
;
1122 NumberFormat
*nf
= NULL
;
1123 UBool found
= FALSE
;
1124 while ( cur
&& !found
) {
1125 if ( cur
->hash
== nsNameHash
) {
1133 cur
= (NSOverride
*)uprv_malloc(sizeof(NSOverride
));
1135 char kw
[ULOC_KEYWORD_AND_VALUES_CAPACITY
];
1136 uprv_strcpy(kw
,"numbers=");
1137 nsName
.extract(0,len
,kw
+8,ULOC_KEYWORD_AND_VALUES_CAPACITY
-8,US_INV
);
1139 Locale
ovrLoc(locale
.getLanguage(),locale
.getCountry(),locale
.getVariant(),kw
);
1140 nf
= NumberFormat::createInstance(ovrLoc
,status
);
1142 // no matter what the locale's default number format looked like, we want
1143 // to modify it so that it doesn't use thousands separators, doesn't always
1144 // show the decimal point, and recognizes integers only when parsing
1146 if (U_SUCCESS(status
)) {
1147 nf
->setGroupingUsed(FALSE
);
1148 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(nf
);
1149 if (decfmt
!= NULL
) {
1150 decfmt
->setDecimalSeparatorAlwaysShown(FALSE
);
1152 nf
->setParseIntegerOnly(TRUE
);
1153 nf
->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
1156 cur
->hash
= nsNameHash
;
1157 cur
->next
= fOverrideList
;
1158 fOverrideList
= cur
;
1161 // clean up before returning
1169 status
= U_MEMORY_ALLOCATION_ERROR
;
1174 // Now that we have an appropriate number formatter, fill in the appropriate spaces in the
1175 // number formatters table.
1177 if (ovrField
.isBogus()) {
1181 for ( int8_t i
=0 ; i
<kDateFieldsCount
; i
++ ) {
1182 fNumberFormatters
[kDateFields
[i
]] = nf
;
1184 if (type
==kOvrStrDate
) {
1188 case kOvrStrTime
: {
1189 for ( int8_t i
=0 ; i
<kTimeFieldsCount
; i
++ ) {
1190 fNumberFormatters
[kTimeFields
[i
]] = nf
;
1196 UChar ch
= ovrField
.charAt(0);
1197 UChar
*patternCharPtr
= u_strchr(DateFormatSymbols::getPatternUChars(), ch
);
1198 UDateFormatField patternCharIndex
;
1200 // if the pattern character is unrecognized, signal an error and bail out
1201 if (patternCharPtr
== NULL
) {
1202 status
= U_INVALID_FORMAT_ERROR
;
1205 patternCharIndex
= (UDateFormatField
)(patternCharPtr
- DateFormatSymbols::getPatternUChars());
1207 // Set the number formatter in the table
1208 fNumberFormatters
[patternCharIndex
] = nf
;
1211 start
= delimiterPosition
+ 1;
1214 //---------------------------------------------------------------------
1216 SimpleDateFormat::subFormat(UnicodeString
&appendTo
,
1219 UDateFormatContextValue capitalizationContext
,
1221 FieldPositionHandler
& handler
,
1223 UErrorCode
& status
) const
1225 if (U_FAILURE(status
)) {
1229 // this function gets called by format() to produce the appropriate substitution
1230 // text for an individual pattern symbol (e.g., "HH" or "yyyy")
1232 UChar
*patternCharPtr
= u_strchr(DateFormatSymbols::getPatternUChars(), ch
);
1233 UDateFormatField patternCharIndex
;
1234 const int32_t maxIntCount
= 10;
1235 int32_t beginOffset
= appendTo
.length();
1236 NumberFormat
*currentNumberFormat
;
1237 DateFormatSymbols::ECapitalizationContextUsageType capContextUsageType
= DateFormatSymbols::kCapContextUsageOther
;
1239 UBool isHebrewCalendar
= (uprv_strcmp(cal
.getType(),"hebrew") == 0);
1240 UBool isChineseCalendar
= (uprv_strcmp(cal
.getType(),"chinese") == 0);
1242 // if the pattern character is unrecognized, signal an error and dump out
1243 if (patternCharPtr
== NULL
)
1245 if (ch
!= 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
1246 status
= U_INVALID_FORMAT_ERROR
;
1251 patternCharIndex
= (UDateFormatField
)(patternCharPtr
- DateFormatSymbols::getPatternUChars());
1252 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
];
1253 int32_t value
= cal
.get(field
, status
);
1254 if (U_FAILURE(status
)) {
1258 currentNumberFormat
= getNumberFormatByIndex(patternCharIndex
);
1259 UnicodeString
hebr("hebr", 4, US_INV
);
1261 switch (patternCharIndex
) {
1263 // for any "G" symbol, write out the appropriate era string
1264 // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name
1265 case UDAT_ERA_FIELD
:
1266 if (isChineseCalendar
) {
1267 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, 1, 9); // as in ICU4J
1270 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowEras
, fSymbols
->fNarrowErasCount
);
1271 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraNarrow
;
1272 } else if (count
== 4) {
1273 _appendSymbol(appendTo
, value
, fSymbols
->fEraNames
, fSymbols
->fEraNamesCount
);
1274 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraWide
;
1276 _appendSymbol(appendTo
, value
, fSymbols
->fEras
, fSymbols
->fErasCount
);
1277 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraAbbrev
;
1282 case UDAT_YEAR_NAME_FIELD
:
1283 if (fSymbols
->fShortYearNames
!= NULL
&& value
<= fSymbols
->fShortYearNamesCount
) {
1284 // the Calendar YEAR field runs 1 through 60 for cyclic years
1285 _appendSymbol(appendTo
, value
- 1, fSymbols
->fShortYearNames
, fSymbols
->fShortYearNamesCount
);
1288 // else fall through to numeric year handling, do not break here
1290 // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits
1292 //Year y yy yyy yyyy yyyyy
1293 //AD 1 1 01 001 0001 00001
1294 //AD 12 12 12 012 0012 00012
1295 //AD 123 123 23 123 0123 00123
1296 //AD 1234 1234 34 1234 1234 01234
1297 //AD 12345 12345 45 12345 12345 12345
1298 case UDAT_YEAR_FIELD
:
1299 case UDAT_YEAR_WOY_FIELD
:
1300 if (fDateOverride
.compare(hebr
)==0 && value
>5000 && value
<6000) {
1304 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, 2, 2);
1306 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, count
, maxIntCount
);
1309 // for "MMMM"/"LLLL", write out the whole month name, for "MMM"/"LLL", write out the month
1310 // abbreviation, for "M"/"L" or "MM"/"LL", write out the month as a number with the
1311 // appropriate number of digits
1312 // for "MMMMM"/"LLLLL", use the narrow form
1313 case UDAT_MONTH_FIELD
:
1314 case UDAT_STANDALONE_MONTH_FIELD
:
1315 if ( isHebrewCalendar
) {
1316 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
1317 if (hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
== 6 && count
>= 3 )
1318 value
= 13; // Show alternate form for Adar II in leap years in Hebrew calendar.
1319 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6 && count
< 3 )
1320 value
--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7.
1323 int32_t isLeapMonth
= (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
)?
1324 cal
.get(UCAL_IS_LEAP_MONTH
, status
): 0;
1325 // should consolidate the next section by using arrays of pointers & counts for the right symbols...
1327 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1328 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fNarrowMonths
, fSymbols
->fNarrowMonthsCount
,
1329 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatNarrow
]): NULL
, status
);
1331 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneNarrowMonths
, fSymbols
->fStandaloneNarrowMonthsCount
,
1332 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneNarrow
]): NULL
, status
);
1334 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthNarrow
;
1335 } else if (count
== 4) {
1336 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1337 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fMonths
, fSymbols
->fMonthsCount
,
1338 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatWide
]): NULL
, status
);
1339 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthFormat
;
1341 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneMonths
, fSymbols
->fStandaloneMonthsCount
,
1342 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneWide
]): NULL
, status
);
1343 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthStandalone
;
1345 } else if (count
== 3) {
1346 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1347 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fShortMonths
, fSymbols
->fShortMonthsCount
,
1348 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatAbbrev
]): NULL
, status
);
1349 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthFormat
;
1351 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneShortMonths
, fSymbols
->fStandaloneShortMonthsCount
,
1352 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev
]): NULL
, status
);
1353 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthStandalone
;
1356 UnicodeString monthNumber
;
1357 zeroPaddingNumber(currentNumberFormat
,monthNumber
, value
+ 1, count
, maxIntCount
);
1358 _appendSymbolWithMonthPattern(appendTo
, 0, &monthNumber
, 1,
1359 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternNumeric
]): NULL
, status
);
1364 // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
1365 case UDAT_HOUR_OF_DAY1_FIELD
:
1367 zeroPaddingNumber(currentNumberFormat
,appendTo
, cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1, count
, maxIntCount
);
1369 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1372 case UDAT_FRACTIONAL_SECOND_FIELD
:
1373 // Fractional seconds left-justify
1375 currentNumberFormat
->setMinimumIntegerDigits((count
> 3) ? 3 : count
);
1376 currentNumberFormat
->setMaximumIntegerDigits(maxIntCount
);
1379 } else if (count
== 2) {
1383 currentNumberFormat
->format(value
, appendTo
, p
);
1385 currentNumberFormat
->setMinimumIntegerDigits(count
- 3);
1386 currentNumberFormat
->format((int32_t)0, appendTo
, p
);
1391 // for "ee" or "e", use local numeric day-of-the-week
1392 // for "EEEEE" or "eeeee", write out the narrow day-of-the-week name
1393 // for "EEEE" or "eeee", write out the wide day-of-the-week name
1394 // for "EEE" or "EE" or "E" or "eee", write out the abbreviated day-of-the-week name
1395 case UDAT_DOW_LOCAL_FIELD
:
1397 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1400 // fall through to EEEEE-EEE handling, but for that we don't want local day-of-week,
1401 // we want standard day-of-week, so first fix value to work for EEEEE-EEE.
1402 value
= cal
.get(UCAL_DAY_OF_WEEK
, status
);
1403 if (U_FAILURE(status
)) {
1406 // fall through, do not break here
1407 case UDAT_DAY_OF_WEEK_FIELD
:
1409 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowWeekdays
,
1410 fSymbols
->fNarrowWeekdaysCount
);
1411 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayNarrow
;
1412 } else if (count
== 4) {
1413 _appendSymbol(appendTo
, value
, fSymbols
->fWeekdays
,
1414 fSymbols
->fWeekdaysCount
);
1415 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1417 _appendSymbol(appendTo
, value
, fSymbols
->fShortWeekdays
,
1418 fSymbols
->fShortWeekdaysCount
);
1419 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1423 // for "ccc", write out the abbreviated day-of-the-week name
1424 // for "cccc", write out the wide day-of-the-week name
1425 // for "ccccc", use the narrow day-of-the-week name
1426 case UDAT_STANDALONE_DAY_FIELD
:
1428 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, 1, maxIntCount
);
1431 // fall through to alpha DOW handling, but for that we don't want local day-of-week,
1432 // we want standard day-of-week, so first fix value.
1433 value
= cal
.get(UCAL_DAY_OF_WEEK
, status
);
1434 if (U_FAILURE(status
)) {
1438 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneNarrowWeekdays
,
1439 fSymbols
->fStandaloneNarrowWeekdaysCount
);
1440 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayNarrow
;
1441 } else if (count
== 4) {
1442 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneWeekdays
,
1443 fSymbols
->fStandaloneWeekdaysCount
);
1444 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1445 } else { // count == 3
1446 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneShortWeekdays
,
1447 fSymbols
->fStandaloneShortWeekdaysCount
);
1448 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1452 // for and "a" symbol, write out the whole AM/PM string
1453 case UDAT_AM_PM_FIELD
:
1454 _appendSymbol(appendTo
, value
, fSymbols
->fAmPms
,
1455 fSymbols
->fAmPmsCount
);
1458 // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
1460 case UDAT_HOUR1_FIELD
:
1462 zeroPaddingNumber(currentNumberFormat
,appendTo
, cal
.getLeastMaximum(UCAL_HOUR
) + 1, count
, maxIntCount
);
1464 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1467 // for the "z" symbols, we have to check our time zone data first. If we have a
1468 // localized name for the time zone, then "zzzz" / "zzz" indicate whether
1469 // daylight time is in effect (long/short) and "zz" / "z" do not (long/short).
1470 // If we don't have a localized time zone name,
1471 // then the time zone shows up as "GMT+hh:mm" or "GMT-hh:mm" (where "hh:mm" is the
1472 // offset from GMT) regardless of how many z's were in the pattern symbol
1473 case UDAT_TIMEZONE_FIELD
:
1474 case UDAT_TIMEZONE_GENERIC_FIELD
:
1475 case UDAT_TIMEZONE_SPECIAL_FIELD
:
1476 case UDAT_TIMEZONE_RFC_FIELD
: // 'Z' - TIMEZONE_RFC
1478 UnicodeString zoneString
;
1479 const TimeZone
& tz
= cal
.getTimeZone();
1480 UDate date
= cal
.getTime(status
);
1481 if (U_SUCCESS(status
)) {
1482 if (patternCharIndex
== UDAT_TIMEZONE_RFC_FIELD
) {
1485 tzFormat()->format(UTZFMT_STYLE_RFC822
, tz
, date
, zoneString
);
1486 } else if (count
== 5) {
1488 tzFormat()->format(UTZFMT_STYLE_ISO8601
, tz
, date
, zoneString
);
1490 // "ZZ", "ZZZ", "ZZZZ"
1491 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT
, tz
, date
, zoneString
);
1493 } else if (patternCharIndex
== UDAT_TIMEZONE_FIELD
) {
1496 tzFormat()->format(UTZFMT_STYLE_SPECIFIC_SHORT
, tz
, date
, zoneString
);
1497 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneShort
;
1500 tzFormat()->format(UTZFMT_STYLE_SPECIFIC_LONG
, tz
, date
, zoneString
);
1501 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneLong
;
1503 } else if (patternCharIndex
== UDAT_TIMEZONE_GENERIC_FIELD
) {
1506 tzFormat()->format(UTZFMT_STYLE_GENERIC_SHORT
, tz
, date
, zoneString
);
1507 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneShort
;
1508 } else if (count
== 4) {
1510 tzFormat()->format(UTZFMT_STYLE_GENERIC_LONG
, tz
, date
, zoneString
);
1511 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneLong
;
1513 } else { // patternCharIndex == UDAT_TIMEZONE_SPECIAL_FIELD
1516 tzFormat()->format(UTZFMT_STYLE_SPECIFIC_SHORT
, tz
, date
, zoneString
);
1517 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneShort
;
1518 } else if (count
== 4) {
1520 tzFormat()->format(UTZFMT_STYLE_GENERIC_LOCATION
, tz
, date
, zoneString
);
1521 capContextUsageType
= DateFormatSymbols::kCapContextUsageZoneLong
;
1525 appendTo
+= zoneString
;
1529 case UDAT_QUARTER_FIELD
:
1531 _appendSymbol(appendTo
, value
/3, fSymbols
->fQuarters
,
1532 fSymbols
->fQuartersCount
);
1533 else if (count
== 3)
1534 _appendSymbol(appendTo
, value
/3, fSymbols
->fShortQuarters
,
1535 fSymbols
->fShortQuartersCount
);
1537 zeroPaddingNumber(currentNumberFormat
,appendTo
, (value
/3) + 1, count
, maxIntCount
);
1540 case UDAT_STANDALONE_QUARTER_FIELD
:
1542 _appendSymbol(appendTo
, value
/3, fSymbols
->fStandaloneQuarters
,
1543 fSymbols
->fStandaloneQuartersCount
);
1544 else if (count
== 3)
1545 _appendSymbol(appendTo
, value
/3, fSymbols
->fStandaloneShortQuarters
,
1546 fSymbols
->fStandaloneShortQuartersCount
);
1548 zeroPaddingNumber(currentNumberFormat
,appendTo
, (value
/3) + 1, count
, maxIntCount
);
1552 // all of the other pattern symbols can be formatted as simple numbers with
1553 // appropriate zero padding
1555 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1558 #if !UCONFIG_NO_BREAK_ITERATION
1559 if (fieldNum
== 0) {
1560 // first field, check to see whether we need to titlecase it
1561 UBool titlecase
= FALSE
;
1562 switch (capitalizationContext
) {
1563 case UDAT_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
:
1566 case UDAT_CAPITALIZATION_FOR_UI_LIST_OR_MENU
:
1567 titlecase
= fSymbols
->fCapitalization
[capContextUsageType
][0];
1569 case UDAT_CAPITALIZATION_FOR_STANDALONE
:
1570 titlecase
= fSymbols
->fCapitalization
[capContextUsageType
][1];
1573 // titlecase = FALSE;
1577 UnicodeString
firstField(appendTo
, beginOffset
);
1578 firstField
.toTitle(NULL
, fLocale
, U_TITLECASE_NO_LOWERCASE
| U_TITLECASE_NO_BREAK_ADJUSTMENT
);
1579 appendTo
.replaceBetween(beginOffset
, appendTo
.length(), firstField
);
1584 handler
.addAttribute(fgPatternIndexToDateFormatField
[patternCharIndex
], beginOffset
, appendTo
.length());
1587 //----------------------------------------------------------------------
1590 SimpleDateFormat::getNumberFormatByIndex(UDateFormatField index
) const {
1591 if (fNumberFormatters
!= NULL
) {
1592 return fNumberFormatters
[index
];
1594 return fNumberFormat
;
1598 //----------------------------------------------------------------------
1600 SimpleDateFormat::zeroPaddingNumber(NumberFormat
*currentNumberFormat
,UnicodeString
&appendTo
,
1601 int32_t value
, int32_t minDigits
, int32_t maxDigits
) const
1603 if (currentNumberFormat
!=NULL
) {
1604 FieldPosition
pos(0);
1606 currentNumberFormat
->setMinimumIntegerDigits(minDigits
);
1607 currentNumberFormat
->setMaximumIntegerDigits(maxDigits
);
1608 currentNumberFormat
->format(value
, appendTo
, pos
); // 3rd arg is there to speed up processing
1612 //----------------------------------------------------------------------
1615 * Format characters that indicate numeric fields. The character
1616 * at index 0 is treated specially.
1618 static const UChar NUMERIC_FORMAT_CHARS
[] = {0x4D, 0x59, 0x79, 0x75, 0x64, 0x65, 0x68, 0x48, 0x6D, 0x73, 0x53, 0x44, 0x46, 0x77, 0x57, 0x6B, 0x4B, 0x00}; /* "MYyudehHmsSDFwWkK" */
1621 * Return true if the given format character, occuring count
1622 * times, represents a numeric field.
1624 UBool
SimpleDateFormat::isNumeric(UChar formatChar
, int32_t count
) {
1625 UnicodeString
s(NUMERIC_FORMAT_CHARS
);
1626 int32_t i
= s
.indexOf(formatChar
);
1627 return (i
> 0 || (i
== 0 && count
< 3));
1631 SimpleDateFormat::parse(const UnicodeString
& text
, Calendar
& cal
, ParsePosition
& parsePos
) const
1633 UErrorCode status
= U_ZERO_ERROR
;
1634 int32_t pos
= parsePos
.getIndex();
1635 int32_t start
= pos
;
1637 UBool ambiguousYear
[] = { FALSE
};
1638 int32_t saveHebrewMonth
= -1;
1641 UBool lenient
= isLenient();
1643 // hack, reset tztype, cast away const
1644 ((SimpleDateFormat
*)this)->tztype
= UTZFMT_TIME_TYPE_UNKNOWN
;
1646 // For parsing abutting numeric fields. 'abutPat' is the
1647 // offset into 'pattern' of the first of 2 or more abutting
1648 // numeric fields. 'abutStart' is the offset into 'text'
1649 // where parsing the fields begins. 'abutPass' starts off as 0
1650 // and increments each time we try to parse the fields.
1651 int32_t abutPat
= -1; // If >=0, we are in a run of abutting numeric fields
1652 int32_t abutStart
= 0;
1653 int32_t abutPass
= 0;
1654 UBool inQuote
= FALSE
;
1656 const UnicodeString
numericFormatChars(NUMERIC_FORMAT_CHARS
);
1657 MessageFormat
* numericLeapMonthFormatter
= NULL
;
1659 Calendar
* calClone
= NULL
;
1660 Calendar
*workCal
= &cal
;
1661 if (&cal
!= fCalendar
&& uprv_strcmp(cal
.getType(), fCalendar
->getType()) != 0) {
1662 // Different calendar type
1663 // We use the time/zone from the input calendar, but
1664 // do not use the input calendar for field calculation.
1665 calClone
= fCalendar
->clone();
1666 if (calClone
!= NULL
) {
1667 calClone
->setTime(cal
.getTime(status
),status
);
1668 if (U_FAILURE(status
)) {
1671 calClone
->setTimeZone(cal
.getTimeZone());
1674 status
= U_MEMORY_ALLOCATION_ERROR
;
1679 if (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
) {
1680 numericLeapMonthFormatter
= new MessageFormat(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternNumeric
], fLocale
, status
);
1681 if (numericLeapMonthFormatter
== NULL
) {
1682 status
= U_MEMORY_ALLOCATION_ERROR
;
1684 } else if (U_FAILURE(status
)) {
1685 goto ExitParse
; // this will delete numericLeapMonthFormatter
1689 for (int32_t i
=0; i
<fPattern
.length(); ++i
) {
1690 UChar ch
= fPattern
.charAt(i
);
1692 // Handle alphabetic field characters.
1693 if (!inQuote
&& ((ch
>= 0x41 && ch
<= 0x5A) || (ch
>= 0x61 && ch
<= 0x7A))) { // [A-Za-z]
1694 int32_t fieldPat
= i
;
1696 // Count the length of this field specifier
1698 while ((i
+1)<fPattern
.length() &&
1699 fPattern
.charAt(i
+1) == ch
) {
1704 if (isNumeric(ch
, count
)) {
1706 // Determine if there is an abutting numeric field. For
1707 // most fields we can just look at the next characters,
1708 // but the 'm' field is either numeric or text,
1709 // depending on the count, so we have to look ahead for
1711 if ((i
+1)<fPattern
.length()) {
1713 UChar nextCh
= fPattern
.charAt(i
+1);
1714 int32_t k
= numericFormatChars
.indexOf(nextCh
);
1717 while (j
<fPattern
.length() &&
1718 fPattern
.charAt(j
) == nextCh
) {
1721 abutting
= (j
-i
) < 4; // nextCount < 3
1726 // Record the start of a set of abutting numeric
1736 abutPat
= -1; // End of any abutting fields
1739 // Handle fields within a run of abutting numeric fields. Take
1740 // the pattern "HHmmss" as an example. We will try to parse
1741 // 2/2/2 characters of the input text, then if that fails,
1742 // 1/2/2. We only adjust the width of the leftmost field; the
1743 // others remain fixed. This allows "123456" => 12:34:56, but
1744 // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
1745 // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
1747 // If we are at the start of a run of abutting fields, then
1748 // shorten this field in each pass. If we can't shorten
1749 // this field any more, then the parse of this set of
1750 // abutting numeric fields has failed.
1751 if (fieldPat
== abutPat
) {
1752 count
-= abutPass
++;
1754 status
= U_PARSE_ERROR
;
1759 pos
= subParse(text
, pos
, ch
, count
,
1760 TRUE
, FALSE
, ambiguousYear
, saveHebrewMonth
, *workCal
, i
, numericLeapMonthFormatter
);
1762 // If the parse fails anywhere in the run, back up to the
1763 // start of the run and retry.
1771 // Handle non-numeric fields and non-abutting numeric
1773 else if (ch
!= 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
1774 int32_t s
= subParse(text
, pos
, ch
, count
,
1775 FALSE
, TRUE
, ambiguousYear
, saveHebrewMonth
, *workCal
, i
, numericLeapMonthFormatter
);
1778 // era not present, in special cases allow this to continue
1781 if (i
+1 < fPattern
.length()) {
1782 // move to next pattern character
1783 UChar ch
= fPattern
.charAt(i
+1);
1785 // check for whitespace
1786 if (PatternProps::isWhiteSpace(ch
)) {
1788 // Advance over run in pattern
1789 while ((i
+1)<fPattern
.length() &&
1790 PatternProps::isWhiteSpace(fPattern
.charAt(i
+1))) {
1797 status
= U_PARSE_ERROR
;
1804 // Handle literal pattern characters. These are any
1805 // quoted characters and non-alphabetic unquoted
1809 abutPat
= -1; // End of any abutting fields
1811 if (! matchLiterals(fPattern
, i
, text
, pos
, lenient
)) {
1812 status
= U_PARSE_ERROR
;
1818 // At this point the fields of Calendar have been set. Calendar
1819 // will fill in default values for missing fields when the time
1822 parsePos
.setIndex(pos
);
1824 // This part is a problem: When we call parsedDate.after, we compute the time.
1825 // Take the date April 3 2004 at 2:30 am. When this is first set up, the year
1826 // will be wrong if we're parsing a 2-digit year pattern. It will be 1904.
1827 // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am
1828 // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
1829 // on that day. It is therefore parsed out to fields as 3:30 am. Then we
1830 // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is
1831 // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
1833 UDate parsedDate = calendar.getTime();
1834 if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
1835 calendar.add(Calendar.YEAR, 100);
1836 parsedDate = calendar.getTime();
1839 // Because of the above condition, save off the fields in case we need to readjust.
1840 // The procedure we use here is not particularly efficient, but there is no other
1841 // way to do this given the API restrictions present in Calendar. We minimize
1842 // inefficiency by only performing this computation when it might apply, that is,
1843 // when the two-digit year is equal to the start year, and thus might fall at the
1844 // front or the back of the default century. This only works because we adjust
1845 // the year correctly to start with in other cases -- see subParse().
1846 if (ambiguousYear
[0] || tztype
!= UTZFMT_TIME_TYPE_UNKNOWN
) // If this is true then the two-digit year == the default start year
1848 // We need a copy of the fields, and we need to avoid triggering a call to
1849 // complete(), which will recalculate the fields. Since we can't access
1850 // the fields[] array in Calendar, we clone the entire object. This will
1851 // stop working if Calendar.clone() is ever rewritten to call complete().
1853 if (ambiguousYear
[0]) {
1855 // Check for failed cloning.
1857 status
= U_MEMORY_ALLOCATION_ERROR
;
1860 UDate parsedDate
= copy
->getTime(status
);
1861 // {sfb} check internalGetDefaultCenturyStart
1862 if (fHaveDefaultCentury
&& (parsedDate
< fDefaultCenturyStart
)) {
1863 // We can't use add here because that does a complete() first.
1864 cal
.set(UCAL_YEAR
, fDefaultCenturyStartYear
+ 100);
1869 if (tztype
!= UTZFMT_TIME_TYPE_UNKNOWN
) {
1871 // Check for failed cloning.
1873 status
= U_MEMORY_ALLOCATION_ERROR
;
1876 const TimeZone
& tz
= cal
.getTimeZone();
1877 BasicTimeZone
*btz
= NULL
;
1879 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
1880 || dynamic_cast<const SimpleTimeZone
*>(&tz
) != NULL
1881 || dynamic_cast<const RuleBasedTimeZone
*>(&tz
) != NULL
1882 || dynamic_cast<const VTimeZone
*>(&tz
) != NULL
) {
1883 btz
= (BasicTimeZone
*)&tz
;
1887 copy
->set(UCAL_ZONE_OFFSET
, 0);
1888 copy
->set(UCAL_DST_OFFSET
, 0);
1889 UDate localMillis
= copy
->getTime(status
);
1891 // Make sure parsed time zone type (Standard or Daylight)
1892 // matches the rule used by the parsed time zone.
1895 if (tztype
== UTZFMT_TIME_TYPE_STANDARD
) {
1896 btz
->getOffsetFromLocal(localMillis
,
1897 BasicTimeZone::kStandard
, BasicTimeZone::kStandard
, raw
, dst
, status
);
1899 btz
->getOffsetFromLocal(localMillis
,
1900 BasicTimeZone::kDaylight
, BasicTimeZone::kDaylight
, raw
, dst
, status
);
1903 // No good way to resolve ambiguous time at transition,
1904 // but following code work in most case.
1905 tz
.getOffset(localMillis
, TRUE
, raw
, dst
, status
);
1908 // Now, compare the results with parsed type, either standard or daylight saving time
1909 int32_t resolvedSavings
= dst
;
1910 if (tztype
== UTZFMT_TIME_TYPE_STANDARD
) {
1912 // Override DST_OFFSET = 0 in the result calendar
1913 resolvedSavings
= 0;
1915 } else { // tztype == TZTYPE_DST
1918 UDate time
= localMillis
+ raw
;
1919 // We use the nearest daylight saving time rule.
1920 TimeZoneTransition beforeTrs
, afterTrs
;
1921 UDate beforeT
= time
, afterT
= time
;
1922 int32_t beforeSav
= 0, afterSav
= 0;
1923 UBool beforeTrsAvail
, afterTrsAvail
;
1925 // Search for DST rule before or on the time
1927 beforeTrsAvail
= btz
->getPreviousTransition(beforeT
, TRUE
, beforeTrs
);
1928 if (!beforeTrsAvail
) {
1931 beforeT
= beforeTrs
.getTime() - 1;
1932 beforeSav
= beforeTrs
.getFrom()->getDSTSavings();
1933 if (beforeSav
!= 0) {
1938 // Search for DST rule after the time
1940 afterTrsAvail
= btz
->getNextTransition(afterT
, FALSE
, afterTrs
);
1941 if (!afterTrsAvail
) {
1944 afterT
= afterTrs
.getTime();
1945 afterSav
= afterTrs
.getTo()->getDSTSavings();
1946 if (afterSav
!= 0) {
1951 if (beforeTrsAvail
&& afterTrsAvail
) {
1952 if (time
- beforeT
> afterT
- time
) {
1953 resolvedSavings
= afterSav
;
1955 resolvedSavings
= beforeSav
;
1957 } else if (beforeTrsAvail
&& beforeSav
!= 0) {
1958 resolvedSavings
= beforeSav
;
1959 } else if (afterTrsAvail
&& afterSav
!= 0) {
1960 resolvedSavings
= afterSav
;
1962 resolvedSavings
= btz
->getDSTSavings();
1965 resolvedSavings
= tz
.getDSTSavings();
1967 if (resolvedSavings
== 0) {
1969 resolvedSavings
= U_MILLIS_PER_HOUR
;
1973 cal
.set(UCAL_ZONE_OFFSET
, raw
);
1974 cal
.set(UCAL_DST_OFFSET
, resolvedSavings
);
1979 // Set the parsed result if local calendar is used
1980 // instead of the input calendar
1981 if (U_SUCCESS(status
) && workCal
!= &cal
) {
1982 cal
.setTimeZone(workCal
->getTimeZone());
1983 cal
.setTime(workCal
->getTime(status
), status
);
1986 if (numericLeapMonthFormatter
!= NULL
) {
1987 delete numericLeapMonthFormatter
;
1989 if (calClone
!= NULL
) {
1993 // If any Calendar calls failed, we pretend that we
1994 // couldn't parse the string, when in reality this isn't quite accurate--
1995 // we did parse it; the Calendar calls just failed.
1996 if (U_FAILURE(status
)) {
1997 parsePos
.setErrorIndex(pos
);
1998 parsePos
.setIndex(start
);
2003 SimpleDateFormat::parse( const UnicodeString
& text
,
2004 ParsePosition
& pos
) const {
2005 // redefined here because the other parse() function hides this function's
2006 // cunterpart on DateFormat
2007 return DateFormat::parse(text
, pos
);
2011 SimpleDateFormat::parse(const UnicodeString
& text
, UErrorCode
& status
) const
2013 // redefined here because the other parse() function hides this function's
2014 // counterpart on DateFormat
2015 return DateFormat::parse(text
, status
);
2017 //----------------------------------------------------------------------
2019 int32_t SimpleDateFormat::matchQuarterString(const UnicodeString
& text
,
2021 UCalendarDateFields field
,
2022 const UnicodeString
* data
,
2024 Calendar
& cal
) const
2027 int32_t count
= dataCount
;
2029 // There may be multiple strings in the data[] array which begin with
2030 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2031 // We keep track of the longest match, and return that. Note that this
2032 // unfortunately requires us to test all array elements.
2033 int32_t bestMatchLength
= 0, bestMatch
= -1;
2035 // {sfb} kludge to support case-insensitive comparison
2036 // {markus 2002oct11} do not just use caseCompareBetween because we do not know
2037 // the length of the match after case folding
2038 // {alan 20040607} don't case change the whole string, since the length
2040 // TODO we need a case-insensitive startsWith function
2041 UnicodeString lcase
, lcaseText
;
2042 text
.extract(start
, INT32_MAX
, lcaseText
);
2043 lcaseText
.foldCase();
2045 for (; i
< count
; ++i
)
2047 // Always compare if we have no match yet; otherwise only compare
2048 // against potentially better matches (longer strings).
2050 lcase
.fastCopyFrom(data
[i
]).foldCase();
2051 int32_t length
= lcase
.length();
2053 if (length
> bestMatchLength
&&
2054 lcaseText
.compareBetween(0, length
, lcase
, 0, length
) == 0)
2057 bestMatchLength
= length
;
2062 cal
.set(field
, bestMatch
* 3);
2064 // Once we have a match, we have to determine the length of the
2065 // original source string. This will usually be == the length of
2066 // the case folded string, but it may differ (e.g. sharp s).
2067 lcase
.fastCopyFrom(data
[bestMatch
]).foldCase();
2069 // Most of the time, the length will be the same as the length
2070 // of the string from the locale data. Sometimes it will be
2071 // different, in which case we will have to figure it out by
2072 // adding a character at a time, until we have a match. We do
2073 // this all in one loop, where we try 'len' first (at index
2075 int32_t len
= data
[bestMatch
].length(); // 99+% of the time
2076 int32_t n
= text
.length() - start
;
2077 for (i
=0; i
<=n
; ++i
) {
2081 } else if (i
== len
) {
2082 continue; // already tried this when i was 0
2084 text
.extract(start
, j
, lcaseText
);
2085 lcaseText
.foldCase();
2086 if (lcase
== lcaseText
) {
2095 //----------------------------------------------------------------------
2096 UBool
SimpleDateFormat::matchLiterals(const UnicodeString
&pattern
,
2097 int32_t &patternOffset
,
2098 const UnicodeString
&text
,
2099 int32_t &textOffset
,
2102 UBool inQuote
= FALSE
;
2103 UnicodeString literal
;
2104 int32_t i
= patternOffset
;
2106 // scan pattern looking for contiguous literal characters
2107 for ( ; i
< pattern
.length(); i
+= 1) {
2108 UChar ch
= pattern
.charAt(i
);
2110 if (!inQuote
&& ((ch
>= 0x41 && ch
<= 0x5A) || (ch
>= 0x61 && ch
<= 0x7A))) { // unquoted [A-Za-z]
2115 // Match a quote literal ('') inside OR outside of quotes
2116 if ((i
+ 1) < pattern
.length() && pattern
.charAt(i
+ 1) == QUOTE
) {
2127 // at this point, literal contains the literal text
2128 // and i is the index of the next non-literal pattern character.
2130 int32_t t
= textOffset
;
2133 // trim leading, trailing whitespace from
2137 // ignore any leading whitespace in the text
2138 while (t
< text
.length() && u_isWhitespace(text
.charAt(t
))) {
2143 for (p
= 0; p
< literal
.length() && t
< text
.length(); p
+= 1, t
+= 1) {
2144 UBool needWhitespace
= FALSE
;
2146 while (p
< literal
.length() && PatternProps::isWhiteSpace(literal
.charAt(p
))) {
2147 needWhitespace
= TRUE
;
2151 if (needWhitespace
) {
2154 while (t
< text
.length()) {
2155 UChar tch
= text
.charAt(t
);
2157 if (!u_isUWhiteSpace(tch
) && !PatternProps::isWhiteSpace(tch
)) {
2164 // TODO: should we require internal spaces
2165 // in lenient mode? (There won't be any
2166 // leading or trailing spaces)
2167 if (!lenient
&& t
== tStart
) {
2168 // didn't find matching whitespace:
2169 // an error in strict mode
2173 // In strict mode, this run of whitespace
2174 // may have been at the end.
2175 if (p
>= literal
.length()) {
2180 if (t
>= text
.length() || literal
.charAt(p
) != text
.charAt(t
)) {
2181 // Ran out of text, or found a non-matching character:
2182 // OK in lenient mode, an error in strict mode.
2191 // At this point if we're in strict mode we have a complete match.
2192 // If we're in lenient mode we may have a partial match, or no
2195 // no match. Pretend it matched a run of whitespace
2196 // and ignorables in the text.
2197 const UnicodeSet
*ignorables
= NULL
;
2198 UChar
*patternCharPtr
= u_strchr(DateFormatSymbols::getPatternUChars(), pattern
.charAt(i
));
2200 if (patternCharPtr
!= NULL
) {
2201 UDateFormatField patternCharIndex
= (UDateFormatField
) (patternCharPtr
- DateFormatSymbols::getPatternUChars());
2203 ignorables
= SimpleDateFormatStaticSets::getIgnorables(patternCharIndex
);
2206 for (t
= textOffset
; t
< text
.length(); t
+= 1) {
2207 UChar ch
= text
.charAt(t
);
2209 if (ignorables
== NULL
|| !ignorables
->contains(ch
)) {
2215 // if we get here, we've got a complete match.
2216 patternOffset
= i
- 1;
2222 //----------------------------------------------------------------------
2224 int32_t SimpleDateFormat::matchString(const UnicodeString
& text
,
2226 UCalendarDateFields field
,
2227 const UnicodeString
* data
,
2229 const UnicodeString
* monthPattern
,
2230 Calendar
& cal
) const
2233 int32_t count
= dataCount
;
2235 if (field
== UCAL_DAY_OF_WEEK
) i
= 1;
2237 // There may be multiple strings in the data[] array which begin with
2238 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2239 // We keep track of the longest match, and return that. Note that this
2240 // unfortunately requires us to test all array elements.
2241 int32_t bestMatchLength
= 0, bestMatch
= -1;
2242 UnicodeString bestMatchName
;
2243 int32_t isLeapMonth
= 0;
2245 // {sfb} kludge to support case-insensitive comparison
2246 // {markus 2002oct11} do not just use caseCompareBetween because we do not know
2247 // the length of the match after case folding
2248 // {alan 20040607} don't case change the whole string, since the length
2250 // TODO we need a case-insensitive startsWith function
2251 UnicodeString lcase
, lcaseText
;
2252 text
.extract(start
, INT32_MAX
, lcaseText
);
2253 lcaseText
.foldCase();
2255 for (; i
< count
; ++i
)
2257 // Always compare if we have no match yet; otherwise only compare
2258 // against potentially better matches (longer strings).
2260 lcase
.fastCopyFrom(data
[i
]).foldCase();
2261 int32_t length
= lcase
.length();
2263 if (length
> bestMatchLength
&&
2264 lcaseText
.compareBetween(0, length
, lcase
, 0, length
) == 0)
2267 bestMatchLength
= length
;
2268 bestMatchName
.setTo(data
[i
]);
2272 if (monthPattern
!= NULL
) {
2273 UErrorCode status
= U_ZERO_ERROR
;
2274 UnicodeString leapMonthName
;
2275 Formattable
monthName((const UnicodeString
&)(data
[i
]));
2276 MessageFormat::format(*monthPattern
, &monthName
, 1, leapMonthName
, status
);
2277 if (U_SUCCESS(status
)) {
2278 lcase
.fastCopyFrom(leapMonthName
).foldCase();
2279 length
= lcase
.length();
2281 if (length
> bestMatchLength
&&
2282 lcaseText
.compareBetween(0, length
, lcase
, 0, length
) == 0)
2285 bestMatchLength
= length
;
2286 bestMatchName
.setTo(leapMonthName
);
2294 // Adjustment for Hebrew Calendar month Adar II
2295 if (!strcmp(cal
.getType(),"hebrew") && field
==UCAL_MONTH
&& bestMatch
==13) {
2299 if (field
== UCAL_YEAR
) {
2300 bestMatch
++; // only get here for cyclic year names, which match 1-based years 1-60
2302 cal
.set(field
, bestMatch
);
2304 if (monthPattern
!= NULL
) {
2305 cal
.set(UCAL_IS_LEAP_MONTH
, isLeapMonth
);
2308 // Once we have a match, we have to determine the length of the
2309 // original source string. This will usually be == the length of
2310 // the case folded string, but it may differ (e.g. sharp s).
2311 lcase
.fastCopyFrom(bestMatchName
).foldCase();
2313 // Most of the time, the length will be the same as the length
2314 // of the string from the locale data. Sometimes it will be
2315 // different, in which case we will have to figure it out by
2316 // adding a character at a time, until we have a match. We do
2317 // this all in one loop, where we try 'len' first (at index
2319 int32_t len
= bestMatchName
.length(); // 99+% of the time
2320 int32_t n
= text
.length() - start
;
2321 for (i
=0; i
<=n
; ++i
) {
2325 } else if (i
== len
) {
2326 continue; // already tried this when i was 0
2328 text
.extract(start
, j
, lcaseText
);
2329 lcaseText
.foldCase();
2330 if (lcase
== lcaseText
) {
2339 //----------------------------------------------------------------------
2342 SimpleDateFormat::set2DigitYearStart(UDate d
, UErrorCode
& status
)
2344 parseAmbiguousDatesAsAfter(d
, status
);
2348 * Private member function that converts the parsed date strings into
2349 * timeFields. Returns -start (for ParsePosition) if failed.
2350 * @param text the time text to be parsed.
2351 * @param start where to start parsing.
2352 * @param ch the pattern character for the date field text to be parsed.
2353 * @param count the count of a pattern character.
2354 * @return the new start position if matching succeeded; a negative number
2355 * indicating matching failure, otherwise.
2357 int32_t SimpleDateFormat::subParse(const UnicodeString
& text
, int32_t& start
, UChar ch
, int32_t count
,
2358 UBool obeyCount
, UBool allowNegative
, UBool ambiguousYear
[], int32_t& saveHebrewMonth
, Calendar
& cal
,
2359 int32_t patLoc
, MessageFormat
* numericLeapMonthFormatter
) const
2365 ParsePosition
pos(0);
2366 UDateFormatField patternCharIndex
;
2367 NumberFormat
*currentNumberFormat
;
2369 UChar
*patternCharPtr
= u_strchr(DateFormatSymbols::getPatternUChars(), ch
);
2370 UBool lenient
= isLenient();
2371 int32_t tzParseOptions
= (lenient
)? UTZFMT_PARSE_OPTION_ALL_STYLES
: UTZFMT_PARSE_OPTION_NONE
;
2372 UBool gotNumber
= FALSE
;
2374 #if defined (U_DEBUG_CAL)
2375 //fprintf(stderr, "%s:%d - [%c] st=%d \n", __FILE__, __LINE__, (char) ch, start);
2378 if (patternCharPtr
== NULL
) {
2382 patternCharIndex
= (UDateFormatField
)(patternCharPtr
- DateFormatSymbols::getPatternUChars());
2383 currentNumberFormat
= getNumberFormatByIndex(patternCharIndex
);
2384 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
];
2385 UnicodeString
hebr("hebr", 4, US_INV
);
2387 if (numericLeapMonthFormatter
!= NULL
) {
2388 numericLeapMonthFormatter
->setFormats((const Format
**)¤tNumberFormat
, 1);
2390 UBool isChineseCalendar
= (uprv_strcmp(cal
.getType(),"chinese") == 0);
2392 // If there are any spaces here, skip over them. If we hit the end
2393 // of the string, then fail.
2395 if (start
>= text
.length()) {
2398 UChar32 c
= text
.char32At(start
);
2399 if (!u_isUWhiteSpace(c
) /*||*/ && !PatternProps::isWhiteSpace(c
)) {
2402 start
+= U16_LENGTH(c
);
2404 pos
.setIndex(start
);
2406 // We handle a few special cases here where we need to parse
2407 // a number value. We handle further, more generic cases below. We need
2408 // to handle some of them here because some fields require extra processing on
2409 // the parsed value.
2410 if (patternCharIndex
== UDAT_HOUR_OF_DAY1_FIELD
|| // k
2411 patternCharIndex
== UDAT_HOUR_OF_DAY0_FIELD
|| // H
2412 patternCharIndex
== UDAT_HOUR1_FIELD
|| // h
2413 patternCharIndex
== UDAT_HOUR0_FIELD
|| // K
2414 (patternCharIndex
== UDAT_DOW_LOCAL_FIELD
&& count
<= 2) || // e
2415 (patternCharIndex
== UDAT_STANDALONE_DAY_FIELD
&& count
<= 2) || // c
2416 (patternCharIndex
== UDAT_MONTH_FIELD
&& count
<= 2) || // M
2417 (patternCharIndex
== UDAT_STANDALONE_MONTH_FIELD
&& count
<= 2) || // L
2418 (patternCharIndex
== UDAT_QUARTER_FIELD
&& count
<= 2) || // Q
2419 (patternCharIndex
== UDAT_STANDALONE_QUARTER_FIELD
&& count
<= 2) || // q
2420 patternCharIndex
== UDAT_YEAR_FIELD
|| // y
2421 patternCharIndex
== UDAT_YEAR_WOY_FIELD
|| // Y
2422 patternCharIndex
== UDAT_YEAR_NAME_FIELD
|| // U (falls back to numeric)
2423 (patternCharIndex
== UDAT_ERA_FIELD
&& isChineseCalendar
) || // G
2424 patternCharIndex
== UDAT_FRACTIONAL_SECOND_FIELD
) // S
2426 int32_t parseStart
= pos
.getIndex();
2427 // It would be good to unify this with the obeyCount logic below,
2428 // but that's going to be difficult.
2429 const UnicodeString
* src
;
2431 UBool parsedNumericLeapMonth
= FALSE
;
2432 if (numericLeapMonthFormatter
!= NULL
&& (patternCharIndex
== UDAT_MONTH_FIELD
|| patternCharIndex
== UDAT_STANDALONE_MONTH_FIELD
)) {
2434 Formattable
* args
= numericLeapMonthFormatter
->parse(text
, pos
, argCount
);
2435 if (args
!= NULL
&& argCount
== 1 && pos
.getIndex() > parseStart
&& args
[0].isNumeric()) {
2436 parsedNumericLeapMonth
= TRUE
;
2437 number
.setLong(args
[0].getLong());
2438 cal
.set(UCAL_IS_LEAP_MONTH
, 1);
2441 pos
.setIndex(parseStart
);
2442 cal
.set(UCAL_IS_LEAP_MONTH
, 0);
2446 if (!parsedNumericLeapMonth
) {
2448 if ((start
+count
) > text
.length()) {
2452 text
.extractBetween(0, start
+ count
, temp
);
2458 parseInt(*src
, number
, pos
, allowNegative
,currentNumberFormat
);
2461 int32_t txtLoc
= pos
.getIndex();
2463 if (txtLoc
> parseStart
) {
2464 value
= number
.getLong();
2467 // suffix processing
2469 txtLoc
= checkIntSuffix(text
, txtLoc
, patLoc
+1, TRUE
);
2470 if (txtLoc
!= pos
.getIndex()) {
2475 txtLoc
= checkIntSuffix(text
, txtLoc
, patLoc
+1, FALSE
);
2478 // Check the range of the value
2480 int32_t bias
= gFieldRangeBias
[patternCharIndex
];
2481 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
|| value
< cal
.getMinimum(field
) + bias
)) {
2485 int32_t bias
= gFieldRangeBiasLenient
[patternCharIndex
];
2486 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
)) {
2491 pos
.setIndex(txtLoc
);
2495 // Make sure that we got a number if
2496 // we want one, and didn't get one
2497 // if we don't want one.
2498 switch (patternCharIndex
) {
2499 case UDAT_HOUR_OF_DAY1_FIELD
:
2500 case UDAT_HOUR_OF_DAY0_FIELD
:
2501 case UDAT_HOUR1_FIELD
:
2502 case UDAT_HOUR0_FIELD
:
2503 // special range check for hours:
2504 if (value
< 0 || value
> 24) {
2508 // fall through to gotNumber check
2510 case UDAT_YEAR_FIELD
:
2511 case UDAT_YEAR_WOY_FIELD
:
2512 case UDAT_FRACTIONAL_SECOND_FIELD
:
2513 // these must be a number
2521 // we check the rest of the fields below.
2525 switch (patternCharIndex
) {
2526 case UDAT_ERA_FIELD
:
2527 if (isChineseCalendar
) {
2531 cal
.set(UCAL_ERA
, value
);
2532 return pos
.getIndex();
2535 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fNarrowEras
, fSymbols
->fNarrowErasCount
, NULL
, cal
);
2536 } else if (count
== 4) {
2537 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fEraNames
, fSymbols
->fEraNamesCount
, NULL
, cal
);
2539 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fEras
, fSymbols
->fErasCount
, NULL
, cal
);
2542 // check return position, if it equals -start, then matchString error
2543 // special case the return code so we don't necessarily fail out until we
2544 // verify no year information also
2550 case UDAT_YEAR_FIELD
:
2551 // If there are 3 or more YEAR pattern characters, this indicates
2552 // that the year value is to be treated literally, without any
2553 // two-digit year adjustments (e.g., from "01" to 2001). Otherwise
2554 // we made adjustments to place the 2-digit year in the proper
2555 // century, for parsed strings from "00" to "99". Any other string
2556 // is treated literally: "2250", "-1", "1", "002".
2557 if (fDateOverride
.compare(hebr
)==0 && value
< 1000) {
2559 } else if ((pos
.getIndex() - start
) == 2 && !isChineseCalendar
2560 && u_isdigit(text
.charAt(start
))
2561 && u_isdigit(text
.charAt(start
+1)))
2563 // Assume for example that the defaultCenturyStart is 6/18/1903.
2564 // This means that two-digit years will be forced into the range
2565 // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
2566 // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
2567 // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
2568 // other fields specify a date before 6/18, or 1903 if they specify a
2569 // date afterwards. As a result, 03 is an ambiguous year. All other
2570 // two-digit years are unambiguous.
2571 if(fHaveDefaultCentury
) { // check if this formatter even has a pivot year
2572 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
2573 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
2574 value
+= (fDefaultCenturyStartYear
/100)*100 +
2575 (value
< ambiguousTwoDigitYear
? 100 : 0);
2578 cal
.set(UCAL_YEAR
, value
);
2580 // Delayed checking for adjustment of Hebrew month numbers in non-leap years.
2581 if (saveHebrewMonth
>= 0) {
2582 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
2583 if (!hc
->isLeapYear(value
) && saveHebrewMonth
>= 6) {
2584 cal
.set(UCAL_MONTH
,saveHebrewMonth
);
2586 cal
.set(UCAL_MONTH
,saveHebrewMonth
-1);
2588 saveHebrewMonth
= -1;
2590 return pos
.getIndex();
2592 case UDAT_YEAR_WOY_FIELD
:
2593 // Comment is the same as for UDAT_Year_FIELDs - look above
2594 if (fDateOverride
.compare(hebr
)==0 && value
< 1000) {
2596 } else if ((pos
.getIndex() - start
) == 2
2597 && u_isdigit(text
.charAt(start
))
2598 && u_isdigit(text
.charAt(start
+1))
2599 && fHaveDefaultCentury
)
2601 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
2602 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
2603 value
+= (fDefaultCenturyStartYear
/100)*100 +
2604 (value
< ambiguousTwoDigitYear
? 100 : 0);
2606 cal
.set(UCAL_YEAR_WOY
, value
);
2607 return pos
.getIndex();
2609 case UDAT_YEAR_NAME_FIELD
:
2610 if (fSymbols
->fShortYearNames
!= NULL
) {
2611 int32_t newStart
= matchString(text
, start
, UCAL_YEAR
, fSymbols
->fShortYearNames
, fSymbols
->fShortYearNamesCount
, NULL
, cal
);
2616 if (gotNumber
&& (lenient
|| value
> fSymbols
->fShortYearNamesCount
)) {
2617 cal
.set(UCAL_YEAR
, value
);
2618 return pos
.getIndex();
2622 case UDAT_MONTH_FIELD
:
2623 case UDAT_STANDALONE_MONTH_FIELD
:
2624 if (gotNumber
) // i.e., M or MM.
2626 // When parsing month numbers from the Hebrew Calendar, we might need to adjust the month depending on whether
2627 // 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
2628 // the year is parsed.
2629 if (!strcmp(cal
.getType(),"hebrew")) {
2630 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
2631 if (cal
.isSet(UCAL_YEAR
)) {
2632 UErrorCode status
= U_ZERO_ERROR
;
2633 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6) {
2634 cal
.set(UCAL_MONTH
, value
);
2636 cal
.set(UCAL_MONTH
, value
- 1);
2639 saveHebrewMonth
= value
;
2642 // Don't want to parse the month if it is a string
2643 // while pattern uses numeric style: M/MM, L/LL
2644 // [We computed 'value' above.]
2645 cal
.set(UCAL_MONTH
, value
- 1);
2647 return pos
.getIndex();
2649 // count >= 3 // i.e., MMM/MMMM, LLL/LLLL
2650 // Want to be able to parse both short and long forms.
2651 // Try count == 4 first:
2652 UnicodeString
* wideMonthPat
= NULL
;
2653 UnicodeString
* shortMonthPat
= NULL
;
2654 if (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
) {
2655 if (patternCharIndex
==UDAT_MONTH_FIELD
) {
2656 wideMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatWide
];
2657 shortMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatAbbrev
];
2659 wideMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneWide
];
2660 shortMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev
];
2663 int32_t newStart
= 0;
2664 if (patternCharIndex
==UDAT_MONTH_FIELD
) {
2665 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fMonths
, fSymbols
->fMonthsCount
, wideMonthPat
, cal
); // try MMMM
2669 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fShortMonths
, fSymbols
->fShortMonthsCount
, shortMonthPat
, cal
); // try MMM
2671 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fStandaloneMonths
, fSymbols
->fStandaloneMonthsCount
, wideMonthPat
, cal
); // try LLLL
2675 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fStandaloneShortMonths
, fSymbols
->fStandaloneShortMonthsCount
, shortMonthPat
, cal
); // try LLL
2677 if (newStart
> 0 || !lenient
) // currently we do not try to parse MMMMM/LLLLL: #8860
2679 // else we allowing parsing as number, below
2683 case UDAT_HOUR_OF_DAY1_FIELD
:
2684 // [We computed 'value' above.]
2685 if (value
== cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1)
2688 // fall through to set field
2690 case UDAT_HOUR_OF_DAY0_FIELD
:
2691 cal
.set(UCAL_HOUR_OF_DAY
, value
);
2692 return pos
.getIndex();
2694 case UDAT_FRACTIONAL_SECOND_FIELD
:
2695 // Fractional seconds left-justify
2696 i
= pos
.getIndex() - start
;
2708 value
= (value
+ (a
>>1)) / a
;
2710 cal
.set(UCAL_MILLISECOND
, value
);
2711 return pos
.getIndex();
2713 case UDAT_DOW_LOCAL_FIELD
:
2714 if (gotNumber
) // i.e., e or ee
2716 // [We computed 'value' above.]
2717 cal
.set(UCAL_DOW_LOCAL
, value
);
2718 return pos
.getIndex();
2720 // else for eee-eeeee fall through to handling of EEE-EEEEE
2721 // fall through, do not break here
2722 case UDAT_DAY_OF_WEEK_FIELD
:
2724 // Want to be able to parse both short and long forms.
2725 // Try count == 4 (EEEE) first:
2726 int32_t newStart
= 0;
2727 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2728 fSymbols
->fWeekdays
, fSymbols
->fWeekdaysCount
, NULL
, cal
)) > 0)
2730 // EEEE failed, now try EEE
2731 else if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2732 fSymbols
->fShortWeekdays
, fSymbols
->fShortWeekdaysCount
, NULL
, cal
)) > 0)
2734 // EEE failed, now try EEEEE
2735 else if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2736 fSymbols
->fNarrowWeekdays
, fSymbols
->fNarrowWeekdaysCount
, NULL
, cal
)) > 0)
2738 else if (!lenient
|| patternCharIndex
== UDAT_DAY_OF_WEEK_FIELD
)
2740 // else we allowing parsing as number, below
2744 case UDAT_STANDALONE_DAY_FIELD
:
2746 if (gotNumber
) // c or cc
2748 // [We computed 'value' above.]
2749 cal
.set(UCAL_DOW_LOCAL
, value
);
2750 return pos
.getIndex();
2752 // Want to be able to parse both short and long forms.
2753 // Try count == 4 (cccc) first:
2754 int32_t newStart
= 0;
2755 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2756 fSymbols
->fStandaloneWeekdays
, fSymbols
->fStandaloneWeekdaysCount
, NULL
, cal
)) > 0)
2758 else if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2759 fSymbols
->fStandaloneShortWeekdays
, fSymbols
->fStandaloneShortWeekdaysCount
, NULL
, cal
)) > 0)
2763 // else we allowing parsing as number, below
2767 case UDAT_AM_PM_FIELD
:
2768 return matchString(text
, start
, UCAL_AM_PM
, fSymbols
->fAmPms
, fSymbols
->fAmPmsCount
, NULL
, cal
);
2770 case UDAT_HOUR1_FIELD
:
2771 // [We computed 'value' above.]
2772 if (value
== cal
.getLeastMaximum(UCAL_HOUR
)+1)
2775 // fall through to set field
2777 case UDAT_HOUR0_FIELD
:
2778 cal
.set(UCAL_HOUR
, value
);
2779 return pos
.getIndex();
2781 case UDAT_QUARTER_FIELD
:
2782 if (gotNumber
) // i.e., Q or QQ.
2784 // Don't want to parse the month if it is a string
2785 // while pattern uses numeric style: Q or QQ.
2786 // [We computed 'value' above.]
2787 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
2788 return pos
.getIndex();
2790 // count >= 3 // i.e., QQQ or QQQQ
2791 // Want to be able to parse both short and long forms.
2792 // Try count == 4 first:
2793 int32_t newStart
= 0;
2795 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
2796 fSymbols
->fQuarters
, fSymbols
->fQuartersCount
, cal
)) > 0)
2798 else if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
2799 fSymbols
->fShortQuarters
, fSymbols
->fShortQuartersCount
, cal
)) > 0)
2803 // else we allowing parsing as number, below
2807 case UDAT_STANDALONE_QUARTER_FIELD
:
2808 if (gotNumber
) // i.e., q or qq.
2810 // Don't want to parse the month if it is a string
2811 // while pattern uses numeric style: q or q.
2812 // [We computed 'value' above.]
2813 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
2814 return pos
.getIndex();
2816 // count >= 3 // i.e., qqq or qqqq
2817 // Want to be able to parse both short and long forms.
2818 // Try count == 4 first:
2819 int32_t newStart
= 0;
2821 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
2822 fSymbols
->fStandaloneQuarters
, fSymbols
->fStandaloneQuartersCount
, cal
)) > 0)
2824 else if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
2825 fSymbols
->fStandaloneShortQuarters
, fSymbols
->fStandaloneShortQuartersCount
, cal
)) > 0)
2829 // else we allowing parsing as number, below
2833 case UDAT_TIMEZONE_FIELD
:
2835 UTimeZoneFormatTimeType tzTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
2836 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_SPECIFIC_SHORT
: UTZFMT_STYLE_SPECIFIC_LONG
;
2837 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzParseOptions
, &tzTimeType
);
2839 ((SimpleDateFormat
*)this)->tztype
= tzTimeType
;
2840 cal
.adoptTimeZone(tz
);
2841 return pos
.getIndex();
2845 case UDAT_TIMEZONE_RFC_FIELD
:
2847 UTimeZoneFormatTimeType tzTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
2848 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_RFC822
: ((count
== 5) ? UTZFMT_STYLE_ISO8601
: UTZFMT_STYLE_LOCALIZED_GMT
);
2849 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, &tzTimeType
);
2851 ((SimpleDateFormat
*)this)->tztype
= tzTimeType
;
2852 cal
.adoptTimeZone(tz
);
2853 return pos
.getIndex();
2857 case UDAT_TIMEZONE_GENERIC_FIELD
:
2859 UTimeZoneFormatTimeType tzTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
2860 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_GENERIC_SHORT
: UTZFMT_STYLE_GENERIC_LONG
;
2861 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzParseOptions
, &tzTimeType
);
2863 ((SimpleDateFormat
*)this)->tztype
= tzTimeType
;
2864 cal
.adoptTimeZone(tz
);
2865 return pos
.getIndex();
2869 case UDAT_TIMEZONE_SPECIAL_FIELD
:
2871 UTimeZoneFormatTimeType tzTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
2872 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_SPECIFIC_SHORT
: UTZFMT_STYLE_GENERIC_LOCATION
;
2873 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, &tzTimeType
);
2875 ((SimpleDateFormat
*)this)->tztype
= tzTimeType
;
2876 cal
.adoptTimeZone(tz
);
2877 return pos
.getIndex();
2883 // Handle "generic" fields
2884 // this is now handled below, outside the switch block
2887 // Handle "generic" fields:
2888 // switch default case now handled here (outside switch block) to allow
2889 // parsing of some string fields as digits for lenient case
2891 int32_t parseStart
= pos
.getIndex();
2892 const UnicodeString
* src
;
2894 if ((start
+count
) > text
.length()) {
2897 text
.extractBetween(0, start
+ count
, temp
);
2902 parseInt(*src
, number
, pos
, allowNegative
,currentNumberFormat
);
2903 if (pos
.getIndex() != parseStart
) {
2904 int32_t value
= number
.getLong();
2906 // Don't need suffix processing here (as in number processing at the beginning of the function);
2907 // the new fields being handled as numeric values (month, weekdays, quarters) should not have suffixes.
2909 // Check the range of the value
2911 int32_t bias
= gFieldRangeBias
[patternCharIndex
];
2912 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
|| value
< cal
.getMinimum(field
) + bias
)) {
2916 int32_t bias
= gFieldRangeBiasLenient
[patternCharIndex
];
2917 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
)) {
2922 // For the following, need to repeat some of the "if (gotNumber)" code above:
2923 // UDAT_[STANDALONE_]MONTH_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_STANDALONE_DAY_FIELD,
2924 // UDAT_[STANDALONE_]QUARTER_FIELD
2925 switch (patternCharIndex
) {
2926 case UDAT_MONTH_FIELD
:
2927 // See notes under UDAT_MONTH_FIELD case above
2928 if (!strcmp(cal
.getType(),"hebrew")) {
2929 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
2930 if (cal
.isSet(UCAL_YEAR
)) {
2931 UErrorCode status
= U_ZERO_ERROR
;
2932 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6) {
2933 cal
.set(UCAL_MONTH
, value
);
2935 cal
.set(UCAL_MONTH
, value
- 1);
2938 saveHebrewMonth
= value
;
2941 cal
.set(UCAL_MONTH
, value
- 1);
2944 case UDAT_STANDALONE_MONTH_FIELD
:
2945 cal
.set(UCAL_MONTH
, value
- 1);
2947 case UDAT_DOW_LOCAL_FIELD
:
2948 case UDAT_STANDALONE_DAY_FIELD
:
2949 cal
.set(UCAL_DOW_LOCAL
, value
);
2951 case UDAT_QUARTER_FIELD
:
2952 case UDAT_STANDALONE_QUARTER_FIELD
:
2953 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
2956 cal
.set(field
, value
);
2959 return pos
.getIndex();
2965 * Parse an integer using fNumberFormat. This method is semantically
2966 * const, but actually may modify fNumberFormat.
2968 void SimpleDateFormat::parseInt(const UnicodeString
& text
,
2969 Formattable
& number
,
2971 UBool allowNegative
,
2972 NumberFormat
*fmt
) const {
2973 parseInt(text
, number
, -1, pos
, allowNegative
,fmt
);
2977 * Parse an integer using fNumberFormat up to maxDigits.
2979 void SimpleDateFormat::parseInt(const UnicodeString
& text
,
2980 Formattable
& number
,
2983 UBool allowNegative
,
2984 NumberFormat
*fmt
) const {
2985 UnicodeString oldPrefix
;
2986 DecimalFormat
* df
= NULL
;
2987 if (!allowNegative
&& (df
= dynamic_cast<DecimalFormat
*>(fmt
)) != NULL
) {
2988 df
->getNegativePrefix(oldPrefix
);
2989 df
->setNegativePrefix(UnicodeString(TRUE
, SUPPRESS_NEGATIVE_PREFIX
, -1));
2991 int32_t oldPos
= pos
.getIndex();
2992 fmt
->parse(text
, number
, pos
);
2994 df
->setNegativePrefix(oldPrefix
);
2997 if (maxDigits
> 0) {
2998 // adjust the result to fit into
2999 // the maxDigits and move the position back
3000 int32_t nDigits
= pos
.getIndex() - oldPos
;
3001 if (nDigits
> maxDigits
) {
3002 int32_t val
= number
.getLong();
3003 nDigits
-= maxDigits
;
3004 while (nDigits
> 0) {
3008 pos
.setIndex(oldPos
+ maxDigits
);
3009 number
.setLong(val
);
3014 //----------------------------------------------------------------------
3016 void SimpleDateFormat::translatePattern(const UnicodeString
& originalPattern
,
3017 UnicodeString
& translatedPattern
,
3018 const UnicodeString
& from
,
3019 const UnicodeString
& to
,
3022 // run through the pattern and convert any pattern symbols from the version
3023 // in "from" to the corresponding character ion "to". This code takes
3024 // quoted strings into account (it doesn't try to translate them), and it signals
3025 // an error if a particular "pattern character" doesn't appear in "from".
3026 // Depending on the values of "from" and "to" this can convert from generic
3027 // to localized patterns or localized to generic.
3028 if (U_FAILURE(status
))
3031 translatedPattern
.remove();
3032 UBool inQuote
= FALSE
;
3033 for (int32_t i
= 0; i
< originalPattern
.length(); ++i
) {
3034 UChar c
= originalPattern
[i
];
3042 else if ((c
>= 0x0061 /*'a'*/ && c
<= 0x007A) /*'z'*/
3043 || (c
>= 0x0041 /*'A'*/ && c
<= 0x005A /*'Z'*/)) {
3044 int32_t ci
= from
.indexOf(c
);
3046 status
= U_INVALID_FORMAT_ERROR
;
3052 translatedPattern
+= c
;
3055 status
= U_INVALID_FORMAT_ERROR
;
3060 //----------------------------------------------------------------------
3063 SimpleDateFormat::toPattern(UnicodeString
& result
) const
3069 //----------------------------------------------------------------------
3072 SimpleDateFormat::toLocalizedPattern(UnicodeString
& result
,
3073 UErrorCode
& status
) const
3075 translatePattern(fPattern
, result
,
3076 UnicodeString(DateFormatSymbols::getPatternUChars()),
3077 fSymbols
->fLocalPatternChars
, status
);
3081 //----------------------------------------------------------------------
3084 SimpleDateFormat::applyPattern(const UnicodeString
& pattern
)
3089 //----------------------------------------------------------------------
3092 SimpleDateFormat::applyLocalizedPattern(const UnicodeString
& pattern
,
3095 translatePattern(pattern
, fPattern
,
3096 fSymbols
->fLocalPatternChars
,
3097 UnicodeString(DateFormatSymbols::getPatternUChars()), status
);
3100 //----------------------------------------------------------------------
3102 const DateFormatSymbols
*
3103 SimpleDateFormat::getDateFormatSymbols() const
3108 //----------------------------------------------------------------------
3111 SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols
* newFormatSymbols
)
3114 fSymbols
= newFormatSymbols
;
3117 //----------------------------------------------------------------------
3119 SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols
& newFormatSymbols
)
3122 fSymbols
= new DateFormatSymbols(newFormatSymbols
);
3125 //----------------------------------------------------------------------
3126 const TimeZoneFormat
*
3127 SimpleDateFormat::getTimeZoneFormat(void) const {
3128 return (const TimeZoneFormat
*)tzFormat();
3131 //----------------------------------------------------------------------
3133 SimpleDateFormat::adoptTimeZoneFormat(TimeZoneFormat
* timeZoneFormatToAdopt
)
3135 delete fTimeZoneFormat
;
3136 fTimeZoneFormat
= timeZoneFormatToAdopt
;
3139 //----------------------------------------------------------------------
3141 SimpleDateFormat::setTimeZoneFormat(const TimeZoneFormat
& newTimeZoneFormat
)
3143 delete fTimeZoneFormat
;
3144 fTimeZoneFormat
= new TimeZoneFormat(newTimeZoneFormat
);
3147 //----------------------------------------------------------------------
3150 void SimpleDateFormat::adoptCalendar(Calendar
* calendarToAdopt
)
3152 UErrorCode status
= U_ZERO_ERROR
;
3153 DateFormat::adoptCalendar(calendarToAdopt
);
3156 initializeSymbols(fLocale
, fCalendar
, status
); // we need new symbols
3157 initializeDefaultCentury(); // we need a new century (possibly)
3161 //----------------------------------------------------------------------
3164 void SimpleDateFormat::setDefaultContext(UDateFormatContextType type
,
3165 UDateFormatContextValue value
, UErrorCode
& status
)
3167 if (U_FAILURE(status
))
3169 if (type
!= UDAT_CAPITALIZATION
) {
3170 status
= U_ILLEGAL_ARGUMENT_ERROR
;
3173 fDefaultCapitalizationContext
= value
;
3177 //----------------------------------------------------------------------
3180 int32_t SimpleDateFormat::getDefaultContext(UDateFormatContextType type
, UErrorCode
& status
) const
3182 if (U_FAILURE(status
))
3184 if (type
!= UDAT_CAPITALIZATION
) {
3185 status
= U_ILLEGAL_ARGUMENT_ERROR
;
3188 return (int32_t)fDefaultCapitalizationContext
;
3192 //----------------------------------------------------------------------
3196 SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field
) const {
3197 return isFieldUnitIgnored(fPattern
, field
);
3202 SimpleDateFormat::isFieldUnitIgnored(const UnicodeString
& pattern
,
3203 UCalendarDateFields field
) {
3204 int32_t fieldLevel
= fgCalendarFieldToLevel
[field
];
3207 UBool inQuote
= FALSE
;
3211 for (int32_t i
= 0; i
< pattern
.length(); ++i
) {
3213 if (ch
!= prevCh
&& count
> 0) {
3214 level
= fgPatternCharToLevel
[prevCh
- PATTERN_CHAR_BASE
];
3215 // the larger the level, the smaller the field unit.
3216 if ( fieldLevel
<= level
) {
3222 if ((i
+1) < pattern
.length() && pattern
[i
+1] == QUOTE
) {
3225 inQuote
= ! inQuote
;
3228 else if ( ! inQuote
&& ((ch
>= 0x0061 /*'a'*/ && ch
<= 0x007A /*'z'*/)
3229 || (ch
>= 0x0041 /*'A'*/ && ch
<= 0x005A /*'Z'*/))) {
3236 level
= fgPatternCharToLevel
[prevCh
- PATTERN_CHAR_BASE
];
3237 if ( fieldLevel
<= level
) {
3244 //----------------------------------------------------------------------
3247 SimpleDateFormat::getSmpFmtLocale(void) const {
3251 //----------------------------------------------------------------------
3254 SimpleDateFormat::checkIntSuffix(const UnicodeString
& text
, int32_t start
,
3255 int32_t patLoc
, UBool isNegative
) const {
3258 int32_t patternMatch
;
3259 int32_t textPreMatch
;
3260 int32_t textPostMatch
;
3262 // check that we are still in range
3263 if ( (start
> text
.length()) ||
3266 (patLoc
> fPattern
.length())) {
3267 // out of range, don't advance location in text
3272 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(fNumberFormat
);
3273 if (decfmt
!= NULL
) {
3275 suf
= decfmt
->getNegativeSuffix(suf
);
3278 suf
= decfmt
->getPositiveSuffix(suf
);
3283 if (suf
.length() <= 0) {
3287 // check suffix will be encountered in the pattern
3288 patternMatch
= compareSimpleAffix(suf
,fPattern
,patLoc
);
3290 // check if a suffix will be encountered in the text
3291 textPreMatch
= compareSimpleAffix(suf
,text
,start
);
3293 // check if a suffix was encountered in the text
3294 textPostMatch
= compareSimpleAffix(suf
,text
,start
-suf
.length());
3296 // check for suffix match
3297 if ((textPreMatch
>= 0) && (patternMatch
>= 0) && (textPreMatch
== patternMatch
)) {
3300 else if ((textPostMatch
>= 0) && (patternMatch
>= 0) && (textPostMatch
== patternMatch
)) {
3301 return start
- suf
.length();
3304 // should not get here
3308 //----------------------------------------------------------------------
3311 SimpleDateFormat::compareSimpleAffix(const UnicodeString
& affix
,
3312 const UnicodeString
& input
,
3313 int32_t pos
) const {
3314 int32_t start
= pos
;
3315 for (int32_t i
=0; i
<affix
.length(); ) {
3316 UChar32 c
= affix
.char32At(i
);
3317 int32_t len
= U16_LENGTH(c
);
3318 if (PatternProps::isWhiteSpace(c
)) {
3319 // We may have a pattern like: \u200F \u0020
3320 // and input text like: \u200F \u0020
3321 // Note that U+200F and U+0020 are Pattern_White_Space but only
3322 // U+0020 is UWhiteSpace. So we have to first do a direct
3323 // match of the run of Pattern_White_Space in the pattern,
3324 // then match any extra characters.
3325 UBool literalMatch
= FALSE
;
3326 while (pos
< input
.length() &&
3327 input
.char32At(pos
) == c
) {
3328 literalMatch
= TRUE
;
3331 if (i
== affix
.length()) {
3334 c
= affix
.char32At(i
);
3335 len
= U16_LENGTH(c
);
3336 if (!PatternProps::isWhiteSpace(c
)) {
3341 // Advance over run in pattern
3342 i
= skipPatternWhiteSpace(affix
, i
);
3344 // Advance over run in input text
3345 // Must see at least one white space char in input,
3346 // unless we've already matched some characters literally.
3348 pos
= skipUWhiteSpace(input
, pos
);
3349 if (pos
== s
&& !literalMatch
) {
3353 // If we skip UWhiteSpace in the input text, we need to skip it in the pattern.
3354 // Otherwise, the previous lines may have skipped over text (such as U+00A0) that
3355 // is also in the affix.
3356 i
= skipUWhiteSpace(affix
, i
);
3358 if (pos
< input
.length() &&
3359 input
.char32At(pos
) == c
) {
3370 //----------------------------------------------------------------------
3373 SimpleDateFormat::skipPatternWhiteSpace(const UnicodeString
& text
, int32_t pos
) const {
3374 const UChar
* s
= text
.getBuffer();
3375 return (int32_t)(PatternProps::skipWhiteSpace(s
+ pos
, text
.length() - pos
) - s
);
3378 //----------------------------------------------------------------------
3381 SimpleDateFormat::skipUWhiteSpace(const UnicodeString
& text
, int32_t pos
) const {
3382 while (pos
< text
.length()) {
3383 UChar32 c
= text
.char32At(pos
);
3384 if (!u_isUWhiteSpace(c
)) {
3387 pos
+= U16_LENGTH(c
);
3392 //----------------------------------------------------------------------
3394 // Lazy TimeZoneFormat instantiation, semantically const.
3396 SimpleDateFormat::tzFormat() const {
3397 if (fTimeZoneFormat
== NULL
) {
3400 if (fTimeZoneFormat
== NULL
) {
3401 UErrorCode status
= U_ZERO_ERROR
;
3402 TimeZoneFormat
*tzfmt
= TimeZoneFormat::createInstance(fLocale
, status
);
3403 if (U_FAILURE(status
)) {
3407 const_cast<SimpleDateFormat
*>(this)->fTimeZoneFormat
= tzfmt
;
3412 return fTimeZoneFormat
;
3417 #endif /* #if !UCONFIG_NO_FORMATTING */