2 *******************************************************************************
3 * Copyright (C) 1997-2015, 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
32 #include "unicode/smpdtfmt.h"
33 #include "unicode/dtfmtsym.h"
34 #include "unicode/ures.h"
35 #include "unicode/msgfmt.h"
36 #include "unicode/calendar.h"
37 #include "unicode/gregocal.h"
38 #include "unicode/timezone.h"
39 #include "unicode/decimfmt.h"
40 #include "unicode/dcfmtsym.h"
41 #include "unicode/uchar.h"
42 #include "unicode/uniset.h"
43 #include "unicode/ustring.h"
44 #include "unicode/basictz.h"
45 #include "unicode/simpletz.h"
46 #include "unicode/rbtz.h"
47 #include "unicode/tzfmt.h"
48 #include "unicode/utf16.h"
49 #include "unicode/vtzone.h"
50 #include "unicode/udisplaycontext.h"
51 #include "unicode/brkiter.h"
53 #include "patternprops.h"
63 #include "sharednumberformat.h"
66 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
70 // *****************************************************************************
71 // class SimpleDateFormat
72 // *****************************************************************************
77 * Last-resort string to use for "GMT" when constructing time zone strings.
79 // For time zones that have no names, use strings GMT+minutes and
80 // GMT-minutes. For instance, in France the time zone is GMT+60.
81 // Also accepted are GMT+H:MM or GMT-H:MM.
82 // Currently not being used
83 //static const UChar gGmt[] = {0x0047, 0x004D, 0x0054, 0x0000}; // "GMT"
84 //static const UChar gGmtPlus[] = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+"
85 //static const UChar gGmtMinus[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-"
86 //static const UChar gDefGmtPat[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
87 //static const UChar gDefGmtNegHmsPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */
88 //static const UChar gDefGmtNegHmPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */
89 //static const UChar gDefGmtPosHmsPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */
90 //static const UChar gDefGmtPosHmPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */
91 //static const UChar gUt[] = {0x0055, 0x0054, 0x0000}; // "UT"
92 //static const UChar gUtc[] = {0x0055, 0x0054, 0x0043, 0x0000}; // "UT"
94 typedef enum GmtPatSize
{
105 // Stuff needed for numbering system overrides
107 typedef enum OvrStrType
{
113 static const UDateFormatField kDateFields
[] = {
117 UDAT_DAY_OF_YEAR_FIELD
,
118 UDAT_DAY_OF_WEEK_IN_MONTH_FIELD
,
119 UDAT_WEEK_OF_YEAR_FIELD
,
120 UDAT_WEEK_OF_MONTH_FIELD
,
122 UDAT_EXTENDED_YEAR_FIELD
,
123 UDAT_JULIAN_DAY_FIELD
,
124 UDAT_STANDALONE_DAY_FIELD
,
125 UDAT_STANDALONE_MONTH_FIELD
,
127 UDAT_STANDALONE_QUARTER_FIELD
,
128 UDAT_YEAR_NAME_FIELD
,
129 UDAT_RELATED_YEAR_FIELD
};
130 static const int8_t kDateFieldsCount
= 16;
132 static const UDateFormatField kTimeFields
[] = {
133 UDAT_HOUR_OF_DAY1_FIELD
,
134 UDAT_HOUR_OF_DAY0_FIELD
,
137 UDAT_FRACTIONAL_SECOND_FIELD
,
140 UDAT_MILLISECONDS_IN_DAY_FIELD
,
141 UDAT_TIMEZONE_RFC_FIELD
,
142 UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
};
143 static const int8_t kTimeFieldsCount
= 10;
146 // This is a pattern-of-last-resort used when we can't load a usable pattern out
148 static const UChar gDefaultPattern
[] =
150 0x79, 0x79, 0x79, 0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0
151 }; /* "yyyyMMdd hh:mm a" */
153 // This prefix is designed to NEVER MATCH real text, in order to
154 // suppress the parsing of negative numbers. Adjust as needed (if
155 // this becomes valid Unicode).
156 static const UChar SUPPRESS_NEGATIVE_PREFIX
[] = {0xAB00, 0};
159 * These are the tags we expect to see in normal resource bundle files associated
162 static const char gDateTimePatternsTag
[]="DateTimePatterns";
164 //static const UChar gEtcUTC[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x54, 0x43, 0x00}; // "Etc/UTC"
165 static const UChar QUOTE
= 0x27; // Single quote
168 * The field range check bias for each UDateFormatField.
169 * The bias is added to the minimum and maximum values
170 * before they are compared to the parsed number.
171 * For example, the calendar stores zero-based month numbers
172 * but the parsed month numbers start at 1, so the bias is 1.
174 * A value of -1 means that the value is not checked.
176 static const int32_t gFieldRangeBias
[] = {
177 -1, // 'G' - UDAT_ERA_FIELD
178 -1, // 'y' - UDAT_YEAR_FIELD
179 1, // 'M' - UDAT_MONTH_FIELD
180 0, // 'd' - UDAT_DATE_FIELD
181 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
182 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
183 0, // 'm' - UDAT_MINUTE_FIELD
184 0, // 's' - UDAT_SECOND_FIELD
185 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
186 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
187 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
188 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
189 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
190 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
191 -1, // 'a' - UDAT_AM_PM_FIELD
192 -1, // 'h' - UDAT_HOUR1_FIELD
193 -1, // 'K' - UDAT_HOUR0_FIELD
194 -1, // 'z' - UDAT_TIMEZONE_FIELD
195 -1, // 'Y' - UDAT_YEAR_WOY_FIELD
196 -1, // 'e' - UDAT_DOW_LOCAL_FIELD
197 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
198 -1, // 'g' - UDAT_JULIAN_DAY_FIELD
199 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
200 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
201 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
202 0, // 'c' - UDAT_STANDALONE_DAY_FIELD
203 1, // 'L' - UDAT_STANDALONE_MONTH_FIELD
204 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
205 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
206 -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
207 -1, // 'U' - UDAT_YEAR_NAME_FIELD
208 -1, // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
209 -1, // 'X' - UDAT_TIMEZONE_ISO_FIELD
210 -1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
211 -1, // 'r' - UDAT_RELATED_YEAR_FIELD
212 -1, // ':' - UDAT_TIME_SEPARATOR_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
251 -1, // ':' - UDAT_TIME_SEPARATOR_FIELD
254 // When calendar uses hebr numbering (i.e. he@calendar=hebrew),
255 // offset the years within the current millenium down to 1-999
256 static const int32_t HEBREW_CAL_CUR_MILLENIUM_START_YEAR
= 5000;
257 static const int32_t HEBREW_CAL_CUR_MILLENIUM_END_YEAR
= 6000;
259 static UMutex LOCK
= U_MUTEX_INITIALIZER
;
261 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat
)
263 SimpleDateFormat::NSOverride::~NSOverride() {
270 void SimpleDateFormat::NSOverride::free() {
271 NSOverride
*cur
= this;
273 NSOverride
*next
= cur
->next
;
279 // no matter what the locale's default number format looked like, we want
280 // to modify it so that it doesn't use thousands separators, doesn't always
281 // show the decimal point, and recognizes integers only when parsing
282 static void fixNumberFormatForDates(NumberFormat
&nf
) {
283 nf
.setGroupingUsed(FALSE
);
284 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(&nf
);
285 if (decfmt
!= NULL
) {
286 decfmt
->setDecimalSeparatorAlwaysShown(FALSE
);
288 nf
.setParseIntegerOnly(TRUE
);
289 nf
.setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
292 static const SharedNumberFormat
*createSharedNumberFormat(
293 NumberFormat
*nfToAdopt
) {
294 fixNumberFormatForDates(*nfToAdopt
);
295 const SharedNumberFormat
*result
= new SharedNumberFormat(nfToAdopt
);
296 if (result
== NULL
) {
302 static const SharedNumberFormat
*createSharedNumberFormat(
303 const Locale
&loc
, UErrorCode
&status
) {
304 NumberFormat
*nf
= NumberFormat::createInstance(loc
, status
);
305 if (U_FAILURE(status
)) {
308 const SharedNumberFormat
*result
= createSharedNumberFormat(nf
);
309 if (result
== NULL
) {
310 status
= U_MEMORY_ALLOCATION_ERROR
;
315 static const SharedNumberFormat
**allocSharedNumberFormatters() {
316 const SharedNumberFormat
**result
= (const SharedNumberFormat
**)
317 uprv_malloc(UDAT_FIELD_COUNT
* sizeof(const SharedNumberFormat
*));
318 if (result
== NULL
) {
321 for (int32_t i
= 0; i
< UDAT_FIELD_COUNT
; ++i
) {
327 static void freeSharedNumberFormatters(const SharedNumberFormat
** list
) {
328 for (int32_t i
= 0; i
< UDAT_FIELD_COUNT
; ++i
) {
329 SharedObject::clearPtr(list
[i
]);
334 const NumberFormat
*SimpleDateFormat::getNumberFormatByIndex(
335 UDateFormatField index
) const {
336 if (fSharedNumberFormatters
== NULL
||
337 fSharedNumberFormatters
[index
] == NULL
) {
338 return fNumberFormat
;
340 return &(**fSharedNumberFormatters
[index
]);
343 class SimpleDateFormatMutableNFNode
{
345 const NumberFormat
*key
;
347 SimpleDateFormatMutableNFNode()
348 : key(NULL
), value(NULL
) { }
349 ~SimpleDateFormatMutableNFNode() {
353 SimpleDateFormatMutableNFNode(const SimpleDateFormatMutableNFNode
&);
354 SimpleDateFormatMutableNFNode
&operator=(const SimpleDateFormatMutableNFNode
&);
357 // Single threaded cache of non const NumberFormats. Designed to be stack
358 // allocated and used for a single format call.
359 class SimpleDateFormatMutableNFs
: public UMemory
{
361 SimpleDateFormatMutableNFs() {
364 // Returns a non-const clone of nf which can be safely modified.
365 // Subsequent calls with same nf will return the same non-const clone.
366 // This object maintains ownership of all returned non-const
367 // NumberFormat objects. On memory allocation error returns NULL.
368 // Caller must check for NULL return value.
369 NumberFormat
*get(const NumberFormat
*nf
) {
374 while (nodes
[idx
].value
) {
375 if (nf
== nodes
[idx
].key
) {
376 return nodes
[idx
].value
;
380 U_ASSERT(idx
< UDAT_FIELD_COUNT
);
382 nodes
[idx
].value
= (NumberFormat
*) nf
->clone();
383 return nodes
[idx
].value
;
386 // +1 extra for sentinel. If each field had its own NumberFormat, this
387 // cache would have to allocate UDAT_FIELD_COUNT mutable versions worst
389 SimpleDateFormatMutableNFNode nodes
[UDAT_FIELD_COUNT
+ 1];
390 SimpleDateFormatMutableNFs(const SimpleDateFormatMutableNFs
&);
391 SimpleDateFormatMutableNFs
&operator=(const SimpleDateFormatMutableNFs
&);
394 //----------------------------------------------------------------------
396 static void updateTimeSepFromPattern(
397 const UnicodeString
& pattern
,
398 DateFormatSymbols
* symbols
) {
399 UnicodeString
hourMinChars("hHKkm", -1, US_INV
); // pattern chars for hours, minutes
400 UnicodeString
colon(":", -1, US_INV
);
401 UBool inQuoted
= FALSE
;
402 UBool lastPatCharWasHourMin
= FALSE
;
403 int32_t patPos
, patLen
= pattern
.length();
404 for (patPos
= 0; patPos
< patLen
; patPos
++) {
405 UChar patChr
= pattern
.charAt(patPos
);
406 if (patChr
== 0x27 /* ASCII-range single quote */) {
407 inQuoted
= !inQuoted
;
408 } else if (!inQuoted
) {
409 if (patChr
== 0x3A /*colon*/ && lastPatCharWasHourMin
) {
410 symbols
->setTimeSeparatorString(colon
);
413 if ((patChr
>= 0x41 && patChr
<= 0x5A) || (patChr
>= 0x61 && patChr
<= 0x7A)) {
414 lastPatCharWasHourMin
= (hourMinChars
.indexOf(patChr
) >= 0);
420 //----------------------------------------------------------------------
422 SimpleDateFormat::~SimpleDateFormat()
425 if (fSharedNumberFormatters
) {
426 freeSharedNumberFormatters(fSharedNumberFormatters
);
428 if (fTimeZoneFormat
) {
429 delete fTimeZoneFormat
;
432 #if !UCONFIG_NO_BREAK_ITERATION
433 delete fCapitalizationBrkIter
;
437 //----------------------------------------------------------------------
439 SimpleDateFormat::SimpleDateFormat(UErrorCode
& status
)
440 : fLocale(Locale::getDefault()),
442 fTimeZoneFormat(NULL
),
443 fSharedNumberFormatters(NULL
),
444 fCapitalizationBrkIter(NULL
)
446 initializeBooleanAttributes();
447 construct(kShort
, (EStyle
) (kShort
+ kDateOffset
), fLocale
, status
);
448 initializeDefaultCentury();
451 //----------------------------------------------------------------------
453 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
456 fLocale(Locale::getDefault()),
458 fTimeZoneFormat(NULL
),
459 fSharedNumberFormatters(NULL
),
460 fCapitalizationBrkIter(NULL
)
462 fDateOverride
.setToBogus();
463 fTimeOverride
.setToBogus();
464 initializeBooleanAttributes();
465 initializeCalendar(NULL
,fLocale
,status
);
466 fSymbols
= DateFormatSymbols::createForLocale(fLocale
, status
);
467 initialize(fLocale
, status
);
468 initializeDefaultCentury();
469 updateTimeSepFromPattern(fPattern
, fSymbols
);
472 //----------------------------------------------------------------------
474 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
475 const UnicodeString
& override
,
478 fLocale(Locale::getDefault()),
480 fTimeZoneFormat(NULL
),
481 fSharedNumberFormatters(NULL
),
482 fCapitalizationBrkIter(NULL
)
484 fDateOverride
.setTo(override
);
485 fTimeOverride
.setToBogus();
486 initializeBooleanAttributes();
487 initializeCalendar(NULL
,fLocale
,status
);
488 fSymbols
= DateFormatSymbols::createForLocale(fLocale
, status
);
489 initialize(fLocale
, status
);
490 initializeDefaultCentury();
492 processOverrideString(fLocale
,override
,kOvrStrBoth
,status
);
496 //----------------------------------------------------------------------
498 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
499 const Locale
& locale
,
503 fTimeZoneFormat(NULL
),
504 fSharedNumberFormatters(NULL
),
505 fCapitalizationBrkIter(NULL
)
508 fDateOverride
.setToBogus();
509 fTimeOverride
.setToBogus();
510 initializeBooleanAttributes();
512 initializeCalendar(NULL
,fLocale
,status
);
513 fSymbols
= DateFormatSymbols::createForLocale(fLocale
, status
);
514 initialize(fLocale
, status
);
515 initializeDefaultCentury();
516 updateTimeSepFromPattern(fPattern
, fSymbols
);
519 //----------------------------------------------------------------------
521 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
522 const UnicodeString
& override
,
523 const Locale
& locale
,
527 fTimeZoneFormat(NULL
),
528 fSharedNumberFormatters(NULL
),
529 fCapitalizationBrkIter(NULL
)
532 fDateOverride
.setTo(override
);
533 fTimeOverride
.setToBogus();
534 initializeBooleanAttributes();
536 initializeCalendar(NULL
,fLocale
,status
);
537 fSymbols
= DateFormatSymbols::createForLocale(fLocale
, status
);
538 initialize(fLocale
, status
);
539 initializeDefaultCentury();
541 processOverrideString(locale
,override
,kOvrStrBoth
,status
);
545 //----------------------------------------------------------------------
547 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
548 DateFormatSymbols
* symbolsToAdopt
,
551 fLocale(Locale::getDefault()),
552 fSymbols(symbolsToAdopt
),
553 fTimeZoneFormat(NULL
),
554 fSharedNumberFormatters(NULL
),
555 fCapitalizationBrkIter(NULL
)
558 fDateOverride
.setToBogus();
559 fTimeOverride
.setToBogus();
560 initializeBooleanAttributes();
562 initializeCalendar(NULL
,fLocale
,status
);
563 initialize(fLocale
, status
);
564 initializeDefaultCentury();
567 //----------------------------------------------------------------------
569 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
570 const DateFormatSymbols
& symbols
,
573 fLocale(Locale::getDefault()),
574 fSymbols(new DateFormatSymbols(symbols
)),
575 fTimeZoneFormat(NULL
),
576 fSharedNumberFormatters(NULL
),
577 fCapitalizationBrkIter(NULL
)
580 fDateOverride
.setToBogus();
581 fTimeOverride
.setToBogus();
582 initializeBooleanAttributes();
584 initializeCalendar(NULL
, fLocale
, status
);
585 initialize(fLocale
, status
);
586 initializeDefaultCentury();
589 //----------------------------------------------------------------------
591 // Not for public consumption; used by DateFormat
592 SimpleDateFormat::SimpleDateFormat(EStyle timeStyle
,
594 const Locale
& locale
,
598 fTimeZoneFormat(NULL
),
599 fSharedNumberFormatters(NULL
),
600 fCapitalizationBrkIter(NULL
)
602 initializeBooleanAttributes();
603 construct(timeStyle
, dateStyle
, fLocale
, status
);
604 if(U_SUCCESS(status
)) {
605 initializeDefaultCentury();
609 //----------------------------------------------------------------------
612 * Not for public consumption; used by DateFormat. This constructor
613 * never fails. If the resource data is not available, it uses the
614 * the last resort symbols.
616 SimpleDateFormat::SimpleDateFormat(const Locale
& locale
,
618 : fPattern(gDefaultPattern
),
621 fTimeZoneFormat(NULL
),
622 fSharedNumberFormatters(NULL
),
623 fCapitalizationBrkIter(NULL
)
625 if (U_FAILURE(status
)) return;
626 initializeBooleanAttributes();
627 initializeCalendar(NULL
, fLocale
, status
);
628 fSymbols
= DateFormatSymbols::createForLocale(fLocale
, status
);
629 if (U_FAILURE(status
))
631 status
= U_ZERO_ERROR
;
633 // This constructor doesn't fail; it uses last resort data
634 fSymbols
= new DateFormatSymbols(status
);
637 status
= U_MEMORY_ALLOCATION_ERROR
;
642 fDateOverride
.setToBogus();
643 fTimeOverride
.setToBogus();
645 initialize(fLocale
, status
);
646 if(U_SUCCESS(status
)) {
647 initializeDefaultCentury();
651 //----------------------------------------------------------------------
653 SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat
& other
)
655 fLocale(other
.fLocale
),
657 fTimeZoneFormat(NULL
),
658 fSharedNumberFormatters(NULL
),
659 fCapitalizationBrkIter(NULL
)
661 initializeBooleanAttributes();
665 //----------------------------------------------------------------------
667 SimpleDateFormat
& SimpleDateFormat::operator=(const SimpleDateFormat
& other
)
669 if (this == &other
) {
672 DateFormat::operator=(other
);
673 fDateOverride
= other
.fDateOverride
;
674 fTimeOverride
= other
.fTimeOverride
;
680 fSymbols
= new DateFormatSymbols(*other
.fSymbols
);
682 fDefaultCenturyStart
= other
.fDefaultCenturyStart
;
683 fDefaultCenturyStartYear
= other
.fDefaultCenturyStartYear
;
684 fHaveDefaultCentury
= other
.fHaveDefaultCentury
;
686 fPattern
= other
.fPattern
;
688 fLocale
= other
.fLocale
;
689 // TimeZoneFormat can now be set independently via setter.
690 // If it is NULL, it will be lazily initialized from locale
691 delete fTimeZoneFormat
;
692 fTimeZoneFormat
= NULL
;
693 if (other
.fTimeZoneFormat
) {
694 fTimeZoneFormat
= new TimeZoneFormat(*other
.fTimeZoneFormat
);
697 #if !UCONFIG_NO_BREAK_ITERATION
698 if (other
.fCapitalizationBrkIter
!= NULL
) {
699 fCapitalizationBrkIter
= (other
.fCapitalizationBrkIter
)->clone();
703 if (fSharedNumberFormatters
!= NULL
) {
704 freeSharedNumberFormatters(fSharedNumberFormatters
);
705 fSharedNumberFormatters
= NULL
;
707 if (other
.fSharedNumberFormatters
!= NULL
) {
708 fSharedNumberFormatters
= allocSharedNumberFormatters();
709 if (fSharedNumberFormatters
) {
710 for (int32_t i
= 0; i
< UDAT_FIELD_COUNT
; ++i
) {
711 SharedObject::copyPtr(
712 other
.fSharedNumberFormatters
[i
],
713 fSharedNumberFormatters
[i
]);
721 //----------------------------------------------------------------------
724 SimpleDateFormat::clone() const
726 return new SimpleDateFormat(*this);
729 //----------------------------------------------------------------------
732 SimpleDateFormat::operator==(const Format
& other
) const
734 if (DateFormat::operator==(other
)) {
735 // The DateFormat::operator== check for fCapitalizationContext equality above
736 // is sufficient to check equality of all derived context-related data.
737 // DateFormat::operator== guarantees following cast is safe
738 SimpleDateFormat
* that
= (SimpleDateFormat
*)&other
;
739 return (fPattern
== that
->fPattern
&&
740 fSymbols
!= NULL
&& // Check for pathological object
741 that
->fSymbols
!= NULL
&& // Check for pathological object
742 *fSymbols
== *that
->fSymbols
&&
743 fHaveDefaultCentury
== that
->fHaveDefaultCentury
&&
744 fDefaultCenturyStart
== that
->fDefaultCenturyStart
&&
745 // Check fTimeZoneFormat, it can be set independently via setter
746 ((fTimeZoneFormat
== NULL
&& that
->fTimeZoneFormat
== NULL
) ||
747 (fTimeZoneFormat
!= NULL
&& that
->fTimeZoneFormat
!= NULL
&& *fTimeZoneFormat
== *that
->fTimeZoneFormat
)) &&
748 // Check override strings (these also indicate any relevant
749 // differences in fNumberFormatters, fOverrideList)
750 fDateOverride
== that
->fDateOverride
&&
751 fTimeOverride
== that
->fTimeOverride
);
756 //----------------------------------------------------------------------
758 void SimpleDateFormat::construct(EStyle timeStyle
,
760 const Locale
& locale
,
763 // called by several constructors to load pattern data from the resources
764 if (U_FAILURE(status
)) return;
766 // We will need the calendar to know what type of symbols to load.
767 initializeCalendar(NULL
, locale
, status
);
768 if (U_FAILURE(status
)) return;
770 CalendarData
calData(locale
, fCalendar
?fCalendar
->getType():NULL
, status
);
771 UResourceBundle
*dateTimePatterns
= calData
.getByKey(gDateTimePatternsTag
, status
);
772 UResourceBundle
*currentBundle
;
774 if (U_FAILURE(status
)) return;
776 if (ures_getSize(dateTimePatterns
) <= kDateTime
)
778 status
= U_INVALID_FORMAT_ERROR
;
782 setLocaleIDs(ures_getLocaleByType(dateTimePatterns
, ULOC_VALID_LOCALE
, &status
),
783 ures_getLocaleByType(dateTimePatterns
, ULOC_ACTUAL_LOCALE
, &status
));
785 // create a symbols object from the locale
786 fSymbols
= DateFormatSymbols::createForLocale(locale
, status
);
787 if (U_FAILURE(status
)) return;
790 status
= U_MEMORY_ALLOCATION_ERROR
;
794 const UChar
*resStr
,*ovrStr
;
795 int32_t resStrLen
,ovrStrLen
= 0;
796 fDateOverride
.setToBogus();
797 fTimeOverride
.setToBogus();
799 // if the pattern should include both date and time information, use the date/time
800 // pattern string as a guide to tell use how to glue together the appropriate date
801 // and time pattern strings. The actual gluing-together is handled by a convenience
802 // method on MessageFormat.
803 if ((timeStyle
!= kNone
) && (dateStyle
!= kNone
))
805 Formattable timeDateArray
[2];
807 // use Formattable::adoptString() so that we can use fastCopyFrom()
808 // instead of Formattable::setString()'s unaware, safe, deep string clone
809 // see Jitterbug 2296
811 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)timeStyle
, NULL
, &status
);
812 if (U_FAILURE(status
)) {
813 status
= U_INVALID_FORMAT_ERROR
;
816 switch (ures_getType(currentBundle
)) {
818 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
822 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
823 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
824 fTimeOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
828 status
= U_INVALID_FORMAT_ERROR
;
829 ures_close(currentBundle
);
833 ures_close(currentBundle
);
835 UnicodeString
*tempus1
= new UnicodeString(TRUE
, resStr
, resStrLen
);
836 // NULL pointer check
837 if (tempus1
== NULL
) {
838 status
= U_MEMORY_ALLOCATION_ERROR
;
841 timeDateArray
[0].adoptString(tempus1
);
843 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)dateStyle
, NULL
, &status
);
844 if (U_FAILURE(status
)) {
845 status
= U_INVALID_FORMAT_ERROR
;
848 switch (ures_getType(currentBundle
)) {
850 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
854 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
855 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
856 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
860 status
= U_INVALID_FORMAT_ERROR
;
861 ures_close(currentBundle
);
865 ures_close(currentBundle
);
867 UnicodeString
*tempus2
= new UnicodeString(TRUE
, resStr
, resStrLen
);
868 // Null pointer check
869 if (tempus2
== NULL
) {
870 status
= U_MEMORY_ALLOCATION_ERROR
;
873 timeDateArray
[1].adoptString(tempus2
);
875 int32_t glueIndex
= kDateTime
;
876 int32_t patternsSize
= ures_getSize(dateTimePatterns
);
877 if (patternsSize
>= (kDateTimeOffset
+ kShort
+ 1)) {
878 // Get proper date time format
879 glueIndex
= (int32_t)(kDateTimeOffset
+ (dateStyle
- kDateOffset
));
882 resStr
= ures_getStringByIndex(dateTimePatterns
, glueIndex
, &resStrLen
, &status
);
883 MessageFormat::format(UnicodeString(TRUE
, resStr
, resStrLen
), timeDateArray
, 2, fPattern
, status
);
885 // if the pattern includes just time data or just date date, load the appropriate
886 // pattern string from the resources
887 // setTo() - see DateFormatSymbols::assignArray comments
888 else if (timeStyle
!= kNone
) {
889 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)timeStyle
, NULL
, &status
);
890 if (U_FAILURE(status
)) {
891 status
= U_INVALID_FORMAT_ERROR
;
894 switch (ures_getType(currentBundle
)) {
896 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
900 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
901 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
902 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
906 status
= U_INVALID_FORMAT_ERROR
;
907 ures_close(currentBundle
);
911 fPattern
.setTo(TRUE
, resStr
, resStrLen
);
912 ures_close(currentBundle
);
914 else if (dateStyle
!= kNone
) {
915 currentBundle
= ures_getByIndex(dateTimePatterns
, (int32_t)dateStyle
, NULL
, &status
);
916 if (U_FAILURE(status
)) {
917 status
= U_INVALID_FORMAT_ERROR
;
920 switch (ures_getType(currentBundle
)) {
922 resStr
= ures_getString(currentBundle
, &resStrLen
, &status
);
926 resStr
= ures_getStringByIndex(currentBundle
, 0, &resStrLen
, &status
);
927 ovrStr
= ures_getStringByIndex(currentBundle
, 1, &ovrStrLen
, &status
);
928 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
932 status
= U_INVALID_FORMAT_ERROR
;
933 ures_close(currentBundle
);
937 fPattern
.setTo(TRUE
, resStr
, resStrLen
);
938 ures_close(currentBundle
);
941 // and if it includes _neither_, that's an error
943 status
= U_INVALID_FORMAT_ERROR
;
945 // finally, finish initializing by creating a Calendar and a NumberFormat
946 initialize(locale
, status
);
949 //----------------------------------------------------------------------
952 SimpleDateFormat::initializeCalendar(TimeZone
* adoptZone
, const Locale
& locale
, UErrorCode
& status
)
954 if(!U_FAILURE(status
)) {
955 fCalendar
= Calendar::createInstance(adoptZone
?adoptZone
:TimeZone::createDefault(), locale
, status
);
961 SimpleDateFormat::initialize(const Locale
& locale
,
964 if (U_FAILURE(status
)) return;
966 // If the locale has @[....]numbers=hanidays we want to *delete* that (so it
967 // it is not used for every field) and then set fDateOverride to "d=hanidays"
968 // (as with std formats for zh@calendar=chinese) to use hanidays for d field.
969 static const UChar hanidaysOverride
[] = {0x64,0x3D,0x68,0x61,0x6E,0x69,0x64,0x61,0x79,0x73,0}; // "d=hanidays"
970 char numbersValue
[ULOC_KEYWORDS_CAPACITY
];
971 UErrorCode numbersStatus
= U_ZERO_ERROR
;
972 Locale
localeNoHanidays(locale
);
973 int32_t numbersLen
= localeNoHanidays
.getKeywordValue("numbers", numbersValue
, ULOC_KEYWORDS_CAPACITY
, numbersStatus
);
974 if ( U_SUCCESS(numbersStatus
) && numbersLen
> 0 ) {
975 if ( uprv_strcmp(numbersValue
, "hanidays") == 0 ) {
976 localeNoHanidays
.setKeywordValue("numbers", NULL
, numbersStatus
);
977 fDateOverride
.setTo(hanidaysOverride
,-1);
981 // We don't need to check that the row count is >= 1, since all 2d arrays have at
983 fNumberFormat
= NumberFormat::createInstance(localeNoHanidays
, status
);
984 if (fNumberFormat
!= NULL
&& U_SUCCESS(status
))
986 fixNumberFormatForDates(*fNumberFormat
);
987 //fNumberFormat->setLenient(TRUE); // Java uses a custom DateNumberFormat to format/parse
989 initNumberFormatters(locale
,status
);
992 else if (U_SUCCESS(status
))
994 status
= U_MISSING_RESOURCE_ERROR
;
998 /* Initialize the fields we use to disambiguate ambiguous years. Separate
999 * so we can call it from readObject().
1001 void SimpleDateFormat::initializeDefaultCentury()
1004 fHaveDefaultCentury
= fCalendar
->haveDefaultCentury();
1005 if(fHaveDefaultCentury
) {
1006 fDefaultCenturyStart
= fCalendar
->defaultCenturyStart();
1007 fDefaultCenturyStartYear
= fCalendar
->defaultCenturyStartYear();
1009 fDefaultCenturyStart
= DBL_MIN
;
1010 fDefaultCenturyStartYear
= -1;
1016 * Initialize the boolean attributes. Separate so we can call it from all constructors.
1018 void SimpleDateFormat::initializeBooleanAttributes()
1020 UErrorCode status
= U_ZERO_ERROR
;
1022 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, true, status
);
1023 setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, true, status
);
1024 setBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH
, true, status
);
1025 setBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, true, status
);
1028 /* Define one-century window into which to disambiguate dates using
1029 * two-digit years. Make public in JDK 1.2.
1031 void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate
, UErrorCode
& status
)
1033 if(U_FAILURE(status
)) {
1037 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1041 fCalendar
->setTime(startDate
, status
);
1042 if(U_SUCCESS(status
)) {
1043 fHaveDefaultCentury
= TRUE
;
1044 fDefaultCenturyStart
= startDate
;
1045 fDefaultCenturyStartYear
= fCalendar
->get(UCAL_YEAR
, status
);
1049 //----------------------------------------------------------------------
1052 SimpleDateFormat::format(Calendar
& cal
, UnicodeString
& appendTo
, FieldPosition
& pos
) const
1054 UErrorCode status
= U_ZERO_ERROR
;
1055 FieldPositionOnlyHandler
handler(pos
);
1056 return _format(cal
, appendTo
, handler
, status
);
1059 //----------------------------------------------------------------------
1062 SimpleDateFormat::format(Calendar
& cal
, UnicodeString
& appendTo
,
1063 FieldPositionIterator
* posIter
, UErrorCode
& status
) const
1065 FieldPositionIteratorHandler
handler(posIter
, status
);
1066 return _format(cal
, appendTo
, handler
, status
);
1069 //----------------------------------------------------------------------
1072 SimpleDateFormat::_format(Calendar
& cal
, UnicodeString
& appendTo
,
1073 FieldPositionHandler
& handler
, UErrorCode
& status
) const
1075 if ( U_FAILURE(status
) ) {
1078 Calendar
* workCal
= &cal
;
1079 Calendar
* calClone
= NULL
;
1080 if (&cal
!= fCalendar
&& uprv_strcmp(cal
.getType(), fCalendar
->getType()) != 0) {
1081 // Different calendar type
1082 // We use the time and time zone from the input calendar, but
1083 // do not use the input calendar for field calculation.
1084 calClone
= fCalendar
->clone();
1085 if (calClone
!= NULL
) {
1086 UDate t
= cal
.getTime(status
);
1087 calClone
->setTime(t
, status
);
1088 calClone
->setTimeZone(cal
.getTimeZone());
1091 status
= U_MEMORY_ALLOCATION_ERROR
;
1096 UBool inQuote
= FALSE
;
1099 int32_t fieldNum
= 0;
1100 UDisplayContext capitalizationContext
= getContext(UDISPCTX_TYPE_CAPITALIZATION
, status
);
1102 // Create temporary cache of mutable number format objects. This way
1103 // subFormat won't have to clone the const NumberFormat for each field.
1104 // if several fields share the same NumberFormat, which will almost
1105 // always be the case, this is a big save.
1106 SimpleDateFormatMutableNFs mutableNFs
;
1107 // loop through the pattern string character by character
1108 for (int32_t i
= 0; i
< fPattern
.length() && U_SUCCESS(status
); ++i
) {
1109 UChar ch
= fPattern
[i
];
1111 // Use subFormat() to format a repeated pattern character
1112 // when a different pattern or non-pattern character is seen
1113 if (ch
!= prevCh
&& count
> 0) {
1114 subFormat(appendTo
, prevCh
, count
, capitalizationContext
, fieldNum
++, handler
, *workCal
, mutableNFs
, status
);
1118 // Consecutive single quotes are a single quote literal,
1119 // either outside of quotes or between quotes
1120 if ((i
+1) < fPattern
.length() && fPattern
[i
+1] == QUOTE
) {
1121 appendTo
+= (UChar
)QUOTE
;
1124 inQuote
= ! inQuote
;
1127 else if (!inQuote
&& isSyntaxChar(ch
)) {
1128 // ch is a date-time pattern character to be interpreted
1129 // by subFormat(); count the number of times it is repeated
1134 // Append quoted characters and unquoted non-pattern characters
1139 // Format the last item in the pattern, if any
1141 subFormat(appendTo
, prevCh
, count
, capitalizationContext
, fieldNum
++, handler
, *workCal
, mutableNFs
, status
);
1144 if (calClone
!= NULL
) {
1151 //----------------------------------------------------------------------
1153 /* Map calendar field into calendar field level.
1154 * the larger the level, the smaller the field unit.
1155 * For example, UCAL_ERA level is 0, UCAL_YEAR level is 10,
1156 * UCAL_MONTH level is 20.
1157 * NOTE: if new fields adds in, the table needs to update.
1160 SimpleDateFormat::fgCalendarFieldToLevel
[] =
1164 /*dDEF*/ 30, 20, 30, 30,
1165 /*ahHm*/ 40, 50, 50, 60,
1172 int32_t SimpleDateFormat::getLevelFromChar(UChar ch
) {
1173 // Map date field LETTER into calendar field level.
1174 // the larger the level, the smaller the field unit.
1175 // NOTE: if new fields adds in, the table needs to update.
1176 static const int32_t mapCharToLevel
[] = {
1177 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1179 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1180 // ! " # $ % & ' ( ) * + , - . /
1181 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1182 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
1183 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1,
1184 // @ A B C D E F G H I J K L M N O
1185 -1, 40, -1, -1, 20, 30, 30, 0, 50, -1, -1, 50, 20, 20, -1, 0,
1186 // P Q R S T U V W X Y Z [ \ ] ^ _
1187 -1, 20, -1, 80, -1, 10, 0, 30, 0, 10, 0, -1, -1, -1, -1, -1,
1188 // ` a b c d e f g h i j k l m n o
1189 -1, 40, -1, 30, 30, 30, -1, 0, 50, -1, -1, 50, 0, 60, -1, -1,
1190 // p q r s t u v w x y z { | } ~
1191 -1, 20, 10, 70, -1, 10, 0, 20, 0, 10, 0, -1, -1, -1, -1, -1
1194 return ch
< UPRV_LENGTHOF(mapCharToLevel
) ? mapCharToLevel
[ch
] : -1;
1197 UBool
SimpleDateFormat::isSyntaxChar(UChar ch
) {
1198 static const UBool mapCharToIsSyntax
[] = {
1200 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1202 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1204 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1206 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1208 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1210 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1212 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1214 FALSE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1216 FALSE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1218 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1220 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1222 TRUE
, TRUE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1224 FALSE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1226 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1228 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1230 TRUE
, TRUE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
1233 return ch
< UPRV_LENGTHOF(mapCharToIsSyntax
) ? mapCharToIsSyntax
[ch
] : FALSE
;
1236 // Map index into pattern character string to Calendar field number.
1237 const UCalendarDateFields
1238 SimpleDateFormat::fgPatternIndexToCalendarField
[] =
1240 /*GyM*/ UCAL_ERA
, UCAL_YEAR
, UCAL_MONTH
,
1241 /*dkH*/ UCAL_DATE
, UCAL_HOUR_OF_DAY
, UCAL_HOUR_OF_DAY
,
1242 /*msS*/ UCAL_MINUTE
, UCAL_SECOND
, UCAL_MILLISECOND
,
1243 /*EDF*/ UCAL_DAY_OF_WEEK
, UCAL_DAY_OF_YEAR
, UCAL_DAY_OF_WEEK_IN_MONTH
,
1244 /*wWa*/ UCAL_WEEK_OF_YEAR
, UCAL_WEEK_OF_MONTH
, UCAL_AM_PM
,
1245 /*hKz*/ UCAL_HOUR
, UCAL_HOUR
, UCAL_ZONE_OFFSET
,
1246 /*Yeu*/ UCAL_YEAR_WOY
, UCAL_DOW_LOCAL
, UCAL_EXTENDED_YEAR
,
1247 /*gAZ*/ UCAL_JULIAN_DAY
, UCAL_MILLISECONDS_IN_DAY
, UCAL_ZONE_OFFSET
,
1248 /*v*/ UCAL_ZONE_OFFSET
,
1249 /*c*/ UCAL_DOW_LOCAL
,
1253 /*V*/ UCAL_ZONE_OFFSET
,
1255 /*O*/ UCAL_ZONE_OFFSET
,
1256 /*Xx*/ UCAL_ZONE_OFFSET
, UCAL_ZONE_OFFSET
,
1257 /*r*/ UCAL_EXTENDED_YEAR
,
1258 /*:*/ UCAL_FIELD_COUNT
, /* => no useful mapping to any calendar field */
1261 // Map index into pattern character string to DateFormat field number
1262 const UDateFormatField
1263 SimpleDateFormat::fgPatternIndexToDateFormatField
[] = {
1264 /*GyM*/ UDAT_ERA_FIELD
, UDAT_YEAR_FIELD
, UDAT_MONTH_FIELD
,
1265 /*dkH*/ UDAT_DATE_FIELD
, UDAT_HOUR_OF_DAY1_FIELD
, UDAT_HOUR_OF_DAY0_FIELD
,
1266 /*msS*/ UDAT_MINUTE_FIELD
, UDAT_SECOND_FIELD
, UDAT_FRACTIONAL_SECOND_FIELD
,
1267 /*EDF*/ UDAT_DAY_OF_WEEK_FIELD
, UDAT_DAY_OF_YEAR_FIELD
, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD
,
1268 /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD
, UDAT_WEEK_OF_MONTH_FIELD
, UDAT_AM_PM_FIELD
,
1269 /*hKz*/ UDAT_HOUR1_FIELD
, UDAT_HOUR0_FIELD
, UDAT_TIMEZONE_FIELD
,
1270 /*Yeu*/ UDAT_YEAR_WOY_FIELD
, UDAT_DOW_LOCAL_FIELD
, UDAT_EXTENDED_YEAR_FIELD
,
1271 /*gAZ*/ UDAT_JULIAN_DAY_FIELD
, UDAT_MILLISECONDS_IN_DAY_FIELD
, UDAT_TIMEZONE_RFC_FIELD
,
1272 /*v*/ UDAT_TIMEZONE_GENERIC_FIELD
,
1273 /*c*/ UDAT_STANDALONE_DAY_FIELD
,
1274 /*L*/ UDAT_STANDALONE_MONTH_FIELD
,
1275 /*Q*/ UDAT_QUARTER_FIELD
,
1276 /*q*/ UDAT_STANDALONE_QUARTER_FIELD
,
1277 /*V*/ UDAT_TIMEZONE_SPECIAL_FIELD
,
1278 /*U*/ UDAT_YEAR_NAME_FIELD
,
1279 /*O*/ UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
,
1280 /*Xx*/ UDAT_TIMEZONE_ISO_FIELD
, UDAT_TIMEZONE_ISO_LOCAL_FIELD
,
1281 /*r*/ UDAT_RELATED_YEAR_FIELD
,
1282 /*:*/ UDAT_TIME_SEPARATOR_FIELD
,
1285 //----------------------------------------------------------------------
1288 * Append symbols[value] to dst. Make sure the array index is not out
1292 _appendSymbol(UnicodeString
& dst
,
1294 const UnicodeString
* symbols
,
1295 int32_t symbolsCount
) {
1296 U_ASSERT(0 <= value
&& value
< symbolsCount
);
1297 if (0 <= value
&& value
< symbolsCount
) {
1298 dst
+= symbols
[value
];
1303 _appendSymbolWithMonthPattern(UnicodeString
& dst
, int32_t value
, const UnicodeString
* symbols
, int32_t symbolsCount
,
1304 const UnicodeString
* monthPattern
, UErrorCode
& status
) {
1305 U_ASSERT(0 <= value
&& value
< symbolsCount
);
1306 if (0 <= value
&& value
< symbolsCount
) {
1307 if (monthPattern
== NULL
) {
1308 dst
+= symbols
[value
];
1310 Formattable
monthName((const UnicodeString
&)(symbols
[value
]));
1311 MessageFormat::format(*monthPattern
, &monthName
, 1, dst
, status
);
1316 //----------------------------------------------------------------------
1318 SimpleDateFormat::initNumberFormatters(const Locale
&locale
,UErrorCode
&status
) {
1319 if (U_FAILURE(status
)) {
1322 if ( fDateOverride
.isBogus() && fTimeOverride
.isBogus() ) {
1326 if (fSharedNumberFormatters
== NULL
) {
1327 fSharedNumberFormatters
= allocSharedNumberFormatters();
1328 if (fSharedNumberFormatters
== NULL
) {
1329 status
= U_MEMORY_ALLOCATION_ERROR
;
1334 if (U_FAILURE(status
)) {
1338 processOverrideString(locale
,fDateOverride
,kOvrStrDate
,status
);
1339 processOverrideString(locale
,fTimeOverride
,kOvrStrTime
,status
);
1343 SimpleDateFormat::processOverrideString(const Locale
&locale
, const UnicodeString
&str
, int8_t type
, UErrorCode
&status
) {
1344 if (str
.isBogus() || U_FAILURE(status
)) {
1350 UnicodeString nsName
;
1351 UnicodeString ovrField
;
1352 UBool moreToProcess
= TRUE
;
1353 NSOverride
*overrideList
= NULL
;
1355 while (moreToProcess
) {
1356 int32_t delimiterPosition
= str
.indexOf((UChar
)ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE
,start
);
1357 if (delimiterPosition
== -1) {
1358 moreToProcess
= FALSE
;
1359 len
= str
.length() - start
;
1361 len
= delimiterPosition
- start
;
1363 UnicodeString
currentString(str
,start
,len
);
1364 int32_t equalSignPosition
= currentString
.indexOf((UChar
)ULOC_KEYWORD_ASSIGN_UNICODE
,0);
1365 if (equalSignPosition
== -1) { // Simple override string such as "hebrew"
1366 nsName
.setTo(currentString
);
1367 ovrField
.setToBogus();
1368 } else { // Field specific override string such as "y=hebrew"
1369 nsName
.setTo(currentString
,equalSignPosition
+1);
1370 ovrField
.setTo(currentString
,0,1); // We just need the first character.
1373 int32_t nsNameHash
= nsName
.hashCode();
1374 // See if the numbering system is in the override list, if not, then add it.
1375 NSOverride
*cur
= overrideList
;
1376 const SharedNumberFormat
*snf
= NULL
;
1377 UBool found
= FALSE
;
1378 while ( cur
&& !found
) {
1379 if ( cur
->hash
== nsNameHash
) {
1387 LocalPointer
<NSOverride
> cur(new NSOverride
);
1388 if (!cur
.isNull()) {
1389 char kw
[ULOC_KEYWORD_AND_VALUES_CAPACITY
];
1390 uprv_strcpy(kw
,"numbers=");
1391 nsName
.extract(0,len
,kw
+8,ULOC_KEYWORD_AND_VALUES_CAPACITY
-8,US_INV
);
1393 Locale
ovrLoc(locale
.getLanguage(),locale
.getCountry(),locale
.getVariant(),kw
);
1394 cur
->hash
= nsNameHash
;
1395 cur
->next
= overrideList
;
1396 SharedObject::copyPtr(
1397 createSharedNumberFormat(ovrLoc
, status
), cur
->snf
);
1398 if (U_FAILURE(status
)) {
1400 overrideList
->free();
1405 overrideList
= cur
.orphan();
1407 status
= U_MEMORY_ALLOCATION_ERROR
;
1409 overrideList
->free();
1415 // Now that we have an appropriate number formatter, fill in the appropriate spaces in the
1416 // number formatters table.
1417 if (ovrField
.isBogus()) {
1421 for ( int8_t i
=0 ; i
<kDateFieldsCount
; i
++ ) {
1422 SharedObject::copyPtr(snf
, fSharedNumberFormatters
[kDateFields
[i
]]);
1424 if (type
==kOvrStrDate
) {
1428 case kOvrStrTime
: {
1429 for ( int8_t i
=0 ; i
<kTimeFieldsCount
; i
++ ) {
1430 SharedObject::copyPtr(snf
, fSharedNumberFormatters
[kTimeFields
[i
]]);
1436 // if the pattern character is unrecognized, signal an error and bail out
1437 UDateFormatField patternCharIndex
=
1438 DateFormatSymbols::getPatternCharIndex(ovrField
.charAt(0));
1439 if (patternCharIndex
== UDAT_FIELD_COUNT
) {
1440 status
= U_INVALID_FORMAT_ERROR
;
1442 overrideList
->free();
1446 SharedObject::copyPtr(snf
, fSharedNumberFormatters
[patternCharIndex
]);
1449 start
= delimiterPosition
+ 1;
1452 overrideList
->free();
1456 //---------------------------------------------------------------------
1458 SimpleDateFormat::subFormat(UnicodeString
&appendTo
,
1461 UDisplayContext capitalizationContext
,
1463 FieldPositionHandler
& handler
,
1465 SimpleDateFormatMutableNFs
&mutableNFs
,
1466 UErrorCode
& status
) const
1468 if (U_FAILURE(status
)) {
1472 // this function gets called by format() to produce the appropriate substitution
1473 // text for an individual pattern symbol (e.g., "HH" or "yyyy")
1475 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(ch
);
1476 const int32_t maxIntCount
= 10;
1477 int32_t beginOffset
= appendTo
.length();
1478 NumberFormat
*currentNumberFormat
;
1479 DateFormatSymbols::ECapitalizationContextUsageType capContextUsageType
= DateFormatSymbols::kCapContextUsageOther
;
1481 UBool isHebrewCalendar
= (uprv_strcmp(cal
.getType(),"hebrew") == 0);
1482 UBool isChineseCalendar
= (uprv_strcmp(cal
.getType(),"chinese") == 0 || uprv_strcmp(cal
.getType(),"dangi") == 0);
1484 // if the pattern character is unrecognized, signal an error and dump out
1485 if (patternCharIndex
== UDAT_FIELD_COUNT
)
1487 if (ch
!= 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
1488 status
= U_INVALID_FORMAT_ERROR
;
1493 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
];
1495 // Don't get value unless it is useful
1496 if (field
< UCAL_FIELD_COUNT
) {
1497 value
= (patternCharIndex
!= UDAT_RELATED_YEAR_FIELD
)? cal
.get(field
, status
): cal
.getRelatedYear(status
);
1499 if (U_FAILURE(status
)) {
1503 currentNumberFormat
= mutableNFs
.get(getNumberFormatByIndex(patternCharIndex
));
1504 if (currentNumberFormat
== NULL
) {
1505 status
= U_MEMORY_ALLOCATION_ERROR
;
1508 UnicodeString
hebr("hebr", 4, US_INV
);
1510 switch (patternCharIndex
) {
1512 // for any "G" symbol, write out the appropriate era string
1513 // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name
1514 case UDAT_ERA_FIELD
:
1515 if (isChineseCalendar
) {
1516 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, 1, 9); // as in ICU4J
1519 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowEras
, fSymbols
->fNarrowErasCount
);
1520 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraNarrow
;
1521 } else if (count
== 4) {
1522 _appendSymbol(appendTo
, value
, fSymbols
->fEraNames
, fSymbols
->fEraNamesCount
);
1523 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraWide
;
1525 _appendSymbol(appendTo
, value
, fSymbols
->fEras
, fSymbols
->fErasCount
);
1526 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraAbbrev
;
1531 case UDAT_YEAR_NAME_FIELD
:
1532 if (fSymbols
->fShortYearNames
!= NULL
&& value
<= fSymbols
->fShortYearNamesCount
) {
1533 // the Calendar YEAR field runs 1 through 60 for cyclic years
1534 _appendSymbol(appendTo
, value
- 1, fSymbols
->fShortYearNames
, fSymbols
->fShortYearNamesCount
);
1537 // else fall through to numeric year handling, do not break here
1539 // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits
1541 //Year y yy yyy yyyy yyyyy
1542 //AD 1 1 01 001 0001 00001
1543 //AD 12 12 12 012 0012 00012
1544 //AD 123 123 23 123 0123 00123
1545 //AD 1234 1234 34 1234 1234 01234
1546 //AD 12345 12345 45 12345 12345 12345
1547 case UDAT_YEAR_FIELD
:
1548 case UDAT_YEAR_WOY_FIELD
:
1549 if (fDateOverride
.compare(hebr
)==0 && value
>HEBREW_CAL_CUR_MILLENIUM_START_YEAR
&& value
<HEBREW_CAL_CUR_MILLENIUM_END_YEAR
) {
1550 value
-=HEBREW_CAL_CUR_MILLENIUM_START_YEAR
;
1553 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, 2, 2);
1555 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, count
, maxIntCount
);
1558 // for "MMMM"/"LLLL", write out the whole month name, for "MMM"/"LLL", write out the month
1559 // abbreviation, for "M"/"L" or "MM"/"LL", write out the month as a number with the
1560 // appropriate number of digits
1561 // for "MMMMM"/"LLLLL", use the narrow form
1562 case UDAT_MONTH_FIELD
:
1563 case UDAT_STANDALONE_MONTH_FIELD
:
1564 if ( isHebrewCalendar
) {
1565 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
1566 if (hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
== 6 && count
>= 3 )
1567 value
= 13; // Show alternate form for Adar II in leap years in Hebrew calendar.
1568 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6 && count
< 3 )
1569 value
--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7.
1572 int32_t isLeapMonth
= (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
)?
1573 cal
.get(UCAL_IS_LEAP_MONTH
, status
): 0;
1574 // should consolidate the next section by using arrays of pointers & counts for the right symbols...
1576 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1577 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fNarrowMonths
, fSymbols
->fNarrowMonthsCount
,
1578 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatNarrow
]): NULL
, status
);
1580 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneNarrowMonths
, fSymbols
->fStandaloneNarrowMonthsCount
,
1581 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneNarrow
]): NULL
, status
);
1583 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthNarrow
;
1584 } else if (count
== 4) {
1585 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1586 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fMonths
, fSymbols
->fMonthsCount
,
1587 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatWide
]): NULL
, status
);
1588 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthFormat
;
1590 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneMonths
, fSymbols
->fStandaloneMonthsCount
,
1591 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneWide
]): NULL
, status
);
1592 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthStandalone
;
1594 } else if (count
== 3) {
1595 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1596 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fShortMonths
, fSymbols
->fShortMonthsCount
,
1597 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatAbbrev
]): NULL
, status
);
1598 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthFormat
;
1600 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneShortMonths
, fSymbols
->fStandaloneShortMonthsCount
,
1601 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev
]): NULL
, status
);
1602 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthStandalone
;
1605 UnicodeString monthNumber
;
1606 zeroPaddingNumber(currentNumberFormat
,monthNumber
, value
+ 1, count
, maxIntCount
);
1607 _appendSymbolWithMonthPattern(appendTo
, 0, &monthNumber
, 1,
1608 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternNumeric
]): NULL
, status
);
1613 // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
1614 case UDAT_HOUR_OF_DAY1_FIELD
:
1616 zeroPaddingNumber(currentNumberFormat
,appendTo
, cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1, count
, maxIntCount
);
1618 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1621 case UDAT_FRACTIONAL_SECOND_FIELD
:
1622 // Fractional seconds left-justify
1624 currentNumberFormat
->setMinimumIntegerDigits((count
> 3) ? 3 : count
);
1625 currentNumberFormat
->setMaximumIntegerDigits(maxIntCount
);
1628 } else if (count
== 2) {
1632 currentNumberFormat
->format(value
, appendTo
, p
);
1634 currentNumberFormat
->setMinimumIntegerDigits(count
- 3);
1635 currentNumberFormat
->format((int32_t)0, appendTo
, p
);
1640 // for "ee" or "e", use local numeric day-of-the-week
1641 // for "EEEEEE" or "eeeeee", write out the short day-of-the-week name
1642 // for "EEEEE" or "eeeee", write out the narrow day-of-the-week name
1643 // for "EEEE" or "eeee", write out the wide day-of-the-week name
1644 // for "EEE" or "EE" or "E" or "eee", write out the abbreviated day-of-the-week name
1645 case UDAT_DOW_LOCAL_FIELD
:
1647 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1650 // fall through to EEEEE-EEE handling, but for that we don't want local day-of-week,
1651 // we want standard day-of-week, so first fix value to work for EEEEE-EEE.
1652 value
= cal
.get(UCAL_DAY_OF_WEEK
, status
);
1653 if (U_FAILURE(status
)) {
1656 // fall through, do not break here
1657 case UDAT_DAY_OF_WEEK_FIELD
:
1659 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowWeekdays
,
1660 fSymbols
->fNarrowWeekdaysCount
);
1661 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayNarrow
;
1662 } else if (count
== 4) {
1663 _appendSymbol(appendTo
, value
, fSymbols
->fWeekdays
,
1664 fSymbols
->fWeekdaysCount
);
1665 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1666 } else if (count
== 6) {
1667 _appendSymbol(appendTo
, value
, fSymbols
->fShorterWeekdays
,
1668 fSymbols
->fShorterWeekdaysCount
);
1669 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1671 _appendSymbol(appendTo
, value
, fSymbols
->fShortWeekdays
,
1672 fSymbols
->fShortWeekdaysCount
);
1673 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1677 // for "ccc", write out the abbreviated day-of-the-week name
1678 // for "cccc", write out the wide day-of-the-week name
1679 // for "ccccc", use the narrow day-of-the-week name
1680 // for "ccccc", use the short day-of-the-week name
1681 case UDAT_STANDALONE_DAY_FIELD
:
1683 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, 1, maxIntCount
);
1686 // fall through to alpha DOW handling, but for that we don't want local day-of-week,
1687 // we want standard day-of-week, so first fix value.
1688 value
= cal
.get(UCAL_DAY_OF_WEEK
, status
);
1689 if (U_FAILURE(status
)) {
1693 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneNarrowWeekdays
,
1694 fSymbols
->fStandaloneNarrowWeekdaysCount
);
1695 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayNarrow
;
1696 } else if (count
== 4) {
1697 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneWeekdays
,
1698 fSymbols
->fStandaloneWeekdaysCount
);
1699 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1700 } else if (count
== 6) {
1701 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneShorterWeekdays
,
1702 fSymbols
->fStandaloneShorterWeekdaysCount
);
1703 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1704 } else { // count == 3
1705 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneShortWeekdays
,
1706 fSymbols
->fStandaloneShortWeekdaysCount
);
1707 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1711 // for "a" symbol, write out the whole AM/PM string
1712 case UDAT_AM_PM_FIELD
:
1714 _appendSymbol(appendTo
, value
, fSymbols
->fAmPms
,
1715 fSymbols
->fAmPmsCount
);
1717 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowAmPms
,
1718 fSymbols
->fNarrowAmPmsCount
);
1722 // for ":", write out the time separator string
1723 case UDAT_TIME_SEPARATOR_FIELD
:
1725 UnicodeString separator
;
1726 appendTo
+= fSymbols
->getTimeSeparatorString(separator
);
1730 // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
1732 case UDAT_HOUR1_FIELD
:
1734 zeroPaddingNumber(currentNumberFormat
,appendTo
, cal
.getLeastMaximum(UCAL_HOUR
) + 1, count
, maxIntCount
);
1736 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1739 case UDAT_TIMEZONE_FIELD
: // 'z'
1740 case UDAT_TIMEZONE_RFC_FIELD
: // 'Z'
1741 case UDAT_TIMEZONE_GENERIC_FIELD
: // 'v'
1742 case UDAT_TIMEZONE_SPECIAL_FIELD
: // 'V'
1743 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
: // 'O'
1744 case UDAT_TIMEZONE_ISO_FIELD
: // 'X'
1745 case UDAT_TIMEZONE_ISO_LOCAL_FIELD
: // 'x'
1748 UnicodeString
zoneString(zsbuf
, 0, UPRV_LENGTHOF(zsbuf
));
1749 const TimeZone
& tz
= cal
.getTimeZone();
1750 UDate date
= cal
.getTime(status
);
1751 if (U_SUCCESS(status
)) {
1752 if (patternCharIndex
== UDAT_TIMEZONE_FIELD
) {
1755 tzFormat()->format(UTZFMT_STYLE_SPECIFIC_SHORT
, tz
, date
, zoneString
);
1756 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneShort
;
1759 tzFormat()->format(UTZFMT_STYLE_SPECIFIC_LONG
, tz
, date
, zoneString
);
1760 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneLong
;
1763 else if (patternCharIndex
== UDAT_TIMEZONE_RFC_FIELD
) {
1766 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
, tz
, date
, zoneString
);
1767 } else if (count
== 5) {
1769 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FULL
, tz
, date
, zoneString
);
1771 // "ZZ", "ZZZ", "ZZZZ"
1772 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT
, tz
, date
, zoneString
);
1775 else if (patternCharIndex
== UDAT_TIMEZONE_GENERIC_FIELD
) {
1778 tzFormat()->format(UTZFMT_STYLE_GENERIC_SHORT
, tz
, date
, zoneString
);
1779 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneShort
;
1780 } else if (count
== 4) {
1782 tzFormat()->format(UTZFMT_STYLE_GENERIC_LONG
, tz
, date
, zoneString
);
1783 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneLong
;
1786 else if (patternCharIndex
== UDAT_TIMEZONE_SPECIAL_FIELD
) {
1789 tzFormat()->format(UTZFMT_STYLE_ZONE_ID_SHORT
, tz
, date
, zoneString
);
1790 } else if (count
== 2) {
1792 tzFormat()->format(UTZFMT_STYLE_ZONE_ID
, tz
, date
, zoneString
);
1793 } else if (count
== 3) {
1795 tzFormat()->format(UTZFMT_STYLE_EXEMPLAR_LOCATION
, tz
, date
, zoneString
);
1796 } else if (count
== 4) {
1798 tzFormat()->format(UTZFMT_STYLE_GENERIC_LOCATION
, tz
, date
, zoneString
);
1799 capContextUsageType
= DateFormatSymbols::kCapContextUsageZoneLong
;
1802 else if (patternCharIndex
== UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
) {
1805 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT_SHORT
, tz
, date
, zoneString
);
1806 } else if (count
== 4) {
1808 tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT
, tz
, date
, zoneString
);
1811 else if (patternCharIndex
== UDAT_TIMEZONE_ISO_FIELD
) {
1814 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_SHORT
, tz
, date
, zoneString
);
1815 } else if (count
== 2) {
1817 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_FIXED
, tz
, date
, zoneString
);
1818 } else if (count
== 3) {
1820 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FIXED
, tz
, date
, zoneString
);
1821 } else if (count
== 4) {
1823 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_FULL
, tz
, date
, zoneString
);
1824 } else if (count
== 5) {
1826 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FULL
, tz
, date
, zoneString
);
1829 else if (patternCharIndex
== UDAT_TIMEZONE_ISO_LOCAL_FIELD
) {
1832 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
, tz
, date
, zoneString
);
1833 } else if (count
== 2) {
1835 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
, tz
, date
, zoneString
);
1836 } else if (count
== 3) {
1838 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
, tz
, date
, zoneString
);
1839 } else if (count
== 4) {
1841 tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
, tz
, date
, zoneString
);
1842 } else if (count
== 5) {
1844 tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
, tz
, date
, zoneString
);
1851 appendTo
+= zoneString
;
1855 case UDAT_QUARTER_FIELD
:
1857 _appendSymbol(appendTo
, value
/3, fSymbols
->fQuarters
,
1858 fSymbols
->fQuartersCount
);
1859 else if (count
== 3)
1860 _appendSymbol(appendTo
, value
/3, fSymbols
->fShortQuarters
,
1861 fSymbols
->fShortQuartersCount
);
1863 zeroPaddingNumber(currentNumberFormat
,appendTo
, (value
/3) + 1, count
, maxIntCount
);
1866 case UDAT_STANDALONE_QUARTER_FIELD
:
1868 _appendSymbol(appendTo
, value
/3, fSymbols
->fStandaloneQuarters
,
1869 fSymbols
->fStandaloneQuartersCount
);
1870 else if (count
== 3)
1871 _appendSymbol(appendTo
, value
/3, fSymbols
->fStandaloneShortQuarters
,
1872 fSymbols
->fStandaloneShortQuartersCount
);
1874 zeroPaddingNumber(currentNumberFormat
,appendTo
, (value
/3) + 1, count
, maxIntCount
);
1878 // all of the other pattern symbols can be formatted as simple numbers with
1879 // appropriate zero padding
1881 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1884 #if !UCONFIG_NO_BREAK_ITERATION
1885 // if first field, check to see whether we need to and are able to titlecase it
1886 if (fieldNum
== 0 && u_islower(appendTo
.char32At(beginOffset
)) && fCapitalizationBrkIter
!= NULL
) {
1887 UBool titlecase
= FALSE
;
1888 switch (capitalizationContext
) {
1889 case UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
:
1892 case UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
:
1893 titlecase
= fSymbols
->fCapitalization
[capContextUsageType
][0];
1895 case UDISPCTX_CAPITALIZATION_FOR_STANDALONE
:
1896 titlecase
= fSymbols
->fCapitalization
[capContextUsageType
][1];
1899 // titlecase = FALSE;
1903 UnicodeString
firstField(appendTo
, beginOffset
);
1904 firstField
.toTitle(fCapitalizationBrkIter
, fLocale
, U_TITLECASE_NO_LOWERCASE
| U_TITLECASE_NO_BREAK_ADJUSTMENT
);
1905 appendTo
.replaceBetween(beginOffset
, appendTo
.length(), firstField
);
1910 handler
.addAttribute(fgPatternIndexToDateFormatField
[patternCharIndex
], beginOffset
, appendTo
.length());
1913 //----------------------------------------------------------------------
1915 void SimpleDateFormat::adoptNumberFormat(NumberFormat
*formatToAdopt
) {
1916 fixNumberFormatForDates(*formatToAdopt
);
1917 delete fNumberFormat
;
1918 fNumberFormat
= formatToAdopt
;
1920 // We successfully set the default number format. Now delete the overrides
1922 if (fSharedNumberFormatters
) {
1923 freeSharedNumberFormatters(fSharedNumberFormatters
);
1924 fSharedNumberFormatters
= NULL
;
1928 void SimpleDateFormat::adoptNumberFormat(const UnicodeString
& fields
, NumberFormat
*formatToAdopt
, UErrorCode
&status
){
1929 fixNumberFormatForDates(*formatToAdopt
);
1930 LocalPointer
<NumberFormat
> fmt(formatToAdopt
);
1931 if (U_FAILURE(status
)) {
1935 // We must ensure fSharedNumberFormatters is allocated.
1936 if (fSharedNumberFormatters
== NULL
) {
1937 fSharedNumberFormatters
= allocSharedNumberFormatters();
1938 if (fSharedNumberFormatters
== NULL
) {
1939 status
= U_MEMORY_ALLOCATION_ERROR
;
1943 const SharedNumberFormat
*newFormat
= createSharedNumberFormat(fmt
.orphan());
1944 if (newFormat
== NULL
) {
1945 status
= U_MEMORY_ALLOCATION_ERROR
;
1948 for (int i
=0; i
<fields
.length(); i
++) {
1949 UChar field
= fields
.charAt(i
);
1950 // if the pattern character is unrecognized, signal an error and bail out
1951 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(field
);
1952 if (patternCharIndex
== UDAT_FIELD_COUNT
) {
1953 status
= U_INVALID_FORMAT_ERROR
;
1954 newFormat
->deleteIfZeroRefCount();
1958 // Set the number formatter in the table
1959 SharedObject::copyPtr(
1960 newFormat
, fSharedNumberFormatters
[patternCharIndex
]);
1962 newFormat
->deleteIfZeroRefCount();
1965 const NumberFormat
*
1966 SimpleDateFormat::getNumberFormatForField(UChar field
) const {
1967 UDateFormatField index
= DateFormatSymbols::getPatternCharIndex(field
);
1968 if (index
== UDAT_FIELD_COUNT
) {
1971 return getNumberFormatByIndex(index
);
1974 //----------------------------------------------------------------------
1976 SimpleDateFormat::zeroPaddingNumber(
1977 NumberFormat
*currentNumberFormat
,
1978 UnicodeString
&appendTo
,
1979 int32_t value
, int32_t minDigits
, int32_t maxDigits
) const
1981 if (currentNumberFormat
!=NULL
) {
1982 FieldPosition
pos(0);
1984 currentNumberFormat
->setMinimumIntegerDigits(minDigits
);
1985 currentNumberFormat
->setMaximumIntegerDigits(maxDigits
);
1986 currentNumberFormat
->format(value
, appendTo
, pos
); // 3rd arg is there to speed up processing
1990 //----------------------------------------------------------------------
1993 * Return true if the given format character, occuring count
1994 * times, represents a numeric field.
1996 UBool
SimpleDateFormat::isNumeric(UChar formatChar
, int32_t count
) {
1997 return DateFormatSymbols::isNumericPatternChar(formatChar
, count
);
2001 SimpleDateFormat::isAtNumericField(const UnicodeString
&pattern
, int32_t patternOffset
) {
2002 if (patternOffset
>= pattern
.length()) {
2006 UChar ch
= pattern
.charAt(patternOffset
);
2007 UDateFormatField f
= DateFormatSymbols::getPatternCharIndex(ch
);
2008 if (f
== UDAT_FIELD_COUNT
) {
2012 int32_t i
= patternOffset
;
2013 while (pattern
.charAt(++i
) == ch
) {}
2014 return DateFormatSymbols::isNumericField(f
, i
- patternOffset
);
2018 SimpleDateFormat::isAfterNonNumericField(const UnicodeString
&pattern
, int32_t patternOffset
) {
2019 if (patternOffset
<= 0) {
2020 // not after any field
2023 UChar ch
= pattern
.charAt(--patternOffset
);
2024 UDateFormatField f
= DateFormatSymbols::getPatternCharIndex(ch
);
2025 if (f
== UDAT_FIELD_COUNT
) {
2026 // not after any field
2029 int32_t i
= patternOffset
;
2030 while (pattern
.charAt(--i
) == ch
) {}
2031 return !DateFormatSymbols::isNumericField(f
, patternOffset
- i
);
2035 SimpleDateFormat::parse(const UnicodeString
& text
, Calendar
& cal
, ParsePosition
& parsePos
) const
2037 UErrorCode status
= U_ZERO_ERROR
;
2038 int32_t pos
= parsePos
.getIndex();
2039 if(parsePos
.getIndex() < 0) {
2040 parsePos
.setErrorIndex(0);
2043 int32_t start
= pos
;
2046 UBool ambiguousYear
[] = { FALSE
};
2047 int32_t saveHebrewMonth
= -1;
2049 UTimeZoneFormatTimeType tzTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
2050 SimpleDateFormatMutableNFs mutableNFs
;
2052 // For parsing abutting numeric fields. 'abutPat' is the
2053 // offset into 'pattern' of the first of 2 or more abutting
2054 // numeric fields. 'abutStart' is the offset into 'text'
2055 // where parsing the fields begins. 'abutPass' starts off as 0
2056 // and increments each time we try to parse the fields.
2057 int32_t abutPat
= -1; // If >=0, we are in a run of abutting numeric fields
2058 int32_t abutStart
= 0;
2059 int32_t abutPass
= 0;
2060 UBool inQuote
= FALSE
;
2062 MessageFormat
* numericLeapMonthFormatter
= NULL
;
2064 Calendar
* calClone
= NULL
;
2065 Calendar
*workCal
= &cal
;
2066 if (&cal
!= fCalendar
&& uprv_strcmp(cal
.getType(), fCalendar
->getType()) != 0) {
2067 // Different calendar type
2068 // We use the time/zone from the input calendar, but
2069 // do not use the input calendar for field calculation.
2070 calClone
= fCalendar
->clone();
2071 if (calClone
!= NULL
) {
2072 calClone
->setTime(cal
.getTime(status
),status
);
2073 if (U_FAILURE(status
)) {
2076 calClone
->setTimeZone(cal
.getTimeZone());
2079 status
= U_MEMORY_ALLOCATION_ERROR
;
2084 if (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
) {
2085 numericLeapMonthFormatter
= new MessageFormat(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternNumeric
], fLocale
, status
);
2086 if (numericLeapMonthFormatter
== NULL
) {
2087 status
= U_MEMORY_ALLOCATION_ERROR
;
2089 } else if (U_FAILURE(status
)) {
2090 goto ExitParse
; // this will delete numericLeapMonthFormatter
2094 for (int32_t i
=0; i
<fPattern
.length(); ++i
) {
2095 UChar ch
= fPattern
.charAt(i
);
2097 // Handle alphabetic field characters.
2098 if (!inQuote
&& isSyntaxChar(ch
)) {
2099 int32_t fieldPat
= i
;
2101 // Count the length of this field specifier
2103 while ((i
+1)<fPattern
.length() &&
2104 fPattern
.charAt(i
+1) == ch
) {
2109 if (isNumeric(ch
, count
)) {
2111 // Determine if there is an abutting numeric field.
2112 // Record the start of a set of abutting numeric fields.
2113 if (isAtNumericField(fPattern
, i
+ 1)) {
2120 abutPat
= -1; // End of any abutting fields
2123 // Handle fields within a run of abutting numeric fields. Take
2124 // the pattern "HHmmss" as an example. We will try to parse
2125 // 2/2/2 characters of the input text, then if that fails,
2126 // 1/2/2. We only adjust the width of the leftmost field; the
2127 // others remain fixed. This allows "123456" => 12:34:56, but
2128 // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
2129 // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
2131 // If we are at the start of a run of abutting fields, then
2132 // shorten this field in each pass. If we can't shorten
2133 // this field any more, then the parse of this set of
2134 // abutting numeric fields has failed.
2135 if (fieldPat
== abutPat
) {
2136 count
-= abutPass
++;
2138 status
= U_PARSE_ERROR
;
2143 pos
= subParse(text
, pos
, ch
, count
,
2144 TRUE
, FALSE
, ambiguousYear
, saveHebrewMonth
, *workCal
, i
, numericLeapMonthFormatter
, &tzTimeType
, mutableNFs
);
2146 // If the parse fails anywhere in the run, back up to the
2147 // start of the run and retry.
2155 // Handle non-numeric fields and non-abutting numeric
2157 else if (ch
!= 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
2158 int32_t s
= subParse(text
, pos
, ch
, count
,
2159 FALSE
, TRUE
, ambiguousYear
, saveHebrewMonth
, *workCal
, i
, numericLeapMonthFormatter
, &tzTimeType
, mutableNFs
);
2162 // era not present, in special cases allow this to continue
2163 // from the position where the era was expected
2166 if (i
+1 < fPattern
.length()) {
2167 // move to next pattern character
2168 UChar ch
= fPattern
.charAt(i
+1);
2170 // check for whitespace
2171 if (PatternProps::isWhiteSpace(ch
)) {
2173 // Advance over run in pattern
2174 while ((i
+1)<fPattern
.length() &&
2175 PatternProps::isWhiteSpace(fPattern
.charAt(i
+1))) {
2182 status
= U_PARSE_ERROR
;
2189 // Handle literal pattern characters. These are any
2190 // quoted characters and non-alphabetic unquoted
2194 abutPat
= -1; // End of any abutting fields
2196 if (! matchLiterals(fPattern
, i
, text
, pos
, getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, status
), getBooleanAttribute(UDAT_PARSE_PARTIAL_MATCH
, status
), isLenient())) {
2197 status
= U_PARSE_ERROR
;
2203 // Special hack for trailing "." after non-numeric field.
2204 if (text
.charAt(pos
) == 0x2e && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, status
)) {
2205 // only do if the last field is not numeric
2206 if (isAfterNonNumericField(fPattern
, fPattern
.length())) {
2207 pos
++; // skip the extra "."
2211 // At this point the fields of Calendar have been set. Calendar
2212 // will fill in default values for missing fields when the time
2215 parsePos
.setIndex(pos
);
2217 // This part is a problem: When we call parsedDate.after, we compute the time.
2218 // Take the date April 3 2004 at 2:30 am. When this is first set up, the year
2219 // will be wrong if we're parsing a 2-digit year pattern. It will be 1904.
2220 // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am
2221 // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
2222 // on that day. It is therefore parsed out to fields as 3:30 am. Then we
2223 // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is
2224 // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
2226 UDate parsedDate = calendar.getTime();
2227 if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
2228 calendar.add(Calendar.YEAR, 100);
2229 parsedDate = calendar.getTime();
2232 // Because of the above condition, save off the fields in case we need to readjust.
2233 // The procedure we use here is not particularly efficient, but there is no other
2234 // way to do this given the API restrictions present in Calendar. We minimize
2235 // inefficiency by only performing this computation when it might apply, that is,
2236 // when the two-digit year is equal to the start year, and thus might fall at the
2237 // front or the back of the default century. This only works because we adjust
2238 // the year correctly to start with in other cases -- see subParse().
2239 if (ambiguousYear
[0] || tzTimeType
!= UTZFMT_TIME_TYPE_UNKNOWN
) // If this is true then the two-digit year == the default start year
2241 // We need a copy of the fields, and we need to avoid triggering a call to
2242 // complete(), which will recalculate the fields. Since we can't access
2243 // the fields[] array in Calendar, we clone the entire object. This will
2244 // stop working if Calendar.clone() is ever rewritten to call complete().
2246 if (ambiguousYear
[0]) {
2248 // Check for failed cloning.
2250 status
= U_MEMORY_ALLOCATION_ERROR
;
2253 UDate parsedDate
= copy
->getTime(status
);
2254 // {sfb} check internalGetDefaultCenturyStart
2255 if (fHaveDefaultCentury
&& (parsedDate
< fDefaultCenturyStart
)) {
2256 // We can't use add here because that does a complete() first.
2257 cal
.set(UCAL_YEAR
, fDefaultCenturyStartYear
+ 100);
2262 if (tzTimeType
!= UTZFMT_TIME_TYPE_UNKNOWN
) {
2264 // Check for failed cloning.
2266 status
= U_MEMORY_ALLOCATION_ERROR
;
2269 const TimeZone
& tz
= cal
.getTimeZone();
2270 BasicTimeZone
*btz
= NULL
;
2272 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
2273 || dynamic_cast<const SimpleTimeZone
*>(&tz
) != NULL
2274 || dynamic_cast<const RuleBasedTimeZone
*>(&tz
) != NULL
2275 || dynamic_cast<const VTimeZone
*>(&tz
) != NULL
) {
2276 btz
= (BasicTimeZone
*)&tz
;
2280 copy
->set(UCAL_ZONE_OFFSET
, 0);
2281 copy
->set(UCAL_DST_OFFSET
, 0);
2282 UDate localMillis
= copy
->getTime(status
);
2284 // Make sure parsed time zone type (Standard or Daylight)
2285 // matches the rule used by the parsed time zone.
2288 if (tzTimeType
== UTZFMT_TIME_TYPE_STANDARD
) {
2289 btz
->getOffsetFromLocal(localMillis
,
2290 BasicTimeZone::kStandard
, BasicTimeZone::kStandard
, raw
, dst
, status
);
2292 btz
->getOffsetFromLocal(localMillis
,
2293 BasicTimeZone::kDaylight
, BasicTimeZone::kDaylight
, raw
, dst
, status
);
2296 // No good way to resolve ambiguous time at transition,
2297 // but following code work in most case.
2298 tz
.getOffset(localMillis
, TRUE
, raw
, dst
, status
);
2301 // Now, compare the results with parsed type, either standard or daylight saving time
2302 int32_t resolvedSavings
= dst
;
2303 if (tzTimeType
== UTZFMT_TIME_TYPE_STANDARD
) {
2305 // Override DST_OFFSET = 0 in the result calendar
2306 resolvedSavings
= 0;
2308 } else { // tztype == TZTYPE_DST
2311 UDate time
= localMillis
+ raw
;
2312 // We use the nearest daylight saving time rule.
2313 TimeZoneTransition beforeTrs
, afterTrs
;
2314 UDate beforeT
= time
, afterT
= time
;
2315 int32_t beforeSav
= 0, afterSav
= 0;
2316 UBool beforeTrsAvail
, afterTrsAvail
;
2318 // Search for DST rule before or on the time
2320 beforeTrsAvail
= btz
->getPreviousTransition(beforeT
, TRUE
, beforeTrs
);
2321 if (!beforeTrsAvail
) {
2324 beforeT
= beforeTrs
.getTime() - 1;
2325 beforeSav
= beforeTrs
.getFrom()->getDSTSavings();
2326 if (beforeSav
!= 0) {
2331 // Search for DST rule after the time
2333 afterTrsAvail
= btz
->getNextTransition(afterT
, FALSE
, afterTrs
);
2334 if (!afterTrsAvail
) {
2337 afterT
= afterTrs
.getTime();
2338 afterSav
= afterTrs
.getTo()->getDSTSavings();
2339 if (afterSav
!= 0) {
2344 if (beforeTrsAvail
&& afterTrsAvail
) {
2345 if (time
- beforeT
> afterT
- time
) {
2346 resolvedSavings
= afterSav
;
2348 resolvedSavings
= beforeSav
;
2350 } else if (beforeTrsAvail
&& beforeSav
!= 0) {
2351 resolvedSavings
= beforeSav
;
2352 } else if (afterTrsAvail
&& afterSav
!= 0) {
2353 resolvedSavings
= afterSav
;
2355 resolvedSavings
= btz
->getDSTSavings();
2358 resolvedSavings
= tz
.getDSTSavings();
2360 if (resolvedSavings
== 0) {
2362 resolvedSavings
= U_MILLIS_PER_HOUR
;
2366 cal
.set(UCAL_ZONE_OFFSET
, raw
);
2367 cal
.set(UCAL_DST_OFFSET
, resolvedSavings
);
2372 // Set the parsed result if local calendar is used
2373 // instead of the input calendar
2374 if (U_SUCCESS(status
) && workCal
!= &cal
) {
2375 cal
.setTimeZone(workCal
->getTimeZone());
2376 cal
.setTime(workCal
->getTime(status
), status
);
2379 if (numericLeapMonthFormatter
!= NULL
) {
2380 delete numericLeapMonthFormatter
;
2382 if (calClone
!= NULL
) {
2386 // If any Calendar calls failed, we pretend that we
2387 // couldn't parse the string, when in reality this isn't quite accurate--
2388 // we did parse it; the Calendar calls just failed.
2389 if (U_FAILURE(status
)) {
2390 parsePos
.setErrorIndex(pos
);
2391 parsePos
.setIndex(start
);
2395 //----------------------------------------------------------------------
2398 matchStringWithOptionalDot(const UnicodeString
&text
,
2400 const UnicodeString
&data
);
2402 int32_t SimpleDateFormat::matchQuarterString(const UnicodeString
& text
,
2404 UCalendarDateFields field
,
2405 const UnicodeString
* data
,
2407 Calendar
& cal
) const
2410 int32_t count
= dataCount
;
2412 // There may be multiple strings in the data[] array which begin with
2413 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2414 // We keep track of the longest match, and return that. Note that this
2415 // unfortunately requires us to test all array elements.
2416 int32_t bestMatchLength
= 0, bestMatch
= -1;
2417 UnicodeString bestMatchName
;
2419 for (; i
< count
; ++i
) {
2420 int32_t matchLength
= 0;
2421 if ((matchLength
= matchStringWithOptionalDot(text
, start
, data
[i
])) > bestMatchLength
) {
2422 bestMatchLength
= matchLength
;
2427 if (bestMatch
>= 0) {
2428 cal
.set(field
, bestMatch
* 3);
2429 return start
+ bestMatchLength
;
2435 //----------------------------------------------------------------------
2436 #define IS_BIDI_MARK(c) (c==0x200E || c==0x200F || c==0x061C)
2438 UBool
SimpleDateFormat::matchLiterals(const UnicodeString
&pattern
,
2439 int32_t &patternOffset
,
2440 const UnicodeString
&text
,
2441 int32_t &textOffset
,
2442 UBool whitespaceLenient
,
2443 UBool partialMatchLenient
,
2446 UBool inQuote
= FALSE
;
2447 UnicodeString literal
;
2448 int32_t i
= patternOffset
;
2450 // scan pattern looking for contiguous literal characters
2451 for ( ; i
< pattern
.length(); i
+= 1) {
2452 UChar ch
= pattern
.charAt(i
);
2454 if (!inQuote
&& isSyntaxChar(ch
)) {
2459 // Match a quote literal ('') inside OR outside of quotes
2460 if ((i
+ 1) < pattern
.length() && pattern
.charAt(i
+ 1) == QUOTE
) {
2468 if (!IS_BIDI_MARK(ch
)) {
2473 // at this point, literal contains the pattern literal text (without bidi marks)
2474 // and i is the index of the next non-literal pattern character.
2476 int32_t t
= textOffset
;
2478 if (whitespaceLenient
) {
2479 // trim leading, trailing whitespace from the pattern literal
2482 // ignore any leading whitespace (or bidi marks) in the text
2483 while (t
< text
.length()) {
2484 UChar ch
= text
.charAt(t
);
2485 if (!u_isWhitespace(ch
) && !IS_BIDI_MARK(ch
)) {
2492 // Get ignorables, move up here
2493 const UnicodeSet
*ignorables
= NULL
;
2494 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(pattern
.charAt(i
));
2495 if (patternCharIndex
!= UDAT_FIELD_COUNT
) {
2496 ignorables
= SimpleDateFormatStaticSets::getIgnorables(patternCharIndex
);
2499 for (p
= 0; p
< literal
.length() && t
< text
.length();) {
2500 UBool needWhitespace
= FALSE
;
2502 // Skip any whitespace at current position in pattern,
2503 // but remember whether we found whitespace in the pattern
2504 // (we already deleted any bidi marks in the pattern).
2505 while (p
< literal
.length() && PatternProps::isWhiteSpace(literal
.charAt(p
))) {
2506 needWhitespace
= TRUE
;
2510 // If the pattern has whitespace at this point, skip it in text as well
2511 // (if the text does not have any, that may be an error for strict parsing)
2512 if (needWhitespace
) {
2513 UBool whitespaceInText
= FALSE
;
2515 // Skip any whitespace (or bidi marks) at current position in text,
2516 // but remember whether we found whitespace in the text at this point.
2517 while (t
< text
.length()) {
2518 UChar tch
= text
.charAt(t
);
2519 if (u_isUWhiteSpace(tch
) || PatternProps::isWhiteSpace(tch
)) {
2520 whitespaceInText
= TRUE
;
2521 } else if (!IS_BIDI_MARK(tch
)) {
2528 // TODO: should we require internal spaces
2529 // in lenient mode? (There won't be any
2530 // leading or trailing spaces)
2531 if (!whitespaceLenient
&& !whitespaceInText
) {
2532 // didn't find matching whitespace:
2533 // an error in strict mode
2537 // In strict mode, this run of whitespace
2538 // may have been at the end.
2539 if (p
>= literal
.length()) {
2543 // Still need to skip any bidi marks in the text
2544 while (t
< text
.length() && IS_BIDI_MARK(text
.charAt(t
))) {
2548 if (t
>= text
.length() || literal
.charAt(p
) != text
.charAt(t
)) {
2549 // Ran out of text, or found a non-matching character:
2550 // OK in lenient mode, an error in strict mode.
2551 if (whitespaceLenient
) {
2552 if (t
== textOffset
&& text
.charAt(t
) == 0x2e &&
2553 isAfterNonNumericField(pattern
, patternOffset
)) {
2554 // Lenient mode and the literal input text begins with a "." and
2555 // we are after a non-numeric field: We skip the "."
2557 continue; // Do not update p.
2559 // if it is actual whitespace and we're whitespace lenient it's OK
2561 UChar wsc
= text
.charAt(t
);
2562 if(PatternProps::isWhiteSpace(wsc
)) {
2563 // Lenient mode and it's just whitespace we skip it
2565 continue; // Do not update p.
2568 // hack around oldleniency being a bit of a catch-all bucket and we're just adding support specifically for paritial matches
2569 // This fix is for http://bugs.icu-project.org/trac/ticket/10855 and adds "&& oldLeniency"
2570 //if(partialMatchLenient && oldLeniency) {
2571 // However this causes problems for Apple, see <rdar://problem/20692829> regressions in Chinese date parsing
2572 // We don't want to go back to just "if(partialMatchLenient)" as in ICU 53, that is too lenient for strict mode.
2573 // So if the pattern character is in the separator set, we allow the text character to be in that set or be an alpha char.
2574 if( partialMatchLenient
&& ( oldLeniency
||
2575 ( ignorables
!= NULL
&& ignorables
->contains(literal
.charAt(p
)) && (ignorables
->contains(text
.charAt(t
)) || u_isalpha(text
.charAt(t
))) ) )
2586 // At this point if we're in strict mode we have a complete match.
2587 // If we're in lenient mode we may have a partial match, or no
2590 // no match. Pretend it matched a run of whitespace
2591 // and ignorables in the text.
2593 for (t
= textOffset
; t
< text
.length(); t
+= 1) {
2594 UChar ch
= text
.charAt(t
);
2596 if (!IS_BIDI_MARK(ch
) && (ignorables
== NULL
|| !ignorables
->contains(ch
))) {
2602 // if we get here, we've got a complete match.
2603 patternOffset
= i
- 1;
2609 //----------------------------------------------------------------------
2611 int32_t SimpleDateFormat::matchString(const UnicodeString
& text
,
2613 UCalendarDateFields field
,
2614 const UnicodeString
* data
,
2616 const UnicodeString
* monthPattern
,
2617 Calendar
& cal
) const
2620 int32_t count
= dataCount
;
2622 if (field
== UCAL_DAY_OF_WEEK
) i
= 1;
2624 // There may be multiple strings in the data[] array which begin with
2625 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2626 // We keep track of the longest match, and return that. Note that this
2627 // unfortunately requires us to test all array elements.
2628 int32_t bestMatchLength
= 0, bestMatch
= -1;
2629 UnicodeString bestMatchName
;
2630 int32_t isLeapMonth
= 0;
2632 for (; i
< count
; ++i
) {
2633 int32_t matchLen
= 0;
2634 if ((matchLen
= matchStringWithOptionalDot(text
, start
, data
[i
])) > bestMatchLength
) {
2636 bestMatchLength
= matchLen
;
2639 if (monthPattern
!= NULL
) {
2640 UErrorCode status
= U_ZERO_ERROR
;
2641 UnicodeString leapMonthName
;
2642 Formattable
monthName((const UnicodeString
&)(data
[i
]));
2643 MessageFormat::format(*monthPattern
, &monthName
, 1, leapMonthName
, status
);
2644 if (U_SUCCESS(status
)) {
2645 if ((matchLen
= matchStringWithOptionalDot(text
, start
, leapMonthName
)) > bestMatchLength
) {
2647 bestMatchLength
= matchLen
;
2654 if (bestMatch
>= 0) {
2655 if (field
< UCAL_FIELD_COUNT
) {
2656 // Adjustment for Hebrew Calendar month Adar II
2657 if (!strcmp(cal
.getType(),"hebrew") && field
==UCAL_MONTH
&& bestMatch
==13) {
2660 if (field
== UCAL_YEAR
) {
2661 bestMatch
++; // only get here for cyclic year names, which match 1-based years 1-60
2663 cal
.set(field
, bestMatch
);
2665 if (monthPattern
!= NULL
) {
2666 cal
.set(UCAL_IS_LEAP_MONTH
, isLeapMonth
);
2670 return start
+ bestMatchLength
;
2677 matchStringWithOptionalDot(const UnicodeString
&text
,
2679 const UnicodeString
&data
) {
2680 UErrorCode sts
= U_ZERO_ERROR
;
2681 int32_t matchLenText
= 0;
2682 int32_t matchLenData
= 0;
2684 u_caseInsensitivePrefixMatch(text
.getBuffer() + index
, text
.length() - index
,
2685 data
.getBuffer(), data
.length(),
2686 0 /* default case option */,
2687 &matchLenText
, &matchLenData
,
2689 U_ASSERT (U_SUCCESS(sts
));
2691 if (matchLenData
== data
.length() /* normal match */
2692 || (data
.charAt(data
.length() - 1) == 0x2e
2693 && matchLenData
== data
.length() - 1 /* match without trailing dot */)) {
2694 return matchLenText
;
2700 //----------------------------------------------------------------------
2703 SimpleDateFormat::set2DigitYearStart(UDate d
, UErrorCode
& status
)
2705 parseAmbiguousDatesAsAfter(d
, status
);
2709 * Private member function that converts the parsed date strings into
2710 * timeFields. Returns -start (for ParsePosition) if failed.
2712 int32_t SimpleDateFormat::subParse(const UnicodeString
& text
, int32_t& start
, UChar ch
, int32_t count
,
2713 UBool obeyCount
, UBool allowNegative
, UBool ambiguousYear
[], int32_t& saveHebrewMonth
, Calendar
& cal
,
2714 int32_t patLoc
, MessageFormat
* numericLeapMonthFormatter
, UTimeZoneFormatTimeType
*tzTimeType
, SimpleDateFormatMutableNFs
&mutableNFs
) const
2720 UErrorCode status
= U_ZERO_ERROR
;
2721 ParsePosition
pos(0);
2722 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(ch
);
2723 NumberFormat
*currentNumberFormat
;
2725 int32_t tzParseOptions
= (isLenient())? UTZFMT_PARSE_OPTION_ALL_STYLES
: UTZFMT_PARSE_OPTION_NONE
;
2726 UBool gotNumber
= FALSE
;
2728 #if defined (U_DEBUG_CAL)
2729 //fprintf(stderr, "%s:%d - [%c] st=%d \n", __FILE__, __LINE__, (char) ch, start);
2732 if (patternCharIndex
== UDAT_FIELD_COUNT
) {
2736 currentNumberFormat
= mutableNFs
.get(getNumberFormatByIndex(patternCharIndex
));
2737 if (currentNumberFormat
== NULL
) {
2740 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
]; // UCAL_FIELD_COUNT if irrelevant
2741 UnicodeString
hebr("hebr", 4, US_INV
);
2743 if (numericLeapMonthFormatter
!= NULL
) {
2744 numericLeapMonthFormatter
->setFormats((const Format
**)¤tNumberFormat
, 1);
2746 UBool isChineseCalendar
= (uprv_strcmp(cal
.getType(),"chinese") == 0 || uprv_strcmp(cal
.getType(),"dangi") == 0);
2748 // If there are any spaces here, skip over them. If we hit the end
2749 // of the string, then fail.
2751 if (start
>= text
.length()) {
2754 UChar32 c
= text
.char32At(start
);
2755 if (!u_isUWhiteSpace(c
) /*||*/ && !PatternProps::isWhiteSpace(c
)) {
2758 start
+= U16_LENGTH(c
);
2760 pos
.setIndex(start
);
2762 // We handle a few special cases here where we need to parse
2763 // a number value. We handle further, more generic cases below. We need
2764 // to handle some of them here because some fields require extra processing on
2765 // the parsed value.
2766 if (patternCharIndex
== UDAT_HOUR_OF_DAY1_FIELD
|| // k
2767 patternCharIndex
== UDAT_HOUR_OF_DAY0_FIELD
|| // H
2768 patternCharIndex
== UDAT_HOUR1_FIELD
|| // h
2769 patternCharIndex
== UDAT_HOUR0_FIELD
|| // K
2770 (patternCharIndex
== UDAT_DOW_LOCAL_FIELD
&& count
<= 2) || // e
2771 (patternCharIndex
== UDAT_STANDALONE_DAY_FIELD
&& count
<= 2) || // c
2772 (patternCharIndex
== UDAT_MONTH_FIELD
&& count
<= 2) || // M
2773 (patternCharIndex
== UDAT_STANDALONE_MONTH_FIELD
&& count
<= 2) || // L
2774 (patternCharIndex
== UDAT_QUARTER_FIELD
&& count
<= 2) || // Q
2775 (patternCharIndex
== UDAT_STANDALONE_QUARTER_FIELD
&& count
<= 2) || // q
2776 patternCharIndex
== UDAT_YEAR_FIELD
|| // y
2777 patternCharIndex
== UDAT_YEAR_WOY_FIELD
|| // Y
2778 patternCharIndex
== UDAT_YEAR_NAME_FIELD
|| // U (falls back to numeric)
2779 (patternCharIndex
== UDAT_ERA_FIELD
&& isChineseCalendar
) || // G
2780 patternCharIndex
== UDAT_FRACTIONAL_SECOND_FIELD
) // S
2782 int32_t parseStart
= pos
.getIndex();
2783 // It would be good to unify this with the obeyCount logic below,
2784 // but that's going to be difficult.
2785 const UnicodeString
* src
;
2787 UBool parsedNumericLeapMonth
= FALSE
;
2788 if (numericLeapMonthFormatter
!= NULL
&& (patternCharIndex
== UDAT_MONTH_FIELD
|| patternCharIndex
== UDAT_STANDALONE_MONTH_FIELD
)) {
2790 Formattable
* args
= numericLeapMonthFormatter
->parse(text
, pos
, argCount
);
2791 if (args
!= NULL
&& argCount
== 1 && pos
.getIndex() > parseStart
&& args
[0].isNumeric()) {
2792 parsedNumericLeapMonth
= TRUE
;
2793 number
.setLong(args
[0].getLong());
2794 cal
.set(UCAL_IS_LEAP_MONTH
, 1);
2797 pos
.setIndex(parseStart
);
2798 cal
.set(UCAL_IS_LEAP_MONTH
, 0);
2802 if (!parsedNumericLeapMonth
) {
2804 if ((start
+count
) > text
.length()) {
2808 text
.extractBetween(0, start
+ count
, temp
);
2814 parseInt(*src
, number
, pos
, allowNegative
,currentNumberFormat
);
2817 int32_t txtLoc
= pos
.getIndex();
2819 if (txtLoc
> parseStart
) {
2820 value
= number
.getLong();
2823 // suffix processing
2825 txtLoc
= checkIntSuffix(text
, txtLoc
, patLoc
+1, TRUE
);
2826 if (txtLoc
!= pos
.getIndex()) {
2831 txtLoc
= checkIntSuffix(text
, txtLoc
, patLoc
+1, FALSE
);
2834 // Check the range of the value
2835 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, status
)) {
2836 int32_t bias
= gFieldRangeBias
[patternCharIndex
];
2837 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
|| value
< cal
.getMinimum(field
) + bias
)) {
2841 int32_t bias
= gFieldRangeBiasLenient
[patternCharIndex
];
2842 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
)) {
2847 pos
.setIndex(txtLoc
);
2851 // Make sure that we got a number if
2852 // we want one, and didn't get one
2853 // if we don't want one.
2854 switch (patternCharIndex
) {
2855 case UDAT_HOUR_OF_DAY1_FIELD
:
2856 case UDAT_HOUR_OF_DAY0_FIELD
:
2857 case UDAT_HOUR1_FIELD
:
2858 case UDAT_HOUR0_FIELD
:
2859 // special range check for hours:
2860 if (value
< 0 || value
> 24) {
2864 // fall through to gotNumber check
2866 case UDAT_YEAR_FIELD
:
2867 case UDAT_YEAR_WOY_FIELD
:
2868 case UDAT_FRACTIONAL_SECOND_FIELD
:
2869 // these must be a number
2877 // we check the rest of the fields below.
2881 switch (patternCharIndex
) {
2882 case UDAT_ERA_FIELD
:
2883 if (isChineseCalendar
) {
2887 cal
.set(UCAL_ERA
, value
);
2888 return pos
.getIndex();
2891 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fNarrowEras
, fSymbols
->fNarrowErasCount
, NULL
, cal
);
2892 } else if (count
== 4) {
2893 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fEraNames
, fSymbols
->fEraNamesCount
, NULL
, cal
);
2895 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fEras
, fSymbols
->fErasCount
, NULL
, cal
);
2898 // check return position, if it equals -start, then matchString error
2899 // special case the return code so we don't necessarily fail out until we
2900 // verify no year information also
2906 case UDAT_YEAR_FIELD
:
2907 // If there are 3 or more YEAR pattern characters, this indicates
2908 // that the year value is to be treated literally, without any
2909 // two-digit year adjustments (e.g., from "01" to 2001). Otherwise
2910 // we made adjustments to place the 2-digit year in the proper
2911 // century, for parsed strings from "00" to "99". Any other string
2912 // is treated literally: "2250", "-1", "1", "002".
2913 if (fDateOverride
.compare(hebr
)==0 && value
< 1000) {
2914 value
+= HEBREW_CAL_CUR_MILLENIUM_START_YEAR
;
2915 } else if ((pos
.getIndex() - start
) == 2 && !isChineseCalendar
2916 && u_isdigit(text
.charAt(start
))
2917 && u_isdigit(text
.charAt(start
+1)))
2919 // only adjust year for patterns less than 3.
2921 // Assume for example that the defaultCenturyStart is 6/18/1903.
2922 // This means that two-digit years will be forced into the range
2923 // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
2924 // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
2925 // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
2926 // other fields specify a date before 6/18, or 1903 if they specify a
2927 // date afterwards. As a result, 03 is an ambiguous year. All other
2928 // two-digit years are unambiguous.
2929 if(fHaveDefaultCentury
) { // check if this formatter even has a pivot year
2930 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
2931 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
2932 value
+= (fDefaultCenturyStartYear
/100)*100 +
2933 (value
< ambiguousTwoDigitYear
? 100 : 0);
2937 cal
.set(UCAL_YEAR
, value
);
2939 // Delayed checking for adjustment of Hebrew month numbers in non-leap years.
2940 if (saveHebrewMonth
>= 0) {
2941 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
2942 if (!hc
->isLeapYear(value
) && saveHebrewMonth
>= 6) {
2943 cal
.set(UCAL_MONTH
,saveHebrewMonth
);
2945 cal
.set(UCAL_MONTH
,saveHebrewMonth
-1);
2947 saveHebrewMonth
= -1;
2949 return pos
.getIndex();
2951 case UDAT_YEAR_WOY_FIELD
:
2952 // Comment is the same as for UDAT_Year_FIELDs - look above
2953 if (fDateOverride
.compare(hebr
)==0 && value
< 1000) {
2954 value
+= HEBREW_CAL_CUR_MILLENIUM_START_YEAR
;
2955 } else if ((pos
.getIndex() - start
) == 2
2956 && u_isdigit(text
.charAt(start
))
2957 && u_isdigit(text
.charAt(start
+1))
2958 && fHaveDefaultCentury
)
2960 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
2961 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
2962 value
+= (fDefaultCenturyStartYear
/100)*100 +
2963 (value
< ambiguousTwoDigitYear
? 100 : 0);
2965 cal
.set(UCAL_YEAR_WOY
, value
);
2966 return pos
.getIndex();
2968 case UDAT_YEAR_NAME_FIELD
:
2969 if (fSymbols
->fShortYearNames
!= NULL
) {
2970 int32_t newStart
= matchString(text
, start
, UCAL_YEAR
, fSymbols
->fShortYearNames
, fSymbols
->fShortYearNamesCount
, NULL
, cal
);
2975 if (gotNumber
&& (getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
,status
) || value
> fSymbols
->fShortYearNamesCount
)) {
2976 cal
.set(UCAL_YEAR
, value
);
2977 return pos
.getIndex();
2981 case UDAT_MONTH_FIELD
:
2982 case UDAT_STANDALONE_MONTH_FIELD
:
2983 if (gotNumber
) // i.e., M or MM.
2985 // When parsing month numbers from the Hebrew Calendar, we might need to adjust the month depending on whether
2986 // 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
2987 // the year is parsed.
2988 if (!strcmp(cal
.getType(),"hebrew")) {
2989 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
2990 if (cal
.isSet(UCAL_YEAR
)) {
2991 UErrorCode status
= U_ZERO_ERROR
;
2992 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6) {
2993 cal
.set(UCAL_MONTH
, value
);
2995 cal
.set(UCAL_MONTH
, value
- 1);
2998 saveHebrewMonth
= value
;
3001 // Don't want to parse the month if it is a string
3002 // while pattern uses numeric style: M/MM, L/LL
3003 // [We computed 'value' above.]
3004 cal
.set(UCAL_MONTH
, value
- 1);
3006 return pos
.getIndex();
3008 // count >= 3 // i.e., MMM/MMMM, LLL/LLLL
3009 // Want to be able to parse both short and long forms.
3010 // Try count == 4 first:
3011 UnicodeString
* wideMonthPat
= NULL
;
3012 UnicodeString
* shortMonthPat
= NULL
;
3013 if (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
) {
3014 if (patternCharIndex
==UDAT_MONTH_FIELD
) {
3015 wideMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatWide
];
3016 shortMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatAbbrev
];
3018 wideMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneWide
];
3019 shortMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev
];
3022 int32_t newStart
= 0;
3023 if (patternCharIndex
==UDAT_MONTH_FIELD
) {
3024 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3025 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fMonths
, fSymbols
->fMonthsCount
, wideMonthPat
, cal
); // try MMMM
3030 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3031 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fShortMonths
, fSymbols
->fShortMonthsCount
, shortMonthPat
, cal
); // try MMM
3034 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3035 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fStandaloneMonths
, fSymbols
->fStandaloneMonthsCount
, wideMonthPat
, cal
); // try LLLL
3040 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3041 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fStandaloneShortMonths
, fSymbols
->fStandaloneShortMonthsCount
, shortMonthPat
, cal
); // try LLL
3044 if (newStart
> 0 || !getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
)) // currently we do not try to parse MMMMM/LLLLL: #8860
3046 // else we allowing parsing as number, below
3050 case UDAT_HOUR_OF_DAY1_FIELD
:
3051 // [We computed 'value' above.]
3052 if (value
== cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1)
3055 // fall through to set field
3057 case UDAT_HOUR_OF_DAY0_FIELD
:
3058 cal
.set(UCAL_HOUR_OF_DAY
, value
);
3059 return pos
.getIndex();
3061 case UDAT_FRACTIONAL_SECOND_FIELD
:
3062 // Fractional seconds left-justify
3063 i
= pos
.getIndex() - start
;
3077 cal
.set(UCAL_MILLISECOND
, value
);
3078 return pos
.getIndex();
3080 case UDAT_DOW_LOCAL_FIELD
:
3081 if (gotNumber
) // i.e., e or ee
3083 // [We computed 'value' above.]
3084 cal
.set(UCAL_DOW_LOCAL
, value
);
3085 return pos
.getIndex();
3087 // else for eee-eeeee fall through to handling of EEE-EEEEE
3088 // fall through, do not break here
3089 case UDAT_DAY_OF_WEEK_FIELD
:
3091 // Want to be able to parse both short and long forms.
3092 // Try count == 4 (EEEE) wide first:
3093 int32_t newStart
= 0;
3094 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3095 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3096 fSymbols
->fWeekdays
, fSymbols
->fWeekdaysCount
, NULL
, cal
)) > 0)
3099 // EEEE wide failed, now try EEE abbreviated
3100 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3101 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3102 fSymbols
->fShortWeekdays
, fSymbols
->fShortWeekdaysCount
, NULL
, cal
)) > 0)
3105 // EEE abbreviated failed, now try EEEEEE short
3106 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 6) {
3107 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3108 fSymbols
->fShorterWeekdays
, fSymbols
->fShorterWeekdaysCount
, NULL
, cal
)) > 0)
3111 // EEEEEE short failed, now try EEEEE narrow
3112 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 5) {
3113 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3114 fSymbols
->fNarrowWeekdays
, fSymbols
->fNarrowWeekdaysCount
, NULL
, cal
)) > 0)
3117 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
) || patternCharIndex
== UDAT_DAY_OF_WEEK_FIELD
)
3119 // else we allowing parsing as number, below
3123 case UDAT_STANDALONE_DAY_FIELD
:
3125 if (gotNumber
) // c or cc
3127 // [We computed 'value' above.]
3128 cal
.set(UCAL_DOW_LOCAL
, value
);
3129 return pos
.getIndex();
3131 // Want to be able to parse both short and long forms.
3132 // Try count == 4 (cccc) first:
3133 int32_t newStart
= 0;
3134 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3135 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3136 fSymbols
->fStandaloneWeekdays
, fSymbols
->fStandaloneWeekdaysCount
, NULL
, cal
)) > 0)
3139 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3140 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3141 fSymbols
->fStandaloneShortWeekdays
, fSymbols
->fStandaloneShortWeekdaysCount
, NULL
, cal
)) > 0)
3144 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 6) {
3145 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3146 fSymbols
->fStandaloneShorterWeekdays
, fSymbols
->fStandaloneShorterWeekdaysCount
, NULL
, cal
)) > 0)
3149 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
))
3151 // else we allowing parsing as number, below
3155 case UDAT_AM_PM_FIELD
:
3157 // optionally try both wide/abbrev and narrow forms
3158 int32_t newStart
= 0;
3160 if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
< 5 ) {
3161 if ((newStart
= matchString(text
, start
, UCAL_AM_PM
, fSymbols
->fAmPms
, fSymbols
->fAmPmsCount
, NULL
, cal
)) > 0) {
3166 if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
>= 5 ) {
3167 if ((newStart
= matchString(text
, start
, UCAL_AM_PM
, fSymbols
->fNarrowAmPms
, fSymbols
->fNarrowAmPmsCount
, NULL
, cal
)) > 0) {
3171 // no matches for given options
3175 case UDAT_HOUR1_FIELD
:
3176 // [We computed 'value' above.]
3177 if (value
== cal
.getLeastMaximum(UCAL_HOUR
)+1)
3180 // fall through to set field
3182 case UDAT_HOUR0_FIELD
:
3183 cal
.set(UCAL_HOUR
, value
);
3184 return pos
.getIndex();
3186 case UDAT_QUARTER_FIELD
:
3187 if (gotNumber
) // i.e., Q or QQ.
3189 // Don't want to parse the month if it is a string
3190 // while pattern uses numeric style: Q or QQ.
3191 // [We computed 'value' above.]
3192 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
3193 return pos
.getIndex();
3195 // count >= 3 // i.e., QQQ or QQQQ
3196 // Want to be able to parse both short and long forms.
3197 // Try count == 4 first:
3198 int32_t newStart
= 0;
3200 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3201 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3202 fSymbols
->fQuarters
, fSymbols
->fQuartersCount
, cal
)) > 0)
3205 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3206 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3207 fSymbols
->fShortQuarters
, fSymbols
->fShortQuartersCount
, cal
)) > 0)
3210 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
))
3212 // else we allowing parsing as number, below
3213 if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
))
3218 case UDAT_STANDALONE_QUARTER_FIELD
:
3219 if (gotNumber
) // i.e., q or qq.
3221 // Don't want to parse the month if it is a string
3222 // while pattern uses numeric style: q or q.
3223 // [We computed 'value' above.]
3224 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
3225 return pos
.getIndex();
3227 // count >= 3 // i.e., qqq or qqqq
3228 // Want to be able to parse both short and long forms.
3229 // Try count == 4 first:
3230 int32_t newStart
= 0;
3232 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3233 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3234 fSymbols
->fStandaloneQuarters
, fSymbols
->fStandaloneQuartersCount
, cal
)) > 0)
3237 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3238 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3239 fSymbols
->fStandaloneShortQuarters
, fSymbols
->fStandaloneShortQuartersCount
, cal
)) > 0)
3242 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
))
3244 // else we allowing parsing as number, below
3245 if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
))
3250 case UDAT_TIMEZONE_FIELD
: // 'z'
3252 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_SPECIFIC_SHORT
: UTZFMT_STYLE_SPECIFIC_LONG
;
3253 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzParseOptions
, tzTimeType
);
3255 cal
.adoptTimeZone(tz
);
3256 return pos
.getIndex();
3260 case UDAT_TIMEZONE_RFC_FIELD
: // 'Z'
3262 UTimeZoneFormatStyle style
= (count
< 4) ?
3263 UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
: ((count
== 5) ? UTZFMT_STYLE_ISO_EXTENDED_FULL
: UTZFMT_STYLE_LOCALIZED_GMT
);
3264 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzTimeType
);
3266 cal
.adoptTimeZone(tz
);
3267 return pos
.getIndex();
3271 case UDAT_TIMEZONE_GENERIC_FIELD
: // 'v'
3273 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_GENERIC_SHORT
: UTZFMT_STYLE_GENERIC_LONG
;
3274 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzParseOptions
, tzTimeType
);
3276 cal
.adoptTimeZone(tz
);
3277 return pos
.getIndex();
3281 case UDAT_TIMEZONE_SPECIAL_FIELD
: // 'V'
3283 UTimeZoneFormatStyle style
;
3286 style
= UTZFMT_STYLE_ZONE_ID_SHORT
;
3289 style
= UTZFMT_STYLE_ZONE_ID
;
3292 style
= UTZFMT_STYLE_EXEMPLAR_LOCATION
;
3295 style
= UTZFMT_STYLE_GENERIC_LOCATION
;
3298 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzTimeType
);
3300 cal
.adoptTimeZone(tz
);
3301 return pos
.getIndex();
3305 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
: // 'O'
3307 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_LOCALIZED_GMT_SHORT
: UTZFMT_STYLE_LOCALIZED_GMT
;
3308 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzTimeType
);
3310 cal
.adoptTimeZone(tz
);
3311 return pos
.getIndex();
3315 case UDAT_TIMEZONE_ISO_FIELD
: // 'X'
3317 UTimeZoneFormatStyle style
;
3320 style
= UTZFMT_STYLE_ISO_BASIC_SHORT
;
3323 style
= UTZFMT_STYLE_ISO_BASIC_FIXED
;
3326 style
= UTZFMT_STYLE_ISO_EXTENDED_FIXED
;
3329 style
= UTZFMT_STYLE_ISO_BASIC_FULL
;
3332 style
= UTZFMT_STYLE_ISO_EXTENDED_FULL
;
3335 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzTimeType
);
3337 cal
.adoptTimeZone(tz
);
3338 return pos
.getIndex();
3342 case UDAT_TIMEZONE_ISO_LOCAL_FIELD
: // 'x'
3344 UTimeZoneFormatStyle style
;
3347 style
= UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
;
3350 style
= UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
;
3353 style
= UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
;
3356 style
= UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
;
3359 style
= UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
;
3362 TimeZone
*tz
= tzFormat()->parse(style
, text
, pos
, tzTimeType
);
3364 cal
.adoptTimeZone(tz
);
3365 return pos
.getIndex();
3369 case UDAT_TIME_SEPARATOR_FIELD
: // ':'
3371 static const UChar def_sep
= DateFormatSymbols::DEFAULT_TIME_SEPARATOR
;
3372 static const UChar alt_sep
= DateFormatSymbols::ALTERNATE_TIME_SEPARATOR
;
3374 // Try matching a time separator.
3376 UnicodeString data
[3];
3377 fSymbols
->getTimeSeparatorString(data
[0]);
3379 // Add the default, if different from the locale.
3380 if (data
[0].compare(&def_sep
, 1) != 0) {
3381 data
[count
++].setTo(def_sep
);
3384 // If lenient, add also the alternate, if different from the locale.
3385 if (isLenient() && data
[0].compare(&alt_sep
, 1) != 0) {
3386 data
[count
++].setTo(alt_sep
);
3389 return matchString(text
, start
, UCAL_FIELD_COUNT
/* => nothing to set */, data
, count
, NULL
, cal
);
3393 // Handle "generic" fields
3394 // this is now handled below, outside the switch block
3397 // Handle "generic" fields:
3398 // switch default case now handled here (outside switch block) to allow
3399 // parsing of some string fields as digits for lenient case
3401 int32_t parseStart
= pos
.getIndex();
3402 const UnicodeString
* src
;
3404 if ((start
+count
) > text
.length()) {
3407 text
.extractBetween(0, start
+ count
, temp
);
3412 parseInt(*src
, number
, pos
, allowNegative
,currentNumberFormat
);
3413 if (pos
.getIndex() != parseStart
) {
3414 int32_t value
= number
.getLong();
3416 // Don't need suffix processing here (as in number processing at the beginning of the function);
3417 // the new fields being handled as numeric values (month, weekdays, quarters) should not have suffixes.
3419 // Check the range of the value
3420 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
)) {
3421 int32_t bias
= gFieldRangeBias
[patternCharIndex
];
3422 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
|| value
< cal
.getMinimum(field
) + bias
)) {
3426 int32_t bias
= gFieldRangeBiasLenient
[patternCharIndex
];
3427 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
)) {
3432 // For the following, need to repeat some of the "if (gotNumber)" code above:
3433 // UDAT_[STANDALONE_]MONTH_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_STANDALONE_DAY_FIELD,
3434 // UDAT_[STANDALONE_]QUARTER_FIELD
3435 switch (patternCharIndex
) {
3436 case UDAT_MONTH_FIELD
:
3437 // See notes under UDAT_MONTH_FIELD case above
3438 if (!strcmp(cal
.getType(),"hebrew")) {
3439 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
3440 if (cal
.isSet(UCAL_YEAR
)) {
3441 UErrorCode status
= U_ZERO_ERROR
;
3442 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6) {
3443 cal
.set(UCAL_MONTH
, value
);
3445 cal
.set(UCAL_MONTH
, value
- 1);
3448 saveHebrewMonth
= value
;
3451 cal
.set(UCAL_MONTH
, value
- 1);
3454 case UDAT_STANDALONE_MONTH_FIELD
:
3455 cal
.set(UCAL_MONTH
, value
- 1);
3457 case UDAT_DOW_LOCAL_FIELD
:
3458 case UDAT_STANDALONE_DAY_FIELD
:
3459 cal
.set(UCAL_DOW_LOCAL
, value
);
3461 case UDAT_QUARTER_FIELD
:
3462 case UDAT_STANDALONE_QUARTER_FIELD
:
3463 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
3465 case UDAT_RELATED_YEAR_FIELD
:
3466 cal
.setRelatedYear(value
);
3469 cal
.set(field
, value
);
3472 return pos
.getIndex();
3478 * Parse an integer using fNumberFormat. This method is semantically
3479 * const, but actually may modify fNumberFormat.
3481 void SimpleDateFormat::parseInt(const UnicodeString
& text
,
3482 Formattable
& number
,
3484 UBool allowNegative
,
3485 NumberFormat
*fmt
) const {
3486 parseInt(text
, number
, -1, pos
, allowNegative
,fmt
);
3490 * Parse an integer using fNumberFormat up to maxDigits.
3492 void SimpleDateFormat::parseInt(const UnicodeString
& text
,
3493 Formattable
& number
,
3496 UBool allowNegative
,
3497 NumberFormat
*fmt
) const {
3498 UnicodeString oldPrefix
;
3499 DecimalFormat
* df
= NULL
;
3500 if (!allowNegative
&& (df
= dynamic_cast<DecimalFormat
*>(fmt
)) != NULL
) {
3501 df
->getNegativePrefix(oldPrefix
);
3502 df
->setNegativePrefix(UnicodeString(TRUE
, SUPPRESS_NEGATIVE_PREFIX
, -1));
3504 int32_t oldPos
= pos
.getIndex();
3505 fmt
->parse(text
, number
, pos
);
3507 df
->setNegativePrefix(oldPrefix
);
3510 if (maxDigits
> 0) {
3511 // adjust the result to fit into
3512 // the maxDigits and move the position back
3513 int32_t nDigits
= pos
.getIndex() - oldPos
;
3514 if (nDigits
> maxDigits
) {
3515 int32_t val
= number
.getLong();
3516 nDigits
-= maxDigits
;
3517 while (nDigits
> 0) {
3521 pos
.setIndex(oldPos
+ maxDigits
);
3522 number
.setLong(val
);
3527 //----------------------------------------------------------------------
3529 void SimpleDateFormat::translatePattern(const UnicodeString
& originalPattern
,
3530 UnicodeString
& translatedPattern
,
3531 const UnicodeString
& from
,
3532 const UnicodeString
& to
,
3535 // run through the pattern and convert any pattern symbols from the version
3536 // in "from" to the corresponding character ion "to". This code takes
3537 // quoted strings into account (it doesn't try to translate them), and it signals
3538 // an error if a particular "pattern character" doesn't appear in "from".
3539 // Depending on the values of "from" and "to" this can convert from generic
3540 // to localized patterns or localized to generic.
3541 if (U_FAILURE(status
)) {
3545 translatedPattern
.remove();
3546 UBool inQuote
= FALSE
;
3547 for (int32_t i
= 0; i
< originalPattern
.length(); ++i
) {
3548 UChar c
= originalPattern
[i
];
3556 } else if (isSyntaxChar(c
)) {
3557 int32_t ci
= from
.indexOf(c
);
3559 status
= U_INVALID_FORMAT_ERROR
;
3565 translatedPattern
+= c
;
3568 status
= U_INVALID_FORMAT_ERROR
;
3573 //----------------------------------------------------------------------
3576 SimpleDateFormat::toPattern(UnicodeString
& result
) const
3582 //----------------------------------------------------------------------
3585 SimpleDateFormat::toLocalizedPattern(UnicodeString
& result
,
3586 UErrorCode
& status
) const
3588 translatePattern(fPattern
, result
,
3589 UnicodeString(DateFormatSymbols::getPatternUChars()),
3590 fSymbols
->fLocalPatternChars
, status
);
3594 //----------------------------------------------------------------------
3597 SimpleDateFormat::applyPattern(const UnicodeString
& pattern
)
3600 updateTimeSepFromPattern(fPattern
, fSymbols
);
3603 //----------------------------------------------------------------------
3606 SimpleDateFormat::applyLocalizedPattern(const UnicodeString
& pattern
,
3609 translatePattern(pattern
, fPattern
,
3610 fSymbols
->fLocalPatternChars
,
3611 UnicodeString(DateFormatSymbols::getPatternUChars()), status
);
3612 updateTimeSepFromPattern(fPattern
, fSymbols
);
3615 //----------------------------------------------------------------------
3617 const DateFormatSymbols
*
3618 SimpleDateFormat::getDateFormatSymbols() const
3623 //----------------------------------------------------------------------
3626 SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols
* newFormatSymbols
)
3629 fSymbols
= newFormatSymbols
;
3632 //----------------------------------------------------------------------
3634 SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols
& newFormatSymbols
)
3637 fSymbols
= new DateFormatSymbols(newFormatSymbols
);
3640 //----------------------------------------------------------------------
3641 const TimeZoneFormat
*
3642 SimpleDateFormat::getTimeZoneFormat(void) const {
3643 return (const TimeZoneFormat
*)tzFormat();
3646 //----------------------------------------------------------------------
3648 SimpleDateFormat::adoptTimeZoneFormat(TimeZoneFormat
* timeZoneFormatToAdopt
)
3650 delete fTimeZoneFormat
;
3651 fTimeZoneFormat
= timeZoneFormatToAdopt
;
3654 //----------------------------------------------------------------------
3656 SimpleDateFormat::setTimeZoneFormat(const TimeZoneFormat
& newTimeZoneFormat
)
3658 delete fTimeZoneFormat
;
3659 fTimeZoneFormat
= new TimeZoneFormat(newTimeZoneFormat
);
3662 //----------------------------------------------------------------------
3665 void SimpleDateFormat::adoptCalendar(Calendar
* calendarToAdopt
)
3667 UErrorCode status
= U_ZERO_ERROR
;
3668 Locale
calLocale(fLocale
);
3669 calLocale
.setKeywordValue("calendar", calendarToAdopt
->getType(), status
);
3670 DateFormatSymbols
*newSymbols
=
3671 DateFormatSymbols::createForLocale(calLocale
, status
);
3672 if (U_FAILURE(status
)) {
3675 DateFormat::adoptCalendar(calendarToAdopt
);
3677 fSymbols
= newSymbols
;
3678 initializeDefaultCentury(); // we need a new century (possibly)
3682 //----------------------------------------------------------------------
3685 // override the DateFormat implementation in order to
3686 // lazily initialize fCapitalizationBrkIter
3688 SimpleDateFormat::setContext(UDisplayContext value
, UErrorCode
& status
)
3690 DateFormat::setContext(value
, status
);
3691 #if !UCONFIG_NO_BREAK_ITERATION
3692 if (U_SUCCESS(status
)) {
3693 if ( fCapitalizationBrkIter
== NULL
&& (value
==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
||
3694 value
==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
|| value
==UDISPCTX_CAPITALIZATION_FOR_STANDALONE
) ) {
3695 UErrorCode status
= U_ZERO_ERROR
;
3696 fCapitalizationBrkIter
= BreakIterator::createSentenceInstance(fLocale
, status
);
3697 if (U_FAILURE(status
)) {
3698 delete fCapitalizationBrkIter
;
3699 fCapitalizationBrkIter
= NULL
;
3707 //----------------------------------------------------------------------
3711 SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field
) const {
3712 return isFieldUnitIgnored(fPattern
, field
);
3717 SimpleDateFormat::isFieldUnitIgnored(const UnicodeString
& pattern
,
3718 UCalendarDateFields field
) {
3719 int32_t fieldLevel
= fgCalendarFieldToLevel
[field
];
3722 UBool inQuote
= FALSE
;
3726 for (int32_t i
= 0; i
< pattern
.length(); ++i
) {
3728 if (ch
!= prevCh
&& count
> 0) {
3729 level
= getLevelFromChar(prevCh
);
3730 // the larger the level, the smaller the field unit.
3731 if (fieldLevel
<= level
) {
3737 if ((i
+1) < pattern
.length() && pattern
[i
+1] == QUOTE
) {
3740 inQuote
= ! inQuote
;
3743 else if (!inQuote
&& isSyntaxChar(ch
)) {
3750 level
= getLevelFromChar(prevCh
);
3751 if (fieldLevel
<= level
) {
3758 //----------------------------------------------------------------------
3761 SimpleDateFormat::getSmpFmtLocale(void) const {
3765 //----------------------------------------------------------------------
3768 SimpleDateFormat::checkIntSuffix(const UnicodeString
& text
, int32_t start
,
3769 int32_t patLoc
, UBool isNegative
) const {
3772 int32_t patternMatch
;
3773 int32_t textPreMatch
;
3774 int32_t textPostMatch
;
3776 // check that we are still in range
3777 if ( (start
> text
.length()) ||
3780 (patLoc
> fPattern
.length())) {
3781 // out of range, don't advance location in text
3786 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(fNumberFormat
);
3787 if (decfmt
!= NULL
) {
3789 suf
= decfmt
->getNegativeSuffix(suf
);
3792 suf
= decfmt
->getPositiveSuffix(suf
);
3797 if (suf
.length() <= 0) {
3801 // check suffix will be encountered in the pattern
3802 patternMatch
= compareSimpleAffix(suf
,fPattern
,patLoc
);
3804 // check if a suffix will be encountered in the text
3805 textPreMatch
= compareSimpleAffix(suf
,text
,start
);
3807 // check if a suffix was encountered in the text
3808 textPostMatch
= compareSimpleAffix(suf
,text
,start
-suf
.length());
3810 // check for suffix match
3811 if ((textPreMatch
>= 0) && (patternMatch
>= 0) && (textPreMatch
== patternMatch
)) {
3814 else if ((textPostMatch
>= 0) && (patternMatch
>= 0) && (textPostMatch
== patternMatch
)) {
3815 return start
- suf
.length();
3818 // should not get here
3822 //----------------------------------------------------------------------
3825 SimpleDateFormat::compareSimpleAffix(const UnicodeString
& affix
,
3826 const UnicodeString
& input
,
3827 int32_t pos
) const {
3828 int32_t start
= pos
;
3829 for (int32_t i
=0; i
<affix
.length(); ) {
3830 UChar32 c
= affix
.char32At(i
);
3831 int32_t len
= U16_LENGTH(c
);
3832 if (PatternProps::isWhiteSpace(c
)) {
3833 // We may have a pattern like: \u200F \u0020
3834 // and input text like: \u200F \u0020
3835 // Note that U+200F and U+0020 are Pattern_White_Space but only
3836 // U+0020 is UWhiteSpace. So we have to first do a direct
3837 // match of the run of Pattern_White_Space in the pattern,
3838 // then match any extra characters.
3839 UBool literalMatch
= FALSE
;
3840 while (pos
< input
.length() &&
3841 input
.char32At(pos
) == c
) {
3842 literalMatch
= TRUE
;
3845 if (i
== affix
.length()) {
3848 c
= affix
.char32At(i
);
3849 len
= U16_LENGTH(c
);
3850 if (!PatternProps::isWhiteSpace(c
)) {
3855 // Advance over run in pattern
3856 i
= skipPatternWhiteSpace(affix
, i
);
3858 // Advance over run in input text
3859 // Must see at least one white space char in input,
3860 // unless we've already matched some characters literally.
3862 pos
= skipUWhiteSpace(input
, pos
);
3863 if (pos
== s
&& !literalMatch
) {
3867 // If we skip UWhiteSpace in the input text, we need to skip it in the pattern.
3868 // Otherwise, the previous lines may have skipped over text (such as U+00A0) that
3869 // is also in the affix.
3870 i
= skipUWhiteSpace(affix
, i
);
3872 if (pos
< input
.length() &&
3873 input
.char32At(pos
) == c
) {
3884 //----------------------------------------------------------------------
3887 SimpleDateFormat::skipPatternWhiteSpace(const UnicodeString
& text
, int32_t pos
) const {
3888 const UChar
* s
= text
.getBuffer();
3889 return (int32_t)(PatternProps::skipWhiteSpace(s
+ pos
, text
.length() - pos
) - s
);
3892 //----------------------------------------------------------------------
3895 SimpleDateFormat::skipUWhiteSpace(const UnicodeString
& text
, int32_t pos
) const {
3896 while (pos
< text
.length()) {
3897 UChar32 c
= text
.char32At(pos
);
3898 if (!u_isUWhiteSpace(c
)) {
3901 pos
+= U16_LENGTH(c
);
3906 //----------------------------------------------------------------------
3908 // Lazy TimeZoneFormat instantiation, semantically const.
3910 SimpleDateFormat::tzFormat() const {
3911 if (fTimeZoneFormat
== NULL
) {
3914 if (fTimeZoneFormat
== NULL
) {
3915 UErrorCode status
= U_ZERO_ERROR
;
3916 TimeZoneFormat
*tzfmt
= TimeZoneFormat::createInstance(fLocale
, status
);
3917 if (U_FAILURE(status
)) {
3921 const_cast<SimpleDateFormat
*>(this)->fTimeZoneFormat
= tzfmt
;
3926 return fTimeZoneFormat
;
3931 #endif /* #if !UCONFIG_NO_FORMATTING */