2 *******************************************************************************
3 * Copyright (C) 1997-2014, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
9 * Modification History:
11 * Date Name Description
12 * 02/19/97 aliu Converted from java.
13 * 03/31/97 aliu Modified extensively to work with 50 locales.
14 * 04/01/97 aliu Added support for centuries.
15 * 07/09/97 helena Made ParsePosition into a class.
16 * 07/21/98 stephen Added initializeDefaultCentury.
17 * Removed getZoneIndex (added in DateFormatSymbols)
18 * Removed subParseLong
20 * 02/22/99 stephen Removed character literals for EBCDIC safety
21 * 10/14/99 aliu Updated 2-digit year parsing so that only "00" thru
22 * "99" are recognized. {j28 4182066}
23 * 11/15/99 weiv Added support for week of year/day of week format
24 ********************************************************************************
27 #define ZID_KEY_MAX 128
29 #include "unicode/utypes.h"
31 #if !UCONFIG_NO_FORMATTING
33 #include "unicode/smpdtfmt.h"
34 #include "unicode/dtfmtsym.h"
35 #include "unicode/ures.h"
36 #include "unicode/msgfmt.h"
37 #include "unicode/calendar.h"
38 #include "unicode/gregocal.h"
39 #include "unicode/timezone.h"
40 #include "unicode/decimfmt.h"
41 #include "unicode/dcfmtsym.h"
42 #include "unicode/uchar.h"
43 #include "unicode/uniset.h"
44 #include "unicode/ustring.h"
45 #include "unicode/basictz.h"
46 #include "unicode/simpletz.h"
47 #include "unicode/rbtz.h"
48 #include "unicode/tzfmt.h"
49 #include "unicode/utf16.h"
50 #include "unicode/vtzone.h"
51 #include "unicode/udisplaycontext.h"
52 #include "unicode/brkiter.h"
54 #include "patternprops.h"
65 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
69 // *****************************************************************************
70 // class SimpleDateFormat
71 // *****************************************************************************
75 static const UChar PATTERN_CHAR_BASE
= 0x40;
78 * Last-resort string to use for "GMT" when constructing time zone strings.
80 // For time zones that have no names, use strings GMT+minutes and
81 // GMT-minutes. For instance, in France the time zone is GMT+60.
82 // Also accepted are GMT+H:MM or GMT-H:MM.
83 // Currently not being used
84 //static const UChar gGmt[] = {0x0047, 0x004D, 0x0054, 0x0000}; // "GMT"
85 //static const UChar gGmtPlus[] = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+"
86 //static const UChar gGmtMinus[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-"
87 //static const UChar gDefGmtPat[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
88 //static const UChar gDefGmtNegHmsPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */
89 //static const UChar gDefGmtNegHmPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */
90 //static const UChar gDefGmtPosHmsPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */
91 //static const UChar gDefGmtPosHmPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */
92 //static const UChar gUt[] = {0x0055, 0x0054, 0x0000}; // "UT"
93 //static const UChar gUtc[] = {0x0055, 0x0054, 0x0043, 0x0000}; // "UT"
95 typedef enum GmtPatSize
{
106 // Stuff needed for numbering system overrides
108 typedef enum OvrStrType
{
114 static const UDateFormatField kDateFields
[] = {
118 UDAT_DAY_OF_YEAR_FIELD
,
119 UDAT_DAY_OF_WEEK_IN_MONTH_FIELD
,
120 UDAT_WEEK_OF_YEAR_FIELD
,
121 UDAT_WEEK_OF_MONTH_FIELD
,
123 UDAT_EXTENDED_YEAR_FIELD
,
124 UDAT_JULIAN_DAY_FIELD
,
125 UDAT_STANDALONE_DAY_FIELD
,
126 UDAT_STANDALONE_MONTH_FIELD
,
128 UDAT_STANDALONE_QUARTER_FIELD
,
129 UDAT_YEAR_NAME_FIELD
,
130 UDAT_RELATED_YEAR_FIELD
};
131 static const int8_t kDateFieldsCount
= 16;
133 static const UDateFormatField kTimeFields
[] = {
134 UDAT_HOUR_OF_DAY1_FIELD
,
135 UDAT_HOUR_OF_DAY0_FIELD
,
138 UDAT_FRACTIONAL_SECOND_FIELD
,
141 UDAT_MILLISECONDS_IN_DAY_FIELD
,
142 UDAT_TIMEZONE_RFC_FIELD
,
143 UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
};
144 static const int8_t kTimeFieldsCount
= 10;
147 // This is a pattern-of-last-resort used when we can't load a usable pattern out
149 static const UChar gDefaultPattern
[] =
151 0x79, 0x79, 0x79, 0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0
152 }; /* "yyyyMMdd hh:mm a" */
154 // This prefix is designed to NEVER MATCH real text, in order to
155 // suppress the parsing of negative numbers. Adjust as needed (if
156 // this becomes valid Unicode).
157 static const UChar SUPPRESS_NEGATIVE_PREFIX
[] = {0xAB00, 0};
160 * These are the tags we expect to see in normal resource bundle files associated
163 static const char gDateTimePatternsTag
[]="DateTimePatterns";
165 //static const UChar gEtcUTC[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x54, 0x43, 0x00}; // "Etc/UTC"
166 static const UChar QUOTE
= 0x27; // Single quote
169 * The field range check bias for each UDateFormatField.
170 * The bias is added to the minimum and maximum values
171 * before they are compared to the parsed number.
172 * For example, the calendar stores zero-based month numbers
173 * but the parsed month numbers start at 1, so the bias is 1.
175 * A value of -1 means that the value is not checked.
177 static const int32_t gFieldRangeBias
[] = {
178 -1, // 'G' - UDAT_ERA_FIELD
179 -1, // 'y' - UDAT_YEAR_FIELD
180 1, // 'M' - UDAT_MONTH_FIELD
181 0, // 'd' - UDAT_DATE_FIELD
182 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
183 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
184 0, // 'm' - UDAT_MINUTE_FIELD
185 0, // 's' - UDAT_SECOND_FIELD
186 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
187 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
188 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
189 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
190 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
191 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
192 -1, // 'a' - UDAT_AM_PM_FIELD
193 -1, // 'h' - UDAT_HOUR1_FIELD
194 -1, // 'K' - UDAT_HOUR0_FIELD
195 -1, // 'z' - UDAT_TIMEZONE_FIELD
196 -1, // 'Y' - UDAT_YEAR_WOY_FIELD
197 -1, // 'e' - UDAT_DOW_LOCAL_FIELD
198 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
199 -1, // 'g' - UDAT_JULIAN_DAY_FIELD
200 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
201 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
202 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
203 0, // 'c' - UDAT_STANDALONE_DAY_FIELD
204 1, // 'L' - UDAT_STANDALONE_MONTH_FIELD
205 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
206 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
207 -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
208 -1, // 'U' - UDAT_YEAR_NAME_FIELD
209 -1, // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
210 -1, // 'X' - UDAT_TIMEZONE_ISO_FIELD
211 -1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
212 -1, // 'r' - UDAT_RELATED_YEAR_FIELD
214 // A slightly looser range check for lenient parsing
215 static const int32_t gFieldRangeBiasLenient
[] = {
216 -1, // 'G' - UDAT_ERA_FIELD
217 -1, // 'y' - UDAT_YEAR_FIELD
218 8, // 'M' - UDAT_MONTH_FIELD (allow calendar max + 7, e.g. 19 for grego 1-based month)
219 18, // 'd' - UDAT_DATE_FIELD (allow calendar max + 18, e.g. 49 for grego; tests require at least 40 for grego)
220 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
221 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
222 40, // 'm' - UDAT_MINUTE_FIELD (allow calendar max + 40, e.g. 99)
223 40, // 's' - UDAT_SECOND_FIELD (allow calendar max + 40, e.g. 99)
224 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
225 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
226 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
227 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
228 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
229 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
230 -1, // 'a' - UDAT_AM_PM_FIELD
231 -1, // 'h' - UDAT_HOUR1_FIELD
232 -1, // 'K' - UDAT_HOUR0_FIELD
233 -1, // 'z' - UDAT_TIMEZONE_FIELD
234 -1, // 'Y' - UDAT_YEAR_WOY_FIELD
235 -1, // 'e' - UDAT_DOW_LOCAL_FIELD
236 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
237 -1, // 'g' - UDAT_JULIAN_DAY_FIELD
238 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
239 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
240 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
241 18, // 'c' - UDAT_STANDALONE_DAY_FIELD (allow calendar max + 18, e.g. 49 for grego)
242 8, // 'L' - UDAT_STANDALONE_MONTH_FIELD (allow calendar max + 7, e.g. 19 for grego 1-based month)
243 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
244 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
245 -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
246 -1, // 'U' - UDAT_YEAR_NAME_FIELD
247 -1, // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
248 -1, // 'X' - UDAT_TIMEZONE_ISO_FIELD
249 -1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
250 -1, // 'r' - UDAT_RELATED_YEAR_FIELD
253 // When calendar uses hebr numbering (i.e. he@calendar=hebrew),
254 // offset the years within the current millenium down to 1-999
255 static const int32_t HEBREW_CAL_CUR_MILLENIUM_START_YEAR
= 5000;
256 static const int32_t HEBREW_CAL_CUR_MILLENIUM_END_YEAR
= 6000;
258 static UMutex LOCK
= U_MUTEX_INITIALIZER
;
260 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat
)
262 //----------------------------------------------------------------------
264 SimpleDateFormat::~SimpleDateFormat()
267 if (fNumberFormatters
) {
268 uprv_free(fNumberFormatters
);
270 if (fTimeZoneFormat
) {
271 delete fTimeZoneFormat
;
274 while (fOverrideList
) {
275 NSOverride
*cur
= fOverrideList
;
276 fOverrideList
= cur
->next
;
281 #if !UCONFIG_NO_BREAK_ITERATION
282 delete fCapitalizationBrkIter
;
286 //----------------------------------------------------------------------
288 SimpleDateFormat::SimpleDateFormat(UErrorCode
& status
)
289 : fLocale(Locale::getDefault()),
291 fTimeZoneFormat(NULL
),
292 fNumberFormatters(NULL
),
294 fCapitalizationBrkIter(NULL
)
296 initializeBooleanAttributes();
297 construct(kShort
, (EStyle
) (kShort
+ kDateOffset
), fLocale
, status
);
298 initializeDefaultCentury();
301 //----------------------------------------------------------------------
303 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
306 fLocale(Locale::getDefault()),
308 fTimeZoneFormat(NULL
),
309 fNumberFormatters(NULL
),
311 fCapitalizationBrkIter(NULL
)
313 fDateOverride
.setToBogus();
314 fTimeOverride
.setToBogus();
315 initializeBooleanAttributes();
316 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
317 initialize(fLocale
, status
);
318 initializeDefaultCentury();
321 //----------------------------------------------------------------------
323 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
324 const UnicodeString
& override
,
327 fLocale(Locale::getDefault()),
329 fTimeZoneFormat(NULL
),
330 fNumberFormatters(NULL
),
332 fCapitalizationBrkIter(NULL
)
334 fDateOverride
.setTo(override
);
335 fTimeOverride
.setToBogus();
336 initializeBooleanAttributes();
337 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
338 initialize(fLocale
, status
);
339 initializeDefaultCentury();
341 processOverrideString(fLocale
,override
,kOvrStrBoth
,status
);
345 //----------------------------------------------------------------------
347 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
348 const Locale
& locale
,
352 fTimeZoneFormat(NULL
),
353 fNumberFormatters(NULL
),
355 fCapitalizationBrkIter(NULL
)
358 fDateOverride
.setToBogus();
359 fTimeOverride
.setToBogus();
360 initializeBooleanAttributes();
362 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
363 initialize(fLocale
, status
);
364 initializeDefaultCentury();
367 //----------------------------------------------------------------------
369 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
370 const UnicodeString
& override
,
371 const Locale
& locale
,
375 fTimeZoneFormat(NULL
),
376 fNumberFormatters(NULL
),
378 fCapitalizationBrkIter(NULL
)
381 fDateOverride
.setTo(override
);
382 fTimeOverride
.setToBogus();
383 initializeBooleanAttributes();
385 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
386 initialize(fLocale
, status
);
387 initializeDefaultCentury();
389 processOverrideString(locale
,override
,kOvrStrBoth
,status
);
393 //----------------------------------------------------------------------
395 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
396 DateFormatSymbols
* symbolsToAdopt
,
399 fLocale(Locale::getDefault()),
400 fSymbols(symbolsToAdopt
),
401 fTimeZoneFormat(NULL
),
402 fNumberFormatters(NULL
),
404 fCapitalizationBrkIter(NULL
)
407 fDateOverride
.setToBogus();
408 fTimeOverride
.setToBogus();
409 initializeBooleanAttributes();
411 initializeCalendar(NULL
,fLocale
,status
);
412 initialize(fLocale
, status
);
413 initializeDefaultCentury();
416 //----------------------------------------------------------------------
418 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
419 const DateFormatSymbols
& symbols
,
422 fLocale(Locale::getDefault()),
423 fSymbols(new DateFormatSymbols(symbols
)),
424 fTimeZoneFormat(NULL
),
425 fNumberFormatters(NULL
),
427 fCapitalizationBrkIter(NULL
)
430 fDateOverride
.setToBogus();
431 fTimeOverride
.setToBogus();
432 initializeBooleanAttributes();
434 initializeCalendar(NULL
, fLocale
, status
);
435 initialize(fLocale
, status
);
436 initializeDefaultCentury();
439 //----------------------------------------------------------------------
441 // Not for public consumption; used by DateFormat
442 SimpleDateFormat::SimpleDateFormat(EStyle timeStyle
,
444 const Locale
& locale
,
448 fTimeZoneFormat(NULL
),
449 fNumberFormatters(NULL
),
451 fCapitalizationBrkIter(NULL
)
453 initializeBooleanAttributes();
454 construct(timeStyle
, dateStyle
, fLocale
, status
);
455 if(U_SUCCESS(status
)) {
456 initializeDefaultCentury();
460 //----------------------------------------------------------------------
463 * Not for public consumption; used by DateFormat. This constructor
464 * never fails. If the resource data is not available, it uses the
465 * the last resort symbols.
467 SimpleDateFormat::SimpleDateFormat(const Locale
& locale
,
469 : fPattern(gDefaultPattern
),
472 fTimeZoneFormat(NULL
),
473 fNumberFormatters(NULL
),
475 fCapitalizationBrkIter(NULL
)
477 if (U_FAILURE(status
)) return;
478 initializeBooleanAttributes();
479 initializeSymbols(fLocale
, initializeCalendar(NULL
, fLocale
, status
),status
);
480 if (U_FAILURE(status
))
482 status
= U_ZERO_ERROR
;
484 // This constructor doesn't fail; it uses last resort data
485 fSymbols
= new DateFormatSymbols(status
);
488 status
= U_MEMORY_ALLOCATION_ERROR
;
493 fDateOverride
.setToBogus();
494 fTimeOverride
.setToBogus();
496 initialize(fLocale
, status
);
497 if(U_SUCCESS(status
)) {
498 initializeDefaultCentury();
502 //----------------------------------------------------------------------
504 SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat
& other
)
506 fLocale(other
.fLocale
),
508 fTimeZoneFormat(NULL
),
509 fNumberFormatters(NULL
),
511 fCapitalizationBrkIter(NULL
)
513 initializeBooleanAttributes();
517 //----------------------------------------------------------------------
519 SimpleDateFormat
& SimpleDateFormat::operator=(const SimpleDateFormat
& other
)
521 if (this == &other
) {
524 DateFormat::operator=(other
);
526 fPattern
= other
.fPattern
;
527 fDateOverride
= other
.fDateOverride
;
528 fTimeOverride
= other
.fTimeOverride
;
530 fLocale
= other
.fLocale
;
534 if (other
.fSymbols
) {
535 fSymbols
= new DateFormatSymbols(*other
.fSymbols
);
538 // TimeZoneFormat can now be set independently via setter.
539 // If it is NULL, it will be lazily initialized from locale
540 delete fTimeZoneFormat
;
541 fTimeZoneFormat
= NULL
;
542 if (other
.fTimeZoneFormat
) {
543 fTimeZoneFormat
= new TimeZoneFormat(*other
.fTimeZoneFormat
);
546 fDefaultCenturyStart
= other
.fDefaultCenturyStart
;
547 fDefaultCenturyStartYear
= other
.fDefaultCenturyStartYear
;
548 fHaveDefaultCentury
= other
.fHaveDefaultCentury
;
550 // Delete fNumberFormatters and fOverrideList,
551 // then rebuild from override strings if necessary
552 if (fNumberFormatters
) {
553 uprv_free(fNumberFormatters
);
555 while (fOverrideList
) {
556 NSOverride
*cur
= fOverrideList
;
557 fOverrideList
= cur
->next
;
561 UErrorCode status
= U_ZERO_ERROR
;
562 initNumberFormatters(fLocale
, status
);
564 #if !UCONFIG_NO_BREAK_ITERATION
565 if (other
.fCapitalizationBrkIter
!= NULL
) {
566 fCapitalizationBrkIter
= (other
.fCapitalizationBrkIter
)->clone();
573 //----------------------------------------------------------------------
576 SimpleDateFormat::clone() const
578 return new SimpleDateFormat(*this);
581 //----------------------------------------------------------------------
584 SimpleDateFormat::operator==(const Format
& other
) const
586 if (DateFormat::operator==(other
)) {
587 // The DateFormat::operator== check for fCapitalizationContext equality above
588 // is sufficient to check equality of all derived context-related data.
589 // DateFormat::operator== guarantees following cast is safe
590 SimpleDateFormat
* that
= (SimpleDateFormat
*)&other
;
591 return (fPattern
== that
->fPattern
&&
592 fSymbols
!= NULL
&& // Check for pathological object
593 that
->fSymbols
!= NULL
&& // Check for pathological object
594 *fSymbols
== *that
->fSymbols
&&
595 fHaveDefaultCentury
== that
->fHaveDefaultCentury
&&
596 fDefaultCenturyStart
== that
->fDefaultCenturyStart
&&
597 // Check fTimeZoneFormat, it can be set independently via setter
598 ((fTimeZoneFormat
== NULL
&& that
->fTimeZoneFormat
== NULL
) ||
599 (fTimeZoneFormat
!= NULL
&& that
->fTimeZoneFormat
!= NULL
&& *fTimeZoneFormat
== *that
->fTimeZoneFormat
)) &&
600 // Check override strings (these also indicate any relevant
601 // differences in fNumberFormatters, fOverrideList)
602 fDateOverride
== that
->fDateOverride
&&
603 fTimeOverride
== that
->fTimeOverride
);
608 //----------------------------------------------------------------------
610 void SimpleDateFormat::construct(EStyle timeStyle
,
612 const Locale
& locale
,
615 // called by several constructors to load pattern data from the resources
616 if (U_FAILURE(status
)) return;
618 // We will need the calendar to know what type of symbols to load.
619 initializeCalendar(NULL
, locale
, status
);
620 if (U_FAILURE(status
)) return;
622 CalendarData
calData(locale
, fCalendar
?fCalendar
->getType():NULL
, status
);
623 UResourceBundle
*dateTimePatterns
= calData
.getByKey(gDateTimePatternsTag
, status
);
624 UResourceBundle
*currentBundle
;
626 if (U_FAILURE(status
)) return;
628 if (ures_getSize(dateTimePatterns
) <= kDateTime
)
630 status
= U_INVALID_FORMAT_ERROR
;
634 setLocaleIDs(ures_getLocaleByType(dateTimePatterns
, ULOC_VALID_LOCALE
, &status
),
635 ures_getLocaleByType(dateTimePatterns
, ULOC_ACTUAL_LOCALE
, &status
));
637 // create a symbols object from the locale
638 initializeSymbols(locale
,fCalendar
, status
);
639 if (U_FAILURE(status
)) return;
642 status
= U_MEMORY_ALLOCATION_ERROR
;
646 const UChar
*resStr
,*ovrStr
;
647 int32_t resStrLen
,ovrStrLen
= 0;
648 fDateOverride
.setToBogus();
649 fTimeOverride
.setToBogus();
651 // if the pattern should include both date and time information, use the date/time
652 // pattern string as a guide to tell use how to glue together the appropriate date
653 // and time pattern strings. The actual gluing-together is handled by a convenience
654 // method on MessageFormat.
655 if ((timeStyle
!= kNone
) && (dateStyle
!= kNone
))
657 Formattable timeDateArray
[2];
659 // use Formattable::adoptString() so that we can use fastCopyFrom()
660 // instead of Formattable::setString()'s unaware, safe, deep string clone
661 // see Jitterbug 2296
663 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)timeStyle
, NULL
, &status
);
664 if (U_FAILURE(status
)) {
665 status
= U_INVALID_FORMAT_ERROR
;
668 switch (ures_getType(currentBundle
)) {
670 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
674 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
675 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
676 fTimeOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
680 status
= U_INVALID_FORMAT_ERROR
;
681 ures_close(currentBundle
);
685 ures_close(currentBundle
);
687 UnicodeString
*tempus1
= new UnicodeString(TRUE
, resStr
, resStrLen
);
688 // NULL pointer check
689 if (tempus1
== NULL
) {
690 status
= U_MEMORY_ALLOCATION_ERROR
;
693 timeDateArray
[0].adoptString(tempus1
);
695 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)dateStyle
, NULL
, &status
);
696 if (U_FAILURE(status
)) {
697 status
= U_INVALID_FORMAT_ERROR
;
700 switch (ures_getType(currentBundle
)) {
702 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
706 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
707 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
708 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
712 status
= U_INVALID_FORMAT_ERROR
;
713 ures_close(currentBundle
);
717 ures_close(currentBundle
);
719 UnicodeString
*tempus2
= new UnicodeString(TRUE
, resStr
, resStrLen
);
720 // Null pointer check
721 if (tempus2
== NULL
) {
722 status
= U_MEMORY_ALLOCATION_ERROR
;
725 timeDateArray
[1].adoptString(tempus2
);
727 int32_t glueIndex
= kDateTime
;
728 int32_t patternsSize
= ures_getSize(dateTimePatterns
);
729 if (patternsSize
>= (kDateTimeOffset
+ kShort
+ 1)) {
730 // Get proper date time format
731 glueIndex
= (int32_t)(kDateTimeOffset
+ (dateStyle
- kDateOffset
));
734 resStr
= ures_getStringByIndex(dateTimePatterns
, glueIndex
, &resStrLen
, &status
);
735 MessageFormat::format(UnicodeString(TRUE
, resStr
, resStrLen
), timeDateArray
, 2, fPattern
, status
);
737 // if the pattern includes just time data or just date date, load the appropriate
738 // pattern string from the resources
739 // setTo() - see DateFormatSymbols::assignArray comments
740 else if (timeStyle
!= kNone
) {
741 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)timeStyle
, NULL
, &status
);
742 if (U_FAILURE(status
)) {
743 status
= U_INVALID_FORMAT_ERROR
;
746 switch (ures_getType(currentBundle
)) {
748 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
752 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
753 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
754 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
758 status
= U_INVALID_FORMAT_ERROR
;
759 ures_close(currentBundle
);
763 fPattern
.setTo(TRUE
, resStr
, resStrLen
);
764 ures_close(currentBundle
);
766 else if (dateStyle
!= kNone
) {
767 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)dateStyle
, NULL
, &status
);
768 if (U_FAILURE(status
)) {
769 status
= U_INVALID_FORMAT_ERROR
;
772 switch (ures_getType(currentBundle
)) {
774 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
778 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
779 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
780 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
784 status
= U_INVALID_FORMAT_ERROR
;
785 ures_close(currentBundle
);
789 fPattern
.setTo(TRUE
, resStr
, resStrLen
);
790 ures_close(currentBundle
);
793 // and if it includes _neither_, that's an error
795 status
= U_INVALID_FORMAT_ERROR
;
797 // finally, finish initializing by creating a Calendar and a NumberFormat
798 initialize(locale
, status
);
801 //----------------------------------------------------------------------
804 SimpleDateFormat::initializeCalendar(TimeZone
* adoptZone
, const Locale
& locale
, UErrorCode
& status
)
806 if(!U_FAILURE(status
)) {
807 fCalendar
= Calendar::createInstance(adoptZone
?adoptZone
:TimeZone::createDefault(), locale
, status
);
809 if (U_SUCCESS(status
) && fCalendar
== NULL
) {
810 status
= U_MEMORY_ALLOCATION_ERROR
;
816 SimpleDateFormat::initializeSymbols(const Locale
& locale
, Calendar
* calendar
, UErrorCode
& status
)
818 if(U_FAILURE(status
)) {
821 // pass in calendar type - use NULL (default) if no calendar set (or err).
822 fSymbols
= new DateFormatSymbols(locale
, calendar
?calendar
->getType() :NULL
, status
);
823 // Null pointer check
824 if (fSymbols
== NULL
) {
825 status
= U_MEMORY_ALLOCATION_ERROR
;
832 SimpleDateFormat::initialize(const Locale
& locale
,
835 if (U_FAILURE(status
)) return;
837 // If the locale has @[....]numbers=hanidays we want to *delete* that (so it
838 // it is not used for every field) and then set fDateOverride to "d=hanidays"
839 // (as with std formats for zh@calendar=chinese) to use hanidays for d field.
840 static const UChar hanidaysOverride
[] = {0x64,0x3D,0x68,0x61,0x6E,0x69,0x64,0x61,0x79,0x73,0}; // "d=hanidays"
841 char numbersValue
[ULOC_KEYWORDS_CAPACITY
];
842 UErrorCode numbersStatus
= U_ZERO_ERROR
;
843 Locale
localeNoHanidays(locale
);
844 int32_t numbersLen
= localeNoHanidays
.getKeywordValue("numbers", numbersValue
, ULOC_KEYWORDS_CAPACITY
, numbersStatus
);
845 if ( U_SUCCESS(numbersStatus
) && numbersLen
> 0 ) {
846 if ( uprv_strcmp(numbersValue
, "hanidays") == 0 ) {
847 localeNoHanidays
.setKeywordValue("numbers", NULL
, numbersStatus
);
848 fDateOverride
.setTo(hanidaysOverride
,-1);
852 // We don't need to check that the row count is >= 1, since all 2d arrays have at
854 fNumberFormat
= NumberFormat::createInstance(localeNoHanidays
, status
);
855 if (fNumberFormat
!= NULL
&& U_SUCCESS(status
))
857 // no matter what the locale's default number format looked like, we want
858 // to modify it so that it doesn't use thousands separators, doesn't always
859 // show the decimal point, and recognizes integers only when parsing
861 fNumberFormat
->setGroupingUsed(FALSE
);
862 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(fNumberFormat
);
863 if (decfmt
!= NULL
) {
864 decfmt
->setDecimalSeparatorAlwaysShown(FALSE
);
866 fNumberFormat
->setParseIntegerOnly(TRUE
);
867 fNumberFormat
->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
869 //fNumberFormat->setLenient(TRUE); // Java uses a custom DateNumberFormat to format/parse
871 initNumberFormatters(locale
,status
);
874 else if (U_SUCCESS(status
))
876 status
= U_MISSING_RESOURCE_ERROR
;
880 /* Initialize the fields we use to disambiguate ambiguous years. Separate
881 * so we can call it from readObject().
883 void SimpleDateFormat::initializeDefaultCentury()
886 fHaveDefaultCentury
= fCalendar
->haveDefaultCentury();
887 if(fHaveDefaultCentury
) {
888 fDefaultCenturyStart
= fCalendar
->defaultCenturyStart();
889 fDefaultCenturyStartYear
= fCalendar
->defaultCenturyStartYear();
891 fDefaultCenturyStart
= DBL_MIN
;
892 fDefaultCenturyStartYear
= -1;
898 * Initialize the boolean attributes. Separate so we can call it from all constructors.
900 void SimpleDateFormat::initializeBooleanAttributes()
902 UErrorCode status
= U_ZERO_ERROR
;
904 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, true, status
);
905 setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, true, status
);
906 setBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH
, true, status
);
907 setBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, true, status
);
910 /* Define one-century window into which to disambiguate dates using
911 * two-digit years. Make public in JDK 1.2.
913 void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate
, UErrorCode
& status
)
915 if(U_FAILURE(status
)) {
919 status
= U_ILLEGAL_ARGUMENT_ERROR
;
923 fCalendar
->setTime(startDate
, status
);
924 if(U_SUCCESS(status
)) {
925 fHaveDefaultCentury
= TRUE
;
926 fDefaultCenturyStart
= startDate
;
927 fDefaultCenturyStartYear
= fCalendar
->get(UCAL_YEAR
, status
);
931 //----------------------------------------------------------------------
934 SimpleDateFormat::format(Calendar
& cal
, UnicodeString
& appendTo
, FieldPosition
& pos
) const
936 UErrorCode status
= U_ZERO_ERROR
;
937 FieldPositionOnlyHandler
handler(pos
);
938 return _format(cal
, appendTo
, handler
, status
);
941 //----------------------------------------------------------------------
944 SimpleDateFormat::format(Calendar
& cal
, UnicodeString
& appendTo
,
945 FieldPositionIterator
* posIter
, UErrorCode
& status
) const
947 FieldPositionIteratorHandler
handler(posIter
, status
);
948 return _format(cal
, appendTo
, handler
, status
);
951 //----------------------------------------------------------------------
954 SimpleDateFormat::_format(Calendar
& cal
, UnicodeString
& appendTo
,
955 FieldPositionHandler
& handler
, UErrorCode
& status
) const
957 if ( U_FAILURE(status
) ) {
960 Calendar
* workCal
= &cal
;
961 Calendar
* calClone
= NULL
;
962 if (&cal
!= fCalendar
&& uprv_strcmp(cal
.getType(), fCalendar
->getType()) != 0) {
963 // Different calendar type
964 // We use the time and time zone from the input calendar, but
965 // do not use the input calendar for field calculation.
966 calClone
= fCalendar
->clone();
967 if (calClone
!= NULL
) {
968 UDate t
= cal
.getTime(status
);
969 calClone
->setTime(t
, status
);
970 calClone
->setTimeZone(cal
.getTimeZone());
973 status
= U_MEMORY_ALLOCATION_ERROR
;
978 UBool inQuote
= FALSE
;
981 int32_t fieldNum
= 0;
982 UDisplayContext capitalizationContext
= getContext(UDISPCTX_TYPE_CAPITALIZATION
, status
);
984 // loop through the pattern string character by character
985 for (int32_t i
= 0; i
< fPattern
.length() && U_SUCCESS(status
); ++i
) {
986 UChar ch
= fPattern
[i
];
988 // Use subFormat() to format a repeated pattern character
989 // when a different pattern or non-pattern character is seen
990 if (ch
!= prevCh
&& count
> 0) {
991 subFormat(appendTo
, prevCh
, count
, capitalizationContext
, fieldNum
++, handler
, *workCal
, status
);
995 // Consecutive single quotes are a single quote literal,
996 // either outside of quotes or between quotes
997 if ((i
+1) < fPattern
.length() && fPattern
[i
+1] == QUOTE
) {
998 appendTo
+= (UChar
)QUOTE
;
1001 inQuote
= ! inQuote
;
1004 else if ( ! inQuote
&& ((ch
>= 0x0061 /*'a'*/ && ch
<= 0x007A /*'z'*/)
1005 || (ch
>= 0x0041 /*'A'*/ && ch
<= 0x005A /*'Z'*/))) {
1006 // ch is a date-time pattern character to be interpreted
1007 // by subFormat(); count the number of times it is repeated
1012 // Append quoted characters and unquoted non-pattern characters
1017 // Format the last item in the pattern, if any
1019 subFormat(appendTo
, prevCh
, count
, capitalizationContext
, fieldNum
++, handler
, *workCal
, status
);
1022 if (calClone
!= NULL
) {
1029 //----------------------------------------------------------------------
1031 /* Map calendar field into calendar field level.
1032 * the larger the level, the smaller the field unit.
1033 * For example, UCAL_ERA level is 0, UCAL_YEAR level is 10,
1034 * UCAL_MONTH level is 20.
1035 * NOTE: if new fields adds in, the table needs to update.
1038 SimpleDateFormat::fgCalendarFieldToLevel
[] =
1042 /*dDEF*/ 30, 20, 30, 30,
1043 /*ahHm*/ 40, 50, 50, 60,
1051 /* Map calendar field LETTER into calendar field level.
1052 * the larger the level, the smaller the field unit.
1053 * NOTE: if new fields adds in, the table needs to update.
1056 SimpleDateFormat::fgPatternCharToLevel
[] = {
1057 // A B C D E F G H I J K L M N O
1058 -1, 40, -1, -1, 20, 30, 30, 0, 50, -1, -1, 50, 20, 20, -1, 0,
1059 // P Q R S T U V W X Y Z
1060 -1, 20, -1, 80, -1, 10, 0, 30, 0, 10, 0, -1, -1, -1, -1, -1,
1061 // a b c d e f g h i j k l m n o
1062 -1, 40, -1, 30, 30, 30, -1, 0, 50, -1, -1, 50, -1, 60, -1, -1,
1063 // p q r s t u v w x y z
1064 -1, 20, 10, 70, -1, 10, 0, 20, 0, 10, 0, -1, -1, -1, -1, -1
1068 // Map index into pattern character string to Calendar field number.
1069 const UCalendarDateFields
1070 SimpleDateFormat::fgPatternIndexToCalendarField
[] =
1072 /*GyM*/ UCAL_ERA
, UCAL_YEAR
, UCAL_MONTH
,
1073 /*dkH*/ UCAL_DATE
, UCAL_HOUR_OF_DAY
, UCAL_HOUR_OF_DAY
,
1074 /*msS*/ UCAL_MINUTE
, UCAL_SECOND
, UCAL_MILLISECOND
,
1075 /*EDF*/ UCAL_DAY_OF_WEEK
, UCAL_DAY_OF_YEAR
, UCAL_DAY_OF_WEEK_IN_MONTH
,
1076 /*wWa*/ UCAL_WEEK_OF_YEAR
, UCAL_WEEK_OF_MONTH
, UCAL_AM_PM
,
1077 /*hKz*/ UCAL_HOUR
, UCAL_HOUR
, UCAL_ZONE_OFFSET
,
1078 /*Yeu*/ UCAL_YEAR_WOY
, UCAL_DOW_LOCAL
, UCAL_EXTENDED_YEAR
,
1079 /*gAZ*/ UCAL_JULIAN_DAY
, UCAL_MILLISECONDS_IN_DAY
, UCAL_ZONE_OFFSET
,
1080 /*v*/ UCAL_ZONE_OFFSET
,
1081 /*c*/ UCAL_DOW_LOCAL
,
1085 /*V*/ UCAL_ZONE_OFFSET
,
1087 /*O*/ UCAL_ZONE_OFFSET
,
1088 /*Xx*/ UCAL_ZONE_OFFSET
, UCAL_ZONE_OFFSET
,
1089 /*r*/ UCAL_EXTENDED_YEAR
,
1092 // Map index into pattern character string to DateFormat field number
1093 const UDateFormatField
1094 SimpleDateFormat::fgPatternIndexToDateFormatField
[] = {
1095 /*GyM*/ UDAT_ERA_FIELD
, UDAT_YEAR_FIELD
, UDAT_MONTH_FIELD
,
1096 /*dkH*/ UDAT_DATE_FIELD
, UDAT_HOUR_OF_DAY1_FIELD
, UDAT_HOUR_OF_DAY0_FIELD
,
1097 /*msS*/ UDAT_MINUTE_FIELD
, UDAT_SECOND_FIELD
, UDAT_FRACTIONAL_SECOND_FIELD
,
1098 /*EDF*/ UDAT_DAY_OF_WEEK_FIELD
, UDAT_DAY_OF_YEAR_FIELD
, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD
,
1099 /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD
, UDAT_WEEK_OF_MONTH_FIELD
, UDAT_AM_PM_FIELD
,
1100 /*hKz*/ UDAT_HOUR1_FIELD
, UDAT_HOUR0_FIELD
, UDAT_TIMEZONE_FIELD
,
1101 /*Yeu*/ UDAT_YEAR_WOY_FIELD
, UDAT_DOW_LOCAL_FIELD
, UDAT_EXTENDED_YEAR_FIELD
,
1102 /*gAZ*/ UDAT_JULIAN_DAY_FIELD
, UDAT_MILLISECONDS_IN_DAY_FIELD
, UDAT_TIMEZONE_RFC_FIELD
,
1103 /*v*/ UDAT_TIMEZONE_GENERIC_FIELD
,
1104 /*c*/ UDAT_STANDALONE_DAY_FIELD
,
1105 /*L*/ UDAT_STANDALONE_MONTH_FIELD
,
1106 /*Q*/ UDAT_QUARTER_FIELD
,
1107 /*q*/ UDAT_STANDALONE_QUARTER_FIELD
,
1108 /*V*/ UDAT_TIMEZONE_SPECIAL_FIELD
,
1109 /*U*/ UDAT_YEAR_NAME_FIELD
,
1110 /*O*/ UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
,
1111 /*Xx*/ UDAT_TIMEZONE_ISO_FIELD
, UDAT_TIMEZONE_ISO_LOCAL_FIELD
,
1112 /*r*/ UDAT_RELATED_YEAR_FIELD
,
1115 //----------------------------------------------------------------------
1118 * Append symbols[value] to dst. Make sure the array index is not out
1122 _appendSymbol(UnicodeString
& dst
,
1124 const UnicodeString
* symbols
,
1125 int32_t symbolsCount
) {
1126 U_ASSERT(0 <= value
&& value
< symbolsCount
);
1127 if (0 <= value
&& value
< symbolsCount
) {
1128 dst
+= symbols
[value
];
1133 _appendSymbolWithMonthPattern(UnicodeString
& dst
, int32_t value
, const UnicodeString
* symbols
, int32_t symbolsCount
,
1134 const UnicodeString
* monthPattern
, UErrorCode
& status
) {
1135 U_ASSERT(0 <= value
&& value
< symbolsCount
);
1136 if (0 <= value
&& value
< symbolsCount
) {
1137 if (monthPattern
== NULL
) {
1138 dst
+= symbols
[value
];
1140 Formattable
monthName((const UnicodeString
&)(symbols
[value
]));
1141 MessageFormat::format(*monthPattern
, &monthName
, 1, dst
, status
);
1146 //----------------------------------------------------------------------
1148 SimpleDateFormat::initNumberFormatters(const Locale
&locale
,UErrorCode
&status
) {
1149 if (U_FAILURE(status
)) {
1152 if ( fDateOverride
.isBogus() && fTimeOverride
.isBogus() ) {
1156 if (fNumberFormatters
== NULL
) {
1157 fNumberFormatters
= (NumberFormat
**)uprv_malloc(UDAT_FIELD_COUNT
* sizeof(NumberFormat
*));
1158 if (fNumberFormatters
) {
1159 for (int32_t i
= 0; i
< UDAT_FIELD_COUNT
; i
++) {
1160 fNumberFormatters
[i
] = fNumberFormat
;
1163 status
= U_MEMORY_ALLOCATION_ERROR
;
1168 processOverrideString(locale
,fDateOverride
,kOvrStrDate
,status
);
1169 processOverrideString(locale
,fTimeOverride
,kOvrStrTime
,status
);
1174 SimpleDateFormat::processOverrideString(const Locale
&locale
, const UnicodeString
&str
, int8_t type
, UErrorCode
&status
) {
1175 if (str
.isBogus()) {
1180 UnicodeString nsName
;
1181 UnicodeString ovrField
;
1182 UBool moreToProcess
= TRUE
;
1184 while (moreToProcess
) {
1185 int32_t delimiterPosition
= str
.indexOf((UChar
)ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE
,start
);
1186 if (delimiterPosition
== -1) {
1187 moreToProcess
= FALSE
;
1188 len
= str
.length() - start
;
1190 len
= delimiterPosition
- start
;
1192 UnicodeString
currentString(str
,start
,len
);
1193 int32_t equalSignPosition
= currentString
.indexOf((UChar
)ULOC_KEYWORD_ASSIGN_UNICODE
,0);
1194 if (equalSignPosition
== -1) { // Simple override string such as "hebrew"
1195 nsName
.setTo(currentString
);
1196 ovrField
.setToBogus();
1197 } else { // Field specific override string such as "y=hebrew"
1198 nsName
.setTo(currentString
,equalSignPosition
+1);
1199 ovrField
.setTo(currentString
,0,1); // We just need the first character.
1202 int32_t nsNameHash
= nsName
.hashCode();
1203 // See if the numbering system is in the override list, if not, then add it.
1204 NSOverride
*cur
= fOverrideList
;
1205 NumberFormat
*nf
= NULL
;
1206 UBool found
= FALSE
;
1207 while ( cur
&& !found
) {
1208 if ( cur
->hash
== nsNameHash
) {
1216 cur
= (NSOverride
*)uprv_malloc(sizeof(NSOverride
));
1218 char kw
[ULOC_KEYWORD_AND_VALUES_CAPACITY
];
1219 uprv_strcpy(kw
,"numbers=");
1220 nsName
.extract(0,len
,kw
+8,ULOC_KEYWORD_AND_VALUES_CAPACITY
-8,US_INV
);
1222 Locale
ovrLoc(locale
.getLanguage(),locale
.getCountry(),locale
.getVariant(),kw
);
1223 nf
= NumberFormat::createInstance(ovrLoc
,status
);
1225 // no matter what the locale's default number format looked like, we want
1226 // to modify it so that it doesn't use thousands separators, doesn't always
1227 // show the decimal point, and recognizes integers only when parsing
1229 if (U_SUCCESS(status
)) {
1230 nf
->setGroupingUsed(FALSE
);
1231 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(nf
);
1232 if (decfmt
!= NULL
) {
1233 decfmt
->setDecimalSeparatorAlwaysShown(FALSE
);
1235 nf
->setParseIntegerOnly(TRUE
);
1236 nf
->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
1239 cur
->hash
= nsNameHash
;
1240 cur
->next
= fOverrideList
;
1241 fOverrideList
= cur
;
1244 // clean up before returning
1252 status
= U_MEMORY_ALLOCATION_ERROR
;
1257 // Now that we have an appropriate number formatter, fill in the appropriate spaces in the
1258 // number formatters table.
1260 if (ovrField
.isBogus()) {
1264 for ( int8_t i
=0 ; i
<kDateFieldsCount
; i
++ ) {
1265 fNumberFormatters
[kDateFields
[i
]] = nf
;
1267 if (type
==kOvrStrDate
) {
1271 case kOvrStrTime
: {
1272 for ( int8_t i
=0 ; i
<kTimeFieldsCount
; i
++ ) {
1273 fNumberFormatters
[kTimeFields
[i
]] = nf
;
1279 // if the pattern character is unrecognized, signal an error and bail out
1280 UDateFormatField patternCharIndex
=
1281 DateFormatSymbols::getPatternCharIndex(ovrField
.charAt(0));
1282 if (patternCharIndex
== UDAT_FIELD_COUNT
) {
1283 status
= U_INVALID_FORMAT_ERROR
;
1287 // Set the number formatter in the table
1288 fNumberFormatters
[patternCharIndex
] = nf
;
1291 start
= delimiterPosition
+ 1;
1295 //---------------------------------------------------------------------
1297 kUnicodeLatnDigitZero
= 0x0030,
1298 kUnicodeLatnDigitNine
= 0x0039,
1302 SimpleDateFormat::subFormat(UnicodeString
&appendTo
,
1305 UDisplayContext capitalizationContext
,
1307 FieldPositionHandler
& handler
,
1309 UErrorCode
& status
) const
1311 if (U_FAILURE(status
)) {
1315 // this function gets called by format() to produce the appropriate substitution
1316 // text for an individual pattern symbol (e.g., "HH" or "yyyy")
1318 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(ch
);
1319 const int32_t maxIntCount
= 10;
1320 int32_t beginOffset
= appendTo
.length();
1321 NumberFormat
*currentNumberFormat
;
1322 DateFormatSymbols::ECapitalizationContextUsageType capContextUsageType
= DateFormatSymbols::kCapContextUsageOther
;
1324 UBool isHebrewCalendar
= (uprv_strcmp(cal
.getType(),"hebrew") == 0);
1325 UBool isChineseCalendar
= (uprv_strcmp(cal
.getType(),"chinese") == 0 || uprv_strcmp(cal
.getType(),"dangi") == 0);
1327 // if the pattern character is unrecognized, signal an error and dump out
1328 if (patternCharIndex
== UDAT_FIELD_COUNT
)
1330 if (ch
!= 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
1331 status
= U_INVALID_FORMAT_ERROR
;
1336 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
];
1337 int32_t value
= (patternCharIndex
!= UDAT_RELATED_YEAR_FIELD
)? cal
.get(field
, status
): cal
.getRelatedYear(status
);
1338 if (U_FAILURE(status
)) {
1342 currentNumberFormat
= getNumberFormatByIndex(patternCharIndex
);
1343 UBool basicNumFmt
= FALSE
;
1344 DecimalFormat
* df
= NULL
;
1345 if ( (df
= dynamic_cast<DecimalFormat
*>(currentNumberFormat
)) != NULL
) {
1346 const DecimalFormatSymbols
* dfSymbols
= df
->getDecimalFormatSymbols();
1347 const UnicodeString
*zeroDigit
= &dfSymbols
->getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol
);
1348 const UnicodeString
*nineDigit
= &dfSymbols
->getConstSymbol(DecimalFormatSymbols::kNineDigitSymbol
);
1349 basicNumFmt
= ( zeroDigit
!=NULL
&& zeroDigit
->length()==1 && zeroDigit
->charAt(0)==kUnicodeLatnDigitZero
&&
1350 nineDigit
!=NULL
&& nineDigit
->length()==1 && nineDigit
->charAt(0)==kUnicodeLatnDigitNine
);
1352 UnicodeString
hebr("hebr", 4, US_INV
);
1354 switch (patternCharIndex
) {
1356 // for any "G" symbol, write out the appropriate era string
1357 // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name
1358 case UDAT_ERA_FIELD
:
1359 if (isChineseCalendar
) {
1360 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, 1, 9, basicNumFmt
); // as in ICU4J
1363 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowEras
, fSymbols
->fNarrowErasCount
);
1364 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraNarrow
;
1365 } else if (count
== 4) {
1366 _appendSymbol(appendTo
, value
, fSymbols
->fEraNames
, fSymbols
->fEraNamesCount
);
1367 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraWide
;
1369 _appendSymbol(appendTo
, value
, fSymbols
->fEras
, fSymbols
->fErasCount
);
1370 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraAbbrev
;
1375 case UDAT_YEAR_NAME_FIELD
:
1376 if (fSymbols
->fShortYearNames
!= NULL
&& value
<= fSymbols
->fShortYearNamesCount
) {
1377 // the Calendar YEAR field runs 1 through 60 for cyclic years
1378 _appendSymbol(appendTo
, value
- 1, fSymbols
->fShortYearNames
, fSymbols
->fShortYearNamesCount
);
1381 // else fall through to numeric year handling, do not break here
1383 // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits
1385 //Year y yy yyy yyyy yyyyy
1386 //AD 1 1 01 001 0001 00001
1387 //AD 12 12 12 012 0012 00012
1388 //AD 123 123 23 123 0123 00123
1389 //AD 1234 1234 34 1234 1234 01234
1390 //AD 12345 12345 45 12345 12345 12345
1391 case UDAT_YEAR_FIELD
:
1392 case UDAT_YEAR_WOY_FIELD
:
1393 if (fDateOverride
.compare(hebr
)==0 && value
>HEBREW_CAL_CUR_MILLENIUM_START_YEAR
&& value
<HEBREW_CAL_CUR_MILLENIUM_END_YEAR
) {
1394 value
-=HEBREW_CAL_CUR_MILLENIUM_START_YEAR
;
1397 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, 2, 2, basicNumFmt
);
1399 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, count
, maxIntCount
, basicNumFmt
);
1402 // for "MMMM"/"LLLL", write out the whole month name, for "MMM"/"LLL", write out the month
1403 // abbreviation, for "M"/"L" or "MM"/"LL", write out the month as a number with the
1404 // appropriate number of digits
1405 // for "MMMMM"/"LLLLL", use the narrow form
1406 case UDAT_MONTH_FIELD
:
1407 case UDAT_STANDALONE_MONTH_FIELD
:
1408 if ( isHebrewCalendar
) {
1409 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
1410 if (hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
== 6 && count
>= 3 )
1411 value
= 13; // Show alternate form for Adar II in leap years in Hebrew calendar.
1412 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6 && count
< 3 )
1413 value
--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7.
1416 int32_t isLeapMonth
= (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
)?
1417 cal
.get(UCAL_IS_LEAP_MONTH
, status
): 0;
1418 // should consolidate the next section by using arrays of pointers & counts for the right symbols...
1420 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1421 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fNarrowMonths
, fSymbols
->fNarrowMonthsCount
,
1422 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatNarrow
]): NULL
, status
);
1424 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneNarrowMonths
, fSymbols
->fStandaloneNarrowMonthsCount
,
1425 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneNarrow
]): NULL
, status
);
1427 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthNarrow
;
1428 } else if (count
== 4) {
1429 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1430 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fMonths
, fSymbols
->fMonthsCount
,
1431 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatWide
]): NULL
, status
);
1432 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthFormat
;
1434 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneMonths
, fSymbols
->fStandaloneMonthsCount
,
1435 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneWide
]): NULL
, status
);
1436 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthStandalone
;
1438 } else if (count
== 3) {
1439 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1440 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fShortMonths
, fSymbols
->fShortMonthsCount
,
1441 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatAbbrev
]): NULL
, status
);
1442 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthFormat
;
1444 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneShortMonths
, fSymbols
->fStandaloneShortMonthsCount
,
1445 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev
]): NULL
, status
);
1446 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthStandalone
;
1449 UnicodeString monthNumber
;
1450 zeroPaddingNumber(currentNumberFormat
,monthNumber
, value
+ 1, count
, maxIntCount
, basicNumFmt
);
1451 _appendSymbolWithMonthPattern(appendTo
, 0, &monthNumber
, 1,
1452 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternNumeric
]): NULL
, status
);
1457 // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
1458 case UDAT_HOUR_OF_DAY1_FIELD
:
1460 zeroPaddingNumber(currentNumberFormat
,appendTo
, cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1, count
, maxIntCount
, basicNumFmt
);
1462 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
, basicNumFmt
);
1465 case UDAT_FRACTIONAL_SECOND_FIELD
:
1466 // Fractional seconds left-justify
1468 currentNumberFormat
->setMinimumIntegerDigits((count
> 3) ? 3 : count
);
1469 currentNumberFormat
->setMaximumIntegerDigits(maxIntCount
);
1472 } else if (count
== 2) {
1476 currentNumberFormat
->format(value
, appendTo
, p
);
1478 currentNumberFormat
->setMinimumIntegerDigits(count
- 3);
1479 currentNumberFormat
->format((int32_t)0, appendTo
, p
);
1484 // for "ee" or "e", use local numeric day-of-the-week
1485 // for "EEEEEE" or "eeeeee", write out the short day-of-the-week name
1486 // for "EEEEE" or "eeeee", write out the narrow day-of-the-week name
1487 // for "EEEE" or "eeee", write out the wide day-of-the-week name
1488 // for "EEE" or "EE" or "E" or "eee", write out the abbreviated day-of-the-week name
1489 case UDAT_DOW_LOCAL_FIELD
:
1491 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
, basicNumFmt
);
1494 // fall through to EEEEE-EEE handling, but for that we don't want local day-of-week,
1495 // we want standard day-of-week, so first fix value to work for EEEEE-EEE.
1496 value
= cal
.get(UCAL_DAY_OF_WEEK
, status
);
1497 if (U_FAILURE(status
)) {
1500 // fall through, do not break here
1501 case UDAT_DAY_OF_WEEK_FIELD
:
1503 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowWeekdays
,
1504 fSymbols
->fNarrowWeekdaysCount
);
1505 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayNarrow
;
1506 } else if (count
== 4) {
1507 _appendSymbol(appendTo
, value
, fSymbols
->fWeekdays
,
1508 fSymbols
->fWeekdaysCount
);
1509 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1510 } else if (count
== 6) {
1511 _appendSymbol(appendTo
, value
, fSymbols
->fShorterWeekdays
,
1512 fSymbols
->fShorterWeekdaysCount
);
1513 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1515 _appendSymbol(appendTo
, value
, fSymbols
->fShortWeekdays
,
1516 fSymbols
->fShortWeekdaysCount
);
1517 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1521 // for "ccc", write out the abbreviated day-of-the-week name
1522 // for "cccc", write out the wide day-of-the-week name
1523 // for "ccccc", use the narrow day-of-the-week name
1524 // for "ccccc", use the short day-of-the-week name
1525 case UDAT_STANDALONE_DAY_FIELD
:
1527 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, 1, maxIntCount
, basicNumFmt
);
1530 // fall through to alpha DOW handling, but for that we don't want local day-of-week,
1531 // we want standard day-of-week, so first fix value.
1532 value
= cal
.get(UCAL_DAY_OF_WEEK
, status
);
1533 if (U_FAILURE(status
)) {
1537 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneNarrowWeekdays
,
1538 fSymbols
->fStandaloneNarrowWeekdaysCount
);
1539 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayNarrow
;
1540 } else if (count
== 4) {
1541 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneWeekdays
,
1542 fSymbols
->fStandaloneWeekdaysCount
);
1543 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1544 } else if (count
== 6) {
1545 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneShorterWeekdays
,
1546 fSymbols
->fStandaloneShorterWeekdaysCount
);
1547 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1548 } else { // count == 3
1549 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneShortWeekdays
,
1550 fSymbols
->fStandaloneShortWeekdaysCount
);
1551 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1555 // for and "a" symbol, write out the whole AM/PM string
1556 case UDAT_AM_PM_FIELD
:
1557 _appendSymbol(appendTo
, value
, fSymbols
->fAmPms
,
1558 fSymbols
->fAmPmsCount
);
1561 // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
1563 case UDAT_HOUR1_FIELD
:
1565 zeroPaddingNumber(currentNumberFormat
,appendTo
, cal
.getLeastMaximum(UCAL_HOUR
) + 1, count
, maxIntCount
, basicNumFmt
);
1567 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
, basicNumFmt
);
1570 case UDAT_TIMEZONE_FIELD
: // 'z'
1571 case UDAT_TIMEZONE_RFC_FIELD
: // 'Z'
1572 case UDAT_TIMEZONE_GENERIC_FIELD
: // 'v'
1573 case UDAT_TIMEZONE_SPECIAL_FIELD
: // 'V'
1574 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
: // 'O'
1575 case UDAT_TIMEZONE_ISO_FIELD
: // 'X'
1576 case UDAT_TIMEZONE_ISO_LOCAL_FIELD
: // 'x'
1578 UnicodeString zoneString
;
1579 const TimeZone
& tz
= cal
.getTimeZone();
1580 UDate date
= cal
.getTime(status
);
1581 if (U_SUCCESS(status
)) {
1582 if (patternCharIndex
== UDAT_TIMEZONE_FIELD
) {
1585 tzFormat()->format(UTZFMT_STYLE_SPECIFIC_SHORT
, tz
, date
, zoneString
);
1586 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneShort
;
1589 tzFormat()->format(UTZFMT_STYLE_SPECIFIC_LONG
, tz
, date
, zoneString
);
1590 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneLong
;
1593 else if (patternCharIndex
== UDAT_TIMEZONE_RFC_FIELD
) {
1596 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
, tz
, date
, zoneString
);
1597 } else if (count
== 5) {
1599 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FULL
, tz
, date
, zoneString
);
1601 // "ZZ", "ZZZ", "ZZZZ"
1602 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT
, tz
, date
, zoneString
);
1605 else if (patternCharIndex
== UDAT_TIMEZONE_GENERIC_FIELD
) {
1608 tzFormat()->format(UTZFMT_STYLE_GENERIC_SHORT
, tz
, date
, zoneString
);
1609 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneShort
;
1610 } else if (count
== 4) {
1612 tzFormat()->format(UTZFMT_STYLE_GENERIC_LONG
, tz
, date
, zoneString
);
1613 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneLong
;
1616 else if (patternCharIndex
== UDAT_TIMEZONE_SPECIAL_FIELD
) {
1619 tzFormat()->format(UTZFMT_STYLE_ZONE_ID_SHORT
, tz
, date
, zoneString
);
1620 } else if (count
== 2) {
1622 tzFormat()->format(UTZFMT_STYLE_ZONE_ID
, tz
, date
, zoneString
);
1623 } else if (count
== 3) {
1625 tzFormat()->format(UTZFMT_STYLE_EXEMPLAR_LOCATION
, tz
, date
, zoneString
);
1626 } else if (count
== 4) {
1628 tzFormat()->format(UTZFMT_STYLE_GENERIC_LOCATION
, tz
, date
, zoneString
);
1629 capContextUsageType
= DateFormatSymbols::kCapContextUsageZoneLong
;
1632 else if (patternCharIndex
== UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
) {
1635 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT_SHORT
, tz
, date
, zoneString
);
1636 } else if (count
== 4) {
1638 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT
, tz
, date
, zoneString
);
1641 else if (patternCharIndex
== UDAT_TIMEZONE_ISO_FIELD
) {
1644 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_SHORT
, tz
, date
, zoneString
);
1645 } else if (count
== 2) {
1647 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_FIXED
, tz
, date
, zoneString
);
1648 } else if (count
== 3) {
1650 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FIXED
, tz
, date
, zoneString
);
1651 } else if (count
== 4) {
1653 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_FULL
, tz
, date
, zoneString
);
1654 } else if (count
== 5) {
1656 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FULL
, tz
, date
, zoneString
);
1659 else if (patternCharIndex
== UDAT_TIMEZONE_ISO_LOCAL_FIELD
) {
1662 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
, tz
, date
, zoneString
);
1663 } else if (count
== 2) {
1665 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
, tz
, date
, zoneString
);
1666 } else if (count
== 3) {
1668 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
, tz
, date
, zoneString
);
1669 } else if (count
== 4) {
1671 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
, tz
, date
, zoneString
);
1672 } else if (count
== 5) {
1674 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
, tz
, date
, zoneString
);
1681 appendTo
+= zoneString
;
1685 case UDAT_QUARTER_FIELD
:
1687 _appendSymbol(appendTo
, value
/3, fSymbols
->fQuarters
,
1688 fSymbols
->fQuartersCount
);
1689 else if (count
== 3)
1690 _appendSymbol(appendTo
, value
/3, fSymbols
->fShortQuarters
,
1691 fSymbols
->fShortQuartersCount
);
1693 zeroPaddingNumber(currentNumberFormat
,appendTo
, (value
/3) + 1, count
, maxIntCount
, basicNumFmt
);
1696 case UDAT_STANDALONE_QUARTER_FIELD
:
1698 _appendSymbol(appendTo
, value
/3, fSymbols
->fStandaloneQuarters
,
1699 fSymbols
->fStandaloneQuartersCount
);
1700 else if (count
== 3)
1701 _appendSymbol(appendTo
, value
/3, fSymbols
->fStandaloneShortQuarters
,
1702 fSymbols
->fStandaloneShortQuartersCount
);
1704 zeroPaddingNumber(currentNumberFormat
,appendTo
, (value
/3) + 1, count
, maxIntCount
, basicNumFmt
);
1708 // all of the other pattern symbols can be formatted as simple numbers with
1709 // appropriate zero padding
1711 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
, basicNumFmt
);
1714 #if !UCONFIG_NO_BREAK_ITERATION
1715 // if first field, check to see whether we need to and are able to titlecase it
1716 if (fieldNum
== 0 && u_islower(appendTo
.char32At(beginOffset
)) && fCapitalizationBrkIter
!= NULL
) {
1717 UBool titlecase
= FALSE
;
1718 switch (capitalizationContext
) {
1719 case UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
:
1722 case UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
:
1723 titlecase
= fSymbols
->fCapitalization
[capContextUsageType
][0];
1725 case UDISPCTX_CAPITALIZATION_FOR_STANDALONE
:
1726 titlecase
= fSymbols
->fCapitalization
[capContextUsageType
][1];
1729 // titlecase = FALSE;
1733 UnicodeString
firstField(appendTo
, beginOffset
);
1734 firstField
.toTitle(fCapitalizationBrkIter
, fLocale
, U_TITLECASE_NO_LOWERCASE
| U_TITLECASE_NO_BREAK_ADJUSTMENT
);
1735 appendTo
.replaceBetween(beginOffset
, appendTo
.length(), firstField
);
1740 handler
.addAttribute(fgPatternIndexToDateFormatField
[patternCharIndex
], beginOffset
, appendTo
.length());
1743 //----------------------------------------------------------------------
1746 SimpleDateFormat::getNumberFormatByIndex(UDateFormatField index
) const {
1747 if (fNumberFormatters
!= NULL
) {
1748 return fNumberFormatters
[index
];
1750 return fNumberFormat
;
1754 //----------------------------------------------------------------------
1755 enum { kBasicNumMaxDigits
= 16 };
1758 SimpleDateFormat::zeroPaddingNumber(NumberFormat
*currentNumberFormat
,UnicodeString
&appendTo
,
1759 int32_t value
, int32_t minDigits
, int32_t maxDigits
, UBool basicNumFmt
) const
1761 if (basicNumFmt
&& maxDigits
<= kBasicNumMaxDigits
&& minDigits
<= maxDigits
&& value
>= 0) {
1762 UChar basicNum
[kBasicNumMaxDigits
];
1763 UChar
* basicNumPtr
= basicNum
+ kBasicNumMaxDigits
;
1764 int32_t remainingValue
= value
;
1765 int32_t currentDigits
= 0;
1767 *--basicNumPtr
= kUnicodeLatnDigitZero
+ (remainingValue
% 10);
1768 remainingValue
/= 10;
1770 } while (currentDigits
< minDigits
|| (remainingValue
> 0 && currentDigits
< maxDigits
));
1771 if (remainingValue
== 0) {
1772 appendTo
.append(basicNumPtr
, currentDigits
);
1776 if (currentNumberFormat
!=NULL
) {
1777 FieldPosition
pos(0);
1779 currentNumberFormat
->setMinimumIntegerDigits(minDigits
);
1780 currentNumberFormat
->setMaximumIntegerDigits(maxDigits
);
1781 currentNumberFormat
->format(value
, appendTo
, pos
); // 3rd arg is there to speed up processing
1785 //----------------------------------------------------------------------
1788 * Return true if the given format character, occuring count
1789 * times, represents a numeric field.
1791 UBool
SimpleDateFormat::isNumeric(UChar formatChar
, int32_t count
) {
1792 return DateFormatSymbols::isNumericPatternChar(formatChar
, count
);
1796 SimpleDateFormat::isAtNumericField(const UnicodeString
&pattern
, int32_t patternOffset
) {
1797 if (patternOffset
>= pattern
.length()) {
1801 UChar ch
= pattern
.charAt(patternOffset
);
1802 UDateFormatField f
= DateFormatSymbols::getPatternCharIndex(ch
);
1803 if (f
== UDAT_FIELD_COUNT
) {
1807 int32_t i
= patternOffset
;
1808 while (pattern
.charAt(++i
) == ch
) {}
1809 return DateFormatSymbols::isNumericField(f
, i
- patternOffset
);
1813 SimpleDateFormat::isAfterNonNumericField(const UnicodeString
&pattern
, int32_t patternOffset
) {
1814 if (patternOffset
<= 0) {
1815 // not after any field
1818 UChar ch
= pattern
.charAt(--patternOffset
);
1819 UDateFormatField f
= DateFormatSymbols::getPatternCharIndex(ch
);
1820 if (f
== UDAT_FIELD_COUNT
) {
1821 // not after any field
1824 int32_t i
= patternOffset
;
1825 while (pattern
.charAt(--i
) == ch
) {}
1826 return !DateFormatSymbols::isNumericField(f
, patternOffset
- i
);
1830 SimpleDateFormat::parse(const UnicodeString
& text
, Calendar
& cal
, ParsePosition
& parsePos
) const
1832 UErrorCode status
= U_ZERO_ERROR
;
1833 int32_t pos
= parsePos
.getIndex();
1834 int32_t start
= pos
;
1836 UBool ambiguousYear
[] = { FALSE
};
1837 int32_t saveHebrewMonth
= -1;
1839 UTimeZoneFormatTimeType tzTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1841 // For parsing abutting numeric fields. 'abutPat' is the
1842 // offset into 'pattern' of the first of 2 or more abutting
1843 // numeric fields. 'abutStart' is the offset into 'text'
1844 // where parsing the fields begins. 'abutPass' starts off as 0
1845 // and increments each time we try to parse the fields.
1846 int32_t abutPat
= -1; // If >=0, we are in a run of abutting numeric fields
1847 int32_t abutStart
= 0;
1848 int32_t abutPass
= 0;
1849 UBool inQuote
= FALSE
;
1851 MessageFormat
* numericLeapMonthFormatter
= NULL
;
1853 Calendar
* calClone
= NULL
;
1854 Calendar
*workCal
= &cal
;
1855 if (&cal
!= fCalendar
&& uprv_strcmp(cal
.getType(), fCalendar
->getType()) != 0) {
1856 // Different calendar type
1857 // We use the time/zone from the input calendar, but
1858 // do not use the input calendar for field calculation.
1859 calClone
= fCalendar
->clone();
1860 if (calClone
!= NULL
) {
1861 calClone
->setTime(cal
.getTime(status
),status
);
1862 if (U_FAILURE(status
)) {
1865 calClone
->setTimeZone(cal
.getTimeZone());
1868 status
= U_MEMORY_ALLOCATION_ERROR
;
1873 if (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
) {
1874 numericLeapMonthFormatter
= new MessageFormat(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternNumeric
], fLocale
, status
);
1875 if (numericLeapMonthFormatter
== NULL
) {
1876 status
= U_MEMORY_ALLOCATION_ERROR
;
1878 } else if (U_FAILURE(status
)) {
1879 goto ExitParse
; // this will delete numericLeapMonthFormatter
1883 for (int32_t i
=0; i
<fPattern
.length(); ++i
) {
1884 UChar ch
= fPattern
.charAt(i
);
1886 // Handle alphabetic field characters.
1887 if (!inQuote
&& ((ch
>= 0x41 && ch
<= 0x5A) || (ch
>= 0x61 && ch
<= 0x7A))) { // [A-Za-z]
1888 int32_t fieldPat
= i
;
1890 // Count the length of this field specifier
1892 while ((i
+1)<fPattern
.length() &&
1893 fPattern
.charAt(i
+1) == ch
) {
1898 if (isNumeric(ch
, count
)) {
1900 // Determine if there is an abutting numeric field.
1901 // Record the start of a set of abutting numeric fields.
1902 if (isAtNumericField(fPattern
, i
+ 1)) {
1909 abutPat
= -1; // End of any abutting fields
1912 // Handle fields within a run of abutting numeric fields. Take
1913 // the pattern "HHmmss" as an example. We will try to parse
1914 // 2/2/2 characters of the input text, then if that fails,
1915 // 1/2/2. We only adjust the width of the leftmost field; the
1916 // others remain fixed. This allows "123456" => 12:34:56, but
1917 // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
1918 // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
1920 // If we are at the start of a run of abutting fields, then
1921 // shorten this field in each pass. If we can't shorten
1922 // this field any more, then the parse of this set of
1923 // abutting numeric fields has failed.
1924 if (fieldPat
== abutPat
) {
1925 count
-= abutPass
++;
1927 status
= U_PARSE_ERROR
;
1932 pos
= subParse(text
, pos
, ch
, count
,
1933 TRUE
, FALSE
, ambiguousYear
, saveHebrewMonth
, *workCal
, i
, numericLeapMonthFormatter
, &tzTimeType
);
1935 // If the parse fails anywhere in the run, back up to the
1936 // start of the run and retry.
1944 // Handle non-numeric fields and non-abutting numeric
1946 else if (ch
!= 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
1947 int32_t s
= subParse(text
, pos
, ch
, count
,
1948 FALSE
, TRUE
, ambiguousYear
, saveHebrewMonth
, *workCal
, i
, numericLeapMonthFormatter
, &tzTimeType
);
1951 // era not present, in special cases allow this to continue
1952 // from the position where the era was expected
1955 if (i
+1 < fPattern
.length()) {
1956 // move to next pattern character
1957 UChar ch
= fPattern
.charAt(i
+1);
1959 // check for whitespace
1960 if (PatternProps::isWhiteSpace(ch
)) {
1962 // Advance over run in pattern
1963 while ((i
+1)<fPattern
.length() &&
1964 PatternProps::isWhiteSpace(fPattern
.charAt(i
+1))) {
1971 status
= U_PARSE_ERROR
;
1978 // Handle literal pattern characters. These are any
1979 // quoted characters and non-alphabetic unquoted
1983 abutPat
= -1; // End of any abutting fields
1985 if (! matchLiterals(fPattern
, i
, text
, pos
, getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, status
), getBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH
, status
))) {
1986 status
= U_PARSE_ERROR
;
1992 // Special hack for trailing "." after non-numeric field.
1993 if (text
.charAt(pos
) == 0x2e && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, status
)) {
1994 // only do if the last field is not numeric
1995 if (isAfterNonNumericField(fPattern
, fPattern
.length())) {
1996 pos
++; // skip the extra "."
2000 // At this point the fields of Calendar have been set. Calendar
2001 // will fill in default values for missing fields when the time
2004 parsePos
.setIndex(pos
);
2006 // This part is a problem: When we call parsedDate.after, we compute the time.
2007 // Take the date April 3 2004 at 2:30 am. When this is first set up, the year
2008 // will be wrong if we're parsing a 2-digit year pattern. It will be 1904.
2009 // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am
2010 // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
2011 // on that day. It is therefore parsed out to fields as 3:30 am. Then we
2012 // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is
2013 // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
2015 UDate parsedDate = calendar.getTime();
2016 if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
2017 calendar.add(Calendar.YEAR, 100);
2018 parsedDate = calendar.getTime();
2021 // Because of the above condition, save off the fields in case we need to readjust.
2022 // The procedure we use here is not particularly efficient, but there is no other
2023 // way to do this given the API restrictions present in Calendar. We minimize
2024 // inefficiency by only performing this computation when it might apply, that is,
2025 // when the two-digit year is equal to the start year, and thus might fall at the
2026 // front or the back of the default century. This only works because we adjust
2027 // the year correctly to start with in other cases -- see subParse().
2028 if (ambiguousYear
[0] || tzTimeType
!= UTZFMT_TIME_TYPE_UNKNOWN
) // If this is true then the two-digit year == the default start year
2030 // We need a copy of the fields, and we need to avoid triggering a call to
2031 // complete(), which will recalculate the fields. Since we can't access
2032 // the fields[] array in Calendar, we clone the entire object. This will
2033 // stop working if Calendar.clone() is ever rewritten to call complete().
2035 if (ambiguousYear
[0]) {
2037 // Check for failed cloning.
2039 status
= U_MEMORY_ALLOCATION_ERROR
;
2042 UDate parsedDate
= copy
->getTime(status
);
2043 // {sfb} check internalGetDefaultCenturyStart
2044 if (fHaveDefaultCentury
&& (parsedDate
< fDefaultCenturyStart
)) {
2045 // We can't use add here because that does a complete() first.
2046 cal
.set(UCAL_YEAR
, fDefaultCenturyStartYear
+ 100);
2051 if (tzTimeType
!= UTZFMT_TIME_TYPE_UNKNOWN
) {
2053 // Check for failed cloning.
2055 status
= U_MEMORY_ALLOCATION_ERROR
;
2058 const TimeZone
& tz
= cal
.getTimeZone();
2059 BasicTimeZone
*btz
= NULL
;
2061 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
2062 || dynamic_cast<const SimpleTimeZone
*>(&tz
) != NULL
2063 || dynamic_cast<const RuleBasedTimeZone
*>(&tz
) != NULL
2064 || dynamic_cast<const VTimeZone
*>(&tz
) != NULL
) {
2065 btz
= (BasicTimeZone
*)&tz
;
2069 copy
->set(UCAL_ZONE_OFFSET
, 0);
2070 copy
->set(UCAL_DST_OFFSET
, 0);
2071 UDate localMillis
= copy
->getTime(status
);
2073 // Make sure parsed time zone type (Standard or Daylight)
2074 // matches the rule used by the parsed time zone.
2077 if (tzTimeType
== UTZFMT_TIME_TYPE_STANDARD
) {
2078 btz
->getOffsetFromLocal(localMillis
,
2079 BasicTimeZone::kStandard
, BasicTimeZone::kStandard
, raw
, dst
, status
);
2081 btz
->getOffsetFromLocal(localMillis
,
2082 BasicTimeZone::kDaylight
, BasicTimeZone::kDaylight
, raw
, dst
, status
);
2085 // No good way to resolve ambiguous time at transition,
2086 // but following code work in most case.
2087 tz
.getOffset(localMillis
, TRUE
, raw
, dst
, status
);
2090 // Now, compare the results with parsed type, either standard or daylight saving time
2091 int32_t resolvedSavings
= dst
;
2092 if (tzTimeType
== UTZFMT_TIME_TYPE_STANDARD
) {
2094 // Override DST_OFFSET = 0 in the result calendar
2095 resolvedSavings
= 0;
2097 } else { // tztype == TZTYPE_DST
2100 UDate time
= localMillis
+ raw
;
2101 // We use the nearest daylight saving time rule.
2102 TimeZoneTransition beforeTrs
, afterTrs
;
2103 UDate beforeT
= time
, afterT
= time
;
2104 int32_t beforeSav
= 0, afterSav
= 0;
2105 UBool beforeTrsAvail
, afterTrsAvail
;
2107 // Search for DST rule before or on the time
2109 beforeTrsAvail
= btz
->getPreviousTransition(beforeT
, TRUE
, beforeTrs
);
2110 if (!beforeTrsAvail
) {
2113 beforeT
= beforeTrs
.getTime() - 1;
2114 beforeSav
= beforeTrs
.getFrom()->getDSTSavings();
2115 if (beforeSav
!= 0) {
2120 // Search for DST rule after the time
2122 afterTrsAvail
= btz
->getNextTransition(afterT
, FALSE
, afterTrs
);
2123 if (!afterTrsAvail
) {
2126 afterT
= afterTrs
.getTime();
2127 afterSav
= afterTrs
.getTo()->getDSTSavings();
2128 if (afterSav
!= 0) {
2133 if (beforeTrsAvail
&& afterTrsAvail
) {
2134 if (time
- beforeT
> afterT
- time
) {
2135 resolvedSavings
= afterSav
;
2137 resolvedSavings
= beforeSav
;
2139 } else if (beforeTrsAvail
&& beforeSav
!= 0) {
2140 resolvedSavings
= beforeSav
;
2141 } else if (afterTrsAvail
&& afterSav
!= 0) {
2142 resolvedSavings
= afterSav
;
2144 resolvedSavings
= btz
->getDSTSavings();
2147 resolvedSavings
= tz
.getDSTSavings();
2149 if (resolvedSavings
== 0) {
2151 resolvedSavings
= U_MILLIS_PER_HOUR
;
2155 cal
.set(UCAL_ZONE_OFFSET
, raw
);
2156 cal
.set(UCAL_DST_OFFSET
, resolvedSavings
);
2161 // Set the parsed result if local calendar is used
2162 // instead of the input calendar
2163 if (U_SUCCESS(status
) && workCal
!= &cal
) {
2164 cal
.setTimeZone(workCal
->getTimeZone());
2165 cal
.setTime(workCal
->getTime(status
), status
);
2168 if (numericLeapMonthFormatter
!= NULL
) {
2169 delete numericLeapMonthFormatter
;
2171 if (calClone
!= NULL
) {
2175 // If any Calendar calls failed, we pretend that we
2176 // couldn't parse the string, when in reality this isn't quite accurate--
2177 // we did parse it; the Calendar calls just failed.
2178 if (U_FAILURE(status
)) {
2179 parsePos
.setErrorIndex(pos
);
2180 parsePos
.setIndex(start
);
2184 //----------------------------------------------------------------------
2187 newBestMatchWithOptionalDot(const UnicodeString
&lcaseText
,
2188 const UnicodeString
&data
,
2189 UnicodeString
&bestMatchName
,
2190 int32_t &bestMatchLength
);
2192 int32_t SimpleDateFormat::matchQuarterString(const UnicodeString
& text
,
2194 UCalendarDateFields field
,
2195 const UnicodeString
* data
,
2197 Calendar
& cal
) const
2200 int32_t count
= dataCount
;
2202 // There may be multiple strings in the data[] array which begin with
2203 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2204 // We keep track of the longest match, and return that. Note that this
2205 // unfortunately requires us to test all array elements.
2206 int32_t bestMatchLength
= 0, bestMatch
= -1;
2207 UnicodeString bestMatchName
;
2209 // {sfb} kludge to support case-insensitive comparison
2210 // {markus 2002oct11} do not just use caseCompareBetween because we do not know
2211 // the length of the match after case folding
2212 // {alan 20040607} don't case change the whole string, since the length
2214 // TODO we need a case-insensitive startsWith function
2215 UnicodeString lcaseText
;
2216 text
.extract(start
, INT32_MAX
, lcaseText
);
2217 lcaseText
.foldCase();
2219 for (; i
< count
; ++i
)
2221 // Always compare if we have no match yet; otherwise only compare
2222 // against potentially better matches (longer strings).
2224 if (newBestMatchWithOptionalDot(lcaseText
, data
[i
], bestMatchName
, bestMatchLength
)) {
2230 cal
.set(field
, bestMatch
* 3);
2232 // Once we have a match, we have to determine the length of the
2233 // original source string. This will usually be == the length of
2234 // the case folded string, but it may differ (e.g. sharp s).
2236 // Most of the time, the length will be the same as the length
2237 // of the string from the locale data. Sometimes it will be
2238 // different, in which case we will have to figure it out by
2239 // adding a character at a time, until we have a match. We do
2240 // this all in one loop, where we try 'len' first (at index
2242 int32_t len
= bestMatchName
.length(); // 99+% of the time
2243 int32_t n
= text
.length() - start
;
2244 for (i
=0; i
<=n
; ++i
) {
2248 } else if (i
== len
) {
2249 continue; // already tried this when i was 0
2251 text
.extract(start
, j
, lcaseText
);
2252 lcaseText
.foldCase();
2253 if (bestMatchName
== lcaseText
) {
2262 //----------------------------------------------------------------------
2263 UBool
SimpleDateFormat::matchLiterals(const UnicodeString
&pattern
,
2264 int32_t &patternOffset
,
2265 const UnicodeString
&text
,
2266 int32_t &textOffset
,
2267 UBool whitespaceLenient
,
2268 UBool partialMatchLenient
)
2270 UBool inQuote
= FALSE
;
2271 UnicodeString literal
;
2272 int32_t i
= patternOffset
;
2274 // scan pattern looking for contiguous literal characters
2275 for ( ; i
< pattern
.length(); i
+= 1) {
2276 UChar ch
= pattern
.charAt(i
);
2278 if (!inQuote
&& ((ch
>= 0x41 && ch
<= 0x5A) || (ch
>= 0x61 && ch
<= 0x7A))) { // unquoted [A-Za-z]
2283 // Match a quote literal ('') inside OR outside of quotes
2284 if ((i
+ 1) < pattern
.length() && pattern
.charAt(i
+ 1) == QUOTE
) {
2295 // at this point, literal contains the literal text
2296 // and i is the index of the next non-literal pattern character.
2298 int32_t t
= textOffset
;
2300 if (whitespaceLenient
) {
2301 // trim leading, trailing whitespace from
2305 // ignore any leading whitespace in the text
2306 while (t
< text
.length() && u_isWhitespace(text
.charAt(t
))) {
2311 for (p
= 0; p
< literal
.length() && t
< text
.length();) {
2312 UBool needWhitespace
= FALSE
;
2314 while (p
< literal
.length() && PatternProps::isWhiteSpace(literal
.charAt(p
))) {
2315 needWhitespace
= TRUE
;
2319 if (needWhitespace
) {
2322 while (t
< text
.length()) {
2323 UChar tch
= text
.charAt(t
);
2325 if (!u_isUWhiteSpace(tch
) && !PatternProps::isWhiteSpace(tch
)) {
2332 // TODO: should we require internal spaces
2333 // in lenient mode? (There won't be any
2334 // leading or trailing spaces)
2335 if (!whitespaceLenient
&& t
== tStart
) {
2336 // didn't find matching whitespace:
2337 // an error in strict mode
2341 // In strict mode, this run of whitespace
2342 // may have been at the end.
2343 if (p
>= literal
.length()) {
2348 if (t
>= text
.length() || literal
.charAt(p
) != text
.charAt(t
)) {
2349 // Ran out of text, or found a non-matching character:
2350 // OK in lenient mode, an error in strict mode.
2351 if (whitespaceLenient
) {
2352 if (t
== textOffset
&& text
.charAt(t
) == 0x2e &&
2353 isAfterNonNumericField(pattern
, patternOffset
)) {
2354 // Lenient mode and the literal input text begins with a "." and
2355 // we are after a non-numeric field: We skip the "."
2357 continue; // Do not update p.
2359 // if it is actual whitespace and we're whitespace lenient it's OK
2360 UChar wsc
= text
.charAt(t
);
2361 if(PatternProps::isWhiteSpace(wsc
))
2364 // or if we're partial match lenient it's OK
2365 if(partialMatchLenient
) {
2375 // At this point if we're in strict mode we have a complete match.
2376 // If we're in lenient mode we may have a partial match, or no
2379 // no match. Pretend it matched a run of whitespace
2380 // and ignorables in the text.
2381 const UnicodeSet
*ignorables
= NULL
;
2382 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(pattern
.charAt(i
));
2383 if (patternCharIndex
!= UDAT_FIELD_COUNT
) {
2384 ignorables
= SimpleDateFormatStaticSets::getIgnorables(patternCharIndex
);
2387 for (t
= textOffset
; t
< text
.length(); t
+= 1) {
2388 UChar ch
= text
.charAt(t
);
2390 if (ignorables
== NULL
|| !ignorables
->contains(ch
)) {
2396 // if we get here, we've got a complete match.
2397 patternOffset
= i
- 1;
2403 //----------------------------------------------------------------------
2405 int32_t SimpleDateFormat::matchString(const UnicodeString
& text
,
2407 UCalendarDateFields field
,
2408 const UnicodeString
* data
,
2410 const UnicodeString
* monthPattern
,
2411 Calendar
& cal
) const
2414 int32_t count
= dataCount
;
2416 if (field
== UCAL_DAY_OF_WEEK
) i
= 1;
2418 // There may be multiple strings in the data[] array which begin with
2419 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2420 // We keep track of the longest match, and return that. Note that this
2421 // unfortunately requires us to test all array elements.
2422 int32_t bestMatchLength
= 0, bestMatch
= -1;
2423 UnicodeString bestMatchName
;
2424 int32_t isLeapMonth
= 0;
2426 // {sfb} kludge to support case-insensitive comparison
2427 // {markus 2002oct11} do not just use caseCompareBetween because we do not know
2428 // the length of the match after case folding
2429 // {alan 20040607} don't case change the whole string, since the length
2431 // TODO we need a case-insensitive startsWith function
2432 UnicodeString lcaseText
;
2433 text
.extract(start
, INT32_MAX
, lcaseText
);
2434 lcaseText
.foldCase();
2436 for (; i
< count
; ++i
)
2438 // Always compare if we have no match yet; otherwise only compare
2439 // against potentially better matches (longer strings).
2441 if (newBestMatchWithOptionalDot(lcaseText
, data
[i
], bestMatchName
, bestMatchLength
)) {
2446 if (monthPattern
!= NULL
) {
2447 UErrorCode status
= U_ZERO_ERROR
;
2448 UnicodeString leapMonthName
;
2449 Formattable
monthName((const UnicodeString
&)(data
[i
]));
2450 MessageFormat::format(*monthPattern
, &monthName
, 1, leapMonthName
, status
);
2451 if (U_SUCCESS(status
)) {
2452 if (newBestMatchWithOptionalDot(lcaseText
, leapMonthName
, bestMatchName
, bestMatchLength
)) {
2461 // Adjustment for Hebrew Calendar month Adar II
2462 if (!strcmp(cal
.getType(),"hebrew") && field
==UCAL_MONTH
&& bestMatch
==13) {
2466 if (field
== UCAL_YEAR
) {
2467 bestMatch
++; // only get here for cyclic year names, which match 1-based years 1-60
2469 cal
.set(field
, bestMatch
);
2471 if (monthPattern
!= NULL
) {
2472 cal
.set(UCAL_IS_LEAP_MONTH
, isLeapMonth
);
2475 // Once we have a match, we have to determine the length of the
2476 // original source string. This will usually be == the length of
2477 // the case folded string, but it may differ (e.g. sharp s).
2479 // Most of the time, the length will be the same as the length
2480 // of the string from the locale data. Sometimes it will be
2481 // different, in which case we will have to figure it out by
2482 // adding a character at a time, until we have a match. We do
2483 // this all in one loop, where we try 'len' first (at index
2485 int32_t len
= bestMatchName
.length(); // 99+% of the time
2486 int32_t n
= text
.length() - start
;
2487 for (i
=0; i
<=n
; ++i
) {
2491 } else if (i
== len
) {
2492 continue; // already tried this when i was 0
2494 text
.extract(start
, j
, lcaseText
);
2495 lcaseText
.foldCase();
2496 if (bestMatchName
== lcaseText
) {
2506 newBestMatchWithOptionalDot(const UnicodeString
&lcaseText
,
2507 const UnicodeString
&data
,
2508 UnicodeString
&bestMatchName
,
2509 int32_t &bestMatchLength
) {
2510 UnicodeString lcase
;
2511 lcase
.fastCopyFrom(data
).foldCase();
2512 int32_t length
= lcase
.length();
2513 if (length
<= bestMatchLength
) {
2514 // data cannot provide a better match.
2518 if (lcaseText
.compareBetween(0, length
, lcase
, 0, length
) == 0) {
2520 bestMatchName
= lcase
;
2521 bestMatchLength
= length
;
2524 if (lcase
.charAt(--length
) == 0x2e) {
2525 if (lcaseText
.compareBetween(0, length
, lcase
, 0, length
) == 0) {
2526 // The input text matches the data except for data's trailing dot.
2527 bestMatchName
= lcase
;
2528 bestMatchName
.truncate(length
);
2529 bestMatchLength
= length
;
2536 //----------------------------------------------------------------------
2539 SimpleDateFormat::set2DigitYearStart(UDate d
, UErrorCode
& status
)
2541 parseAmbiguousDatesAsAfter(d
, status
);
2545 * Private member function that converts the parsed date strings into
2546 * timeFields. Returns -start (for ParsePosition) if failed.
2548 int32_t SimpleDateFormat::subParse(const UnicodeString
& text
, int32_t& start
, UChar ch
, int32_t count
,
2549 UBool obeyCount
, UBool allowNegative
, UBool ambiguousYear
[], int32_t& saveHebrewMonth
, Calendar
& cal
,
2550 int32_t patLoc
, MessageFormat
* numericLeapMonthFormatter
, UTimeZoneFormatTimeType
*tzTimeType
) const
2556 UErrorCode status
= U_ZERO_ERROR
;
2557 ParsePosition
pos(0);
2558 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(ch
);
2559 NumberFormat
*currentNumberFormat
;
2561 int32_t tzParseOptions
= (isLenient())? UTZFMT_PARSE_OPTION_ALL_STYLES
: UTZFMT_PARSE_OPTION_NONE
;
2562 UBool gotNumber
= FALSE
;
2564 #if defined (U_DEBUG_CAL)
2565 //fprintf(stderr, "%s:%d - [%c] st=%d \n", __FILE__, __LINE__, (char) ch, start);
2568 if (patternCharIndex
== UDAT_FIELD_COUNT
) {
2572 currentNumberFormat
= getNumberFormatByIndex(patternCharIndex
);
2573 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
];
2574 UnicodeString
hebr("hebr", 4, US_INV
);
2576 if (numericLeapMonthFormatter
!= NULL
) {
2577 numericLeapMonthFormatter
->setFormats((const Format
**)¤tNumberFormat
, 1);
2579 UBool isChineseCalendar
= (uprv_strcmp(cal
.getType(),"chinese") == 0 || uprv_strcmp(cal
.getType(),"dangi") == 0);
2581 // If there are any spaces here, skip over them. If we hit the end
2582 // of the string, then fail.
2584 if (start
>= text
.length()) {
2587 UChar32 c
= text
.char32At(start
);
2588 if (!u_isUWhiteSpace(c
) /*||*/ && !PatternProps::isWhiteSpace(c
)) {
2591 start
+= U16_LENGTH(c
);
2593 pos
.setIndex(start
);
2595 // We handle a few special cases here where we need to parse
2596 // a number value. We handle further, more generic cases below. We need
2597 // to handle some of them here because some fields require extra processing on
2598 // the parsed value.
2599 if (patternCharIndex
== UDAT_HOUR_OF_DAY1_FIELD
|| // k
2600 patternCharIndex
== UDAT_HOUR_OF_DAY0_FIELD
|| // H
2601 patternCharIndex
== UDAT_HOUR1_FIELD
|| // h
2602 patternCharIndex
== UDAT_HOUR0_FIELD
|| // K
2603 (patternCharIndex
== UDAT_DOW_LOCAL_FIELD
&& count
<= 2) || // e
2604 (patternCharIndex
== UDAT_STANDALONE_DAY_FIELD
&& count
<= 2) || // c
2605 (patternCharIndex
== UDAT_MONTH_FIELD
&& count
<= 2) || // M
2606 (patternCharIndex
== UDAT_STANDALONE_MONTH_FIELD
&& count
<= 2) || // L
2607 (patternCharIndex
== UDAT_QUARTER_FIELD
&& count
<= 2) || // Q
2608 (patternCharIndex
== UDAT_STANDALONE_QUARTER_FIELD
&& count
<= 2) || // q
2609 patternCharIndex
== UDAT_YEAR_FIELD
|| // y
2610 patternCharIndex
== UDAT_YEAR_WOY_FIELD
|| // Y
2611 patternCharIndex
== UDAT_YEAR_NAME_FIELD
|| // U (falls back to numeric)
2612 (patternCharIndex
== UDAT_ERA_FIELD
&& isChineseCalendar
) || // G
2613 patternCharIndex
== UDAT_FRACTIONAL_SECOND_FIELD
) // S
2615 int32_t parseStart
= pos
.getIndex();
2616 // It would be good to unify this with the obeyCount logic below,
2617 // but that's going to be difficult.
2618 const UnicodeString
* src
;
2620 UBool parsedNumericLeapMonth
= FALSE
;
2621 if (numericLeapMonthFormatter
!= NULL
&& (patternCharIndex
== UDAT_MONTH_FIELD
|| patternCharIndex
== UDAT_STANDALONE_MONTH_FIELD
)) {
2623 Formattable
* args
= numericLeapMonthFormatter
->parse(text
, pos
, argCount
);
2624 if (args
!= NULL
&& argCount
== 1 && pos
.getIndex() > parseStart
&& args
[0].isNumeric()) {
2625 parsedNumericLeapMonth
= TRUE
;
2626 number
.setLong(args
[0].getLong());
2627 cal
.set(UCAL_IS_LEAP_MONTH
, 1);
2630 pos
.setIndex(parseStart
);
2631 cal
.set(UCAL_IS_LEAP_MONTH
, 0);
2635 if (!parsedNumericLeapMonth
) {
2637 if ((start
+count
) > text
.length()) {
2641 text
.extractBetween(0, start
+ count
, temp
);
2647 parseInt(*src
, number
, pos
, allowNegative
,currentNumberFormat
);
2650 int32_t txtLoc
= pos
.getIndex();
2652 if (txtLoc
> parseStart
) {
2653 value
= number
.getLong();
2656 // suffix processing
2658 txtLoc
= checkIntSuffix(text
, txtLoc
, patLoc
+1, TRUE
);
2659 if (txtLoc
!= pos
.getIndex()) {
2664 txtLoc
= checkIntSuffix(text
, txtLoc
, patLoc
+1, FALSE
);
2667 // Check the range of the value
2668 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, status
)) {
2669 int32_t bias
= gFieldRangeBias
[patternCharIndex
];
2670 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
|| value
< cal
.getMinimum(field
) + bias
)) {
2674 int32_t bias
= gFieldRangeBiasLenient
[patternCharIndex
];
2675 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
)) {
2680 pos
.setIndex(txtLoc
);
2684 // Make sure that we got a number if
2685 // we want one, and didn't get one
2686 // if we don't want one.
2687 switch (patternCharIndex
) {
2688 case UDAT_HOUR_OF_DAY1_FIELD
:
2689 case UDAT_HOUR_OF_DAY0_FIELD
:
2690 case UDAT_HOUR1_FIELD
:
2691 case UDAT_HOUR0_FIELD
:
2692 // special range check for hours:
2693 if (value
< 0 || value
> 24) {
2697 // fall through to gotNumber check
2699 case UDAT_YEAR_FIELD
:
2700 case UDAT_YEAR_WOY_FIELD
:
2701 case UDAT_FRACTIONAL_SECOND_FIELD
:
2702 // these must be a number
2710 // we check the rest of the fields below.
2714 switch (patternCharIndex
) {
2715 case UDAT_ERA_FIELD
:
2716 if (isChineseCalendar
) {
2720 cal
.set(UCAL_ERA
, value
);
2721 return pos
.getIndex();
2724 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fNarrowEras
, fSymbols
->fNarrowErasCount
, NULL
, cal
);
2725 } else if (count
== 4) {
2726 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fEraNames
, fSymbols
->fEraNamesCount
, NULL
, cal
);
2728 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fEras
, fSymbols
->fErasCount
, NULL
, cal
);
2731 // check return position, if it equals -start, then matchString error
2732 // special case the return code so we don't necessarily fail out until we
2733 // verify no year information also
2739 case UDAT_YEAR_FIELD
:
2740 // If there are 3 or more YEAR pattern characters, this indicates
2741 // that the year value is to be treated literally, without any
2742 // two-digit year adjustments (e.g., from "01" to 2001). Otherwise
2743 // we made adjustments to place the 2-digit year in the proper
2744 // century, for parsed strings from "00" to "99". Any other string
2745 // is treated literally: "2250", "-1", "1", "002".
2746 if (fDateOverride
.compare(hebr
)==0 && value
< 1000) {
2747 value
+= HEBREW_CAL_CUR_MILLENIUM_START_YEAR
;
2748 } else if ((pos
.getIndex() - start
) == 2 && !isChineseCalendar
2749 && u_isdigit(text
.charAt(start
))
2750 && u_isdigit(text
.charAt(start
+1)))
2752 // only adjust year for patterns less than 3.
2754 // Assume for example that the defaultCenturyStart is 6/18/1903.
2755 // This means that two-digit years will be forced into the range
2756 // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
2757 // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
2758 // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
2759 // other fields specify a date before 6/18, or 1903 if they specify a
2760 // date afterwards. As a result, 03 is an ambiguous year. All other
2761 // two-digit years are unambiguous.
2762 if(fHaveDefaultCentury
) { // check if this formatter even has a pivot year
2763 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
2764 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
2765 value
+= (fDefaultCenturyStartYear
/100)*100 +
2766 (value
< ambiguousTwoDigitYear
? 100 : 0);
2770 cal
.set(UCAL_YEAR
, value
);
2772 // Delayed checking for adjustment of Hebrew month numbers in non-leap years.
2773 if (saveHebrewMonth
>= 0) {
2774 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
2775 if (!hc
->isLeapYear(value
) && saveHebrewMonth
>= 6) {
2776 cal
.set(UCAL_MONTH
,saveHebrewMonth
);
2778 cal
.set(UCAL_MONTH
,saveHebrewMonth
-1);
2780 saveHebrewMonth
= -1;
2782 return pos
.getIndex();
2784 case UDAT_YEAR_WOY_FIELD
:
2785 // Comment is the same as for UDAT_Year_FIELDs - look above
2786 if (fDateOverride
.compare(hebr
)==0 && value
< 1000) {
2787 value
+= HEBREW_CAL_CUR_MILLENIUM_START_YEAR
;
2788 } else if ((pos
.getIndex() - start
) == 2
2789 && u_isdigit(text
.charAt(start
))
2790 && u_isdigit(text
.charAt(start
+1))
2791 && fHaveDefaultCentury
)
2793 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
2794 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
2795 value
+= (fDefaultCenturyStartYear
/100)*100 +
2796 (value
< ambiguousTwoDigitYear
? 100 : 0);
2798 cal
.set(UCAL_YEAR_WOY
, value
);
2799 return pos
.getIndex();
2801 case UDAT_YEAR_NAME_FIELD
:
2802 if (fSymbols
->fShortYearNames
!= NULL
) {
2803 int32_t newStart
= matchString(text
, start
, UCAL_YEAR
, fSymbols
->fShortYearNames
, fSymbols
->fShortYearNamesCount
, NULL
, cal
);
2808 if (gotNumber
&& (getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
,status
) || value
> fSymbols
->fShortYearNamesCount
)) {
2809 cal
.set(UCAL_YEAR
, value
);
2810 return pos
.getIndex();
2814 case UDAT_MONTH_FIELD
:
2815 case UDAT_STANDALONE_MONTH_FIELD
:
2816 if (gotNumber
) // i.e., M or MM.
2818 // When parsing month numbers from the Hebrew Calendar, we might need to adjust the month depending on whether
2819 // 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
2820 // the year is parsed.
2821 if (!strcmp(cal
.getType(),"hebrew")) {
2822 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
2823 if (cal
.isSet(UCAL_YEAR
)) {
2824 UErrorCode status
= U_ZERO_ERROR
;
2825 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6) {
2826 cal
.set(UCAL_MONTH
, value
);
2828 cal
.set(UCAL_MONTH
, value
- 1);
2831 saveHebrewMonth
= value
;
2834 // Don't want to parse the month if it is a string
2835 // while pattern uses numeric style: M/MM, L/LL
2836 // [We computed 'value' above.]
2837 cal
.set(UCAL_MONTH
, value
- 1);
2839 return pos
.getIndex();
2841 // count >= 3 // i.e., MMM/MMMM, LLL/LLLL
2842 // Want to be able to parse both short and long forms.
2843 // Try count == 4 first:
2844 UnicodeString
* wideMonthPat
= NULL
;
2845 UnicodeString
* shortMonthPat
= NULL
;
2846 if (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
) {
2847 if (patternCharIndex
==UDAT_MONTH_FIELD
) {
2848 wideMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatWide
];
2849 shortMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatAbbrev
];
2851 wideMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneWide
];
2852 shortMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev
];
2855 int32_t newStart
= 0;
2856 if (patternCharIndex
==UDAT_MONTH_FIELD
) {
2857 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
2858 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fMonths
, fSymbols
->fMonthsCount
, wideMonthPat
, cal
); // try MMMM
2863 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
2864 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fShortMonths
, fSymbols
->fShortMonthsCount
, shortMonthPat
, cal
); // try MMM
2867 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
2868 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fStandaloneMonths
, fSymbols
->fStandaloneMonthsCount
, wideMonthPat
, cal
); // try LLLL
2873 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
2874 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fStandaloneShortMonths
, fSymbols
->fStandaloneShortMonthsCount
, shortMonthPat
, cal
); // try LLL
2877 if (newStart
> 0 || !getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
)) // currently we do not try to parse MMMMM/LLLLL: #8860
2879 // else we allowing parsing as number, below
2883 case UDAT_HOUR_OF_DAY1_FIELD
:
2884 // [We computed 'value' above.]
2885 if (value
== cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1)
2888 // fall through to set field
2890 case UDAT_HOUR_OF_DAY0_FIELD
:
2891 cal
.set(UCAL_HOUR_OF_DAY
, value
);
2892 return pos
.getIndex();
2894 case UDAT_FRACTIONAL_SECOND_FIELD
:
2895 // Fractional seconds left-justify
2896 i
= pos
.getIndex() - start
;
2910 cal
.set(UCAL_MILLISECOND
, value
);
2911 return pos
.getIndex();
2913 case UDAT_DOW_LOCAL_FIELD
:
2914 if (gotNumber
) // i.e., e or ee
2916 // [We computed 'value' above.]
2917 cal
.set(UCAL_DOW_LOCAL
, value
);
2918 return pos
.getIndex();
2920 // else for eee-eeeee fall through to handling of EEE-EEEEE
2921 // fall through, do not break here
2922 case UDAT_DAY_OF_WEEK_FIELD
:
2924 // Want to be able to parse both short and long forms.
2925 // Try count == 4 (EEEE) wide first:
2926 int32_t newStart
= 0;
2927 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
2928 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2929 fSymbols
->fWeekdays
, fSymbols
->fWeekdaysCount
, NULL
, cal
)) > 0)
2932 // EEEE wide failed, now try EEE abbreviated
2933 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
2934 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2935 fSymbols
->fShortWeekdays
, fSymbols
->fShortWeekdaysCount
, NULL
, cal
)) > 0)
2938 // EEE abbreviated failed, now try EEEEEE short
2939 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 6) {
2940 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2941 fSymbols
->fShorterWeekdays
, fSymbols
->fShorterWeekdaysCount
, NULL
, cal
)) > 0)
2944 // EEEEEE short failed, now try EEEEE narrow
2945 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 5) {
2946 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2947 fSymbols
->fNarrowWeekdays
, fSymbols
->fNarrowWeekdaysCount
, NULL
, cal
)) > 0)
2950 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
) || patternCharIndex
== UDAT_DAY_OF_WEEK_FIELD
)
2952 // else we allowing parsing as number, below
2956 case UDAT_STANDALONE_DAY_FIELD
:
2958 if (gotNumber
) // c or cc
2960 // [We computed 'value' above.]
2961 cal
.set(UCAL_DOW_LOCAL
, value
);
2962 return pos
.getIndex();
2964 // Want to be able to parse both short and long forms.
2965 // Try count == 4 (cccc) first:
2966 int32_t newStart
= 0;
2967 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
2968 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2969 fSymbols
->fStandaloneWeekdays
, fSymbols
->fStandaloneWeekdaysCount
, NULL
, cal
)) > 0)
2972 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
2973 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2974 fSymbols
->fStandaloneShortWeekdays
, fSymbols
->fStandaloneShortWeekdaysCount
, NULL
, cal
)) > 0)
2977 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 6) {
2978 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
2979 fSymbols
->fStandaloneShorterWeekdays
, fSymbols
->fStandaloneShorterWeekdaysCount
, NULL
, cal
)) > 0)
2982 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
))
2984 // else we allowing parsing as number, below
2988 case UDAT_AM_PM_FIELD
:
2989 return matchString(text
, start
, UCAL_AM_PM
, fSymbols
->fAmPms
, fSymbols
->fAmPmsCount
, NULL
, cal
);
2991 case UDAT_HOUR1_FIELD
:
2992 // [We computed 'value' above.]
2993 if (value
== cal
.getLeastMaximum(UCAL_HOUR
)+1)
2996 // fall through to set field
2998 case UDAT_HOUR0_FIELD
:
2999 cal
.set(UCAL_HOUR
, value
);
3000 return pos
.getIndex();
3002 case UDAT_QUARTER_FIELD
:
3003 if (gotNumber
) // i.e., Q or QQ.
3005 // Don't want to parse the month if it is a string
3006 // while pattern uses numeric style: Q or QQ.
3007 // [We computed 'value' above.]
3008 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
3009 return pos
.getIndex();
3011 // count >= 3 // i.e., QQQ or QQQQ
3012 // Want to be able to parse both short and long forms.
3013 // Try count == 4 first:
3014 int32_t newStart
= 0;
3016 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3017 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3018 fSymbols
->fQuarters
, fSymbols
->fQuartersCount
, cal
)) > 0)
3021 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3022 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3023 fSymbols
->fShortQuarters
, fSymbols
->fShortQuartersCount
, cal
)) > 0)
3026 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
))
3028 // else we allowing parsing as number, below
3029 if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
))
3034 case UDAT_STANDALONE_QUARTER_FIELD
:
3035 if (gotNumber
) // i.e., q or qq.
3037 // Don't want to parse the month if it is a string
3038 // while pattern uses numeric style: q or q.
3039 // [We computed 'value' above.]
3040 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
3041 return pos
.getIndex();
3043 // count >= 3 // i.e., qqq or qqqq
3044 // Want to be able to parse both short and long forms.
3045 // Try count == 4 first:
3046 int32_t newStart
= 0;
3048 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3049 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3050 fSymbols
->fStandaloneQuarters
, fSymbols
->fStandaloneQuartersCount
, cal
)) > 0)
3053 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3054 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3055 fSymbols
->fStandaloneShortQuarters
, fSymbols
->fStandaloneShortQuartersCount
, cal
)) > 0)
3058 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
))
3060 // else we allowing parsing as number, below
3061 if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
))
3066 case UDAT_TIMEZONE_FIELD
: // 'z'
3068 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_SPECIFIC_SHORT
: UTZFMT_STYLE_SPECIFIC_LONG
;
3069 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzParseOptions
, tzTimeType
);
3071 cal
.adoptTimeZone(tz
);
3072 return pos
.getIndex();
3076 case UDAT_TIMEZONE_RFC_FIELD
: // 'Z'
3078 UTimeZoneFormatStyle style
= (count
< 4) ?
3079 UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
: ((count
== 5) ? UTZFMT_STYLE_ISO_EXTENDED_FULL
: UTZFMT_STYLE_LOCALIZED_GMT
);
3080 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzTimeType
);
3082 cal
.adoptTimeZone(tz
);
3083 return pos
.getIndex();
3087 case UDAT_TIMEZONE_GENERIC_FIELD
: // 'v'
3089 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_GENERIC_SHORT
: UTZFMT_STYLE_GENERIC_LONG
;
3090 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzParseOptions
, tzTimeType
);
3092 cal
.adoptTimeZone(tz
);
3093 return pos
.getIndex();
3097 case UDAT_TIMEZONE_SPECIAL_FIELD
: // 'V'
3099 UTimeZoneFormatStyle style
;
3102 style
= UTZFMT_STYLE_ZONE_ID_SHORT
;
3105 style
= UTZFMT_STYLE_ZONE_ID
;
3108 style
= UTZFMT_STYLE_EXEMPLAR_LOCATION
;
3111 style
= UTZFMT_STYLE_GENERIC_LOCATION
;
3114 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzTimeType
);
3116 cal
.adoptTimeZone(tz
);
3117 return pos
.getIndex();
3121 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
: // 'O'
3123 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_LOCALIZED_GMT_SHORT
: UTZFMT_STYLE_LOCALIZED_GMT
;
3124 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzTimeType
);
3126 cal
.adoptTimeZone(tz
);
3127 return pos
.getIndex();
3131 case UDAT_TIMEZONE_ISO_FIELD
: // 'X'
3133 UTimeZoneFormatStyle style
;
3136 style
= UTZFMT_STYLE_ISO_BASIC_SHORT
;
3139 style
= UTZFMT_STYLE_ISO_BASIC_FIXED
;
3142 style
= UTZFMT_STYLE_ISO_EXTENDED_FIXED
;
3145 style
= UTZFMT_STYLE_ISO_BASIC_FULL
;
3148 style
= UTZFMT_STYLE_ISO_EXTENDED_FULL
;
3151 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzTimeType
);
3153 cal
.adoptTimeZone(tz
);
3154 return pos
.getIndex();
3158 case UDAT_TIMEZONE_ISO_LOCAL_FIELD
: // 'x'
3160 UTimeZoneFormatStyle style
;
3163 style
= UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
;
3166 style
= UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
;
3169 style
= UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
;
3172 style
= UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
;
3175 style
= UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
;
3178 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzTimeType
);
3180 cal
.adoptTimeZone(tz
);
3181 return pos
.getIndex();
3187 // Handle "generic" fields
3188 // this is now handled below, outside the switch block
3191 // Handle "generic" fields:
3192 // switch default case now handled here (outside switch block) to allow
3193 // parsing of some string fields as digits for lenient case
3195 int32_t parseStart
= pos
.getIndex();
3196 const UnicodeString
* src
;
3198 if ((start
+count
) > text
.length()) {
3201 text
.extractBetween(0, start
+ count
, temp
);
3206 parseInt(*src
, number
, pos
, allowNegative
,currentNumberFormat
);
3207 if (pos
.getIndex() != parseStart
) {
3208 int32_t value
= number
.getLong();
3210 // Don't need suffix processing here (as in number processing at the beginning of the function);
3211 // the new fields being handled as numeric values (month, weekdays, quarters) should not have suffixes.
3213 // Check the range of the value
3214 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
)) {
3215 int32_t bias
= gFieldRangeBias
[patternCharIndex
];
3216 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
|| value
< cal
.getMinimum(field
) + bias
)) {
3220 int32_t bias
= gFieldRangeBiasLenient
[patternCharIndex
];
3221 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
)) {
3226 // For the following, need to repeat some of the "if (gotNumber)" code above:
3227 // UDAT_[STANDALONE_]MONTH_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_STANDALONE_DAY_FIELD,
3228 // UDAT_[STANDALONE_]QUARTER_FIELD
3229 switch (patternCharIndex
) {
3230 case UDAT_MONTH_FIELD
:
3231 // See notes under UDAT_MONTH_FIELD case above
3232 if (!strcmp(cal
.getType(),"hebrew")) {
3233 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
3234 if (cal
.isSet(UCAL_YEAR
)) {
3235 UErrorCode status
= U_ZERO_ERROR
;
3236 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6) {
3237 cal
.set(UCAL_MONTH
, value
);
3239 cal
.set(UCAL_MONTH
, value
- 1);
3242 saveHebrewMonth
= value
;
3245 cal
.set(UCAL_MONTH
, value
- 1);
3248 case UDAT_STANDALONE_MONTH_FIELD
:
3249 cal
.set(UCAL_MONTH
, value
- 1);
3251 case UDAT_DOW_LOCAL_FIELD
:
3252 case UDAT_STANDALONE_DAY_FIELD
:
3253 cal
.set(UCAL_DOW_LOCAL
, value
);
3255 case UDAT_QUARTER_FIELD
:
3256 case UDAT_STANDALONE_QUARTER_FIELD
:
3257 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
3259 case UDAT_RELATED_YEAR_FIELD
:
3260 cal
.setRelatedYear(value
);
3263 cal
.set(field
, value
);
3266 return pos
.getIndex();
3272 * Parse an integer using fNumberFormat. This method is semantically
3273 * const, but actually may modify fNumberFormat.
3275 void SimpleDateFormat::parseInt(const UnicodeString
& text
,
3276 Formattable
& number
,
3278 UBool allowNegative
,
3279 NumberFormat
*fmt
) const {
3280 parseInt(text
, number
, -1, pos
, allowNegative
,fmt
);
3284 * Parse an integer using fNumberFormat up to maxDigits.
3286 void SimpleDateFormat::parseInt(const UnicodeString
& text
,
3287 Formattable
& number
,
3290 UBool allowNegative
,
3291 NumberFormat
*fmt
) const {
3292 UnicodeString oldPrefix
;
3293 DecimalFormat
* df
= NULL
;
3294 if (!allowNegative
&& (df
= dynamic_cast<DecimalFormat
*>(fmt
)) != NULL
) {
3295 df
->getNegativePrefix(oldPrefix
);
3296 df
->setNegativePrefix(UnicodeString(TRUE
, SUPPRESS_NEGATIVE_PREFIX
, -1));
3298 int32_t oldPos
= pos
.getIndex();
3299 fmt
->parse(text
, number
, pos
);
3301 df
->setNegativePrefix(oldPrefix
);
3304 if (maxDigits
> 0) {
3305 // adjust the result to fit into
3306 // the maxDigits and move the position back
3307 int32_t nDigits
= pos
.getIndex() - oldPos
;
3308 if (nDigits
> maxDigits
) {
3309 int32_t val
= number
.getLong();
3310 nDigits
-= maxDigits
;
3311 while (nDigits
> 0) {
3315 pos
.setIndex(oldPos
+ maxDigits
);
3316 number
.setLong(val
);
3321 //----------------------------------------------------------------------
3323 void SimpleDateFormat::translatePattern(const UnicodeString
& originalPattern
,
3324 UnicodeString
& translatedPattern
,
3325 const UnicodeString
& from
,
3326 const UnicodeString
& to
,
3329 // run through the pattern and convert any pattern symbols from the version
3330 // in "from" to the corresponding character ion "to". This code takes
3331 // quoted strings into account (it doesn't try to translate them), and it signals
3332 // an error if a particular "pattern character" doesn't appear in "from".
3333 // Depending on the values of "from" and "to" this can convert from generic
3334 // to localized patterns or localized to generic.
3335 if (U_FAILURE(status
))
3338 translatedPattern
.remove();
3339 UBool inQuote
= FALSE
;
3340 for (int32_t i
= 0; i
< originalPattern
.length(); ++i
) {
3341 UChar c
= originalPattern
[i
];
3349 else if ((c
>= 0x0061 /*'a'*/ && c
<= 0x007A) /*'z'*/
3350 || (c
>= 0x0041 /*'A'*/ && c
<= 0x005A /*'Z'*/)) {
3351 int32_t ci
= from
.indexOf(c
);
3353 status
= U_INVALID_FORMAT_ERROR
;
3359 translatedPattern
+= c
;
3362 status
= U_INVALID_FORMAT_ERROR
;
3367 //----------------------------------------------------------------------
3370 SimpleDateFormat::toPattern(UnicodeString
& result
) const
3376 //----------------------------------------------------------------------
3379 SimpleDateFormat::toLocalizedPattern(UnicodeString
& result
,
3380 UErrorCode
& status
) const
3382 translatePattern(fPattern
, result
,
3383 UnicodeString(DateFormatSymbols::getPatternUChars()),
3384 fSymbols
->fLocalPatternChars
, status
);
3388 //----------------------------------------------------------------------
3391 SimpleDateFormat::applyPattern(const UnicodeString
& pattern
)
3396 //----------------------------------------------------------------------
3399 SimpleDateFormat::applyLocalizedPattern(const UnicodeString
& pattern
,
3402 translatePattern(pattern
, fPattern
,
3403 fSymbols
->fLocalPatternChars
,
3404 UnicodeString(DateFormatSymbols::getPatternUChars()), status
);
3407 //----------------------------------------------------------------------
3409 const DateFormatSymbols
*
3410 SimpleDateFormat::getDateFormatSymbols() const
3415 //----------------------------------------------------------------------
3418 SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols
* newFormatSymbols
)
3421 fSymbols
= newFormatSymbols
;
3424 //----------------------------------------------------------------------
3426 SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols
& newFormatSymbols
)
3429 fSymbols
= new DateFormatSymbols(newFormatSymbols
);
3432 //----------------------------------------------------------------------
3433 const TimeZoneFormat
*
3434 SimpleDateFormat::getTimeZoneFormat(void) const {
3435 return (const TimeZoneFormat
*)tzFormat();
3438 //----------------------------------------------------------------------
3440 SimpleDateFormat::adoptTimeZoneFormat(TimeZoneFormat
* timeZoneFormatToAdopt
)
3442 delete fTimeZoneFormat
;
3443 fTimeZoneFormat
= timeZoneFormatToAdopt
;
3446 //----------------------------------------------------------------------
3448 SimpleDateFormat::setTimeZoneFormat(const TimeZoneFormat
& newTimeZoneFormat
)
3450 delete fTimeZoneFormat
;
3451 fTimeZoneFormat
= new TimeZoneFormat(newTimeZoneFormat
);
3454 //----------------------------------------------------------------------
3457 void SimpleDateFormat::adoptCalendar(Calendar
* calendarToAdopt
)
3459 UErrorCode status
= U_ZERO_ERROR
;
3460 DateFormat::adoptCalendar(calendarToAdopt
);
3463 initializeSymbols(fLocale
, fCalendar
, status
); // we need new symbols
3464 initializeDefaultCentury(); // we need a new century (possibly)
3468 //----------------------------------------------------------------------
3471 // override the DateFormat implementation in order to
3472 // lazily initialize fCapitalizationBrkIter
3474 SimpleDateFormat::setContext(UDisplayContext value
, UErrorCode
& status
)
3476 DateFormat::setContext(value
, status
);
3477 #if !UCONFIG_NO_BREAK_ITERATION
3478 if (U_SUCCESS(status
)) {
3479 if ( fCapitalizationBrkIter
== NULL
&& (value
==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
||
3480 value
==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
|| value
==UDISPCTX_CAPITALIZATION_FOR_STANDALONE
) ) {
3481 UErrorCode status
= U_ZERO_ERROR
;
3482 fCapitalizationBrkIter
= BreakIterator::createSentenceInstance(fLocale
, status
);
3483 if (U_FAILURE(status
)) {
3484 delete fCapitalizationBrkIter
;
3485 fCapitalizationBrkIter
= NULL
;
3493 //----------------------------------------------------------------------
3497 SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field
) const {
3498 return isFieldUnitIgnored(fPattern
, field
);
3503 SimpleDateFormat::isFieldUnitIgnored(const UnicodeString
& pattern
,
3504 UCalendarDateFields field
) {
3505 int32_t fieldLevel
= fgCalendarFieldToLevel
[field
];
3508 UBool inQuote
= FALSE
;
3512 for (int32_t i
= 0; i
< pattern
.length(); ++i
) {
3514 if (ch
!= prevCh
&& count
> 0) {
3515 level
= fgPatternCharToLevel
[prevCh
- PATTERN_CHAR_BASE
];
3516 // the larger the level, the smaller the field unit.
3517 if ( fieldLevel
<= level
) {
3523 if ((i
+1) < pattern
.length() && pattern
[i
+1] == QUOTE
) {
3526 inQuote
= ! inQuote
;
3529 else if ( ! inQuote
&& ((ch
>= 0x0061 /*'a'*/ && ch
<= 0x007A /*'z'*/)
3530 || (ch
>= 0x0041 /*'A'*/ && ch
<= 0x005A /*'Z'*/))) {
3537 level
= fgPatternCharToLevel
[prevCh
- PATTERN_CHAR_BASE
];
3538 if ( fieldLevel
<= level
) {
3545 //----------------------------------------------------------------------
3548 SimpleDateFormat::getSmpFmtLocale(void) const {
3552 //----------------------------------------------------------------------
3555 SimpleDateFormat::checkIntSuffix(const UnicodeString
& text
, int32_t start
,
3556 int32_t patLoc
, UBool isNegative
) const {
3559 int32_t patternMatch
;
3560 int32_t textPreMatch
;
3561 int32_t textPostMatch
;
3563 // check that we are still in range
3564 if ( (start
> text
.length()) ||
3567 (patLoc
> fPattern
.length())) {
3568 // out of range, don't advance location in text
3573 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(fNumberFormat
);
3574 if (decfmt
!= NULL
) {
3576 suf
= decfmt
->getNegativeSuffix(suf
);
3579 suf
= decfmt
->getPositiveSuffix(suf
);
3584 if (suf
.length() <= 0) {
3588 // check suffix will be encountered in the pattern
3589 patternMatch
= compareSimpleAffix(suf
,fPattern
,patLoc
);
3591 // check if a suffix will be encountered in the text
3592 textPreMatch
= compareSimpleAffix(suf
,text
,start
);
3594 // check if a suffix was encountered in the text
3595 textPostMatch
= compareSimpleAffix(suf
,text
,start
-suf
.length());
3597 // check for suffix match
3598 if ((textPreMatch
>= 0) && (patternMatch
>= 0) && (textPreMatch
== patternMatch
)) {
3601 else if ((textPostMatch
>= 0) && (patternMatch
>= 0) && (textPostMatch
== patternMatch
)) {
3602 return start
- suf
.length();
3605 // should not get here
3609 //----------------------------------------------------------------------
3612 SimpleDateFormat::compareSimpleAffix(const UnicodeString
& affix
,
3613 const UnicodeString
& input
,
3614 int32_t pos
) const {
3615 int32_t start
= pos
;
3616 for (int32_t i
=0; i
<affix
.length(); ) {
3617 UChar32 c
= affix
.char32At(i
);
3618 int32_t len
= U16_LENGTH(c
);
3619 if (PatternProps::isWhiteSpace(c
)) {
3620 // We may have a pattern like: \u200F \u0020
3621 // and input text like: \u200F \u0020
3622 // Note that U+200F and U+0020 are Pattern_White_Space but only
3623 // U+0020 is UWhiteSpace. So we have to first do a direct
3624 // match of the run of Pattern_White_Space in the pattern,
3625 // then match any extra characters.
3626 UBool literalMatch
= FALSE
;
3627 while (pos
< input
.length() &&
3628 input
.char32At(pos
) == c
) {
3629 literalMatch
= TRUE
;
3632 if (i
== affix
.length()) {
3635 c
= affix
.char32At(i
);
3636 len
= U16_LENGTH(c
);
3637 if (!PatternProps::isWhiteSpace(c
)) {
3642 // Advance over run in pattern
3643 i
= skipPatternWhiteSpace(affix
, i
);
3645 // Advance over run in input text
3646 // Must see at least one white space char in input,
3647 // unless we've already matched some characters literally.
3649 pos
= skipUWhiteSpace(input
, pos
);
3650 if (pos
== s
&& !literalMatch
) {
3654 // If we skip UWhiteSpace in the input text, we need to skip it in the pattern.
3655 // Otherwise, the previous lines may have skipped over text (such as U+00A0) that
3656 // is also in the affix.
3657 i
= skipUWhiteSpace(affix
, i
);
3659 if (pos
< input
.length() &&
3660 input
.char32At(pos
) == c
) {
3671 //----------------------------------------------------------------------
3674 SimpleDateFormat::skipPatternWhiteSpace(const UnicodeString
& text
, int32_t pos
) const {
3675 const UChar
* s
= text
.getBuffer();
3676 return (int32_t)(PatternProps::skipWhiteSpace(s
+ pos
, text
.length() - pos
) - s
);
3679 //----------------------------------------------------------------------
3682 SimpleDateFormat::skipUWhiteSpace(const UnicodeString
& text
, int32_t pos
) const {
3683 while (pos
< text
.length()) {
3684 UChar32 c
= text
.char32At(pos
);
3685 if (!u_isUWhiteSpace(c
)) {
3688 pos
+= U16_LENGTH(c
);
3693 //----------------------------------------------------------------------
3695 // Lazy TimeZoneFormat instantiation, semantically const.
3697 SimpleDateFormat::tzFormat() const {
3698 if (fTimeZoneFormat
== NULL
) {
3701 if (fTimeZoneFormat
== NULL
) {
3702 UErrorCode status
= U_ZERO_ERROR
;
3703 TimeZoneFormat
*tzfmt
= TimeZoneFormat::createInstance(fLocale
, status
);
3704 if (U_FAILURE(status
)) {
3708 const_cast<SimpleDateFormat
*>(this)->fTimeZoneFormat
= tzfmt
;
3713 return fTimeZoneFormat
;
3718 #endif /* #if !UCONFIG_NO_FORMATTING */