1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 1997-2016, International Business Machines Corporation and *
6 * others. All Rights Reserved. *
7 *******************************************************************************
11 * Modification History:
13 * Date Name Description
14 * 02/19/97 aliu Converted from java.
15 * 03/31/97 aliu Modified extensively to work with 50 locales.
16 * 04/01/97 aliu Added support for centuries.
17 * 07/09/97 helena Made ParsePosition into a class.
18 * 07/21/98 stephen Added initializeDefaultCentury.
19 * Removed getZoneIndex (added in DateFormatSymbols)
20 * Removed subParseLong
22 * 02/22/99 stephen Removed character literals for EBCDIC safety
23 * 10/14/99 aliu Updated 2-digit year parsing so that only "00" thru
24 * "99" are recognized. {j28 4182066}
25 * 11/15/99 weiv Added support for week of year/day of week format
26 ********************************************************************************
29 #define ZID_KEY_MAX 128
31 #include "unicode/utypes.h"
33 #if !UCONFIG_NO_FORMATTING
34 #include "unicode/smpdtfmt.h"
35 #include "unicode/dtfmtsym.h"
36 #include "unicode/ures.h"
37 #include "unicode/msgfmt.h"
38 #include "unicode/calendar.h"
39 #include "unicode/gregocal.h"
40 #include "unicode/timezone.h"
41 #include "unicode/decimfmt.h"
42 #include "unicode/dcfmtsym.h"
43 #include "unicode/uchar.h"
44 #include "unicode/uniset.h"
45 #include "unicode/ustring.h"
46 #include "unicode/basictz.h"
47 #include "unicode/simpleformatter.h"
48 #include "unicode/simpletz.h"
49 #include "unicode/rbtz.h"
50 #include "unicode/tzfmt.h"
51 #include "unicode/ucasemap.h"
52 #include "unicode/utf16.h"
53 #include "unicode/vtzone.h"
54 #include "unicode/udisplaycontext.h"
55 #include "unicode/brkiter.h"
56 #include "unicode/rbnf.h"
57 #include "unicode/dtptngen.h"
60 #include "patternprops.h"
69 #include "sharednumberformat.h"
70 #include "ucasemap_imp.h"
75 #include "dayperiodrules.h"
76 #include "tznames_impl.h" // ZONE_NAME_U16_MAX
77 #include "number_utypes.h"
79 #define DEBUG_SYNTHETIC_TIMEFMTS 0
81 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) || DEBUG_SYNTHETIC_TIMEFMTS
85 // *****************************************************************************
86 // class SimpleDateFormat
87 // *****************************************************************************
92 * Last-resort string to use for "GMT" when constructing time zone strings.
94 // For time zones that have no names, use strings GMT+minutes and
95 // GMT-minutes. For instance, in France the time zone is GMT+60.
96 // Also accepted are GMT+H:MM or GMT-H:MM.
97 // Currently not being used
98 //static const UChar gGmt[] = {0x0047, 0x004D, 0x0054, 0x0000}; // "GMT"
99 //static const UChar gGmtPlus[] = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+"
100 //static const UChar gGmtMinus[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-"
101 //static const UChar gDefGmtPat[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
102 //static const UChar gDefGmtNegHmsPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */
103 //static const UChar gDefGmtNegHmPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */
104 //static const UChar gDefGmtPosHmsPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */
105 //static const UChar gDefGmtPosHmPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */
106 //static const UChar gUt[] = {0x0055, 0x0054, 0x0000}; // "UT"
107 //static const UChar gUtc[] = {0x0055, 0x0054, 0x0043, 0x0000}; // "UT"
109 typedef enum GmtPatSize
{
120 // Stuff needed for numbering system overrides
122 typedef enum OvrStrType
{
128 static const UDateFormatField kDateFields
[] = {
132 UDAT_DAY_OF_YEAR_FIELD
,
133 UDAT_DAY_OF_WEEK_IN_MONTH_FIELD
,
134 UDAT_WEEK_OF_YEAR_FIELD
,
135 UDAT_WEEK_OF_MONTH_FIELD
,
137 UDAT_EXTENDED_YEAR_FIELD
,
138 UDAT_JULIAN_DAY_FIELD
,
139 UDAT_STANDALONE_DAY_FIELD
,
140 UDAT_STANDALONE_MONTH_FIELD
,
142 UDAT_STANDALONE_QUARTER_FIELD
,
143 UDAT_YEAR_NAME_FIELD
,
144 UDAT_RELATED_YEAR_FIELD
};
145 static const int8_t kDateFieldsCount
= 16;
147 static const UDateFormatField kTimeFields
[] = {
148 UDAT_HOUR_OF_DAY1_FIELD
,
149 UDAT_HOUR_OF_DAY0_FIELD
,
152 UDAT_FRACTIONAL_SECOND_FIELD
,
155 UDAT_MILLISECONDS_IN_DAY_FIELD
,
156 UDAT_TIMEZONE_RFC_FIELD
,
157 UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
};
158 static const int8_t kTimeFieldsCount
= 10;
161 // This is a pattern-of-last-resort used when we can't load a usable pattern out
163 static const UChar gDefaultPattern
[] =
165 0x79, 0x79, 0x79, 0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0
166 }; /* "yyyyMMdd hh:mm a" */
168 // This prefix is designed to NEVER MATCH real text, in order to
169 // suppress the parsing of negative numbers. Adjust as needed (if
170 // this becomes valid Unicode).
171 static const UChar SUPPRESS_NEGATIVE_PREFIX
[] = {0xAB00, 0};
174 * These are the tags we expect to see in normal resource bundle files associated
177 static const UChar QUOTE
= 0x27; // Single quote
180 * The field range check bias for each UDateFormatField.
181 * The bias is added to the minimum and maximum values
182 * before they are compared to the parsed number.
183 * For example, the calendar stores zero-based month numbers
184 * but the parsed month numbers start at 1, so the bias is 1.
186 * A value of -1 means that the value is not checked.
188 static const int32_t gFieldRangeBias
[] = {
189 -1, // 'G' - UDAT_ERA_FIELD
190 -1, // 'y' - UDAT_YEAR_FIELD
191 1, // 'M' - UDAT_MONTH_FIELD
192 0, // 'd' - UDAT_DATE_FIELD
193 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
194 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
195 0, // 'm' - UDAT_MINUTE_FIELD
196 0, // 's' - UDAT_SECOND_FIELD
197 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
198 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
199 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
200 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
201 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
202 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
203 -1, // 'a' - UDAT_AM_PM_FIELD
204 -1, // 'h' - UDAT_HOUR1_FIELD
205 -1, // 'K' - UDAT_HOUR0_FIELD
206 -1, // 'z' - UDAT_TIMEZONE_FIELD
207 -1, // 'Y' - UDAT_YEAR_WOY_FIELD
208 -1, // 'e' - UDAT_DOW_LOCAL_FIELD
209 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
210 -1, // 'g' - UDAT_JULIAN_DAY_FIELD
211 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
212 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
213 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
214 0, // 'c' - UDAT_STANDALONE_DAY_FIELD
215 1, // 'L' - UDAT_STANDALONE_MONTH_FIELD
216 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
217 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
218 -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
219 -1, // 'U' - UDAT_YEAR_NAME_FIELD
220 -1, // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
221 -1, // 'X' - UDAT_TIMEZONE_ISO_FIELD
222 -1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
223 -1, // 'r' - UDAT_RELATED_YEAR_FIELD
224 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
225 -1, // ':' - UDAT_TIME_SEPARATOR_FIELD
227 -1, // (no pattern character currently) - UDAT_TIME_SEPARATOR_FIELD
230 // A slightly looser range check for lenient parsing
231 static const int32_t gFieldRangeBiasLenient
[] = {
232 -1, // 'G' - UDAT_ERA_FIELD
233 -1, // 'y' - UDAT_YEAR_FIELD
234 8, // 'M' - UDAT_MONTH_FIELD (allow calendar max + 7, e.g. 19 for grego 1-based month)
235 18, // 'd' - UDAT_DATE_FIELD (allow calendar max + 18, e.g. 49 for grego; tests require at least 40 for grego)
236 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
237 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
238 40, // 'm' - UDAT_MINUTE_FIELD (allow calendar max + 40, e.g. 99)
239 40, // 's' - UDAT_SECOND_FIELD (allow calendar max + 40, e.g. 99)
240 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
241 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
242 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
243 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
244 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
245 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
246 -1, // 'a' - UDAT_AM_PM_FIELD
247 -1, // 'h' - UDAT_HOUR1_FIELD
248 -1, // 'K' - UDAT_HOUR0_FIELD
249 -1, // 'z' - UDAT_TIMEZONE_FIELD
250 -1, // 'Y' - UDAT_YEAR_WOY_FIELD
251 -1, // 'e' - UDAT_DOW_LOCAL_FIELD
252 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
253 -1, // 'g' - UDAT_JULIAN_DAY_FIELD
254 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
255 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
256 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
257 18, // 'c' - UDAT_STANDALONE_DAY_FIELD (allow calendar max + 18, e.g. 49 for grego)
258 8, // 'L' - UDAT_STANDALONE_MONTH_FIELD (allow calendar max + 7, e.g. 19 for grego 1-based month)
259 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
260 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
261 -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
262 -1, // 'U' - UDAT_YEAR_NAME_FIELD
263 -1, // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
264 -1, // 'X' - UDAT_TIMEZONE_ISO_FIELD
265 -1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
266 -1, // 'r' - UDAT_RELATED_YEAR_FIELD
267 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
268 -1, // ':' - UDAT_TIME_SEPARATOR_FIELD
270 -1, // (no pattern character currently) - UDAT_TIME_SEPARATOR_FIELD
274 // When calendar uses hebr numbering (i.e. he@calendar=hebrew),
275 // offset the years within the current millenium down to 1-999
276 static const int32_t HEBREW_CAL_CUR_MILLENIUM_START_YEAR
= 5000;
277 static const int32_t HEBREW_CAL_CUR_MILLENIUM_END_YEAR
= 6000;
279 static UMutex
*LOCK() {
280 static UMutex
*m
= STATIC_NEW(UMutex
);
284 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat
)
286 SimpleDateFormat::NSOverride::~NSOverride() {
293 void SimpleDateFormat::NSOverride::free() {
294 NSOverride
*cur
= this;
296 NSOverride
*next_temp
= cur
->next
;
302 // no matter what the locale's default number format looked like, we want
303 // to modify it so that it doesn't use thousands separators, doesn't always
304 // show the decimal point, and recognizes integers only when parsing
305 static void fixNumberFormatForDates(NumberFormat
&nf
) {
306 // Use new group setter equivalent to
307 // setGroupingUsed(FALSE);
308 // setDecimalSeparatorAlwaysShown(FALSE);
309 // setParseIntegerOnly(TRUE);
310 // setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
311 nf
.setDateSettings(); // Apple rdar://50064762
314 static const SharedNumberFormat
*createSharedNumberFormat(
315 NumberFormat
*nfToAdopt
) {
316 fixNumberFormatForDates(*nfToAdopt
);
317 const SharedNumberFormat
*result
= new SharedNumberFormat(nfToAdopt
);
318 if (result
== NULL
) {
324 static const SharedNumberFormat
*createSharedNumberFormat(
325 const Locale
&loc
, UErrorCode
&status
) {
326 NumberFormat
*nf
= NumberFormat::createInstance(loc
, status
);
327 if (U_FAILURE(status
)) {
330 const SharedNumberFormat
*result
= createSharedNumberFormat(nf
);
331 if (result
== NULL
) {
332 status
= U_MEMORY_ALLOCATION_ERROR
;
337 static const SharedNumberFormat
**allocSharedNumberFormatters() {
338 const SharedNumberFormat
**result
= (const SharedNumberFormat
**)
339 uprv_malloc(UDAT_FIELD_COUNT
* sizeof(const SharedNumberFormat
*));
340 if (result
== NULL
) {
343 for (int32_t i
= 0; i
< UDAT_FIELD_COUNT
; ++i
) {
349 static void freeSharedNumberFormatters(const SharedNumberFormat
** list
) {
350 for (int32_t i
= 0; i
< UDAT_FIELD_COUNT
; ++i
) {
351 SharedObject::clearPtr(list
[i
]);
356 const NumberFormat
*SimpleDateFormat::getNumberFormatByIndex(
357 UDateFormatField index
) const {
358 if (fSharedNumberFormatters
== NULL
||
359 fSharedNumberFormatters
[index
] == NULL
) {
360 return fNumberFormat
;
362 return &(**fSharedNumberFormatters
[index
]);
365 //----------------------------------------------------------------------
367 SimpleDateFormat::~SimpleDateFormat()
370 if (fSharedNumberFormatters
) {
371 freeSharedNumberFormatters(fSharedNumberFormatters
);
373 if (fTimeZoneFormat
) {
374 delete fTimeZoneFormat
;
376 freeFastNumberFormatters();
378 #if !UCONFIG_NO_BREAK_ITERATION
379 delete fCapitalizationBrkIter
;
383 //----------------------------------------------------------------------
385 SimpleDateFormat::SimpleDateFormat(UErrorCode
& status
)
386 : fLocale(Locale::getDefault()),
388 fTimeZoneFormat(NULL
),
389 fSharedNumberFormatters(NULL
),
390 fCapitalizationBrkIter(NULL
)
392 initializeBooleanAttributes();
393 construct(kShort
, (EStyle
) (kShort
+ kDateOffset
), fLocale
, status
);
394 initializeDefaultCentury();
397 //----------------------------------------------------------------------
399 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
402 fLocale(Locale::getDefault()),
404 fTimeZoneFormat(NULL
),
405 fSharedNumberFormatters(NULL
),
406 fCapitalizationBrkIter(NULL
)
408 fDateOverride
.setToBogus();
409 fTimeOverride
.setToBogus();
410 initializeBooleanAttributes();
411 initializeCalendar(NULL
,fLocale
,status
);
412 fSymbols
= DateFormatSymbols::createForLocale(fLocale
, status
);
413 initialize(fLocale
, status
);
414 initializeDefaultCentury();
418 //----------------------------------------------------------------------
420 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
421 const UnicodeString
& override
,
424 fLocale(Locale::getDefault()),
426 fTimeZoneFormat(NULL
),
427 fSharedNumberFormatters(NULL
),
428 fCapitalizationBrkIter(NULL
)
430 fDateOverride
.setTo(override
);
431 fTimeOverride
.setToBogus();
432 initializeBooleanAttributes();
433 initializeCalendar(NULL
,fLocale
,status
);
434 fSymbols
= DateFormatSymbols::createForLocale(fLocale
, status
);
435 initialize(fLocale
, status
);
436 initializeDefaultCentury();
438 processOverrideString(fLocale
,override
,kOvrStrBoth
,status
);
442 //----------------------------------------------------------------------
444 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
445 const Locale
& locale
,
449 fTimeZoneFormat(NULL
),
450 fSharedNumberFormatters(NULL
),
451 fCapitalizationBrkIter(NULL
)
454 fDateOverride
.setToBogus();
455 fTimeOverride
.setToBogus();
456 initializeBooleanAttributes();
458 initializeCalendar(NULL
,fLocale
,status
);
459 fSymbols
= DateFormatSymbols::createForLocale(fLocale
, status
);
460 initialize(fLocale
, status
);
461 initializeDefaultCentury();
464 //----------------------------------------------------------------------
466 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
467 const UnicodeString
& override
,
468 const Locale
& locale
,
472 fTimeZoneFormat(NULL
),
473 fSharedNumberFormatters(NULL
),
474 fCapitalizationBrkIter(NULL
)
477 fDateOverride
.setTo(override
);
478 fTimeOverride
.setToBogus();
479 initializeBooleanAttributes();
481 initializeCalendar(NULL
,fLocale
,status
);
482 fSymbols
= DateFormatSymbols::createForLocale(fLocale
, status
);
483 initialize(fLocale
, status
);
484 initializeDefaultCentury();
486 processOverrideString(locale
,override
,kOvrStrBoth
,status
);
490 //----------------------------------------------------------------------
492 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
493 DateFormatSymbols
* symbolsToAdopt
,
496 fLocale(Locale::getDefault()),
497 fSymbols(symbolsToAdopt
),
498 fTimeZoneFormat(NULL
),
499 fSharedNumberFormatters(NULL
),
500 fCapitalizationBrkIter(NULL
)
503 fDateOverride
.setToBogus();
504 fTimeOverride
.setToBogus();
505 initializeBooleanAttributes();
507 initializeCalendar(NULL
,fLocale
,status
);
508 initialize(fLocale
, status
);
509 initializeDefaultCentury();
512 //----------------------------------------------------------------------
514 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
515 const DateFormatSymbols
& symbols
,
518 fLocale(Locale::getDefault()),
519 fSymbols(new DateFormatSymbols(symbols
)),
520 fTimeZoneFormat(NULL
),
521 fSharedNumberFormatters(NULL
),
522 fCapitalizationBrkIter(NULL
)
525 fDateOverride
.setToBogus();
526 fTimeOverride
.setToBogus();
527 initializeBooleanAttributes();
529 initializeCalendar(NULL
, fLocale
, status
);
530 initialize(fLocale
, status
);
531 initializeDefaultCentury();
534 //----------------------------------------------------------------------
536 // Not for public consumption; used by DateFormat
537 SimpleDateFormat::SimpleDateFormat(EStyle timeStyle
,
539 const Locale
& locale
,
543 fTimeZoneFormat(NULL
),
544 fSharedNumberFormatters(NULL
),
545 fCapitalizationBrkIter(NULL
)
547 initializeBooleanAttributes();
548 construct(timeStyle
, dateStyle
, fLocale
, status
);
549 if(U_SUCCESS(status
)) {
550 initializeDefaultCentury();
554 //----------------------------------------------------------------------
557 * Not for public consumption; used by DateFormat. This constructor
558 * never fails. If the resource data is not available, it uses the
559 * the last resort symbols.
561 SimpleDateFormat::SimpleDateFormat(const Locale
& locale
,
563 : fPattern(gDefaultPattern
),
566 fTimeZoneFormat(NULL
),
567 fSharedNumberFormatters(NULL
),
568 fCapitalizationBrkIter(NULL
)
570 if (U_FAILURE(status
)) return;
571 initializeBooleanAttributes();
572 initializeCalendar(NULL
, fLocale
, status
);
573 fSymbols
= DateFormatSymbols::createForLocale(fLocale
, status
);
574 if (U_FAILURE(status
))
576 status
= U_ZERO_ERROR
;
578 // This constructor doesn't fail; it uses last resort data
579 fSymbols
= new DateFormatSymbols(status
);
582 status
= U_MEMORY_ALLOCATION_ERROR
;
587 fDateOverride
.setToBogus();
588 fTimeOverride
.setToBogus();
590 initialize(fLocale
, status
);
591 if(U_SUCCESS(status
)) {
592 initializeDefaultCentury();
596 //----------------------------------------------------------------------
598 SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat
& other
)
600 fLocale(other
.fLocale
),
602 fTimeZoneFormat(NULL
),
603 fSharedNumberFormatters(NULL
),
604 fCapitalizationBrkIter(NULL
)
606 initializeBooleanAttributes();
610 //----------------------------------------------------------------------
612 SimpleDateFormat
& SimpleDateFormat::operator=(const SimpleDateFormat
& other
)
614 if (this == &other
) {
617 freeFastNumberFormatters(); // deletes refs to fNumberFormat's symbols
618 DateFormat::operator=(other
);
619 fDateOverride
= other
.fDateOverride
;
620 fTimeOverride
= other
.fTimeOverride
;
626 fSymbols
= new DateFormatSymbols(*other
.fSymbols
);
628 fDefaultCenturyStart
= other
.fDefaultCenturyStart
;
629 fDefaultCenturyStartYear
= other
.fDefaultCenturyStartYear
;
630 fHaveDefaultCentury
= other
.fHaveDefaultCentury
;
632 fPattern
= other
.fPattern
;
633 fHasMinute
= other
.fHasMinute
;
634 fHasSecond
= other
.fHasSecond
;
636 fLocale
= other
.fLocale
;
637 // TimeZoneFormat can now be set independently via setter.
638 // If it is NULL, it will be lazily initialized from locale
639 delete fTimeZoneFormat
;
640 fTimeZoneFormat
= NULL
;
641 if (other
.fTimeZoneFormat
) {
642 fTimeZoneFormat
= new TimeZoneFormat(*other
.fTimeZoneFormat
);
645 #if !UCONFIG_NO_BREAK_ITERATION
646 if (other
.fCapitalizationBrkIter
!= NULL
) {
647 fCapitalizationBrkIter
= (other
.fCapitalizationBrkIter
)->clone();
651 if (fSharedNumberFormatters
!= NULL
) {
652 freeSharedNumberFormatters(fSharedNumberFormatters
);
653 fSharedNumberFormatters
= NULL
;
655 if (other
.fSharedNumberFormatters
!= NULL
) {
656 fSharedNumberFormatters
= allocSharedNumberFormatters();
657 if (fSharedNumberFormatters
) {
658 for (int32_t i
= 0; i
< UDAT_FIELD_COUNT
; ++i
) {
659 SharedObject::copyPtr(
660 other
.fSharedNumberFormatters
[i
],
661 fSharedNumberFormatters
[i
]);
666 UErrorCode localStatus
= U_ZERO_ERROR
;
667 initFastNumberFormatters(localStatus
);
672 //----------------------------------------------------------------------
675 SimpleDateFormat::clone() const
677 return new SimpleDateFormat(*this);
680 //----------------------------------------------------------------------
683 SimpleDateFormat::operator==(const Format
& other
) const
685 if (DateFormat::operator==(other
)) {
686 // The DateFormat::operator== check for fCapitalizationContext equality above
687 // is sufficient to check equality of all derived context-related data.
688 // DateFormat::operator== guarantees following cast is safe
689 SimpleDateFormat
* that
= (SimpleDateFormat
*)&other
;
690 return (fPattern
== that
->fPattern
&&
691 fSymbols
!= NULL
&& // Check for pathological object
692 that
->fSymbols
!= NULL
&& // Check for pathological object
693 *fSymbols
== *that
->fSymbols
&&
694 fHaveDefaultCentury
== that
->fHaveDefaultCentury
&&
695 fDefaultCenturyStart
== that
->fDefaultCenturyStart
&&
696 // Check fTimeZoneFormat, it can be set independently via setter
697 ((fTimeZoneFormat
== NULL
&& that
->fTimeZoneFormat
== NULL
) ||
698 (fTimeZoneFormat
!= NULL
&& that
->fTimeZoneFormat
!= NULL
&& *fTimeZoneFormat
== *that
->fTimeZoneFormat
)) &&
699 // Check override strings (these also indicate any relevant
700 // differences in fNumberFormatters, fOverrideList)
701 fDateOverride
== that
->fDateOverride
&&
702 fTimeOverride
== that
->fTimeOverride
);
707 //----------------------------------------------------------------------
708 static const UChar
* timeSkeletons
[4] = {
709 u
"jmmsszzzz", // kFull
715 enum { kBaseNameMax
= ULOC_LANG_CAPACITY
+ ULOC_SCRIPT_CAPACITY
+ ULOC_COUNTRY_CAPACITY
}; // includes separators and 0 term
717 void SimpleDateFormat::construct(EStyle timeStyle
,
719 const Locale
& locale
,
722 // called by several constructors to load pattern data from the resources
723 if (U_FAILURE(status
)) return;
725 // We will need the calendar to know what type of symbols to load.
726 initializeCalendar(NULL
, locale
, status
);
727 if (U_FAILURE(status
)) return;
729 // Load date time patterns directly from resources.
730 const char* cType
= fCalendar
? fCalendar
->getType() : NULL
;
731 LocalUResourceBundlePointer
bundle(ures_open(NULL
, locale
.getBaseName(), &status
));
732 if (U_FAILURE(status
)) return;
734 UBool cTypeIsGregorian
= TRUE
;
735 LocalUResourceBundlePointer dateTimePatterns
;
736 if (cType
!= NULL
&& uprv_strcmp(cType
, "gregorian") != 0) {
737 CharString
resourcePath("calendar/", status
);
738 resourcePath
.append(cType
, status
).append("/DateTimePatterns", status
);
739 dateTimePatterns
.adoptInstead(
740 ures_getByKeyWithFallback(bundle
.getAlias(), resourcePath
.data(),
741 (UResourceBundle
*)NULL
, &status
));
742 cTypeIsGregorian
= FALSE
;
745 // Check for "gregorian" fallback.
746 if (cTypeIsGregorian
|| status
== U_MISSING_RESOURCE_ERROR
) {
747 status
= U_ZERO_ERROR
;
748 dateTimePatterns
.adoptInstead(
749 ures_getByKeyWithFallback(bundle
.getAlias(),
750 "calendar/gregorian/DateTimePatterns",
751 (UResourceBundle
*)NULL
, &status
));
753 if (U_FAILURE(status
)) return;
755 LocalUResourceBundlePointer currentBundle
;
757 if (ures_getSize(dateTimePatterns
.getAlias()) <= kDateTime
)
759 status
= U_INVALID_FORMAT_ERROR
;
763 setLocaleIDs(ures_getLocaleByType(dateTimePatterns
.getAlias(), ULOC_VALID_LOCALE
, &status
),
764 ures_getLocaleByType(dateTimePatterns
.getAlias(), ULOC_ACTUAL_LOCALE
, &status
));
766 // create a symbols object from the locale
767 fSymbols
= DateFormatSymbols::createForLocale(locale
, status
);
768 if (U_FAILURE(status
)) return;
771 status
= U_MEMORY_ALLOCATION_ERROR
;
775 const UChar
*resStr
,*ovrStr
;
776 int32_t resStrLen
,ovrStrLen
= 0;
777 fDateOverride
.setToBogus();
778 fTimeOverride
.setToBogus();
780 UnicodeString timePattern
;
781 if (timeStyle
>= kFull
&& timeStyle
<= kShort
) {
782 const char* baseLoc
= locale
.getBaseName();
783 if (baseLoc
!=NULL
&& baseLoc
[0]!=0 && uprv_strcmp(baseLoc
,"und")!=0) {
784 UErrorCode useStatus
= U_ZERO_ERROR
;
785 const char* validLoc
= getLocaleID(ULOC_VALID_LOCALE
, useStatus
);
786 if (U_SUCCESS(useStatus
) && uprv_strcmp(validLoc
,baseLoc
)!=0) {
787 char minLoc
[kBaseNameMax
];
788 uloc_minimizeSubtags(baseLoc
, minLoc
, kBaseNameMax
, &useStatus
);
789 minLoc
[kBaseNameMax
-1] = 0; // ensure zero term
790 const char* actualLoc
= getLocaleID(ULOC_ACTUAL_LOCALE
, useStatus
);
791 if (U_SUCCESS(useStatus
) && uprv_strcmp(actualLoc
,minLoc
)!=0) {
792 // The standard time formats may have the wrong time cycle, because:
793 // * the valid locale is not the same as the base locale, or
794 // * the actual locale the patterns are coming from is not the same
795 // as the minimized locale.
796 // We could *also* check whether they do actually have a mismatch with
797 // the time cycle preferences for the region, but that is a lot more
798 // work for little or no additional benefit, since just going ahead
799 // and always synthesizing the time format as per the following should
800 // create a locale-appropriate pattern with cycle that matches the
801 // region preferences anyway (for completely unsupported languages,
802 // this will use root patterns for the appropriate cycle for the
803 // likely subtags resion).
804 LocalPointer
<DateTimePatternGenerator
> dtpg(DateTimePatternGenerator::createInstance(locale
, useStatus
, TRUE
));
805 if (U_SUCCESS(useStatus
)) {
806 UnicodeString
timeSkeleton(TRUE
, timeSkeletons
[timeStyle
], -1);
807 timePattern
= dtpg
->getBestPattern(timeSkeleton
, useStatus
);
808 #if DEBUG_SYNTHETIC_TIMEFMTS
809 if (timePattern
.length() != 0) {
811 timePattern
.extract(0,timePattern
.length(),bbuf
,32);
812 printf("\n## for locale %s, validLoc %s, minLoc %s, actualLoc %s, synth timePat %s\n", locale
.getName(), validLoc
, minLoc
, actualLoc
, bbuf
);
821 // if the pattern should include both date and time information, use the date/time
822 // pattern string as a guide to tell use how to glue together the appropriate date
823 // and time pattern strings.
824 if ((timeStyle
!= kNone
) && (dateStyle
!= kNone
))
826 UnicodeString
tempus1(timePattern
);
827 if (tempus1
.length() == 0) {
828 currentBundle
.adoptInstead(
829 ures_getByIndex(dateTimePatterns
.getAlias(), (int32_t)timeStyle
, NULL
, &status
));
830 if (U_FAILURE(status
)) {
831 status
= U_INVALID_FORMAT_ERROR
;
834 switch (ures_getType(currentBundle
.getAlias())) {
836 resStr
= ures_getString(currentBundle
.getAlias(), &resStrLen
, &status
);
840 resStr
= ures_getStringByIndex(currentBundle
.getAlias(), 0, &resStrLen
, &status
);
841 ovrStr
= ures_getStringByIndex(currentBundle
.getAlias(), 1, &ovrStrLen
, &status
);
842 fTimeOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
846 status
= U_INVALID_FORMAT_ERROR
;
851 tempus1
.setTo(TRUE
, resStr
, resStrLen
);
854 currentBundle
.adoptInstead(
855 ures_getByIndex(dateTimePatterns
.getAlias(), (int32_t)dateStyle
, NULL
, &status
));
856 if (U_FAILURE(status
)) {
857 status
= U_INVALID_FORMAT_ERROR
;
860 switch (ures_getType(currentBundle
.getAlias())) {
862 resStr
= ures_getString(currentBundle
.getAlias(), &resStrLen
, &status
);
866 resStr
= ures_getStringByIndex(currentBundle
.getAlias(), 0, &resStrLen
, &status
);
867 ovrStr
= ures_getStringByIndex(currentBundle
.getAlias(), 1, &ovrStrLen
, &status
);
868 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
872 status
= U_INVALID_FORMAT_ERROR
;
877 UnicodeString
tempus2(TRUE
, resStr
, resStrLen
);
879 int32_t glueIndex
= kDateTime
;
880 int32_t patternsSize
= ures_getSize(dateTimePatterns
.getAlias());
881 if (patternsSize
>= (kDateTimeOffset
+ kShort
+ 1)) {
882 // Get proper date time format
883 glueIndex
= (int32_t)(kDateTimeOffset
+ (dateStyle
- kDateOffset
));
886 resStr
= ures_getStringByIndex(dateTimePatterns
.getAlias(), glueIndex
, &resStrLen
, &status
);
887 SimpleFormatter(UnicodeString(TRUE
, resStr
, resStrLen
), 2, 2, status
).
888 format(tempus1
, tempus2
, fPattern
, status
);
890 // if the pattern includes just time data or just date date, load the appropriate
891 // pattern string from the resources
892 // setTo() - see DateFormatSymbols::assignArray comments
893 else if (timeStyle
!= kNone
) {
894 fPattern
.setTo(timePattern
);
895 if (fPattern
.length() == 0) {
896 currentBundle
.adoptInstead(
897 ures_getByIndex(dateTimePatterns
.getAlias(), (int32_t)timeStyle
, NULL
, &status
));
898 if (U_FAILURE(status
)) {
899 status
= U_INVALID_FORMAT_ERROR
;
902 switch (ures_getType(currentBundle
.getAlias())) {
904 resStr
= ures_getString(currentBundle
.getAlias(), &resStrLen
, &status
);
908 resStr
= ures_getStringByIndex(currentBundle
.getAlias(), 0, &resStrLen
, &status
);
909 ovrStr
= ures_getStringByIndex(currentBundle
.getAlias(), 1, &ovrStrLen
, &status
);
910 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
914 status
= U_INVALID_FORMAT_ERROR
;
918 fPattern
.setTo(TRUE
, resStr
, resStrLen
);
921 else if (dateStyle
!= kNone
) {
922 currentBundle
.adoptInstead(
923 ures_getByIndex(dateTimePatterns
.getAlias(), (int32_t)dateStyle
, NULL
, &status
));
924 if (U_FAILURE(status
)) {
925 status
= U_INVALID_FORMAT_ERROR
;
928 switch (ures_getType(currentBundle
.getAlias())) {
930 resStr
= ures_getString(currentBundle
.getAlias(), &resStrLen
, &status
);
934 resStr
= ures_getStringByIndex(currentBundle
.getAlias(), 0, &resStrLen
, &status
);
935 ovrStr
= ures_getStringByIndex(currentBundle
.getAlias(), 1, &ovrStrLen
, &status
);
936 fDateOverride
.setTo(TRUE
, ovrStr
, ovrStrLen
);
940 status
= U_INVALID_FORMAT_ERROR
;
944 fPattern
.setTo(TRUE
, resStr
, resStrLen
);
947 // and if it includes _neither_, that's an error
949 status
= U_INVALID_FORMAT_ERROR
;
951 // finally, finish initializing by creating a Calendar and a NumberFormat
952 initialize(locale
, status
);
955 //----------------------------------------------------------------------
958 SimpleDateFormat::initializeCalendar(TimeZone
* adoptZone
, const Locale
& locale
, UErrorCode
& status
)
960 if(!U_FAILURE(status
)) {
961 fCalendar
= Calendar::createInstance(adoptZone
?adoptZone
:TimeZone::createDefault(), locale
, status
);
967 SimpleDateFormat::initialize(const Locale
& locale
,
970 if (U_FAILURE(status
)) return;
972 parsePattern(); // Need this before initNumberFormatters(), to set fHasHanYearChar
974 // If the locale has @[....]numbers=hanidays we want to *delete* that (so it
975 // it is not used for every field) and then set fDateOverride to "d=hanidays"
976 // (as with std formats for zh@calendar=chinese) to use hanidays for d field.
977 static const UChar hanidaysOverride
[] = {0x64,0x3D,0x68,0x61,0x6E,0x69,0x64,0x61,0x79,0x73,0}; // "d=hanidays"
978 char numbersValue
[ULOC_KEYWORDS_CAPACITY
];
979 UErrorCode numbersStatus
= U_ZERO_ERROR
;
980 Locale
localeNoHanidays(locale
);
981 int32_t numbersLen
= localeNoHanidays
.getKeywordValue("numbers", numbersValue
, ULOC_KEYWORDS_CAPACITY
, numbersStatus
);
982 if ( U_SUCCESS(numbersStatus
) && numbersLen
> 0 ) {
983 if ( uprv_strcmp(numbersValue
, "hanidays") == 0 ) {
984 localeNoHanidays
.setKeywordValue("numbers", NULL
, numbersStatus
);
985 fDateOverride
.setTo(hanidaysOverride
,-1);
988 // Simple-minded hack to force Gannen year numbering for ja@calendar=japanese
989 // if format is non-numeric (includes 年) and fDateOverride is not already specified.
990 // Now this does get updated if applyPattern subsequently changes the pattern type.
991 if (fDateOverride
.isBogus() && fHasHanYearChar
&&
992 fCalendar
!= nullptr && uprv_strcmp(fCalendar
->getType(),"japanese") == 0 &&
993 uprv_strcmp(fLocale
.getLanguage(),"ja") == 0) {
994 fDateOverride
.setTo(u
"y=jpanyear", -1);
997 // We don't need to check that the row count is >= 1, since all 2d arrays have at
999 fNumberFormat
= NumberFormat::createInstance(localeNoHanidays
, status
);
1000 if (fNumberFormat
!= NULL
&& U_SUCCESS(status
))
1002 fixNumberFormatForDates(*fNumberFormat
);
1003 //fNumberFormat->setLenient(TRUE); // Java uses a custom DateNumberFormat to format/parse
1005 initNumberFormatters(locale
, status
);
1006 initFastNumberFormatters(status
);
1009 else if (U_SUCCESS(status
))
1011 status
= U_MISSING_RESOURCE_ERROR
;
1015 /* Initialize the fields we use to disambiguate ambiguous years. Separate
1016 * so we can call it from readObject().
1018 void SimpleDateFormat::initializeDefaultCentury()
1021 fHaveDefaultCentury
= fCalendar
->haveDefaultCentury();
1022 if(fHaveDefaultCentury
) {
1023 fDefaultCenturyStart
= fCalendar
->defaultCenturyStart();
1024 fDefaultCenturyStartYear
= fCalendar
->defaultCenturyStartYear();
1026 fDefaultCenturyStart
= DBL_MIN
;
1027 fDefaultCenturyStartYear
= -1;
1033 * Initialize the boolean attributes. Separate so we can call it from all constructors.
1035 void SimpleDateFormat::initializeBooleanAttributes()
1037 UErrorCode status
= U_ZERO_ERROR
;
1039 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, true, status
);
1040 setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, true, status
);
1041 setBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH
, true, status
);
1042 setBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, true, status
);
1045 /* Define one-century window into which to disambiguate dates using
1046 * two-digit years. Make public in JDK 1.2.
1048 void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate
, UErrorCode
& status
)
1050 if(U_FAILURE(status
)) {
1054 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1058 fCalendar
->setTime(startDate
, status
);
1059 if(U_SUCCESS(status
)) {
1060 fHaveDefaultCentury
= TRUE
;
1061 fDefaultCenturyStart
= startDate
;
1062 fDefaultCenturyStartYear
= fCalendar
->get(UCAL_YEAR
, status
);
1066 //----------------------------------------------------------------------
1069 SimpleDateFormat::format(Calendar
& cal
, UnicodeString
& appendTo
, FieldPosition
& pos
) const
1071 UErrorCode status
= U_ZERO_ERROR
;
1072 FieldPositionOnlyHandler
handler(pos
);
1073 return _format(cal
, appendTo
, handler
, status
);
1076 //----------------------------------------------------------------------
1079 SimpleDateFormat::format(Calendar
& cal
, UnicodeString
& appendTo
,
1080 FieldPositionIterator
* posIter
, UErrorCode
& status
) const
1082 FieldPositionIteratorHandler
handler(posIter
, status
);
1083 return _format(cal
, appendTo
, handler
, status
);
1086 //----------------------------------------------------------------------
1089 SimpleDateFormat::_format(Calendar
& cal
, UnicodeString
& appendTo
,
1090 FieldPositionHandler
& handler
, UErrorCode
& status
) const
1092 if ( U_FAILURE(status
) ) {
1095 Calendar
* workCal
= &cal
;
1096 Calendar
* calClone
= NULL
;
1097 if (&cal
!= fCalendar
&& uprv_strcmp(cal
.getType(), fCalendar
->getType()) != 0) {
1098 // Different calendar type
1099 // We use the time and time zone from the input calendar, but
1100 // do not use the input calendar for field calculation.
1101 calClone
= fCalendar
->clone();
1102 if (calClone
!= NULL
) {
1103 UDate t
= cal
.getTime(status
);
1104 calClone
->setTime(t
, status
);
1105 calClone
->setTimeZone(cal
.getTimeZone());
1108 status
= U_MEMORY_ALLOCATION_ERROR
;
1113 UBool inQuote
= FALSE
;
1116 int32_t fieldNum
= 0;
1117 UDisplayContext capitalizationContext
= getContext(UDISPCTX_TYPE_CAPITALIZATION
, status
);
1119 // loop through the pattern string character by character
1120 for (int32_t i
= 0; i
< fPattern
.length() && U_SUCCESS(status
); ++i
) {
1121 UChar ch
= fPattern
[i
];
1123 // Use subFormat() to format a repeated pattern character
1124 // when a different pattern or non-pattern character is seen
1125 if (ch
!= prevCh
&& count
> 0) {
1126 subFormat(appendTo
, prevCh
, count
, capitalizationContext
, fieldNum
++, handler
, *workCal
, status
);
1130 // Consecutive single quotes are a single quote literal,
1131 // either outside of quotes or between quotes
1132 if ((i
+1) < fPattern
.length() && fPattern
[i
+1] == QUOTE
) {
1133 appendTo
+= (UChar
)QUOTE
;
1136 inQuote
= ! inQuote
;
1139 else if (!inQuote
&& isSyntaxChar(ch
)) {
1140 // ch is a date-time pattern character to be interpreted
1141 // by subFormat(); count the number of times it is repeated
1146 // Append quoted characters and unquoted non-pattern characters
1151 // Format the last item in the pattern, if any
1153 subFormat(appendTo
, prevCh
, count
, capitalizationContext
, fieldNum
++, handler
, *workCal
, status
);
1156 if (calClone
!= NULL
) {
1163 //----------------------------------------------------------------------
1165 /* Map calendar field into calendar field level.
1166 * the larger the level, the smaller the field unit.
1167 * For example, UCAL_ERA level is 0, UCAL_YEAR level is 10,
1168 * UCAL_MONTH level is 20.
1169 * NOTE: if new fields adds in, the table needs to update.
1172 SimpleDateFormat::fgCalendarFieldToLevel
[] =
1176 /*dDEF*/ 30, 20, 30, 30,
1177 /*ahHm*/ 40, 50, 50, 60,
1184 int32_t SimpleDateFormat::getLevelFromChar(UChar ch
) {
1185 // Map date field LETTER into calendar field level.
1186 // the larger the level, the smaller the field unit.
1187 // NOTE: if new fields adds in, the table needs to update.
1188 static const int32_t mapCharToLevel
[] = {
1189 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1191 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1192 // ! " # $ % & ' ( ) * + , - . /
1193 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1194 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1195 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
1196 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1,
1198 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
1199 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1201 // @ A B C D E F G H I J K L M N O
1202 -1, 40, -1, -1, 20, 30, 30, 0, 50, -1, -1, 50, 20, 20, -1, 0,
1203 // P Q R S T U V W X Y Z [ \ ] ^ _
1204 -1, 20, -1, 80, -1, 10, 0, 30, 0, 10, 0, -1, -1, -1, -1, -1,
1205 // ` a b c d e f g h i j k l m n o
1206 -1, 40, -1, 30, 30, 30, -1, 0, 50, -1, -1, 50, 0, 60, -1, -1,
1207 // p q r s t u v w x y z { | } ~
1208 -1, 20, 10, 70, -1, 10, 0, 20, 0, 10, 0, -1, -1, -1, -1, -1
1211 return ch
< UPRV_LENGTHOF(mapCharToLevel
) ? mapCharToLevel
[ch
] : -1;
1214 UBool
SimpleDateFormat::isSyntaxChar(UChar ch
) {
1215 static const UBool mapCharToIsSyntax
[] = {
1217 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1219 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1221 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1223 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1225 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1227 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1229 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1230 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1232 FALSE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1235 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1238 FALSE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1240 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1242 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1244 TRUE
, TRUE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1246 FALSE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1248 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1250 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1252 TRUE
, TRUE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
1255 return ch
< UPRV_LENGTHOF(mapCharToIsSyntax
) ? mapCharToIsSyntax
[ch
] : FALSE
;
1258 // Map index into pattern character string to Calendar field number.
1259 const UCalendarDateFields
1260 SimpleDateFormat::fgPatternIndexToCalendarField
[] =
1262 /*GyM*/ UCAL_ERA
, UCAL_YEAR
, UCAL_MONTH
,
1263 /*dkH*/ UCAL_DATE
, UCAL_HOUR_OF_DAY
, UCAL_HOUR_OF_DAY
,
1264 /*msS*/ UCAL_MINUTE
, UCAL_SECOND
, UCAL_MILLISECOND
,
1265 /*EDF*/ UCAL_DAY_OF_WEEK
, UCAL_DAY_OF_YEAR
, UCAL_DAY_OF_WEEK_IN_MONTH
,
1266 /*wWa*/ UCAL_WEEK_OF_YEAR
, UCAL_WEEK_OF_MONTH
, UCAL_AM_PM
,
1267 /*hKz*/ UCAL_HOUR
, UCAL_HOUR
, UCAL_ZONE_OFFSET
,
1268 /*Yeu*/ UCAL_YEAR_WOY
, UCAL_DOW_LOCAL
, UCAL_EXTENDED_YEAR
,
1269 /*gAZ*/ UCAL_JULIAN_DAY
, UCAL_MILLISECONDS_IN_DAY
, UCAL_ZONE_OFFSET
,
1270 /*v*/ UCAL_ZONE_OFFSET
,
1271 /*c*/ UCAL_DOW_LOCAL
,
1275 /*V*/ UCAL_ZONE_OFFSET
,
1277 /*O*/ UCAL_ZONE_OFFSET
,
1278 /*Xx*/ UCAL_ZONE_OFFSET
, UCAL_ZONE_OFFSET
,
1279 /*r*/ UCAL_EXTENDED_YEAR
,
1280 /*bB*/ UCAL_FIELD_COUNT
, UCAL_FIELD_COUNT
, // no mappings to calendar fields
1281 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1282 /*:*/ UCAL_FIELD_COUNT
, /* => no useful mapping to any calendar field */
1284 /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/ UCAL_FIELD_COUNT
, /* => no useful mapping to any calendar field */
1288 // Map index into pattern character string to DateFormat field number
1289 const UDateFormatField
1290 SimpleDateFormat::fgPatternIndexToDateFormatField
[] = {
1291 /*GyM*/ UDAT_ERA_FIELD
, UDAT_YEAR_FIELD
, UDAT_MONTH_FIELD
,
1292 /*dkH*/ UDAT_DATE_FIELD
, UDAT_HOUR_OF_DAY1_FIELD
, UDAT_HOUR_OF_DAY0_FIELD
,
1293 /*msS*/ UDAT_MINUTE_FIELD
, UDAT_SECOND_FIELD
, UDAT_FRACTIONAL_SECOND_FIELD
,
1294 /*EDF*/ UDAT_DAY_OF_WEEK_FIELD
, UDAT_DAY_OF_YEAR_FIELD
, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD
,
1295 /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD
, UDAT_WEEK_OF_MONTH_FIELD
, UDAT_AM_PM_FIELD
,
1296 /*hKz*/ UDAT_HOUR1_FIELD
, UDAT_HOUR0_FIELD
, UDAT_TIMEZONE_FIELD
,
1297 /*Yeu*/ UDAT_YEAR_WOY_FIELD
, UDAT_DOW_LOCAL_FIELD
, UDAT_EXTENDED_YEAR_FIELD
,
1298 /*gAZ*/ UDAT_JULIAN_DAY_FIELD
, UDAT_MILLISECONDS_IN_DAY_FIELD
, UDAT_TIMEZONE_RFC_FIELD
,
1299 /*v*/ UDAT_TIMEZONE_GENERIC_FIELD
,
1300 /*c*/ UDAT_STANDALONE_DAY_FIELD
,
1301 /*L*/ UDAT_STANDALONE_MONTH_FIELD
,
1302 /*Q*/ UDAT_QUARTER_FIELD
,
1303 /*q*/ UDAT_STANDALONE_QUARTER_FIELD
,
1304 /*V*/ UDAT_TIMEZONE_SPECIAL_FIELD
,
1305 /*U*/ UDAT_YEAR_NAME_FIELD
,
1306 /*O*/ UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
,
1307 /*Xx*/ UDAT_TIMEZONE_ISO_FIELD
, UDAT_TIMEZONE_ISO_LOCAL_FIELD
,
1308 /*r*/ UDAT_RELATED_YEAR_FIELD
,
1309 /*bB*/ UDAT_AM_PM_MIDNIGHT_NOON_FIELD
, UDAT_FLEXIBLE_DAY_PERIOD_FIELD
,
1310 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1311 /*:*/ UDAT_TIME_SEPARATOR_FIELD
,
1313 /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/ UDAT_TIME_SEPARATOR_FIELD
,
1317 //----------------------------------------------------------------------
1320 * Append symbols[value] to dst. Make sure the array index is not out
1324 _appendSymbol(UnicodeString
& dst
,
1326 const UnicodeString
* symbols
,
1327 int32_t symbolsCount
) {
1328 U_ASSERT(0 <= value
&& value
< symbolsCount
);
1329 if (0 <= value
&& value
< symbolsCount
) {
1330 dst
+= symbols
[value
];
1335 _appendSymbolWithMonthPattern(UnicodeString
& dst
, int32_t value
, const UnicodeString
* symbols
, int32_t symbolsCount
,
1336 const UnicodeString
* monthPattern
, UErrorCode
& status
) {
1337 U_ASSERT(0 <= value
&& value
< symbolsCount
);
1338 if (0 <= value
&& value
< symbolsCount
) {
1339 if (monthPattern
== NULL
) {
1340 dst
+= symbols
[value
];
1342 SimpleFormatter(*monthPattern
, 1, 1, status
).format(symbols
[value
], dst
, status
);
1347 //----------------------------------------------------------------------
1349 static number::LocalizedNumberFormatter
*
1350 createFastFormatter(const DecimalFormat
* df
, int32_t minInt
, int32_t maxInt
) {
1351 return new number::LocalizedNumberFormatter(
1352 df
->toNumberFormatter()
1353 .integerWidth(number::IntegerWidth::zeroFillTo(minInt
).truncateAt(maxInt
)));
1356 void SimpleDateFormat::initFastNumberFormatters(UErrorCode
& status
) {
1357 if (U_FAILURE(status
)) {
1360 auto* df
= dynamic_cast<DecimalFormat
*>(fNumberFormat
);
1361 if (df
== nullptr) {
1364 df
->setDFSShallowCopy(TRUE
);
1365 fFastNumberFormatters
[SMPDTFMT_NF_1x10
] = createFastFormatter(df
, 1, 10);
1366 fFastNumberFormatters
[SMPDTFMT_NF_2x10
] = createFastFormatter(df
, 2, 10);
1367 fFastNumberFormatters
[SMPDTFMT_NF_3x10
] = createFastFormatter(df
, 3, 10);
1368 fFastNumberFormatters
[SMPDTFMT_NF_4x10
] = createFastFormatter(df
, 4, 10);
1369 fFastNumberFormatters
[SMPDTFMT_NF_2x2
] = createFastFormatter(df
, 2, 2);
1370 df
->setDFSShallowCopy(FALSE
);
1373 void SimpleDateFormat::freeFastNumberFormatters() {
1374 delete fFastNumberFormatters
[SMPDTFMT_NF_1x10
];
1375 delete fFastNumberFormatters
[SMPDTFMT_NF_2x10
];
1376 delete fFastNumberFormatters
[SMPDTFMT_NF_3x10
];
1377 delete fFastNumberFormatters
[SMPDTFMT_NF_4x10
];
1378 delete fFastNumberFormatters
[SMPDTFMT_NF_2x2
];
1379 fFastNumberFormatters
[SMPDTFMT_NF_1x10
] = nullptr;
1380 fFastNumberFormatters
[SMPDTFMT_NF_2x10
] = nullptr;
1381 fFastNumberFormatters
[SMPDTFMT_NF_3x10
] = nullptr;
1382 fFastNumberFormatters
[SMPDTFMT_NF_4x10
] = nullptr;
1383 fFastNumberFormatters
[SMPDTFMT_NF_2x2
] = nullptr;
1388 SimpleDateFormat::initNumberFormatters(const Locale
&locale
,UErrorCode
&status
) {
1389 if (U_FAILURE(status
)) {
1392 if ( fDateOverride
.isBogus() && fTimeOverride
.isBogus() ) {
1396 if (fSharedNumberFormatters
== NULL
) {
1397 fSharedNumberFormatters
= allocSharedNumberFormatters();
1398 if (fSharedNumberFormatters
== NULL
) {
1399 status
= U_MEMORY_ALLOCATION_ERROR
;
1402 umtx_unlock(LOCK());
1404 if (U_FAILURE(status
)) {
1408 processOverrideString(locale
,fDateOverride
,kOvrStrDate
,status
);
1409 processOverrideString(locale
,fTimeOverride
,kOvrStrTime
,status
);
1413 SimpleDateFormat::processOverrideString(const Locale
&locale
, const UnicodeString
&str
, int8_t type
, UErrorCode
&status
) {
1414 if (str
.isBogus() || U_FAILURE(status
)) {
1420 UnicodeString nsName
;
1421 UnicodeString ovrField
;
1422 UBool moreToProcess
= TRUE
;
1423 NSOverride
*overrideList
= NULL
;
1425 while (moreToProcess
) {
1426 int32_t delimiterPosition
= str
.indexOf((UChar
)ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE
,start
);
1427 if (delimiterPosition
== -1) {
1428 moreToProcess
= FALSE
;
1429 len
= str
.length() - start
;
1431 len
= delimiterPosition
- start
;
1433 UnicodeString
currentString(str
,start
,len
);
1434 int32_t equalSignPosition
= currentString
.indexOf((UChar
)ULOC_KEYWORD_ASSIGN_UNICODE
,0);
1435 if (equalSignPosition
== -1) { // Simple override string such as "hebrew"
1436 nsName
.setTo(currentString
);
1437 ovrField
.setToBogus();
1438 } else { // Field specific override string such as "y=hebrew"
1439 nsName
.setTo(currentString
,equalSignPosition
+1);
1440 ovrField
.setTo(currentString
,0,1); // We just need the first character.
1443 int32_t nsNameHash
= nsName
.hashCode();
1444 // See if the numbering system is in the override list, if not, then add it.
1445 NSOverride
*curr
= overrideList
;
1446 const SharedNumberFormat
*snf
= NULL
;
1447 UBool found
= FALSE
;
1448 while ( curr
&& !found
) {
1449 if ( curr
->hash
== nsNameHash
) {
1457 LocalPointer
<NSOverride
> cur(new NSOverride
);
1458 if (!cur
.isNull()) {
1459 char kw
[ULOC_KEYWORD_AND_VALUES_CAPACITY
];
1460 uprv_strcpy(kw
,"numbers=");
1461 nsName
.extract(0,len
,kw
+8,ULOC_KEYWORD_AND_VALUES_CAPACITY
-8,US_INV
);
1463 Locale
ovrLoc(locale
.getLanguage(),locale
.getCountry(),locale
.getVariant(),kw
);
1464 cur
->hash
= nsNameHash
;
1465 cur
->next
= overrideList
;
1466 SharedObject::copyPtr(
1467 createSharedNumberFormat(ovrLoc
, status
), cur
->snf
);
1468 if (U_FAILURE(status
)) {
1470 overrideList
->free();
1475 overrideList
= cur
.orphan();
1477 status
= U_MEMORY_ALLOCATION_ERROR
;
1479 overrideList
->free();
1485 // Now that we have an appropriate number formatter, fill in the appropriate spaces in the
1486 // number formatters table.
1487 if (ovrField
.isBogus()) {
1491 for ( int8_t i
=0 ; i
<kDateFieldsCount
; i
++ ) {
1492 SharedObject::copyPtr(snf
, fSharedNumberFormatters
[kDateFields
[i
]]);
1494 if (type
==kOvrStrDate
) {
1499 case kOvrStrTime
: {
1500 for ( int8_t i
=0 ; i
<kTimeFieldsCount
; i
++ ) {
1501 SharedObject::copyPtr(snf
, fSharedNumberFormatters
[kTimeFields
[i
]]);
1507 // if the pattern character is unrecognized, signal an error and bail out
1508 UDateFormatField patternCharIndex
=
1509 DateFormatSymbols::getPatternCharIndex(ovrField
.charAt(0));
1510 if (patternCharIndex
== UDAT_FIELD_COUNT
) {
1511 status
= U_INVALID_FORMAT_ERROR
;
1513 overrideList
->free();
1517 SharedObject::copyPtr(snf
, fSharedNumberFormatters
[patternCharIndex
]);
1520 start
= delimiterPosition
+ 1;
1523 overrideList
->free();
1527 //---------------------------------------------------------------------
1529 SimpleDateFormat::subFormat(UnicodeString
&appendTo
,
1532 UDisplayContext capitalizationContext
,
1534 FieldPositionHandler
& handler
,
1536 UErrorCode
& status
) const
1538 if (U_FAILURE(status
)) {
1542 // this function gets called by format() to produce the appropriate substitution
1543 // text for an individual pattern symbol (e.g., "HH" or "yyyy")
1545 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(ch
);
1546 const int32_t maxIntCount
= 10;
1547 int32_t beginOffset
= appendTo
.length();
1548 const NumberFormat
*currentNumberFormat
;
1549 DateFormatSymbols::ECapitalizationContextUsageType capContextUsageType
= DateFormatSymbols::kCapContextUsageOther
;
1551 UBool isHebrewCalendar
= (uprv_strcmp(cal
.getType(),"hebrew") == 0);
1552 UBool isChineseCalendar
= (uprv_strcmp(cal
.getType(),"chinese") == 0 || uprv_strcmp(cal
.getType(),"dangi") == 0);
1554 // if the pattern character is unrecognized, signal an error and dump out
1555 if (patternCharIndex
== UDAT_FIELD_COUNT
)
1557 if (ch
!= 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
1558 status
= U_INVALID_FORMAT_ERROR
;
1563 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
];
1565 // Don't get value unless it is useful
1566 if (field
< UCAL_FIELD_COUNT
) {
1567 value
= (patternCharIndex
!= UDAT_RELATED_YEAR_FIELD
)? cal
.get(field
, status
): cal
.getRelatedYear(status
);
1569 if (U_FAILURE(status
)) {
1573 currentNumberFormat
= getNumberFormatByIndex(patternCharIndex
);
1574 if (currentNumberFormat
== NULL
) {
1575 status
= U_INTERNAL_PROGRAM_ERROR
;
1578 UnicodeString
hebr("hebr", 4, US_INV
);
1580 switch (patternCharIndex
) {
1582 // for any "G" symbol, write out the appropriate era string
1583 // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name
1584 case UDAT_ERA_FIELD
:
1585 if (isChineseCalendar
) {
1586 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, 1, 9); // as in ICU4J
1589 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowEras
, fSymbols
->fNarrowErasCount
);
1590 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraNarrow
;
1591 } else if (count
== 4) {
1592 _appendSymbol(appendTo
, value
, fSymbols
->fEraNames
, fSymbols
->fEraNamesCount
);
1593 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraWide
;
1595 _appendSymbol(appendTo
, value
, fSymbols
->fEras
, fSymbols
->fErasCount
);
1596 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraAbbrev
;
1601 case UDAT_YEAR_NAME_FIELD
:
1602 if (fSymbols
->fShortYearNames
!= NULL
&& value
<= fSymbols
->fShortYearNamesCount
) {
1603 // the Calendar YEAR field runs 1 through 60 for cyclic years
1604 _appendSymbol(appendTo
, value
- 1, fSymbols
->fShortYearNames
, fSymbols
->fShortYearNamesCount
);
1607 // else fall through to numeric year handling, do not break here
1610 // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits
1612 //Year y yy yyy yyyy yyyyy
1613 //AD 1 1 01 001 0001 00001
1614 //AD 12 12 12 012 0012 00012
1615 //AD 123 123 23 123 0123 00123
1616 //AD 1234 1234 34 1234 1234 01234
1617 //AD 12345 12345 45 12345 12345 12345
1618 case UDAT_YEAR_FIELD
:
1619 case UDAT_YEAR_WOY_FIELD
:
1620 if (fDateOverride
.compare(hebr
)==0 && value
>HEBREW_CAL_CUR_MILLENIUM_START_YEAR
&& value
<HEBREW_CAL_CUR_MILLENIUM_END_YEAR
) {
1621 value
-=HEBREW_CAL_CUR_MILLENIUM_START_YEAR
;
1624 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, 2, 2);
1626 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, count
, maxIntCount
);
1629 // for "MMMM"/"LLLL", write out the whole month name, for "MMM"/"LLL", write out the month
1630 // abbreviation, for "M"/"L" or "MM"/"LL", write out the month as a number with the
1631 // appropriate number of digits
1632 // for "MMMMM"/"LLLLL", use the narrow form
1633 case UDAT_MONTH_FIELD
:
1634 case UDAT_STANDALONE_MONTH_FIELD
:
1635 if ( isHebrewCalendar
) {
1636 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
1637 if (hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
== 6 && count
>= 3 )
1638 value
= 13; // Show alternate form for Adar II in leap years in Hebrew calendar.
1639 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6 && count
< 3 )
1640 value
--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7.
1643 int32_t isLeapMonth
= (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
)?
1644 cal
.get(UCAL_IS_LEAP_MONTH
, status
): 0;
1645 // should consolidate the next section by using arrays of pointers & counts for the right symbols...
1647 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1648 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fNarrowMonths
, fSymbols
->fNarrowMonthsCount
,
1649 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatNarrow
]): NULL
, status
);
1651 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneNarrowMonths
, fSymbols
->fStandaloneNarrowMonthsCount
,
1652 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneNarrow
]): NULL
, status
);
1654 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthNarrow
;
1655 } else if (count
== 4) {
1656 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1657 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fMonths
, fSymbols
->fMonthsCount
,
1658 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatWide
]): NULL
, status
);
1659 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthFormat
;
1661 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneMonths
, fSymbols
->fStandaloneMonthsCount
,
1662 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneWide
]): NULL
, status
);
1663 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthStandalone
;
1665 } else if (count
== 3) {
1666 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1667 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fShortMonths
, fSymbols
->fShortMonthsCount
,
1668 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatAbbrev
]): NULL
, status
);
1669 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthFormat
;
1671 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneShortMonths
, fSymbols
->fStandaloneShortMonthsCount
,
1672 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev
]): NULL
, status
);
1673 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthStandalone
;
1676 UnicodeString monthNumber
;
1677 zeroPaddingNumber(currentNumberFormat
,monthNumber
, value
+ 1, count
, maxIntCount
);
1678 _appendSymbolWithMonthPattern(appendTo
, 0, &monthNumber
, 1,
1679 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternNumeric
]): NULL
, status
);
1684 // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
1685 case UDAT_HOUR_OF_DAY1_FIELD
:
1687 zeroPaddingNumber(currentNumberFormat
,appendTo
, cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1, count
, maxIntCount
);
1689 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1692 case UDAT_FRACTIONAL_SECOND_FIELD
:
1693 // Fractional seconds left-justify
1695 int32_t minDigits
= (count
> 3) ? 3 : count
;
1698 } else if (count
== 2) {
1701 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, minDigits
, maxIntCount
);
1703 zeroPaddingNumber(currentNumberFormat
, appendTo
, 0, count
- 3, maxIntCount
);
1708 // for "ee" or "e", use local numeric day-of-the-week
1709 // for "EEEEEE" or "eeeeee", write out the short day-of-the-week name
1710 // for "EEEEE" or "eeeee", write out the narrow day-of-the-week name
1711 // for "EEEE" or "eeee", write out the wide day-of-the-week name
1712 // for "EEE" or "EE" or "E" or "eee", write out the abbreviated day-of-the-week name
1713 case UDAT_DOW_LOCAL_FIELD
:
1715 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1718 // fall through to EEEEE-EEE handling, but for that we don't want local day-of-week,
1719 // we want standard day-of-week, so first fix value to work for EEEEE-EEE.
1720 value
= cal
.get(UCAL_DAY_OF_WEEK
, status
);
1721 if (U_FAILURE(status
)) {
1724 // fall through, do not break here
1726 case UDAT_DAY_OF_WEEK_FIELD
:
1728 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowWeekdays
,
1729 fSymbols
->fNarrowWeekdaysCount
);
1730 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayNarrow
;
1731 } else if (count
== 4) {
1732 _appendSymbol(appendTo
, value
, fSymbols
->fWeekdays
,
1733 fSymbols
->fWeekdaysCount
);
1734 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1735 } else if (count
== 6) {
1736 _appendSymbol(appendTo
, value
, fSymbols
->fShorterWeekdays
,
1737 fSymbols
->fShorterWeekdaysCount
);
1738 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1740 _appendSymbol(appendTo
, value
, fSymbols
->fShortWeekdays
,
1741 fSymbols
->fShortWeekdaysCount
);
1742 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1746 // for "ccc", write out the abbreviated day-of-the-week name
1747 // for "cccc", write out the wide day-of-the-week name
1748 // for "ccccc", use the narrow day-of-the-week name
1749 // for "ccccc", use the short day-of-the-week name
1750 case UDAT_STANDALONE_DAY_FIELD
:
1752 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, 1, maxIntCount
);
1755 // fall through to alpha DOW handling, but for that we don't want local day-of-week,
1756 // we want standard day-of-week, so first fix value.
1757 value
= cal
.get(UCAL_DAY_OF_WEEK
, status
);
1758 if (U_FAILURE(status
)) {
1762 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneNarrowWeekdays
,
1763 fSymbols
->fStandaloneNarrowWeekdaysCount
);
1764 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayNarrow
;
1765 } else if (count
== 4) {
1766 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneWeekdays
,
1767 fSymbols
->fStandaloneWeekdaysCount
);
1768 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1769 } else if (count
== 6) {
1770 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneShorterWeekdays
,
1771 fSymbols
->fStandaloneShorterWeekdaysCount
);
1772 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1773 } else { // count == 3
1774 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneShortWeekdays
,
1775 fSymbols
->fStandaloneShortWeekdaysCount
);
1776 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1780 // for "a" symbol, write out the whole AM/PM string
1781 case UDAT_AM_PM_FIELD
:
1783 _appendSymbol(appendTo
, value
, fSymbols
->fAmPms
,
1784 fSymbols
->fAmPmsCount
);
1786 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowAmPms
,
1787 fSymbols
->fNarrowAmPmsCount
);
1791 // if we see pattern character for UDAT_TIME_SEPARATOR_FIELD (none currently defined),
1792 // write out the time separator string. Leave support in for future definition.
1793 case UDAT_TIME_SEPARATOR_FIELD
:
1795 UnicodeString separator
;
1796 appendTo
+= fSymbols
->getTimeSeparatorString(separator
);
1800 // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
1802 case UDAT_HOUR1_FIELD
:
1804 zeroPaddingNumber(currentNumberFormat
,appendTo
, cal
.getLeastMaximum(UCAL_HOUR
) + 1, count
, maxIntCount
);
1806 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1809 case UDAT_TIMEZONE_FIELD
: // 'z'
1810 case UDAT_TIMEZONE_RFC_FIELD
: // 'Z'
1811 case UDAT_TIMEZONE_GENERIC_FIELD
: // 'v'
1812 case UDAT_TIMEZONE_SPECIAL_FIELD
: // 'V'
1813 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
: // 'O'
1814 case UDAT_TIMEZONE_ISO_FIELD
: // 'X'
1815 case UDAT_TIMEZONE_ISO_LOCAL_FIELD
: // 'x'
1817 UChar zsbuf
[ZONE_NAME_U16_MAX
];
1818 UnicodeString
zoneString(zsbuf
, 0, UPRV_LENGTHOF(zsbuf
));
1819 const TimeZone
& tz
= cal
.getTimeZone();
1820 UDate date
= cal
.getTime(status
);
1821 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
1822 if (U_SUCCESS(status
)) {
1823 if (patternCharIndex
== UDAT_TIMEZONE_FIELD
) {
1826 tzfmt
->format(UTZFMT_STYLE_SPECIFIC_SHORT
, tz
, date
, zoneString
);
1827 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneShort
;
1830 tzfmt
->format(UTZFMT_STYLE_SPECIFIC_LONG
, tz
, date
, zoneString
);
1831 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneLong
;
1834 else if (patternCharIndex
== UDAT_TIMEZONE_RFC_FIELD
) {
1837 tzfmt
->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
, tz
, date
, zoneString
);
1838 } else if (count
== 5) {
1840 tzfmt
->format(UTZFMT_STYLE_ISO_EXTENDED_FULL
, tz
, date
, zoneString
);
1842 // "ZZ", "ZZZ", "ZZZZ"
1843 tzfmt
->format(UTZFMT_STYLE_LOCALIZED_GMT
, tz
, date
, zoneString
);
1846 else if (patternCharIndex
== UDAT_TIMEZONE_GENERIC_FIELD
) {
1849 tzfmt
->format(UTZFMT_STYLE_GENERIC_SHORT
, tz
, date
, zoneString
);
1850 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneShort
;
1851 } else if (count
== 4) {
1853 tzfmt
->format(UTZFMT_STYLE_GENERIC_LONG
, tz
, date
, zoneString
);
1854 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneLong
;
1857 else if (patternCharIndex
== UDAT_TIMEZONE_SPECIAL_FIELD
) {
1860 tzfmt
->format(UTZFMT_STYLE_ZONE_ID_SHORT
, tz
, date
, zoneString
);
1861 } else if (count
== 2) {
1863 tzfmt
->format(UTZFMT_STYLE_ZONE_ID
, tz
, date
, zoneString
);
1864 } else if (count
== 3) {
1866 tzfmt
->format(UTZFMT_STYLE_EXEMPLAR_LOCATION
, tz
, date
, zoneString
);
1867 } else if (count
== 4) {
1869 tzfmt
->format(UTZFMT_STYLE_GENERIC_LOCATION
, tz
, date
, zoneString
);
1870 capContextUsageType
= DateFormatSymbols::kCapContextUsageZoneLong
;
1873 else if (patternCharIndex
== UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
) {
1876 tzfmt
->format(UTZFMT_STYLE_LOCALIZED_GMT_SHORT
, tz
, date
, zoneString
);
1877 } else if (count
== 4) {
1879 tzfmt
->format(UTZFMT_STYLE_LOCALIZED_GMT
, tz
, date
, zoneString
);
1882 else if (patternCharIndex
== UDAT_TIMEZONE_ISO_FIELD
) {
1885 tzfmt
->format(UTZFMT_STYLE_ISO_BASIC_SHORT
, tz
, date
, zoneString
);
1886 } else if (count
== 2) {
1888 tzfmt
->format(UTZFMT_STYLE_ISO_BASIC_FIXED
, tz
, date
, zoneString
);
1889 } else if (count
== 3) {
1891 tzfmt
->format(UTZFMT_STYLE_ISO_EXTENDED_FIXED
, tz
, date
, zoneString
);
1892 } else if (count
== 4) {
1894 tzfmt
->format(UTZFMT_STYLE_ISO_BASIC_FULL
, tz
, date
, zoneString
);
1895 } else if (count
== 5) {
1897 tzfmt
->format(UTZFMT_STYLE_ISO_EXTENDED_FULL
, tz
, date
, zoneString
);
1900 else if (patternCharIndex
== UDAT_TIMEZONE_ISO_LOCAL_FIELD
) {
1903 tzfmt
->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
, tz
, date
, zoneString
);
1904 } else if (count
== 2) {
1906 tzfmt
->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
, tz
, date
, zoneString
);
1907 } else if (count
== 3) {
1909 tzfmt
->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
, tz
, date
, zoneString
);
1910 } else if (count
== 4) {
1912 tzfmt
->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
, tz
, date
, zoneString
);
1913 } else if (count
== 5) {
1915 tzfmt
->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
, tz
, date
, zoneString
);
1922 appendTo
+= zoneString
;
1926 case UDAT_QUARTER_FIELD
:
1928 _appendSymbol(appendTo
, value
/3, fSymbols
->fQuarters
,
1929 fSymbols
->fQuartersCount
);
1930 else if (count
== 3)
1931 _appendSymbol(appendTo
, value
/3, fSymbols
->fShortQuarters
,
1932 fSymbols
->fShortQuartersCount
);
1934 zeroPaddingNumber(currentNumberFormat
,appendTo
, (value
/3) + 1, count
, maxIntCount
);
1937 case UDAT_STANDALONE_QUARTER_FIELD
:
1939 _appendSymbol(appendTo
, value
/3, fSymbols
->fStandaloneQuarters
,
1940 fSymbols
->fStandaloneQuartersCount
);
1941 else if (count
== 3)
1942 _appendSymbol(appendTo
, value
/3, fSymbols
->fStandaloneShortQuarters
,
1943 fSymbols
->fStandaloneShortQuartersCount
);
1945 zeroPaddingNumber(currentNumberFormat
,appendTo
, (value
/3) + 1, count
, maxIntCount
);
1948 case UDAT_AM_PM_MIDNIGHT_NOON_FIELD
:
1950 const UnicodeString
*toAppend
= NULL
;
1951 int32_t hour
= cal
.get(UCAL_HOUR_OF_DAY
, status
);
1953 // Note: "midnight" can be ambiguous as to whether it refers to beginning of day or end of day.
1954 // For ICU 57 output of "midnight" is temporarily suppressed.
1956 // For "midnight" and "noon":
1957 // Time, as displayed, must be exactly noon or midnight.
1958 // This means minutes and seconds, if present, must be zero.
1959 if ((/*hour == 0 ||*/ hour
== 12) &&
1960 (!fHasMinute
|| cal
.get(UCAL_MINUTE
, status
) == 0) &&
1961 (!fHasSecond
|| cal
.get(UCAL_SECOND
, status
) == 0)) {
1962 // Stealing am/pm value to use as our array index.
1963 // It works out: am/midnight are both 0, pm/noon are both 1,
1964 // 12 am is 12 midnight, and 12 pm is 12 noon.
1965 int32_t val
= cal
.get(UCAL_AM_PM
, status
);
1968 toAppend
= &fSymbols
->fAbbreviatedDayPeriods
[val
];
1969 } else if (count
== 4 || count
> 5) {
1970 toAppend
= &fSymbols
->fWideDayPeriods
[val
];
1971 } else { // count == 5
1972 toAppend
= &fSymbols
->fNarrowDayPeriods
[val
];
1976 // toAppend is NULL if time isn't exactly midnight or noon (as displayed).
1977 // toAppend is bogus if time is midnight or noon, but no localized string exists.
1978 // In either case, fall back to am/pm.
1979 if (toAppend
== NULL
|| toAppend
->isBogus()) {
1980 // Reformat with identical arguments except ch, now changed to 'a'.
1981 subFormat(appendTo
, 0x61, count
, capitalizationContext
, fieldNum
,
1982 handler
, cal
, status
);
1984 appendTo
+= *toAppend
;
1990 case UDAT_FLEXIBLE_DAY_PERIOD_FIELD
:
1992 // TODO: Maybe fetch the DayperiodRules during initialization (instead of at the first
1993 // loading of an instance) if a relevant pattern character (b or B) is used.
1994 const DayPeriodRules
*ruleSet
= DayPeriodRules::getInstance(this->getSmpFmtLocale(), status
);
1995 if (U_FAILURE(status
)) {
1996 // Data doesn't conform to spec, therefore loading failed.
1999 if (ruleSet
== NULL
) {
2000 // Data doesn't exist for the locale we're looking for.
2001 // Falling back to am/pm.
2002 subFormat(appendTo
, 0x61, count
, capitalizationContext
, fieldNum
,
2003 handler
, cal
, status
);
2007 // Get current display time.
2008 int32_t hour
= cal
.get(UCAL_HOUR_OF_DAY
, status
);
2011 minute
= cal
.get(UCAL_MINUTE
, status
);
2015 second
= cal
.get(UCAL_SECOND
, status
);
2018 // Determine day period.
2019 DayPeriodRules::DayPeriod periodType
;
2020 if (hour
== 0 && minute
== 0 && second
== 0 && ruleSet
->hasMidnight()) {
2021 periodType
= DayPeriodRules::DAYPERIOD_MIDNIGHT
;
2022 } else if (hour
== 12 && minute
== 0 && second
== 0 && ruleSet
->hasNoon()) {
2023 periodType
= DayPeriodRules::DAYPERIOD_NOON
;
2025 periodType
= ruleSet
->getDayPeriodForHour(hour
);
2028 // Rule set exists, therefore periodType can't be UNKNOWN.
2029 // Get localized string.
2030 U_ASSERT(periodType
!= DayPeriodRules::DAYPERIOD_UNKNOWN
);
2031 UnicodeString
*toAppend
= NULL
;
2034 // Note: "midnight" can be ambiguous as to whether it refers to beginning of day or end of day.
2035 // For ICU 57 output of "midnight" is temporarily suppressed.
2037 if (periodType
!= DayPeriodRules::DAYPERIOD_AM
&&
2038 periodType
!= DayPeriodRules::DAYPERIOD_PM
&&
2039 periodType
!= DayPeriodRules::DAYPERIOD_MIDNIGHT
) {
2040 index
= (int32_t)periodType
;
2042 toAppend
= &fSymbols
->fAbbreviatedDayPeriods
[index
]; // i.e. short
2043 } else if (count
== 4 || count
> 5) {
2044 toAppend
= &fSymbols
->fWideDayPeriods
[index
];
2045 } else { // count == 5
2046 toAppend
= &fSymbols
->fNarrowDayPeriods
[index
];
2050 // Fallback schedule:
2051 // Midnight/Noon -> General Periods -> AM/PM.
2053 // Midnight/Noon -> General Periods.
2054 if ((toAppend
== NULL
|| toAppend
->isBogus()) &&
2055 (periodType
== DayPeriodRules::DAYPERIOD_MIDNIGHT
||
2056 periodType
== DayPeriodRules::DAYPERIOD_NOON
)) {
2057 periodType
= ruleSet
->getDayPeriodForHour(hour
);
2058 index
= (int32_t)periodType
;
2061 toAppend
= &fSymbols
->fAbbreviatedDayPeriods
[index
]; // i.e. short
2062 } else if (count
== 4 || count
> 5) {
2063 toAppend
= &fSymbols
->fWideDayPeriods
[index
];
2064 } else { // count == 5
2065 toAppend
= &fSymbols
->fNarrowDayPeriods
[index
];
2069 // General Periods -> AM/PM.
2070 if (periodType
== DayPeriodRules::DAYPERIOD_AM
||
2071 periodType
== DayPeriodRules::DAYPERIOD_PM
||
2072 toAppend
->isBogus()) {
2073 subFormat(appendTo
, 0x61, count
, capitalizationContext
, fieldNum
,
2074 handler
, cal
, status
);
2077 appendTo
+= *toAppend
;
2083 // all of the other pattern symbols can be formatted as simple numbers with
2084 // appropriate zero padding
2086 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
2089 #if !UCONFIG_NO_BREAK_ITERATION
2090 // if first field, check to see whether we need to and are able to titlecase it
2091 if (fieldNum
== 0 && fCapitalizationBrkIter
!= NULL
&& appendTo
.length() > beginOffset
&&
2092 u_islower(appendTo
.char32At(beginOffset
))) {
2093 UBool titlecase
= FALSE
;
2094 switch (capitalizationContext
) {
2095 case UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
:
2098 case UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
:
2099 titlecase
= fSymbols
->fCapitalization
[capContextUsageType
][0];
2101 case UDISPCTX_CAPITALIZATION_FOR_STANDALONE
:
2102 titlecase
= fSymbols
->fCapitalization
[capContextUsageType
][1];
2105 // titlecase = FALSE;
2109 UnicodeString
firstField(appendTo
, beginOffset
);
2110 firstField
.toTitle(fCapitalizationBrkIter
, fLocale
, U_TITLECASE_NO_LOWERCASE
| U_TITLECASE_NO_BREAK_ADJUSTMENT
);
2111 appendTo
.replaceBetween(beginOffset
, appendTo
.length(), firstField
);
2116 handler
.addAttribute(fgPatternIndexToDateFormatField
[patternCharIndex
], beginOffset
, appendTo
.length());
2119 //----------------------------------------------------------------------
2121 void SimpleDateFormat::adoptNumberFormat(NumberFormat
*formatToAdopt
) {
2122 fixNumberFormatForDates(*formatToAdopt
);
2123 freeFastNumberFormatters(); // deletes refs to fNumberFormat's symbols
2124 delete fNumberFormat
;
2125 fNumberFormat
= formatToAdopt
;
2127 // We successfully set the default number format. Now delete the overrides
2129 if (fSharedNumberFormatters
) {
2130 freeSharedNumberFormatters(fSharedNumberFormatters
);
2131 fSharedNumberFormatters
= NULL
;
2134 // Also re-compute the fast formatters.
2135 UErrorCode localStatus
= U_ZERO_ERROR
;
2136 initFastNumberFormatters(localStatus
);
2139 void SimpleDateFormat::adoptNumberFormat(const UnicodeString
& fields
, NumberFormat
*formatToAdopt
, UErrorCode
&status
){
2140 fixNumberFormatForDates(*formatToAdopt
);
2141 LocalPointer
<NumberFormat
> fmt(formatToAdopt
);
2142 if (U_FAILURE(status
)) {
2146 // We must ensure fSharedNumberFormatters is allocated.
2147 if (fSharedNumberFormatters
== NULL
) {
2148 fSharedNumberFormatters
= allocSharedNumberFormatters();
2149 if (fSharedNumberFormatters
== NULL
) {
2150 status
= U_MEMORY_ALLOCATION_ERROR
;
2154 const SharedNumberFormat
*newFormat
= createSharedNumberFormat(fmt
.orphan());
2155 if (newFormat
== NULL
) {
2156 status
= U_MEMORY_ALLOCATION_ERROR
;
2159 for (int i
=0; i
<fields
.length(); i
++) {
2160 UChar field
= fields
.charAt(i
);
2161 // if the pattern character is unrecognized, signal an error and bail out
2162 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(field
);
2163 if (patternCharIndex
== UDAT_FIELD_COUNT
) {
2164 status
= U_INVALID_FORMAT_ERROR
;
2165 newFormat
->deleteIfZeroRefCount();
2169 // Set the number formatter in the table
2170 SharedObject::copyPtr(
2171 newFormat
, fSharedNumberFormatters
[patternCharIndex
]);
2173 newFormat
->deleteIfZeroRefCount();
2176 const NumberFormat
*
2177 SimpleDateFormat::getNumberFormatForField(UChar field
) const {
2178 UDateFormatField index
= DateFormatSymbols::getPatternCharIndex(field
);
2179 if (index
== UDAT_FIELD_COUNT
) {
2182 return getNumberFormatByIndex(index
);
2185 //----------------------------------------------------------------------
2187 SimpleDateFormat::zeroPaddingNumber(
2188 const NumberFormat
*currentNumberFormat
,
2189 UnicodeString
&appendTo
,
2190 int32_t value
, int32_t minDigits
, int32_t maxDigits
) const
2192 const number::LocalizedNumberFormatter
* fastFormatter
= nullptr;
2193 // NOTE: This uses the heuristic that these five min/max int settings account for the vast majority
2194 // of SimpleDateFormat number formatting cases at the time of writing (ICU 62).
2195 if (currentNumberFormat
== fNumberFormat
) {
2196 if (maxDigits
== 10) {
2197 if (minDigits
== 1) {
2198 fastFormatter
= fFastNumberFormatters
[SMPDTFMT_NF_1x10
];
2199 } else if (minDigits
== 2) {
2200 fastFormatter
= fFastNumberFormatters
[SMPDTFMT_NF_2x10
];
2201 } else if (minDigits
== 3) {
2202 fastFormatter
= fFastNumberFormatters
[SMPDTFMT_NF_3x10
];
2203 } else if (minDigits
== 4) {
2204 fastFormatter
= fFastNumberFormatters
[SMPDTFMT_NF_4x10
];
2206 } else if (maxDigits
== 2) {
2207 if (minDigits
== 2) {
2208 fastFormatter
= fFastNumberFormatters
[SMPDTFMT_NF_2x2
];
2212 if (fastFormatter
!= nullptr) {
2213 // Can use fast path
2214 number::impl::UFormattedNumberData result
;
2215 result
.quantity
.setToInt(value
);
2216 UErrorCode localStatus
= U_ZERO_ERROR
;
2217 fastFormatter
->formatImpl(&result
, localStatus
);
2218 if (U_FAILURE(localStatus
)) {
2221 appendTo
.append(result
.getStringRef().toTempUnicodeString());
2225 // Check for RBNF (no clone necessary)
2226 auto* rbnf
= dynamic_cast<const RuleBasedNumberFormat
*>(currentNumberFormat
);
2227 if (rbnf
!= nullptr) {
2228 FieldPosition
pos(FieldPosition::DONT_CARE
);
2229 rbnf
->format(value
, appendTo
, pos
); // 3rd arg is there to speed up processing
2233 // Fall back to slow path (clone and mutate the NumberFormat)
2234 if (currentNumberFormat
!= nullptr) {
2235 FieldPosition
pos(FieldPosition::DONT_CARE
);
2236 LocalPointer
<NumberFormat
> nf(dynamic_cast<NumberFormat
*>(currentNumberFormat
->clone()));
2237 nf
->setMinimumIntegerDigits(minDigits
);
2238 nf
->setMaximumIntegerDigits(maxDigits
);
2239 nf
->format(value
, appendTo
, pos
); // 3rd arg is there to speed up processing
2243 //----------------------------------------------------------------------
2246 * Return true if the given format character, occuring count
2247 * times, represents a numeric field.
2249 UBool
SimpleDateFormat::isNumeric(UChar formatChar
, int32_t count
) {
2250 return DateFormatSymbols::isNumericPatternChar(formatChar
, count
);
2254 SimpleDateFormat::isAtNumericField(const UnicodeString
&pattern
, int32_t patternOffset
) {
2255 if (patternOffset
>= pattern
.length()) {
2259 UChar ch
= pattern
.charAt(patternOffset
);
2260 UDateFormatField f
= DateFormatSymbols::getPatternCharIndex(ch
);
2261 if (f
== UDAT_FIELD_COUNT
) {
2265 int32_t i
= patternOffset
;
2266 while (pattern
.charAt(++i
) == ch
) {}
2267 return DateFormatSymbols::isNumericField(f
, i
- patternOffset
);
2271 SimpleDateFormat::isAfterNonNumericField(const UnicodeString
&pattern
, int32_t patternOffset
) {
2272 if (patternOffset
<= 0) {
2273 // not after any field
2276 UChar ch
= pattern
.charAt(--patternOffset
);
2277 UDateFormatField f
= DateFormatSymbols::getPatternCharIndex(ch
);
2278 if (f
== UDAT_FIELD_COUNT
) {
2279 // not after any field
2282 int32_t i
= patternOffset
;
2283 while (pattern
.charAt(--i
) == ch
) {}
2284 return !DateFormatSymbols::isNumericField(f
, patternOffset
- i
);
2288 SimpleDateFormat::parse(const UnicodeString
& text
, Calendar
& cal
, ParsePosition
& parsePos
) const
2290 UErrorCode status
= U_ZERO_ERROR
;
2291 int32_t pos
= parsePos
.getIndex();
2292 if(parsePos
.getIndex() < 0) {
2293 parsePos
.setErrorIndex(0);
2296 int32_t start
= pos
;
2298 // Hold the day period until everything else is parsed, because we need
2299 // the hour to interpret time correctly.
2300 int32_t dayPeriodInt
= -1;
2302 UBool ambiguousYear
[] = { FALSE
};
2303 int32_t saveHebrewMonth
= -1;
2305 UTimeZoneFormatTimeType tzTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
2307 // For parsing abutting numeric fields. 'abutPat' is the
2308 // offset into 'pattern' of the first of 2 or more abutting
2309 // numeric fields. 'abutStart' is the offset into 'text'
2310 // where parsing the fields begins. 'abutPass' starts off as 0
2311 // and increments each time we try to parse the fields.
2312 int32_t abutPat
= -1; // If >=0, we are in a run of abutting numeric fields
2313 int32_t abutStart
= 0;
2314 int32_t abutPass
= 0;
2315 UBool inQuote
= FALSE
;
2317 MessageFormat
* numericLeapMonthFormatter
= NULL
;
2319 Calendar
* calClone
= NULL
;
2320 Calendar
*workCal
= &cal
;
2321 if (&cal
!= fCalendar
&& uprv_strcmp(cal
.getType(), fCalendar
->getType()) != 0) {
2322 // Different calendar type
2323 // We use the time/zone from the input calendar, but
2324 // do not use the input calendar for field calculation.
2325 calClone
= fCalendar
->clone();
2326 if (calClone
!= NULL
) {
2327 calClone
->setTime(cal
.getTime(status
),status
);
2328 if (U_FAILURE(status
)) {
2331 calClone
->setTimeZone(cal
.getTimeZone());
2334 status
= U_MEMORY_ALLOCATION_ERROR
;
2339 if (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
) {
2340 numericLeapMonthFormatter
= new MessageFormat(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternNumeric
], fLocale
, status
);
2341 if (numericLeapMonthFormatter
== NULL
) {
2342 status
= U_MEMORY_ALLOCATION_ERROR
;
2344 } else if (U_FAILURE(status
)) {
2345 goto ExitParse
; // this will delete numericLeapMonthFormatter
2349 for (int32_t i
=0; i
<fPattern
.length(); ++i
) {
2350 UChar ch
= fPattern
.charAt(i
);
2352 // Handle alphabetic field characters.
2353 if (!inQuote
&& isSyntaxChar(ch
)) {
2354 int32_t fieldPat
= i
;
2356 // Count the length of this field specifier
2358 while ((i
+1)<fPattern
.length() &&
2359 fPattern
.charAt(i
+1) == ch
) {
2364 if (isNumeric(ch
, count
)) {
2366 // Determine if there is an abutting numeric field.
2367 // Record the start of a set of abutting numeric fields.
2368 if (isAtNumericField(fPattern
, i
+ 1)) {
2375 abutPat
= -1; // End of any abutting fields
2378 // Handle fields within a run of abutting numeric fields. Take
2379 // the pattern "HHmmss" as an example. We will try to parse
2380 // 2/2/2 characters of the input text, then if that fails,
2381 // 1/2/2. We only adjust the width of the leftmost field; the
2382 // others remain fixed. This allows "123456" => 12:34:56, but
2383 // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
2384 // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
2386 // If we are at the start of a run of abutting fields, then
2387 // shorten this field in each pass. If we can't shorten
2388 // this field any more, then the parse of this set of
2389 // abutting numeric fields has failed.
2390 if (fieldPat
== abutPat
) {
2391 count
-= abutPass
++;
2393 status
= U_PARSE_ERROR
;
2398 pos
= subParse(text
, pos
, ch
, count
,
2399 TRUE
, FALSE
, ambiguousYear
, saveHebrewMonth
, *workCal
, i
, numericLeapMonthFormatter
, &tzTimeType
);
2401 // If the parse fails anywhere in the run, back up to the
2402 // start of the run and retry.
2410 // Handle non-numeric fields and non-abutting numeric
2412 else if (ch
!= 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
2413 int32_t s
= subParse(text
, pos
, ch
, count
,
2414 FALSE
, TRUE
, ambiguousYear
, saveHebrewMonth
, *workCal
, i
, numericLeapMonthFormatter
, &tzTimeType
, &dayPeriodInt
);
2417 // era not present, in special cases allow this to continue
2418 // from the position where the era was expected
2421 if (i
+1 < fPattern
.length()) {
2422 // move to next pattern character
2423 UChar c
= fPattern
.charAt(i
+1);
2425 // check for whitespace
2426 if (PatternProps::isWhiteSpace(c
)) {
2428 // Advance over run in pattern
2429 while ((i
+1)<fPattern
.length() &&
2430 PatternProps::isWhiteSpace(fPattern
.charAt(i
+1))) {
2437 status
= U_PARSE_ERROR
;
2444 // Handle literal pattern characters. These are any
2445 // quoted characters and non-alphabetic unquoted
2449 abutPat
= -1; // End of any abutting fields
2451 if (! matchLiterals(fPattern
, i
, text
, pos
, getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, status
), getBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH
, status
), isLenient())) {
2452 status
= U_PARSE_ERROR
;
2458 // Special hack for trailing "." after non-numeric field.
2459 if (text
.charAt(pos
) == 0x2e && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, status
)) {
2460 // only do if the last field is not numeric
2461 if (isAfterNonNumericField(fPattern
, fPattern
.length())) {
2462 pos
++; // skip the extra "."
2466 // If dayPeriod is set, use it in conjunction with hour-of-day to determine am/pm.
2467 if (dayPeriodInt
>= 0) {
2468 DayPeriodRules::DayPeriod dayPeriod
= (DayPeriodRules::DayPeriod
)dayPeriodInt
;
2469 const DayPeriodRules
*ruleSet
= DayPeriodRules::getInstance(this->getSmpFmtLocale(), status
);
2471 if (!cal
.isSet(UCAL_HOUR
) && !cal
.isSet(UCAL_HOUR_OF_DAY
)) {
2472 // If hour is not set, set time to the midpoint of current day period, overwriting
2473 // minutes if it's set.
2474 double midPoint
= ruleSet
->getMidPointForDayPeriod(dayPeriod
, status
);
2476 // If we can't get midPoint we do nothing.
2477 if (U_SUCCESS(status
)) {
2478 // Truncate midPoint toward zero to get the hour.
2479 // Any leftover means it was a half-hour.
2480 int32_t midPointHour
= (int32_t) midPoint
;
2481 int32_t midPointMinute
= (midPoint
- midPointHour
) > 0 ? 30 : 0;
2483 // No need to set am/pm because hour-of-day is set last therefore takes precedence.
2484 cal
.set(UCAL_HOUR_OF_DAY
, midPointHour
);
2485 cal
.set(UCAL_MINUTE
, midPointMinute
);
2490 if (cal
.isSet(UCAL_HOUR_OF_DAY
)) { // Hour is parsed in 24-hour format.
2491 hourOfDay
= cal
.get(UCAL_HOUR_OF_DAY
, status
);
2492 } else { // Hour is parsed in 12-hour format.
2493 hourOfDay
= cal
.get(UCAL_HOUR
, status
);
2494 // cal.get() turns 12 to 0 for 12-hour time; change 0 to 12
2495 // so 0 unambiguously means a 24-hour time from above.
2496 if (hourOfDay
== 0) { hourOfDay
= 12; }
2498 U_ASSERT(0 <= hourOfDay
&& hourOfDay
<= 23);
2501 // If hour-of-day is 0 or 13 thru 23 then input time in unambiguously in 24-hour format.
2502 if (hourOfDay
== 0 || (13 <= hourOfDay
&& hourOfDay
<= 23)) {
2503 // Make hour-of-day take precedence over (hour + am/pm) by setting it again.
2504 cal
.set(UCAL_HOUR_OF_DAY
, hourOfDay
);
2506 // We have a 12-hour time and need to choose between am and pm.
2507 // Behave as if dayPeriod spanned 6 hours each way from its center point.
2508 // This will parse correctly for consistent time + period (e.g. 10 at night) as
2509 // well as provide a reasonable recovery for inconsistent time + period (e.g.
2510 // 9 in the afternoon).
2512 // Assume current time is in the AM.
2513 // - Change 12 back to 0 for easier handling of 12am.
2514 // - Append minutes as fractional hours because e.g. 8:15 and 8:45 could be parsed
2515 // into different half-days if center of dayPeriod is at 14:30.
2516 // - cal.get(MINUTE) will return 0 if MINUTE is unset, which works.
2517 if (hourOfDay
== 12) { hourOfDay
= 0; }
2518 double currentHour
= hourOfDay
+ (cal
.get(UCAL_MINUTE
, status
)) / 60.0;
2519 double midPointHour
= ruleSet
->getMidPointForDayPeriod(dayPeriod
, status
);
2521 if (U_SUCCESS(status
)) {
2522 double hoursAheadMidPoint
= currentHour
- midPointHour
;
2524 // Assume current time is in the AM.
2525 if (-6 <= hoursAheadMidPoint
&& hoursAheadMidPoint
< 6) {
2526 // Assumption holds; set time as such.
2527 cal
.set(UCAL_AM_PM
, 0);
2529 cal
.set(UCAL_AM_PM
, 1);
2536 // At this point the fields of Calendar have been set. Calendar
2537 // will fill in default values for missing fields when the time
2540 parsePos
.setIndex(pos
);
2542 // This part is a problem: When we call parsedDate.after, we compute the time.
2543 // Take the date April 3 2004 at 2:30 am. When this is first set up, the year
2544 // will be wrong if we're parsing a 2-digit year pattern. It will be 1904.
2545 // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am
2546 // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
2547 // on that day. It is therefore parsed out to fields as 3:30 am. Then we
2548 // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is
2549 // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
2551 UDate parsedDate = calendar.getTime();
2552 if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
2553 calendar.add(Calendar.YEAR, 100);
2554 parsedDate = calendar.getTime();
2557 // Because of the above condition, save off the fields in case we need to readjust.
2558 // The procedure we use here is not particularly efficient, but there is no other
2559 // way to do this given the API restrictions present in Calendar. We minimize
2560 // inefficiency by only performing this computation when it might apply, that is,
2561 // when the two-digit year is equal to the start year, and thus might fall at the
2562 // front or the back of the default century. This only works because we adjust
2563 // the year correctly to start with in other cases -- see subParse().
2564 if (ambiguousYear
[0] || tzTimeType
!= UTZFMT_TIME_TYPE_UNKNOWN
) // If this is true then the two-digit year == the default start year
2566 // We need a copy of the fields, and we need to avoid triggering a call to
2567 // complete(), which will recalculate the fields. Since we can't access
2568 // the fields[] array in Calendar, we clone the entire object. This will
2569 // stop working if Calendar.clone() is ever rewritten to call complete().
2571 if (ambiguousYear
[0]) {
2573 // Check for failed cloning.
2575 status
= U_MEMORY_ALLOCATION_ERROR
;
2578 UDate parsedDate
= copy
->getTime(status
);
2579 // {sfb} check internalGetDefaultCenturyStart
2580 if (fHaveDefaultCentury
&& (parsedDate
< fDefaultCenturyStart
)) {
2581 // We can't use add here because that does a complete() first.
2582 cal
.set(UCAL_YEAR
, fDefaultCenturyStartYear
+ 100);
2587 if (tzTimeType
!= UTZFMT_TIME_TYPE_UNKNOWN
) {
2589 // Check for failed cloning.
2591 status
= U_MEMORY_ALLOCATION_ERROR
;
2594 const TimeZone
& tz
= cal
.getTimeZone();
2595 BasicTimeZone
*btz
= NULL
;
2597 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
2598 || dynamic_cast<const SimpleTimeZone
*>(&tz
) != NULL
2599 || dynamic_cast<const RuleBasedTimeZone
*>(&tz
) != NULL
2600 || dynamic_cast<const VTimeZone
*>(&tz
) != NULL
) {
2601 btz
= (BasicTimeZone
*)&tz
;
2605 copy
->set(UCAL_ZONE_OFFSET
, 0);
2606 copy
->set(UCAL_DST_OFFSET
, 0);
2607 UDate localMillis
= copy
->getTime(status
);
2609 // Make sure parsed time zone type (Standard or Daylight)
2610 // matches the rule used by the parsed time zone.
2613 if (tzTimeType
== UTZFMT_TIME_TYPE_STANDARD
) {
2614 btz
->getOffsetFromLocal(localMillis
,
2615 BasicTimeZone::kStandard
, BasicTimeZone::kStandard
, raw
, dst
, status
);
2617 btz
->getOffsetFromLocal(localMillis
,
2618 BasicTimeZone::kDaylight
, BasicTimeZone::kDaylight
, raw
, dst
, status
);
2621 // No good way to resolve ambiguous time at transition,
2622 // but following code work in most case.
2623 tz
.getOffset(localMillis
, TRUE
, raw
, dst
, status
);
2626 // Now, compare the results with parsed type, either standard or daylight saving time
2627 int32_t resolvedSavings
= dst
;
2628 if (tzTimeType
== UTZFMT_TIME_TYPE_STANDARD
) {
2630 // Override DST_OFFSET = 0 in the result calendar
2631 resolvedSavings
= 0;
2633 } else { // tztype == TZTYPE_DST
2636 UDate time
= localMillis
+ raw
;
2637 // We use the nearest daylight saving time rule.
2638 TimeZoneTransition beforeTrs
, afterTrs
;
2639 UDate beforeT
= time
, afterT
= time
;
2640 int32_t beforeSav
= 0, afterSav
= 0;
2641 UBool beforeTrsAvail
, afterTrsAvail
;
2643 // Search for DST rule before or on the time
2645 beforeTrsAvail
= btz
->getPreviousTransition(beforeT
, TRUE
, beforeTrs
);
2646 if (!beforeTrsAvail
) {
2649 beforeT
= beforeTrs
.getTime() - 1;
2650 beforeSav
= beforeTrs
.getFrom()->getDSTSavings();
2651 if (beforeSav
!= 0) {
2656 // Search for DST rule after the time
2658 afterTrsAvail
= btz
->getNextTransition(afterT
, FALSE
, afterTrs
);
2659 if (!afterTrsAvail
) {
2662 afterT
= afterTrs
.getTime();
2663 afterSav
= afterTrs
.getTo()->getDSTSavings();
2664 if (afterSav
!= 0) {
2669 if (beforeTrsAvail
&& afterTrsAvail
) {
2670 if (time
- beforeT
> afterT
- time
) {
2671 resolvedSavings
= afterSav
;
2673 resolvedSavings
= beforeSav
;
2675 } else if (beforeTrsAvail
&& beforeSav
!= 0) {
2676 resolvedSavings
= beforeSav
;
2677 } else if (afterTrsAvail
&& afterSav
!= 0) {
2678 resolvedSavings
= afterSav
;
2680 resolvedSavings
= btz
->getDSTSavings();
2683 resolvedSavings
= tz
.getDSTSavings();
2685 if (resolvedSavings
== 0) {
2687 resolvedSavings
= U_MILLIS_PER_HOUR
;
2691 cal
.set(UCAL_ZONE_OFFSET
, raw
);
2692 cal
.set(UCAL_DST_OFFSET
, resolvedSavings
);
2697 // Set the parsed result if local calendar is used
2698 // instead of the input calendar
2699 if (U_SUCCESS(status
) && workCal
!= &cal
) {
2700 cal
.setTimeZone(workCal
->getTimeZone());
2701 cal
.setTime(workCal
->getTime(status
), status
);
2704 if (numericLeapMonthFormatter
!= NULL
) {
2705 delete numericLeapMonthFormatter
;
2707 if (calClone
!= NULL
) {
2711 // If any Calendar calls failed, we pretend that we
2712 // couldn't parse the string, when in reality this isn't quite accurate--
2713 // we did parse it; the Calendar calls just failed.
2714 if (U_FAILURE(status
)) {
2715 parsePos
.setErrorIndex(pos
);
2716 parsePos
.setIndex(start
);
2720 //----------------------------------------------------------------------
2723 matchStringWithOptionalDot(const UnicodeString
&text
,
2725 const UnicodeString
&data
);
2727 int32_t SimpleDateFormat::matchQuarterString(const UnicodeString
& text
,
2729 UCalendarDateFields field
,
2730 const UnicodeString
* data
,
2732 Calendar
& cal
) const
2735 int32_t count
= dataCount
;
2737 // There may be multiple strings in the data[] array which begin with
2738 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2739 // We keep track of the longest match, and return that. Note that this
2740 // unfortunately requires us to test all array elements.
2741 int32_t bestMatchLength
= 0, bestMatch
= -1;
2742 UnicodeString bestMatchName
;
2744 for (; i
< count
; ++i
) {
2745 int32_t matchLength
= 0;
2746 if ((matchLength
= matchStringWithOptionalDot(text
, start
, data
[i
])) > bestMatchLength
) {
2747 bestMatchLength
= matchLength
;
2752 if (bestMatch
>= 0) {
2753 cal
.set(field
, bestMatch
* 3);
2754 return start
+ bestMatchLength
;
2760 int32_t SimpleDateFormat::matchDayPeriodStrings(const UnicodeString
& text
, int32_t start
,
2761 const UnicodeString
* data
, int32_t dataCount
,
2762 int32_t &dayPeriod
) const
2765 int32_t bestMatchLength
= 0, bestMatch
= -1;
2767 for (int32_t i
= 0; i
< dataCount
; ++i
) {
2768 int32_t matchLength
= 0;
2769 if ((matchLength
= matchStringWithOptionalDot(text
, start
, data
[i
])) > bestMatchLength
) {
2770 bestMatchLength
= matchLength
;
2775 if (bestMatch
>= 0) {
2776 dayPeriod
= bestMatch
;
2777 return start
+ bestMatchLength
;
2783 //----------------------------------------------------------------------
2784 #define IS_BIDI_MARK(c) (c==0x200E || c==0x200F || c==0x061C)
2786 UBool
SimpleDateFormat::matchLiterals(const UnicodeString
&pattern
,
2787 int32_t &patternOffset
,
2788 const UnicodeString
&text
,
2789 int32_t &textOffset
,
2790 UBool whitespaceLenient
,
2791 UBool partialMatchLenient
,
2794 UBool inQuote
= FALSE
;
2795 UnicodeString literal
;
2796 int32_t i
= patternOffset
;
2798 // scan pattern looking for contiguous literal characters
2799 for ( ; i
< pattern
.length(); i
+= 1) {
2800 UChar ch
= pattern
.charAt(i
);
2802 if (!inQuote
&& isSyntaxChar(ch
)) {
2807 // Match a quote literal ('') inside OR outside of quotes
2808 if ((i
+ 1) < pattern
.length() && pattern
.charAt(i
+ 1) == QUOTE
) {
2816 if (!IS_BIDI_MARK(ch
)) {
2821 // at this point, literal contains the pattern literal text (without bidi marks)
2822 // and i is the index of the next non-literal pattern character.
2824 int32_t t
= textOffset
;
2826 if (whitespaceLenient
) {
2827 // trim leading, trailing whitespace from the pattern literal
2830 // ignore any leading whitespace (or bidi marks) in the text
2831 while (t
< text
.length()) {
2832 UChar ch
= text
.charAt(t
);
2833 if (!u_isWhitespace(ch
) && !IS_BIDI_MARK(ch
)) {
2840 // Get ignorables, move up here
2841 const UnicodeSet
*ignorables
= NULL
;
2842 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(pattern
.charAt(i
));
2843 if (patternCharIndex
!= UDAT_FIELD_COUNT
) {
2844 ignorables
= SimpleDateFormatStaticSets::getIgnorables(patternCharIndex
);
2847 for (p
= 0; p
< literal
.length() && t
< text
.length();) {
2848 UBool needWhitespace
= FALSE
;
2850 // Skip any whitespace at current position in pattern,
2851 // but remember whether we found whitespace in the pattern
2852 // (we already deleted any bidi marks in the pattern).
2853 while (p
< literal
.length() && PatternProps::isWhiteSpace(literal
.charAt(p
))) {
2854 needWhitespace
= TRUE
;
2858 // If the pattern has whitespace at this point, skip it in text as well
2859 // (if the text does not have any, that may be an error for strict parsing)
2860 if (needWhitespace
) {
2861 UBool whitespaceInText
= FALSE
;
2863 // Skip any whitespace (or bidi marks) at current position in text,
2864 // but remember whether we found whitespace in the text at this point.
2865 while (t
< text
.length()) {
2866 UChar tch
= text
.charAt(t
);
2867 if (u_isUWhiteSpace(tch
) || PatternProps::isWhiteSpace(tch
)) {
2868 whitespaceInText
= TRUE
;
2869 } else if (!IS_BIDI_MARK(tch
)) {
2876 // TODO: should we require internal spaces
2877 // in lenient mode? (There won't be any
2878 // leading or trailing spaces)
2879 if (!whitespaceLenient
&& !whitespaceInText
) {
2880 // didn't find matching whitespace:
2881 // an error in strict mode
2885 // In strict mode, this run of whitespace
2886 // may have been at the end.
2887 if (p
>= literal
.length()) {
2891 // Still need to skip any bidi marks in the text
2892 while (t
< text
.length() && IS_BIDI_MARK(text
.charAt(t
))) {
2896 if (t
>= text
.length() || literal
.charAt(p
) != text
.charAt(t
)) {
2897 // Ran out of text, or found a non-matching character:
2898 // OK in lenient mode, an error in strict mode.
2899 if (whitespaceLenient
) {
2900 if (t
== textOffset
&& text
.charAt(t
) == 0x2e &&
2901 isAfterNonNumericField(pattern
, patternOffset
)) {
2902 // Lenient mode and the literal input text begins with a "." and
2903 // we are after a non-numeric field: We skip the "."
2905 continue; // Do not update p.
2907 // if it is actual whitespace and we're whitespace lenient it's OK
2909 UChar wsc
= text
.charAt(t
);
2910 if(PatternProps::isWhiteSpace(wsc
)) {
2911 // Lenient mode and it's just whitespace we skip it
2913 continue; // Do not update p.
2916 // hack around oldleniency being a bit of a catch-all bucket and we're just adding support specifically for paritial matches
2917 // This fix is for http://bugs.icu-project.org/trac/ticket/10855 and adds "&& oldLeniency"
2918 //if(partialMatchLenient && oldLeniency) {
2919 // However this causes problems for Apple, see <rdar://problem/20692829> regressions in Chinese date parsing
2920 // We don't want to go back to just "if(partialMatchLenient)" as in ICU 53, that is too lenient for strict mode.
2921 // 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.
2922 if( partialMatchLenient
&& ( oldLeniency
||
2923 ( ignorables
!= NULL
&& ignorables
->contains(literal
.charAt(p
)) && (ignorables
->contains(text
.charAt(t
)) || u_isalpha(text
.charAt(t
))) ) )
2934 // At this point if we're in strict mode we have a complete match.
2935 // If we're in lenient mode we may have a partial match, or no
2938 // no match. Pretend it matched a run of whitespace
2939 // and ignorables in the text.
2941 for (t
= textOffset
; t
< text
.length(); t
+= 1) {
2942 UChar ch
= text
.charAt(t
);
2944 if (!IS_BIDI_MARK(ch
) && (ignorables
== NULL
|| !ignorables
->contains(ch
))) {
2950 // if we get here, we've got a complete match.
2951 patternOffset
= i
- 1;
2957 //----------------------------------------------------------------------
2959 int32_t SimpleDateFormat::matchString(const UnicodeString
& text
,
2961 UCalendarDateFields field
,
2962 const UnicodeString
* data
,
2964 const UnicodeString
* monthPattern
,
2965 Calendar
& cal
) const
2968 int32_t count
= dataCount
;
2970 if (field
== UCAL_DAY_OF_WEEK
) i
= 1;
2972 // There may be multiple strings in the data[] array which begin with
2973 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2974 // We keep track of the longest match, and return that. Note that this
2975 // unfortunately requires us to test all array elements.
2976 int32_t bestMatchLength
= 0, bestMatch
= -1;
2977 UnicodeString bestMatchName
;
2978 int32_t isLeapMonth
= 0;
2980 for (; i
< count
; ++i
) {
2981 int32_t matchLen
= 0;
2982 if ((matchLen
= matchStringWithOptionalDot(text
, start
, data
[i
])) > bestMatchLength
) {
2984 bestMatchLength
= matchLen
;
2987 if (monthPattern
!= NULL
) {
2988 UErrorCode status
= U_ZERO_ERROR
;
2989 UnicodeString leapMonthName
;
2990 SimpleFormatter(*monthPattern
, 1, 1, status
).format(data
[i
], leapMonthName
, status
);
2991 if (U_SUCCESS(status
)) {
2992 if ((matchLen
= matchStringWithOptionalDot(text
, start
, leapMonthName
)) > bestMatchLength
) {
2994 bestMatchLength
= matchLen
;
3001 if (bestMatch
>= 0) {
3002 if (field
< UCAL_FIELD_COUNT
) {
3003 // Adjustment for Hebrew Calendar month Adar II
3004 if (!strcmp(cal
.getType(),"hebrew") && field
==UCAL_MONTH
&& bestMatch
==13) {
3007 if (field
== UCAL_YEAR
) {
3008 bestMatch
++; // only get here for cyclic year names, which match 1-based years 1-60
3010 cal
.set(field
, bestMatch
);
3012 if (monthPattern
!= NULL
) {
3013 cal
.set(UCAL_IS_LEAP_MONTH
, isLeapMonth
);
3017 return start
+ bestMatchLength
;
3024 matchStringWithOptionalDot(const UnicodeString
&text
,
3026 const UnicodeString
&data
) {
3027 UErrorCode sts
= U_ZERO_ERROR
;
3028 int32_t matchLenText
= 0;
3029 int32_t matchLenData
= 0;
3031 u_caseInsensitivePrefixMatch(text
.getBuffer() + index
, text
.length() - index
,
3032 data
.getBuffer(), data
.length(),
3033 0 /* default case option */,
3034 &matchLenText
, &matchLenData
,
3036 U_ASSERT (U_SUCCESS(sts
));
3038 if (matchLenData
== data
.length() /* normal match */
3039 || (data
.charAt(data
.length() - 1) == 0x2e
3040 && matchLenData
== data
.length() - 1 /* match without trailing dot */)) {
3041 return matchLenText
;
3047 //----------------------------------------------------------------------
3050 SimpleDateFormat::set2DigitYearStart(UDate d
, UErrorCode
& status
)
3052 parseAmbiguousDatesAsAfter(d
, status
);
3056 * Private member function that converts the parsed date strings into
3057 * timeFields. Returns -start (for ParsePosition) if failed.
3059 int32_t SimpleDateFormat::subParse(const UnicodeString
& text
, int32_t& start
, UChar ch
, int32_t count
,
3060 UBool obeyCount
, UBool allowNegative
, UBool ambiguousYear
[], int32_t& saveHebrewMonth
, Calendar
& cal
,
3061 int32_t patLoc
, MessageFormat
* numericLeapMonthFormatter
, UTimeZoneFormatTimeType
*tzTimeType
,
3062 int32_t *dayPeriod
) const
3068 UErrorCode status
= U_ZERO_ERROR
;
3069 ParsePosition
pos(0);
3070 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(ch
);
3071 const NumberFormat
*currentNumberFormat
;
3073 int32_t tzParseOptions
= (isLenient())? UTZFMT_PARSE_OPTION_ALL_STYLES
: UTZFMT_PARSE_OPTION_NONE
;
3074 UBool gotNumber
= FALSE
;
3076 #if defined (U_DEBUG_CAL)
3077 //fprintf(stderr, "%s:%d - [%c] st=%d \n", __FILE__, __LINE__, (char) ch, start);
3080 if (patternCharIndex
== UDAT_FIELD_COUNT
) {
3084 currentNumberFormat
= getNumberFormatByIndex(patternCharIndex
);
3085 if (currentNumberFormat
== NULL
) {
3088 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
]; // UCAL_FIELD_COUNT if irrelevant
3089 UnicodeString
hebr("hebr", 4, US_INV
);
3091 if (numericLeapMonthFormatter
!= NULL
) {
3092 numericLeapMonthFormatter
->setFormats((const Format
**)¤tNumberFormat
, 1);
3094 UBool isChineseCalendar
= (uprv_strcmp(cal
.getType(),"chinese") == 0 || uprv_strcmp(cal
.getType(),"dangi") == 0);
3096 // If there are any spaces here, skip over them. If we hit the end
3097 // of the string, then fail.
3099 if (start
>= text
.length()) {
3102 UChar32 c
= text
.char32At(start
);
3103 if (!u_isUWhiteSpace(c
) /*||*/ && !PatternProps::isWhiteSpace(c
)) {
3106 start
+= U16_LENGTH(c
);
3108 pos
.setIndex(start
);
3110 // We handle a few special cases here where we need to parse
3111 // a number value. We handle further, more generic cases below. We need
3112 // to handle some of them here because some fields require extra processing on
3113 // the parsed value.
3114 if (patternCharIndex
== UDAT_HOUR_OF_DAY1_FIELD
|| // k
3115 patternCharIndex
== UDAT_HOUR_OF_DAY0_FIELD
|| // H
3116 patternCharIndex
== UDAT_HOUR1_FIELD
|| // h
3117 patternCharIndex
== UDAT_HOUR0_FIELD
|| // K
3118 (patternCharIndex
== UDAT_DOW_LOCAL_FIELD
&& count
<= 2) || // e
3119 (patternCharIndex
== UDAT_STANDALONE_DAY_FIELD
&& count
<= 2) || // c
3120 (patternCharIndex
== UDAT_MONTH_FIELD
&& count
<= 2) || // M
3121 (patternCharIndex
== UDAT_STANDALONE_MONTH_FIELD
&& count
<= 2) || // L
3122 (patternCharIndex
== UDAT_QUARTER_FIELD
&& count
<= 2) || // Q
3123 (patternCharIndex
== UDAT_STANDALONE_QUARTER_FIELD
&& count
<= 2) || // q
3124 patternCharIndex
== UDAT_YEAR_FIELD
|| // y
3125 patternCharIndex
== UDAT_YEAR_WOY_FIELD
|| // Y
3126 patternCharIndex
== UDAT_YEAR_NAME_FIELD
|| // U (falls back to numeric)
3127 (patternCharIndex
== UDAT_ERA_FIELD
&& isChineseCalendar
) || // G
3128 patternCharIndex
== UDAT_FRACTIONAL_SECOND_FIELD
) // S
3130 int32_t parseStart
= pos
.getIndex();
3131 // It would be good to unify this with the obeyCount logic below,
3132 // but that's going to be difficult.
3133 const UnicodeString
* src
;
3135 UBool parsedNumericLeapMonth
= FALSE
;
3136 if (numericLeapMonthFormatter
!= NULL
&& (patternCharIndex
== UDAT_MONTH_FIELD
|| patternCharIndex
== UDAT_STANDALONE_MONTH_FIELD
)) {
3138 Formattable
* args
= numericLeapMonthFormatter
->parse(text
, pos
, argCount
);
3139 if (args
!= NULL
&& argCount
== 1 && pos
.getIndex() > parseStart
&& args
[0].isNumeric()) {
3140 parsedNumericLeapMonth
= TRUE
;
3141 number
.setLong(args
[0].getLong());
3142 cal
.set(UCAL_IS_LEAP_MONTH
, 1);
3145 pos
.setIndex(parseStart
);
3146 cal
.set(UCAL_IS_LEAP_MONTH
, 0);
3150 if (!parsedNumericLeapMonth
) {
3152 if ((start
+count
) > text
.length()) {
3156 text
.extractBetween(0, start
+ count
, temp
);
3162 parseInt(*src
, number
, pos
, allowNegative
,currentNumberFormat
);
3165 int32_t txtLoc
= pos
.getIndex();
3167 if (txtLoc
> parseStart
) {
3168 value
= number
.getLong();
3171 // suffix processing
3173 txtLoc
= checkIntSuffix(text
, txtLoc
, patLoc
+1, TRUE
);
3174 if (txtLoc
!= pos
.getIndex()) {
3179 txtLoc
= checkIntSuffix(text
, txtLoc
, patLoc
+1, FALSE
);
3182 // Check the range of the value
3183 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, status
)) {
3184 int32_t bias
= gFieldRangeBias
[patternCharIndex
];
3185 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
|| value
< cal
.getMinimum(field
) + bias
)) {
3189 int32_t bias
= gFieldRangeBiasLenient
[patternCharIndex
];
3190 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
)) {
3195 pos
.setIndex(txtLoc
);
3199 // Make sure that we got a number if
3200 // we want one, and didn't get one
3201 // if we don't want one.
3202 switch (patternCharIndex
) {
3203 case UDAT_HOUR_OF_DAY1_FIELD
:
3204 case UDAT_HOUR_OF_DAY0_FIELD
:
3205 case UDAT_HOUR1_FIELD
:
3206 case UDAT_HOUR0_FIELD
:
3207 // special range check for hours:
3208 if (value
< 0 || value
> 24) {
3212 // fall through to gotNumber check
3214 case UDAT_YEAR_FIELD
:
3215 case UDAT_YEAR_WOY_FIELD
:
3216 case UDAT_FRACTIONAL_SECOND_FIELD
:
3217 // these must be a number
3225 // we check the rest of the fields below.
3229 switch (patternCharIndex
) {
3230 case UDAT_ERA_FIELD
:
3231 if (isChineseCalendar
) {
3235 cal
.set(UCAL_ERA
, value
);
3236 return pos
.getIndex();
3239 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fNarrowEras
, fSymbols
->fNarrowErasCount
, NULL
, cal
);
3240 } else if (count
== 4) {
3241 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fEraNames
, fSymbols
->fEraNamesCount
, NULL
, cal
);
3243 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fEras
, fSymbols
->fErasCount
, NULL
, cal
);
3246 // check return position, if it equals -start, then matchString error
3247 // special case the return code so we don't necessarily fail out until we
3248 // verify no year information also
3254 case UDAT_YEAR_FIELD
:
3255 // If there are 3 or more YEAR pattern characters, this indicates
3256 // that the year value is to be treated literally, without any
3257 // two-digit year adjustments (e.g., from "01" to 2001). Otherwise
3258 // we made adjustments to place the 2-digit year in the proper
3259 // century, for parsed strings from "00" to "99". Any other string
3260 // is treated literally: "2250", "-1", "1", "002".
3261 if (fDateOverride
.compare(hebr
)==0 && value
< 1000) {
3262 value
+= HEBREW_CAL_CUR_MILLENIUM_START_YEAR
;
3263 } else if (text
.moveIndex32(start
, 2) == pos
.getIndex() && !isChineseCalendar
3264 && u_isdigit(text
.char32At(start
))
3265 && u_isdigit(text
.char32At(text
.moveIndex32(start
, 1))))
3267 // only adjust year for patterns less than 3.
3269 // Assume for example that the defaultCenturyStart is 6/18/1903.
3270 // This means that two-digit years will be forced into the range
3271 // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
3272 // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
3273 // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
3274 // other fields specify a date before 6/18, or 1903 if they specify a
3275 // date afterwards. As a result, 03 is an ambiguous year. All other
3276 // two-digit years are unambiguous.
3277 if(fHaveDefaultCentury
) { // check if this formatter even has a pivot year
3278 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
3279 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
3280 value
+= (fDefaultCenturyStartYear
/100)*100 +
3281 (value
< ambiguousTwoDigitYear
? 100 : 0);
3285 cal
.set(UCAL_YEAR
, value
);
3287 // Delayed checking for adjustment of Hebrew month numbers in non-leap years.
3288 if (saveHebrewMonth
>= 0) {
3289 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
3290 if (!hc
->isLeapYear(value
) && saveHebrewMonth
>= 6) {
3291 cal
.set(UCAL_MONTH
,saveHebrewMonth
);
3293 cal
.set(UCAL_MONTH
,saveHebrewMonth
-1);
3295 saveHebrewMonth
= -1;
3297 return pos
.getIndex();
3299 case UDAT_YEAR_WOY_FIELD
:
3300 // Comment is the same as for UDAT_Year_FIELDs - look above
3301 if (fDateOverride
.compare(hebr
)==0 && value
< 1000) {
3302 value
+= HEBREW_CAL_CUR_MILLENIUM_START_YEAR
;
3303 } else if (text
.moveIndex32(start
, 2) == pos
.getIndex()
3304 && u_isdigit(text
.char32At(start
))
3305 && u_isdigit(text
.char32At(text
.moveIndex32(start
, 1)))
3306 && fHaveDefaultCentury
)
3308 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
3309 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
3310 value
+= (fDefaultCenturyStartYear
/100)*100 +
3311 (value
< ambiguousTwoDigitYear
? 100 : 0);
3313 cal
.set(UCAL_YEAR_WOY
, value
);
3314 return pos
.getIndex();
3316 case UDAT_YEAR_NAME_FIELD
:
3317 if (fSymbols
->fShortYearNames
!= NULL
) {
3318 int32_t newStart
= matchString(text
, start
, UCAL_YEAR
, fSymbols
->fShortYearNames
, fSymbols
->fShortYearNamesCount
, NULL
, cal
);
3323 if (gotNumber
&& (getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
,status
) || value
> fSymbols
->fShortYearNamesCount
)) {
3324 cal
.set(UCAL_YEAR
, value
);
3325 return pos
.getIndex();
3329 case UDAT_MONTH_FIELD
:
3330 case UDAT_STANDALONE_MONTH_FIELD
:
3331 if (gotNumber
) // i.e., M or MM.
3333 // When parsing month numbers from the Hebrew Calendar, we might need to adjust the month depending on whether
3334 // 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
3335 // the year is parsed.
3336 if (!strcmp(cal
.getType(),"hebrew")) {
3337 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
3338 if (cal
.isSet(UCAL_YEAR
)) {
3339 UErrorCode monthStatus
= U_ZERO_ERROR
;
3340 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
, monthStatus
)) && value
>= 6) {
3341 cal
.set(UCAL_MONTH
, value
);
3343 cal
.set(UCAL_MONTH
, value
- 1);
3346 saveHebrewMonth
= value
;
3349 // Don't want to parse the month if it is a string
3350 // while pattern uses numeric style: M/MM, L/LL
3351 // [We computed 'value' above.]
3352 cal
.set(UCAL_MONTH
, value
- 1);
3354 return pos
.getIndex();
3356 // count >= 3 // i.e., MMM/MMMM, LLL/LLLL
3357 // Want to be able to parse both short and long forms.
3358 // Try count == 4 first:
3359 UnicodeString
* wideMonthPat
= NULL
;
3360 UnicodeString
* shortMonthPat
= NULL
;
3361 if (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
) {
3362 if (patternCharIndex
==UDAT_MONTH_FIELD
) {
3363 wideMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatWide
];
3364 shortMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatAbbrev
];
3366 wideMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneWide
];
3367 shortMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev
];
3370 int32_t newStart
= 0;
3371 if (patternCharIndex
==UDAT_MONTH_FIELD
) {
3372 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3373 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fMonths
, fSymbols
->fMonthsCount
, wideMonthPat
, cal
); // try MMMM
3378 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3379 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fShortMonths
, fSymbols
->fShortMonthsCount
, shortMonthPat
, cal
); // try MMM
3382 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3383 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fStandaloneMonths
, fSymbols
->fStandaloneMonthsCount
, wideMonthPat
, cal
); // try LLLL
3388 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3389 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fStandaloneShortMonths
, fSymbols
->fStandaloneShortMonthsCount
, shortMonthPat
, cal
); // try LLL
3392 if (newStart
> 0 || !getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
)) // currently we do not try to parse MMMMM/LLLLL: #8860
3394 // else we allowing parsing as number, below
3398 case UDAT_HOUR_OF_DAY1_FIELD
:
3399 // [We computed 'value' above.]
3400 if (value
== cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1)
3403 // fall through to set field
3405 case UDAT_HOUR_OF_DAY0_FIELD
:
3406 cal
.set(UCAL_HOUR_OF_DAY
, value
);
3407 return pos
.getIndex();
3409 case UDAT_FRACTIONAL_SECOND_FIELD
:
3410 // Fractional seconds left-justify
3411 i
= countDigits(text
, start
, pos
.getIndex());
3425 cal
.set(UCAL_MILLISECOND
, value
);
3426 return pos
.getIndex();
3428 case UDAT_DOW_LOCAL_FIELD
:
3429 if (gotNumber
) // i.e., e or ee
3431 // [We computed 'value' above.]
3432 cal
.set(UCAL_DOW_LOCAL
, value
);
3433 return pos
.getIndex();
3435 // else for eee-eeeee fall through to handling of EEE-EEEEE
3436 // fall through, do not break here
3438 case UDAT_DAY_OF_WEEK_FIELD
:
3440 // Want to be able to parse both short and long forms.
3441 // Try count == 4 (EEEE) wide first:
3442 int32_t newStart
= 0;
3443 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3444 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3445 fSymbols
->fWeekdays
, fSymbols
->fWeekdaysCount
, NULL
, cal
)) > 0)
3448 // EEEE wide failed, now try EEE abbreviated
3449 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3450 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3451 fSymbols
->fShortWeekdays
, fSymbols
->fShortWeekdaysCount
, NULL
, cal
)) > 0)
3454 // EEE abbreviated failed, now try EEEEEE short
3455 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 6) {
3456 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3457 fSymbols
->fShorterWeekdays
, fSymbols
->fShorterWeekdaysCount
, NULL
, cal
)) > 0)
3460 // EEEEEE short failed, now try EEEEE narrow
3461 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 5) {
3462 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3463 fSymbols
->fNarrowWeekdays
, fSymbols
->fNarrowWeekdaysCount
, NULL
, cal
)) > 0)
3466 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
) || patternCharIndex
== UDAT_DAY_OF_WEEK_FIELD
)
3468 // else we allowing parsing as number, below
3472 case UDAT_STANDALONE_DAY_FIELD
:
3474 if (gotNumber
) // c or cc
3476 // [We computed 'value' above.]
3477 cal
.set(UCAL_DOW_LOCAL
, value
);
3478 return pos
.getIndex();
3480 // Want to be able to parse both short and long forms.
3481 // Try count == 4 (cccc) first:
3482 int32_t newStart
= 0;
3483 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3484 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3485 fSymbols
->fStandaloneWeekdays
, fSymbols
->fStandaloneWeekdaysCount
, NULL
, cal
)) > 0)
3488 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3489 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3490 fSymbols
->fStandaloneShortWeekdays
, fSymbols
->fStandaloneShortWeekdaysCount
, NULL
, cal
)) > 0)
3493 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 6) {
3494 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3495 fSymbols
->fStandaloneShorterWeekdays
, fSymbols
->fStandaloneShorterWeekdaysCount
, NULL
, cal
)) > 0)
3498 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
))
3500 // else we allowing parsing as number, below
3504 case UDAT_AM_PM_FIELD
:
3506 // optionally try both wide/abbrev and narrow forms
3507 int32_t newStart
= 0;
3509 if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
< 5 ) {
3510 if ((newStart
= matchString(text
, start
, UCAL_AM_PM
, fSymbols
->fAmPms
, fSymbols
->fAmPmsCount
, NULL
, cal
)) > 0) {
3515 if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
>= 5 ) {
3516 if ((newStart
= matchString(text
, start
, UCAL_AM_PM
, fSymbols
->fNarrowAmPms
, fSymbols
->fNarrowAmPmsCount
, NULL
, cal
)) > 0) {
3520 // no matches for given options
3524 case UDAT_HOUR1_FIELD
:
3525 // [We computed 'value' above.]
3526 if (value
== cal
.getLeastMaximum(UCAL_HOUR
)+1)
3529 // fall through to set field
3531 case UDAT_HOUR0_FIELD
:
3532 cal
.set(UCAL_HOUR
, value
);
3533 return pos
.getIndex();
3535 case UDAT_QUARTER_FIELD
:
3536 if (gotNumber
) // i.e., Q or QQ.
3538 // Don't want to parse the month if it is a string
3539 // while pattern uses numeric style: Q or QQ.
3540 // [We computed 'value' above.]
3541 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
3542 return pos
.getIndex();
3544 // count >= 3 // i.e., QQQ or QQQQ
3545 // Want to be able to parse both short and long forms.
3546 // Try count == 4 first:
3547 int32_t newStart
= 0;
3549 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3550 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3551 fSymbols
->fQuarters
, fSymbols
->fQuartersCount
, cal
)) > 0)
3554 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3555 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3556 fSymbols
->fShortQuarters
, fSymbols
->fShortQuartersCount
, cal
)) > 0)
3559 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
))
3561 // else we allowing parsing as number, below
3562 if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
))
3567 case UDAT_STANDALONE_QUARTER_FIELD
:
3568 if (gotNumber
) // i.e., q or qq.
3570 // Don't want to parse the month if it is a string
3571 // while pattern uses numeric style: q or q.
3572 // [We computed 'value' above.]
3573 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
3574 return pos
.getIndex();
3576 // count >= 3 // i.e., qqq or qqqq
3577 // Want to be able to parse both short and long forms.
3578 // Try count == 4 first:
3579 int32_t newStart
= 0;
3581 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3582 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3583 fSymbols
->fStandaloneQuarters
, fSymbols
->fStandaloneQuartersCount
, cal
)) > 0)
3586 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3587 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3588 fSymbols
->fStandaloneShortQuarters
, fSymbols
->fStandaloneShortQuartersCount
, cal
)) > 0)
3591 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
))
3593 // else we allowing parsing as number, below
3594 if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
))
3599 case UDAT_TIMEZONE_FIELD
: // 'z'
3601 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_SPECIFIC_SHORT
: UTZFMT_STYLE_SPECIFIC_LONG
;
3602 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
3603 if (U_SUCCESS(status
)) {
3604 TimeZone
*tz
= tzfmt
->parse(style
, text
, pos
, tzParseOptions
, tzTimeType
);
3606 cal
.adoptTimeZone(tz
);
3607 return pos
.getIndex();
3613 case UDAT_TIMEZONE_RFC_FIELD
: // 'Z'
3615 UTimeZoneFormatStyle style
= (count
< 4) ?
3616 UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
: ((count
== 5) ? UTZFMT_STYLE_ISO_EXTENDED_FULL
: UTZFMT_STYLE_LOCALIZED_GMT
);
3617 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
3618 if (U_SUCCESS(status
)) {
3619 TimeZone
*tz
= tzfmt
->parse(style
, text
, pos
, tzTimeType
);
3621 cal
.adoptTimeZone(tz
);
3622 return pos
.getIndex();
3627 case UDAT_TIMEZONE_GENERIC_FIELD
: // 'v'
3629 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_GENERIC_SHORT
: UTZFMT_STYLE_GENERIC_LONG
;
3630 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
3631 if (U_SUCCESS(status
)) {
3632 TimeZone
*tz
= tzfmt
->parse(style
, text
, pos
, tzParseOptions
, tzTimeType
);
3634 cal
.adoptTimeZone(tz
);
3635 return pos
.getIndex();
3640 case UDAT_TIMEZONE_SPECIAL_FIELD
: // 'V'
3642 UTimeZoneFormatStyle style
;
3645 style
= UTZFMT_STYLE_ZONE_ID_SHORT
;
3648 style
= UTZFMT_STYLE_ZONE_ID
;
3651 style
= UTZFMT_STYLE_EXEMPLAR_LOCATION
;
3654 style
= UTZFMT_STYLE_GENERIC_LOCATION
;
3657 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
3658 if (U_SUCCESS(status
)) {
3659 TimeZone
*tz
= tzfmt
->parse(style
, text
, pos
, tzTimeType
);
3661 cal
.adoptTimeZone(tz
);
3662 return pos
.getIndex();
3667 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
: // 'O'
3669 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_LOCALIZED_GMT_SHORT
: UTZFMT_STYLE_LOCALIZED_GMT
;
3670 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
3671 if (U_SUCCESS(status
)) {
3672 TimeZone
*tz
= tzfmt
->parse(style
, text
, pos
, tzTimeType
);
3674 cal
.adoptTimeZone(tz
);
3675 return pos
.getIndex();
3680 case UDAT_TIMEZONE_ISO_FIELD
: // 'X'
3682 UTimeZoneFormatStyle style
;
3685 style
= UTZFMT_STYLE_ISO_BASIC_SHORT
;
3688 style
= UTZFMT_STYLE_ISO_BASIC_FIXED
;
3691 style
= UTZFMT_STYLE_ISO_EXTENDED_FIXED
;
3694 style
= UTZFMT_STYLE_ISO_BASIC_FULL
;
3697 style
= UTZFMT_STYLE_ISO_EXTENDED_FULL
;
3700 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
3701 if (U_SUCCESS(status
)) {
3702 TimeZone
*tz
= tzfmt
->parse(style
, text
, pos
, tzTimeType
);
3704 cal
.adoptTimeZone(tz
);
3705 return pos
.getIndex();
3710 case UDAT_TIMEZONE_ISO_LOCAL_FIELD
: // 'x'
3712 UTimeZoneFormatStyle style
;
3715 style
= UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
;
3718 style
= UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
;
3721 style
= UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
;
3724 style
= UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
;
3727 style
= UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
;
3730 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
3731 if (U_SUCCESS(status
)) {
3732 TimeZone
*tz
= tzfmt
->parse(style
, text
, pos
, tzTimeType
);
3734 cal
.adoptTimeZone(tz
);
3735 return pos
.getIndex();
3740 // currently no pattern character is defined for UDAT_TIME_SEPARATOR_FIELD
3741 // so we should not get here. Leave support in for future definition.
3742 case UDAT_TIME_SEPARATOR_FIELD
:
3744 static const UChar def_sep
= DateFormatSymbols::DEFAULT_TIME_SEPARATOR
;
3745 static const UChar alt_sep
= DateFormatSymbols::ALTERNATE_TIME_SEPARATOR
;
3747 // Try matching a time separator.
3748 int32_t count_sep
= 1;
3749 UnicodeString data
[3];
3750 fSymbols
->getTimeSeparatorString(data
[0]);
3752 // Add the default, if different from the locale.
3753 if (data
[0].compare(&def_sep
, 1) != 0) {
3754 data
[count_sep
++].setTo(def_sep
);
3757 // If lenient, add also the alternate, if different from the locale.
3758 if (isLenient() && data
[0].compare(&alt_sep
, 1) != 0) {
3759 data
[count_sep
++].setTo(alt_sep
);
3762 return matchString(text
, start
, UCAL_FIELD_COUNT
/* => nothing to set */, data
, count_sep
, NULL
, cal
);
3765 case UDAT_AM_PM_MIDNIGHT_NOON_FIELD
:
3767 U_ASSERT(dayPeriod
!= NULL
);
3768 int32_t ampmStart
= subParse(text
, start
, 0x61, count
,
3769 obeyCount
, allowNegative
, ambiguousYear
, saveHebrewMonth
, cal
,
3770 patLoc
, numericLeapMonthFormatter
, tzTimeType
);
3772 if (ampmStart
> 0) {
3775 int32_t newStart
= 0;
3777 // Only match the first two strings from the day period strings array.
3778 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3779 if ((newStart
= matchDayPeriodStrings(text
, start
, fSymbols
->fAbbreviatedDayPeriods
,
3780 2, *dayPeriod
)) > 0) {
3784 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 5) {
3785 if ((newStart
= matchDayPeriodStrings(text
, start
, fSymbols
->fNarrowDayPeriods
,
3786 2, *dayPeriod
)) > 0) {
3790 // count == 4, but allow other counts
3791 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
)) {
3792 if ((newStart
= matchDayPeriodStrings(text
, start
, fSymbols
->fWideDayPeriods
,
3793 2, *dayPeriod
)) > 0) {
3802 case UDAT_FLEXIBLE_DAY_PERIOD_FIELD
:
3804 U_ASSERT(dayPeriod
!= NULL
);
3805 int32_t newStart
= 0;
3807 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3808 if ((newStart
= matchDayPeriodStrings(text
, start
, fSymbols
->fAbbreviatedDayPeriods
,
3809 fSymbols
->fAbbreviatedDayPeriodsCount
, *dayPeriod
)) > 0) {
3813 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 5) {
3814 if ((newStart
= matchDayPeriodStrings(text
, start
, fSymbols
->fNarrowDayPeriods
,
3815 fSymbols
->fNarrowDayPeriodsCount
, *dayPeriod
)) > 0) {
3819 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3820 if ((newStart
= matchDayPeriodStrings(text
, start
, fSymbols
->fWideDayPeriods
,
3821 fSymbols
->fWideDayPeriodsCount
, *dayPeriod
)) > 0) {
3830 // Handle "generic" fields
3831 // this is now handled below, outside the switch block
3834 // Handle "generic" fields:
3835 // switch default case now handled here (outside switch block) to allow
3836 // parsing of some string fields as digits for lenient case
3838 int32_t parseStart
= pos
.getIndex();
3839 const UnicodeString
* src
;
3841 if ((start
+count
) > text
.length()) {
3844 text
.extractBetween(0, start
+ count
, temp
);
3849 parseInt(*src
, number
, pos
, allowNegative
,currentNumberFormat
);
3850 if (pos
.getIndex() != parseStart
) {
3851 int32_t val
= number
.getLong();
3853 // Don't need suffix processing here (as in number processing at the beginning of the function);
3854 // the new fields being handled as numeric values (month, weekdays, quarters) should not have suffixes.
3856 // Check the range of the value
3857 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
)) {
3858 int32_t bias
= gFieldRangeBias
[patternCharIndex
];
3859 if (bias
>= 0 && (val
> cal
.getMaximum(field
) + bias
|| val
< cal
.getMinimum(field
) + bias
)) {
3863 int32_t bias
= gFieldRangeBiasLenient
[patternCharIndex
];
3864 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
)) {
3869 // For the following, need to repeat some of the "if (gotNumber)" code above:
3870 // UDAT_[STANDALONE_]MONTH_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_STANDALONE_DAY_FIELD,
3871 // UDAT_[STANDALONE_]QUARTER_FIELD
3872 switch (patternCharIndex
) {
3873 case UDAT_MONTH_FIELD
:
3874 // See notes under UDAT_MONTH_FIELD case above
3875 if (!strcmp(cal
.getType(),"hebrew")) {
3876 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
3877 if (cal
.isSet(UCAL_YEAR
)) {
3878 UErrorCode monthStatus
= U_ZERO_ERROR
;
3879 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
, monthStatus
)) && val
>= 6) {
3880 cal
.set(UCAL_MONTH
, val
);
3882 cal
.set(UCAL_MONTH
, val
- 1);
3885 saveHebrewMonth
= val
;
3888 cal
.set(UCAL_MONTH
, val
- 1);
3891 case UDAT_STANDALONE_MONTH_FIELD
:
3892 cal
.set(UCAL_MONTH
, val
- 1);
3894 case UDAT_DOW_LOCAL_FIELD
:
3895 case UDAT_STANDALONE_DAY_FIELD
:
3896 cal
.set(UCAL_DOW_LOCAL
, val
);
3898 case UDAT_QUARTER_FIELD
:
3899 case UDAT_STANDALONE_QUARTER_FIELD
:
3900 cal
.set(UCAL_MONTH
, (val
- 1) * 3);
3902 case UDAT_RELATED_YEAR_FIELD
:
3903 cal
.setRelatedYear(val
);
3906 cal
.set(field
, val
);
3909 return pos
.getIndex();
3915 * Parse an integer using fNumberFormat. This method is semantically
3916 * const, but actually may modify fNumberFormat.
3918 void SimpleDateFormat::parseInt(const UnicodeString
& text
,
3919 Formattable
& number
,
3921 UBool allowNegative
,
3922 const NumberFormat
*fmt
) const {
3923 parseInt(text
, number
, -1, pos
, allowNegative
,fmt
);
3927 * Parse an integer using fNumberFormat up to maxDigits.
3929 void SimpleDateFormat::parseInt(const UnicodeString
& text
,
3930 Formattable
& number
,
3933 UBool allowNegative
,
3934 const NumberFormat
*fmt
) const {
3935 UnicodeString oldPrefix
;
3936 auto* fmtAsDF
= dynamic_cast<const DecimalFormat
*>(fmt
);
3937 LocalPointer
<DecimalFormat
> df
;
3938 if (!allowNegative
&& fmtAsDF
!= nullptr) {
3939 df
.adoptInstead(dynamic_cast<DecimalFormat
*>(fmtAsDF
->clone()));
3941 // Memory allocation error
3944 df
->setNegativePrefix(UnicodeString(TRUE
, SUPPRESS_NEGATIVE_PREFIX
, -1));
3945 fmt
= df
.getAlias();
3947 int32_t oldPos
= pos
.getIndex();
3948 fmt
->parse(text
, number
, pos
);
3950 if (maxDigits
> 0) {
3951 // adjust the result to fit into
3952 // the maxDigits and move the position back
3953 int32_t nDigits
= pos
.getIndex() - oldPos
;
3954 if (nDigits
> maxDigits
) {
3955 int32_t val
= number
.getLong();
3956 nDigits
-= maxDigits
;
3957 while (nDigits
> 0) {
3961 pos
.setIndex(oldPos
+ maxDigits
);
3962 number
.setLong(val
);
3967 int32_t SimpleDateFormat::countDigits(const UnicodeString
& text
, int32_t start
, int32_t end
) const {
3968 int32_t numDigits
= 0;
3969 int32_t idx
= start
;
3971 UChar32 cp
= text
.char32At(idx
);
3972 if (u_isdigit(cp
)) {
3975 idx
+= U16_LENGTH(cp
);
3980 //----------------------------------------------------------------------
3982 void SimpleDateFormat::translatePattern(const UnicodeString
& originalPattern
,
3983 UnicodeString
& translatedPattern
,
3984 const UnicodeString
& from
,
3985 const UnicodeString
& to
,
3988 // run through the pattern and convert any pattern symbols from the version
3989 // in "from" to the corresponding character in "to". This code takes
3990 // quoted strings into account (it doesn't try to translate them), and it signals
3991 // an error if a particular "pattern character" doesn't appear in "from".
3992 // Depending on the values of "from" and "to" this can convert from generic
3993 // to localized patterns or localized to generic.
3994 if (U_FAILURE(status
)) {
3998 translatedPattern
.remove();
3999 UBool inQuote
= FALSE
;
4000 for (int32_t i
= 0; i
< originalPattern
.length(); ++i
) {
4001 UChar c
= originalPattern
[i
];
4009 } else if (isSyntaxChar(c
)) {
4010 int32_t ci
= from
.indexOf(c
);
4012 status
= U_INVALID_FORMAT_ERROR
;
4018 translatedPattern
+= c
;
4021 status
= U_INVALID_FORMAT_ERROR
;
4026 //----------------------------------------------------------------------
4029 SimpleDateFormat::toPattern(UnicodeString
& result
) const
4035 //----------------------------------------------------------------------
4038 SimpleDateFormat::toLocalizedPattern(UnicodeString
& result
,
4039 UErrorCode
& status
) const
4041 translatePattern(fPattern
, result
,
4042 UnicodeString(DateFormatSymbols::getPatternUChars()),
4043 fSymbols
->fLocalPatternChars
, status
);
4047 //----------------------------------------------------------------------
4050 SimpleDateFormat::applyPattern(const UnicodeString
& pattern
)
4055 // Hack to update use of Gannen year numbering for ja@calendar=japanese -
4056 // use only if format is non-numeric (includes 年) and no other fDateOverride.
4057 if (fCalendar
!= nullptr && uprv_strcmp(fCalendar
->getType(),"japanese") == 0 &&
4058 uprv_strcmp(fLocale
.getLanguage(),"ja") == 0) {
4059 if (fDateOverride
==UnicodeString(u
"y=jpanyear") && !fHasHanYearChar
) {
4060 // Gannen numbering is set but new pattern should not use it, unset;
4061 // use procedure from adoptNumberFormat to clear overrides
4062 if (fSharedNumberFormatters
) {
4063 freeSharedNumberFormatters(fSharedNumberFormatters
);
4064 fSharedNumberFormatters
= NULL
;
4066 fDateOverride
.setToBogus(); // record status
4067 } else if (fDateOverride
.isBogus() && fHasHanYearChar
) {
4068 // No current override (=> no Gannen numbering) but new pattern needs it;
4069 // use procedures from initNUmberFormatters / adoptNumberFormat
4071 if (fSharedNumberFormatters
== NULL
) {
4072 fSharedNumberFormatters
= allocSharedNumberFormatters();
4074 umtx_unlock(LOCK());
4075 if (fSharedNumberFormatters
!= NULL
) {
4076 Locale
ovrLoc(fLocale
.getLanguage(),fLocale
.getCountry(),fLocale
.getVariant(),"numbers=jpanyear");
4077 UErrorCode status
= U_ZERO_ERROR
;
4078 const SharedNumberFormat
*snf
= createSharedNumberFormat(ovrLoc
, status
);
4079 if (U_SUCCESS(status
)) {
4080 // Now that we have an appropriate number formatter, fill in the
4081 // appropriate slot in the number formatters table.
4082 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(u
'y');
4083 SharedObject::copyPtr(snf
, fSharedNumberFormatters
[patternCharIndex
]);
4084 snf
->deleteIfZeroRefCount();
4085 fDateOverride
.setTo(u
"y=jpanyear", -1); // record status
4092 //----------------------------------------------------------------------
4095 SimpleDateFormat::applyLocalizedPattern(const UnicodeString
& pattern
,
4098 translatePattern(pattern
, fPattern
,
4099 fSymbols
->fLocalPatternChars
,
4100 UnicodeString(DateFormatSymbols::getPatternUChars()), status
);
4103 //----------------------------------------------------------------------
4105 const DateFormatSymbols
*
4106 SimpleDateFormat::getDateFormatSymbols() const
4111 //----------------------------------------------------------------------
4114 SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols
* newFormatSymbols
)
4117 fSymbols
= newFormatSymbols
;
4120 //----------------------------------------------------------------------
4122 SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols
& newFormatSymbols
)
4125 fSymbols
= new DateFormatSymbols(newFormatSymbols
);
4128 //----------------------------------------------------------------------
4129 const TimeZoneFormat
*
4130 SimpleDateFormat::getTimeZoneFormat(void) const {
4131 // TimeZoneFormat initialization might fail when out of memory.
4132 // If we always initialize TimeZoneFormat instance, we can return
4133 // such status there. For now, this implementation lazily instantiates
4134 // a TimeZoneFormat for performance optimization reasons, but cannot
4135 // propagate such error (probably just out of memory case) to the caller.
4136 UErrorCode status
= U_ZERO_ERROR
;
4137 return (const TimeZoneFormat
*)tzFormat(status
);
4140 //----------------------------------------------------------------------
4142 SimpleDateFormat::adoptTimeZoneFormat(TimeZoneFormat
* timeZoneFormatToAdopt
)
4144 delete fTimeZoneFormat
;
4145 fTimeZoneFormat
= timeZoneFormatToAdopt
;
4148 //----------------------------------------------------------------------
4150 SimpleDateFormat::setTimeZoneFormat(const TimeZoneFormat
& newTimeZoneFormat
)
4152 delete fTimeZoneFormat
;
4153 fTimeZoneFormat
= new TimeZoneFormat(newTimeZoneFormat
);
4156 //----------------------------------------------------------------------
4159 void SimpleDateFormat::adoptCalendar(Calendar
* calendarToAdopt
)
4161 UErrorCode status
= U_ZERO_ERROR
;
4162 Locale
calLocale(fLocale
);
4163 DateFormatSymbols
*newSymbols
= fSymbols
;
4164 if (!newSymbols
|| fCalendar
->getType() != calendarToAdopt
->getType()) {
4165 calLocale
.setKeywordValue("calendar", calendarToAdopt
->getType(), status
);
4166 newSymbols
= DateFormatSymbols::createForLocale(calLocale
, status
);
4167 if (U_FAILURE(status
)) {
4171 DateFormat::adoptCalendar(calendarToAdopt
);
4172 if (fSymbols
!= newSymbols
) {
4174 fSymbols
= newSymbols
;
4176 initializeDefaultCentury(); // we need a new century (possibly)
4180 //----------------------------------------------------------------------
4183 // override the DateFormat implementation in order to
4184 // lazily initialize fCapitalizationBrkIter
4186 SimpleDateFormat::setContext(UDisplayContext value
, UErrorCode
& status
)
4188 DateFormat::setContext(value
, status
);
4189 #if !UCONFIG_NO_BREAK_ITERATION
4190 if (U_SUCCESS(status
)) {
4191 if ( fCapitalizationBrkIter
== NULL
&& (value
==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
||
4192 value
==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
|| value
==UDISPCTX_CAPITALIZATION_FOR_STANDALONE
) ) {
4193 status
= U_ZERO_ERROR
;
4194 fCapitalizationBrkIter
= BreakIterator::createSentenceInstance(fLocale
, status
);
4195 if (U_FAILURE(status
)) {
4196 delete fCapitalizationBrkIter
;
4197 fCapitalizationBrkIter
= NULL
;
4205 //----------------------------------------------------------------------
4209 SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field
) const {
4210 return isFieldUnitIgnored(fPattern
, field
);
4215 SimpleDateFormat::isFieldUnitIgnored(const UnicodeString
& pattern
,
4216 UCalendarDateFields field
) {
4217 int32_t fieldLevel
= fgCalendarFieldToLevel
[field
];
4220 UBool inQuote
= FALSE
;
4224 for (int32_t i
= 0; i
< pattern
.length(); ++i
) {
4226 if (ch
!= prevCh
&& count
> 0) {
4227 level
= getLevelFromChar(prevCh
);
4228 // the larger the level, the smaller the field unit.
4229 if (fieldLevel
<= level
) {
4235 if ((i
+1) < pattern
.length() && pattern
[i
+1] == QUOTE
) {
4238 inQuote
= ! inQuote
;
4241 else if (!inQuote
&& isSyntaxChar(ch
)) {
4248 level
= getLevelFromChar(prevCh
);
4249 if (fieldLevel
<= level
) {
4256 //----------------------------------------------------------------------
4259 SimpleDateFormat::getSmpFmtLocale(void) const {
4263 //----------------------------------------------------------------------
4266 SimpleDateFormat::checkIntSuffix(const UnicodeString
& text
, int32_t start
,
4267 int32_t patLoc
, UBool isNegative
) const {
4270 int32_t patternMatch
;
4271 int32_t textPreMatch
;
4272 int32_t textPostMatch
;
4274 // check that we are still in range
4275 if ( (start
> text
.length()) ||
4278 (patLoc
> fPattern
.length())) {
4279 // out of range, don't advance location in text
4284 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(fNumberFormat
);
4285 if (decfmt
!= NULL
) {
4287 suf
= decfmt
->getNegativeSuffix(suf
);
4290 suf
= decfmt
->getPositiveSuffix(suf
);
4295 if (suf
.length() <= 0) {
4299 // check suffix will be encountered in the pattern
4300 patternMatch
= compareSimpleAffix(suf
,fPattern
,patLoc
);
4302 // check if a suffix will be encountered in the text
4303 textPreMatch
= compareSimpleAffix(suf
,text
,start
);
4305 // check if a suffix was encountered in the text
4306 textPostMatch
= compareSimpleAffix(suf
,text
,start
-suf
.length());
4308 // check for suffix match
4309 if ((textPreMatch
>= 0) && (patternMatch
>= 0) && (textPreMatch
== patternMatch
)) {
4312 else if ((textPostMatch
>= 0) && (patternMatch
>= 0) && (textPostMatch
== patternMatch
)) {
4313 return start
- suf
.length();
4316 // should not get here
4320 //----------------------------------------------------------------------
4323 SimpleDateFormat::compareSimpleAffix(const UnicodeString
& affix
,
4324 const UnicodeString
& input
,
4325 int32_t pos
) const {
4326 int32_t start
= pos
;
4327 for (int32_t i
=0; i
<affix
.length(); ) {
4328 UChar32 c
= affix
.char32At(i
);
4329 int32_t len
= U16_LENGTH(c
);
4330 if (PatternProps::isWhiteSpace(c
)) {
4331 // We may have a pattern like: \u200F \u0020
4332 // and input text like: \u200F \u0020
4333 // Note that U+200F and U+0020 are Pattern_White_Space but only
4334 // U+0020 is UWhiteSpace. So we have to first do a direct
4335 // match of the run of Pattern_White_Space in the pattern,
4336 // then match any extra characters.
4337 UBool literalMatch
= FALSE
;
4338 while (pos
< input
.length() &&
4339 input
.char32At(pos
) == c
) {
4340 literalMatch
= TRUE
;
4343 if (i
== affix
.length()) {
4346 c
= affix
.char32At(i
);
4347 len
= U16_LENGTH(c
);
4348 if (!PatternProps::isWhiteSpace(c
)) {
4353 // Advance over run in pattern
4354 i
= skipPatternWhiteSpace(affix
, i
);
4356 // Advance over run in input text
4357 // Must see at least one white space char in input,
4358 // unless we've already matched some characters literally.
4360 pos
= skipUWhiteSpace(input
, pos
);
4361 if (pos
== s
&& !literalMatch
) {
4365 // If we skip UWhiteSpace in the input text, we need to skip it in the pattern.
4366 // Otherwise, the previous lines may have skipped over text (such as U+00A0) that
4367 // is also in the affix.
4368 i
= skipUWhiteSpace(affix
, i
);
4370 if (pos
< input
.length() &&
4371 input
.char32At(pos
) == c
) {
4382 //----------------------------------------------------------------------
4385 SimpleDateFormat::skipPatternWhiteSpace(const UnicodeString
& text
, int32_t pos
) const {
4386 const UChar
* s
= text
.getBuffer();
4387 return (int32_t)(PatternProps::skipWhiteSpace(s
+ pos
, text
.length() - pos
) - s
);
4390 //----------------------------------------------------------------------
4393 SimpleDateFormat::skipUWhiteSpace(const UnicodeString
& text
, int32_t pos
) const {
4394 while (pos
< text
.length()) {
4395 UChar32 c
= text
.char32At(pos
);
4396 if (!u_isUWhiteSpace(c
)) {
4399 pos
+= U16_LENGTH(c
);
4404 //----------------------------------------------------------------------
4406 // Lazy TimeZoneFormat instantiation, semantically const.
4408 SimpleDateFormat::tzFormat(UErrorCode
&status
) const {
4409 if (fTimeZoneFormat
== NULL
) {
4412 if (fTimeZoneFormat
== NULL
) {
4413 TimeZoneFormat
*tzfmt
= TimeZoneFormat::createInstance(fLocale
, status
);
4414 if (U_FAILURE(status
)) {
4418 const_cast<SimpleDateFormat
*>(this)->fTimeZoneFormat
= tzfmt
;
4421 umtx_unlock(LOCK());
4423 return fTimeZoneFormat
;
4426 void SimpleDateFormat::parsePattern() {
4429 fHasHanYearChar
= FALSE
;
4431 int len
= fPattern
.length();
4432 UBool inQuote
= FALSE
;
4433 for (int32_t i
= 0; i
< len
; ++i
) {
4434 UChar ch
= fPattern
[i
];
4438 if (ch
== 0x5E74) { // don't care whether this is inside quotes
4439 fHasHanYearChar
= TRUE
;
4442 if (ch
== 0x6D) { // 0x6D == 'm'
4445 if (ch
== 0x73) { // 0x73 == 's'
4454 #endif /* #if !UCONFIG_NO_FORMATTING */