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"
78 #include "dtptngen_impl.h" // for datePatternHasNumericCore()
80 #define DEBUG_SYNTHETIC_TIMEFMTS 0
82 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) || DEBUG_SYNTHETIC_TIMEFMTS
86 // *****************************************************************************
87 // class SimpleDateFormat
88 // *****************************************************************************
93 * Last-resort string to use for "GMT" when constructing time zone strings.
95 // For time zones that have no names, use strings GMT+minutes and
96 // GMT-minutes. For instance, in France the time zone is GMT+60.
97 // Also accepted are GMT+H:MM or GMT-H:MM.
98 // Currently not being used
99 //static const UChar gGmt[] = {0x0047, 0x004D, 0x0054, 0x0000}; // "GMT"
100 //static const UChar gGmtPlus[] = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+"
101 //static const UChar gGmtMinus[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-"
102 //static const UChar gDefGmtPat[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
103 //static const UChar gDefGmtNegHmsPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */
104 //static const UChar gDefGmtNegHmPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */
105 //static const UChar gDefGmtPosHmsPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */
106 //static const UChar gDefGmtPosHmPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */
107 //static const UChar gUt[] = {0x0055, 0x0054, 0x0000}; // "UT"
108 //static const UChar gUtc[] = {0x0055, 0x0054, 0x0043, 0x0000}; // "UT"
110 typedef enum GmtPatSize
{
121 // Stuff needed for numbering system overrides
123 typedef enum OvrStrType
{
129 static const UDateFormatField kDateFields
[] = {
133 UDAT_DAY_OF_YEAR_FIELD
,
134 UDAT_DAY_OF_WEEK_IN_MONTH_FIELD
,
135 UDAT_WEEK_OF_YEAR_FIELD
,
136 UDAT_WEEK_OF_MONTH_FIELD
,
138 UDAT_EXTENDED_YEAR_FIELD
,
139 UDAT_JULIAN_DAY_FIELD
,
140 UDAT_STANDALONE_DAY_FIELD
,
141 UDAT_STANDALONE_MONTH_FIELD
,
143 UDAT_STANDALONE_QUARTER_FIELD
,
144 UDAT_YEAR_NAME_FIELD
,
145 UDAT_RELATED_YEAR_FIELD
};
146 static const int8_t kDateFieldsCount
= 16;
148 static const UDateFormatField kTimeFields
[] = {
149 UDAT_HOUR_OF_DAY1_FIELD
,
150 UDAT_HOUR_OF_DAY0_FIELD
,
153 UDAT_FRACTIONAL_SECOND_FIELD
,
156 UDAT_MILLISECONDS_IN_DAY_FIELD
,
157 UDAT_TIMEZONE_RFC_FIELD
,
158 UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
};
159 static const int8_t kTimeFieldsCount
= 10;
162 // This is a pattern-of-last-resort used when we can't load a usable pattern out
164 static const UChar gDefaultPattern
[] =
166 0x79, 0x79, 0x79, 0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0
167 }; /* "yyyyMMdd hh:mm a" */
169 // This prefix is designed to NEVER MATCH real text, in order to
170 // suppress the parsing of negative numbers. Adjust as needed (if
171 // this becomes valid Unicode).
172 static const UChar SUPPRESS_NEGATIVE_PREFIX
[] = {0xAB00, 0};
175 * These are the tags we expect to see in normal resource bundle files associated
178 static const UChar QUOTE
= 0x27; // Single quote
181 * The field range check bias for each UDateFormatField.
182 * The bias is added to the minimum and maximum values
183 * before they are compared to the parsed number.
184 * For example, the calendar stores zero-based month numbers
185 * but the parsed month numbers start at 1, so the bias is 1.
187 * A value of -1 means that the value is not checked.
189 static const int32_t gFieldRangeBias
[] = {
190 -1, // 'G' - UDAT_ERA_FIELD
191 -1, // 'y' - UDAT_YEAR_FIELD
192 1, // 'M' - UDAT_MONTH_FIELD
193 0, // 'd' - UDAT_DATE_FIELD
194 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
195 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
196 0, // 'm' - UDAT_MINUTE_FIELD
197 0, // 's' - UDAT_SECOND_FIELD
198 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
199 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
200 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
201 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
202 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
203 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
204 -1, // 'a' - UDAT_AM_PM_FIELD
205 -1, // 'h' - UDAT_HOUR1_FIELD
206 -1, // 'K' - UDAT_HOUR0_FIELD
207 -1, // 'z' - UDAT_TIMEZONE_FIELD
208 -1, // 'Y' - UDAT_YEAR_WOY_FIELD
209 -1, // 'e' - UDAT_DOW_LOCAL_FIELD
210 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
211 -1, // 'g' - UDAT_JULIAN_DAY_FIELD
212 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
213 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
214 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
215 0, // 'c' - UDAT_STANDALONE_DAY_FIELD
216 1, // 'L' - UDAT_STANDALONE_MONTH_FIELD
217 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
218 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
219 -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
220 -1, // 'U' - UDAT_YEAR_NAME_FIELD
221 -1, // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
222 -1, // 'X' - UDAT_TIMEZONE_ISO_FIELD
223 -1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
224 -1, // 'r' - UDAT_RELATED_YEAR_FIELD
225 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
226 -1, // ':' - UDAT_TIME_SEPARATOR_FIELD
228 -1, // (no pattern character currently) - UDAT_TIME_SEPARATOR_FIELD
231 // A slightly looser range check for lenient parsing
232 static const int32_t gFieldRangeBiasLenient
[] = {
233 -1, // 'G' - UDAT_ERA_FIELD
234 -1, // 'y' - UDAT_YEAR_FIELD
235 8, // 'M' - UDAT_MONTH_FIELD (allow calendar max + 7, e.g. 19 for grego 1-based month)
236 18, // 'd' - UDAT_DATE_FIELD (allow calendar max + 18, e.g. 49 for grego; tests require at least 40 for grego)
237 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
238 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
239 40, // 'm' - UDAT_MINUTE_FIELD (allow calendar max + 40, e.g. 99)
240 40, // 's' - UDAT_SECOND_FIELD (allow calendar max + 40, e.g. 99)
241 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
242 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
243 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
244 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
245 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
246 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
247 -1, // 'a' - UDAT_AM_PM_FIELD
248 -1, // 'h' - UDAT_HOUR1_FIELD
249 -1, // 'K' - UDAT_HOUR0_FIELD
250 -1, // 'z' - UDAT_TIMEZONE_FIELD
251 -1, // 'Y' - UDAT_YEAR_WOY_FIELD
252 -1, // 'e' - UDAT_DOW_LOCAL_FIELD
253 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
254 -1, // 'g' - UDAT_JULIAN_DAY_FIELD
255 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
256 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
257 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
258 18, // 'c' - UDAT_STANDALONE_DAY_FIELD (allow calendar max + 18, e.g. 49 for grego)
259 8, // 'L' - UDAT_STANDALONE_MONTH_FIELD (allow calendar max + 7, e.g. 19 for grego 1-based month)
260 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
261 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
262 -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
263 -1, // 'U' - UDAT_YEAR_NAME_FIELD
264 -1, // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
265 -1, // 'X' - UDAT_TIMEZONE_ISO_FIELD
266 -1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
267 -1, // 'r' - UDAT_RELATED_YEAR_FIELD
268 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
269 -1, // ':' - UDAT_TIME_SEPARATOR_FIELD
271 -1, // (no pattern character currently) - UDAT_TIME_SEPARATOR_FIELD
275 // When calendar uses hebr numbering (i.e. he@calendar=hebrew),
276 // offset the years within the current millenium down to 1-999
277 static const int32_t HEBREW_CAL_CUR_MILLENIUM_START_YEAR
= 5000;
278 static const int32_t HEBREW_CAL_CUR_MILLENIUM_END_YEAR
= 6000;
282 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat
)
284 SimpleDateFormat::NSOverride::~NSOverride() {
291 void SimpleDateFormat::NSOverride::free() {
292 NSOverride
*cur
= this;
294 NSOverride
*next_temp
= cur
->next
;
300 // no matter what the locale's default number format looked like, we want
301 // to modify it so that it doesn't use thousands separators, doesn't always
302 // show the decimal point, and recognizes integers only when parsing
303 static void fixNumberFormatForDates(NumberFormat
&nf
) {
304 // Use new group setter equivalent to
305 // setGroupingUsed(FALSE);
306 // setDecimalSeparatorAlwaysShown(FALSE);
307 // setParseIntegerOnly(TRUE);
308 // setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
309 nf
.setDateSettings(); // Apple rdar://50064762
312 static const SharedNumberFormat
*createSharedNumberFormat(
313 NumberFormat
*nfToAdopt
) {
314 fixNumberFormatForDates(*nfToAdopt
);
315 const SharedNumberFormat
*result
= new SharedNumberFormat(nfToAdopt
);
316 if (result
== NULL
) {
322 static const SharedNumberFormat
*createSharedNumberFormat(
323 const Locale
&loc
, UErrorCode
&status
) {
324 NumberFormat
*nf
= NumberFormat::createInstance(loc
, status
);
325 if (U_FAILURE(status
)) {
328 const SharedNumberFormat
*result
= createSharedNumberFormat(nf
);
329 if (result
== NULL
) {
330 status
= U_MEMORY_ALLOCATION_ERROR
;
335 static const SharedNumberFormat
**allocSharedNumberFormatters() {
336 const SharedNumberFormat
**result
= (const SharedNumberFormat
**)
337 uprv_malloc(UDAT_FIELD_COUNT
* sizeof(const SharedNumberFormat
*));
338 if (result
== NULL
) {
341 for (int32_t i
= 0; i
< UDAT_FIELD_COUNT
; ++i
) {
347 static void freeSharedNumberFormatters(const SharedNumberFormat
** list
) {
348 for (int32_t i
= 0; i
< UDAT_FIELD_COUNT
; ++i
) {
349 SharedObject::clearPtr(list
[i
]);
354 const NumberFormat
*SimpleDateFormat::getNumberFormatByIndex(
355 UDateFormatField index
) const {
356 if (fSharedNumberFormatters
== NULL
||
357 fSharedNumberFormatters
[index
] == NULL
) {
358 return fNumberFormat
;
360 return &(**fSharedNumberFormatters
[index
]);
363 //----------------------------------------------------------------------
365 SimpleDateFormat::~SimpleDateFormat()
368 if (fSharedNumberFormatters
) {
369 freeSharedNumberFormatters(fSharedNumberFormatters
);
371 if (fTimeZoneFormat
) {
372 delete fTimeZoneFormat
;
374 freeFastNumberFormatters();
376 #if !UCONFIG_NO_BREAK_ITERATION
377 delete fCapitalizationBrkIter
;
381 //----------------------------------------------------------------------
383 SimpleDateFormat::SimpleDateFormat(UErrorCode
& status
)
384 : fLocale(Locale::getDefault()),
386 fTimeZoneFormat(NULL
),
387 fSharedNumberFormatters(NULL
),
388 fCapitalizationBrkIter(NULL
)
390 initializeBooleanAttributes();
391 construct(kShort
, (EStyle
) (kShort
+ kDateOffset
), fLocale
, status
);
392 initializeDefaultCentury();
395 //----------------------------------------------------------------------
397 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
400 fLocale(Locale::getDefault()),
402 fTimeZoneFormat(NULL
),
403 fSharedNumberFormatters(NULL
),
404 fCapitalizationBrkIter(NULL
)
406 fDateOverride
.setToBogus();
407 fTimeOverride
.setToBogus();
408 initializeBooleanAttributes();
409 initializeCalendar(NULL
,fLocale
,status
);
410 fSymbols
= DateFormatSymbols::createForLocale(fLocale
, status
);
411 initialize(fLocale
, status
);
412 initializeDefaultCentury();
416 //----------------------------------------------------------------------
418 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
419 const UnicodeString
& override
,
422 fLocale(Locale::getDefault()),
424 fTimeZoneFormat(NULL
),
425 fSharedNumberFormatters(NULL
),
426 fCapitalizationBrkIter(NULL
)
428 fDateOverride
.setTo(override
);
429 fTimeOverride
.setToBogus();
430 initializeBooleanAttributes();
431 initializeCalendar(NULL
,fLocale
,status
);
432 fSymbols
= DateFormatSymbols::createForLocale(fLocale
, status
);
433 initialize(fLocale
, status
);
434 initializeDefaultCentury();
436 processOverrideString(fLocale
,override
,kOvrStrBoth
,status
);
440 //----------------------------------------------------------------------
442 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
443 const Locale
& locale
,
447 fTimeZoneFormat(NULL
),
448 fSharedNumberFormatters(NULL
),
449 fCapitalizationBrkIter(NULL
)
452 fDateOverride
.setToBogus();
453 fTimeOverride
.setToBogus();
454 initializeBooleanAttributes();
456 initializeCalendar(NULL
,fLocale
,status
);
457 fSymbols
= DateFormatSymbols::createForLocale(fLocale
, status
);
458 initialize(fLocale
, status
);
459 initializeDefaultCentury();
462 //----------------------------------------------------------------------
464 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
465 const UnicodeString
& override
,
466 const Locale
& locale
,
470 fTimeZoneFormat(NULL
),
471 fSharedNumberFormatters(NULL
),
472 fCapitalizationBrkIter(NULL
)
475 fDateOverride
.setTo(override
);
476 fTimeOverride
.setToBogus();
477 initializeBooleanAttributes();
479 initializeCalendar(NULL
,fLocale
,status
);
480 fSymbols
= DateFormatSymbols::createForLocale(fLocale
, status
);
481 initialize(fLocale
, status
);
482 initializeDefaultCentury();
484 processOverrideString(locale
,override
,kOvrStrBoth
,status
);
488 //----------------------------------------------------------------------
490 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
491 DateFormatSymbols
* symbolsToAdopt
,
494 fLocale(Locale::getDefault()),
495 fSymbols(symbolsToAdopt
),
496 fTimeZoneFormat(NULL
),
497 fSharedNumberFormatters(NULL
),
498 fCapitalizationBrkIter(NULL
)
501 fDateOverride
.setToBogus();
502 fTimeOverride
.setToBogus();
503 initializeBooleanAttributes();
505 initializeCalendar(NULL
,fLocale
,status
);
506 initialize(fLocale
, status
);
507 initializeDefaultCentury();
510 //----------------------------------------------------------------------
512 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
513 const DateFormatSymbols
& symbols
,
516 fLocale(Locale::getDefault()),
517 fSymbols(new DateFormatSymbols(symbols
)),
518 fTimeZoneFormat(NULL
),
519 fSharedNumberFormatters(NULL
),
520 fCapitalizationBrkIter(NULL
)
523 fDateOverride
.setToBogus();
524 fTimeOverride
.setToBogus();
525 initializeBooleanAttributes();
527 initializeCalendar(NULL
, fLocale
, status
);
528 initialize(fLocale
, status
);
529 initializeDefaultCentury();
532 //----------------------------------------------------------------------
534 // Not for public consumption; used by DateFormat
535 SimpleDateFormat::SimpleDateFormat(EStyle timeStyle
,
537 const Locale
& locale
,
541 fTimeZoneFormat(NULL
),
542 fSharedNumberFormatters(NULL
),
543 fCapitalizationBrkIter(NULL
)
545 initializeBooleanAttributes();
546 construct(timeStyle
, dateStyle
, fLocale
, status
);
547 if(U_SUCCESS(status
)) {
548 initializeDefaultCentury();
552 //----------------------------------------------------------------------
555 * Not for public consumption; used by DateFormat. This constructor
556 * never fails. If the resource data is not available, it uses the
557 * the last resort symbols.
559 SimpleDateFormat::SimpleDateFormat(const Locale
& locale
,
561 : fPattern(gDefaultPattern
),
564 fTimeZoneFormat(NULL
),
565 fSharedNumberFormatters(NULL
),
566 fCapitalizationBrkIter(NULL
)
568 if (U_FAILURE(status
)) return;
569 initializeBooleanAttributes();
570 initializeCalendar(NULL
, fLocale
, status
);
571 fSymbols
= DateFormatSymbols::createForLocale(fLocale
, status
);
572 if (U_FAILURE(status
))
574 status
= U_ZERO_ERROR
;
576 // This constructor doesn't fail; it uses last resort data
577 fSymbols
= new DateFormatSymbols(status
);
580 status
= U_MEMORY_ALLOCATION_ERROR
;
585 fDateOverride
.setToBogus();
586 fTimeOverride
.setToBogus();
588 initialize(fLocale
, status
);
589 if(U_SUCCESS(status
)) {
590 initializeDefaultCentury();
594 //----------------------------------------------------------------------
596 SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat
& other
)
598 fLocale(other
.fLocale
),
600 fTimeZoneFormat(NULL
),
601 fSharedNumberFormatters(NULL
),
602 fCapitalizationBrkIter(NULL
)
604 initializeBooleanAttributes();
608 //----------------------------------------------------------------------
610 SimpleDateFormat
& SimpleDateFormat::operator=(const SimpleDateFormat
& other
)
612 if (this == &other
) {
615 freeFastNumberFormatters(); // deletes refs to fNumberFormat's symbols
616 DateFormat::operator=(other
);
617 fDateOverride
= other
.fDateOverride
;
618 fTimeOverride
= other
.fTimeOverride
;
624 fSymbols
= new DateFormatSymbols(*other
.fSymbols
);
626 fDefaultCenturyStart
= other
.fDefaultCenturyStart
;
627 fDefaultCenturyStartYear
= other
.fDefaultCenturyStartYear
;
628 fHaveDefaultCentury
= other
.fHaveDefaultCentury
;
630 fPattern
= other
.fPattern
;
631 fHasMinute
= other
.fHasMinute
;
632 fHasSecond
= other
.fHasSecond
;
634 fLocale
= other
.fLocale
;
635 // TimeZoneFormat can now be set independently via setter.
636 // If it is NULL, it will be lazily initialized from locale
637 delete fTimeZoneFormat
;
638 fTimeZoneFormat
= NULL
;
639 if (other
.fTimeZoneFormat
) {
640 fTimeZoneFormat
= new TimeZoneFormat(*other
.fTimeZoneFormat
);
643 #if !UCONFIG_NO_BREAK_ITERATION
644 if (other
.fCapitalizationBrkIter
!= NULL
) {
645 fCapitalizationBrkIter
= (other
.fCapitalizationBrkIter
)->clone();
649 if (fSharedNumberFormatters
!= NULL
) {
650 freeSharedNumberFormatters(fSharedNumberFormatters
);
651 fSharedNumberFormatters
= NULL
;
653 if (other
.fSharedNumberFormatters
!= NULL
) {
654 fSharedNumberFormatters
= allocSharedNumberFormatters();
655 if (fSharedNumberFormatters
) {
656 for (int32_t i
= 0; i
< UDAT_FIELD_COUNT
; ++i
) {
657 SharedObject::copyPtr(
658 other
.fSharedNumberFormatters
[i
],
659 fSharedNumberFormatters
[i
]);
664 UErrorCode localStatus
= U_ZERO_ERROR
;
665 initFastNumberFormatters(localStatus
);
670 //----------------------------------------------------------------------
673 SimpleDateFormat::clone() const
675 return new SimpleDateFormat(*this);
678 //----------------------------------------------------------------------
681 SimpleDateFormat::operator==(const Format
& other
) const
683 if (DateFormat::operator==(other
)) {
684 // The DateFormat::operator== check for fCapitalizationContext equality above
685 // is sufficient to check equality of all derived context-related data.
686 // DateFormat::operator== guarantees following cast is safe
687 SimpleDateFormat
* that
= (SimpleDateFormat
*)&other
;
688 return (fPattern
== that
->fPattern
&&
689 fSymbols
!= NULL
&& // Check for pathological object
690 that
->fSymbols
!= NULL
&& // Check for pathological object
691 *fSymbols
== *that
->fSymbols
&&
692 fHaveDefaultCentury
== that
->fHaveDefaultCentury
&&
693 fDefaultCenturyStart
== that
->fDefaultCenturyStart
&&
694 // Check fTimeZoneFormat, it can be set independently via setter
695 ((fTimeZoneFormat
== NULL
&& that
->fTimeZoneFormat
== NULL
) ||
696 (fTimeZoneFormat
!= NULL
&& that
->fTimeZoneFormat
!= NULL
&& *fTimeZoneFormat
== *that
->fTimeZoneFormat
)) &&
697 // Check override strings (these also indicate any relevant
698 // differences in fNumberFormatters, fOverrideList)
699 fDateOverride
== that
->fDateOverride
&&
700 fTimeOverride
== that
->fTimeOverride
);
705 //----------------------------------------------------------------------
706 static const UChar
* timeSkeletons
[4] = {
707 u
"jmmsszzzz", // kFull
713 enum { kBaseNameMax
= ULOC_LANG_CAPACITY
+ ULOC_SCRIPT_CAPACITY
+ ULOC_COUNTRY_CAPACITY
}; // includes separators and 0 term
715 void SimpleDateFormat::construct(EStyle timeStyle
,
717 const Locale
& locale
,
720 // called by several constructors to load pattern data from the resources
721 if (U_FAILURE(status
)) return;
723 // We will need the calendar to know what type of symbols to load.
724 initializeCalendar(NULL
, locale
, status
);
725 if (U_FAILURE(status
)) return;
727 // Load date time patterns directly from resources.
728 // rdar://problem/26911014: If no resource bundle exists for the requested locale, we generally want to bias ourselves
729 // toward preserving the country (rather than the language, which is how things normally work) when retrieving certain
730 // date/time formatting patterns. We want to do this for date patterns, but not for time and date+time patterns
731 // (this might change-- see rdar://problem/62242807 ). We use countryBundle below for these patterns. Note that
732 // countryBundle isn't _always_ what you get from calling ures_openWithCountryFallback() because date patterns are
733 // complicated and sometimes include language-based text. The openResourceBundleForDatePatterns() function in
734 // dtptngen_impl.h handles the exceptions and goves us either the result of ures_openWithCountryFallback() or
735 // ures_open() depending on the actual patterns.
736 const char* cType
= fCalendar
? fCalendar
->getType() : NULL
;
737 UBool fallingBackByCountry
= FALSE
;
738 LocalUResourceBundlePointer
bundle(ures_open(NULL
, locale
.getBaseName(), &status
));
739 LocalUResourceBundlePointer
countryBundle(ures_openWithCountryFallback(NULL
, locale
.getBaseName(), &fallingBackByCountry
, &status
));
740 if (U_FAILURE(status
)) return;
742 // If we're potentially falling back by country, check to see whether the language and country fallback locales
743 // have the same numbering system. If they don't, fall back by language instead. (Many date/time patterns have
744 // embedded assumptions about which numbering system they're being used with and don't behave well with other ones,
745 // especially if different writing directions are involved-- see rdar://69523017.)
746 if (fallingBackByCountry
) {
748 const UChar
* languageLocaleNumbers
= ures_getStringByKeyWithFallback(bundle
.getAlias(), "NumberElements/default", &dummy
, &status
);
749 const UChar
* countryLocaleNumbers
= ures_getStringByKeyWithFallback(countryBundle
.getAlias(), "NumberElements/default", &dummy
, &status
);
750 if (U_FAILURE(status
) || u_strcmp(languageLocaleNumbers
, countryLocaleNumbers
) != 0) {
751 fallingBackByCountry
= FALSE
;
755 UBool cTypeIsGregorian
= TRUE
;
756 LocalUResourceBundlePointer dateTimePatterns
;
757 LocalUResourceBundlePointer countryDateTimePatterns
;
758 if (cType
!= NULL
&& uprv_strcmp(cType
, "gregorian") != 0) {
759 CharString
resourcePath("calendar/", status
);
760 resourcePath
.append(cType
, status
).append("/DateTimePatterns", status
);
761 dateTimePatterns
.adoptInstead(
762 ures_getByKeyWithFallback(bundle
.getAlias(), resourcePath
.data(),
763 (UResourceBundle
*)NULL
, &status
));
764 countryDateTimePatterns
.adoptInstead(
765 ures_getByKeyWithFallback(countryBundle
.getAlias(), resourcePath
.data(),
766 (UResourceBundle
*)NULL
, &status
));
767 cTypeIsGregorian
= FALSE
;
770 // Check for "gregorian" fallback.
771 if (cTypeIsGregorian
|| status
== U_MISSING_RESOURCE_ERROR
) {
772 status
= U_ZERO_ERROR
;
773 dateTimePatterns
.adoptInstead(
774 ures_getByKeyWithFallback(bundle
.getAlias(),
775 "calendar/gregorian/DateTimePatterns",
776 (UResourceBundle
*)NULL
, &status
));
777 countryDateTimePatterns
.adoptInstead(
778 ures_getByKeyWithFallback(countryBundle
.getAlias(),
779 "calendar/gregorian/DateTimePatterns",
780 (UResourceBundle
*)NULL
, &status
));
782 if (U_FAILURE(status
)) return;
784 if (ures_getSize(dateTimePatterns
.getAlias()) <= kDateTime
)
786 status
= U_INVALID_FORMAT_ERROR
;
790 setLocaleIDs(ures_getLocaleByType(dateTimePatterns
.getAlias(), ULOC_VALID_LOCALE
, &status
),
791 ures_getLocaleByType(dateTimePatterns
.getAlias(), ULOC_ACTUAL_LOCALE
, &status
));
793 // create a symbols object from the locale
794 fSymbols
= DateFormatSymbols::createForLocale(locale
, status
);
795 if (U_FAILURE(status
)) return;
798 status
= U_MEMORY_ALLOCATION_ERROR
;
802 fDateOverride
.setToBogus();
803 fTimeOverride
.setToBogus();
805 // if the pattern should include both date and time information, use the date/time
806 // pattern string as a guide to tell use how to glue together the appropriate date
807 // and time pattern strings.
808 if ((timeStyle
!= kNone
) && (dateStyle
!= kNone
))
810 UnicodeString ovrStr
;
811 UnicodeString tempus1
= getPatternForTimeStyle(timeStyle
, locale
, dateTimePatterns
.getAlias(), ovrStr
, status
);
812 if (!ovrStr
.isEmpty()) {
813 fTimeOverride
= ovrStr
;
816 UnicodeString tempus2
= getPatternForDateStyle(dateStyle
, dateTimePatterns
.getAlias(), countryDateTimePatterns
.getAlias(), fallingBackByCountry
, ovrStr
, status
);
817 if (!ovrStr
.isEmpty()) {
818 fDateOverride
= ovrStr
;
821 int32_t glueIndex
= kDateTime
;
822 int32_t patternsSize
= ures_getSize(dateTimePatterns
.getAlias());
823 if (patternsSize
>= (kDateTimeOffset
+ kShort
+ 1)) {
824 // Get proper date time format
825 glueIndex
= (int32_t)(kDateTimeOffset
+ (dateStyle
- kDateOffset
));
828 UnicodeString
resStr(ures_getUnicodeStringByIndex(dateTimePatterns
.getAlias(), glueIndex
, &status
));
829 SimpleFormatter(resStr
, 2, 2, status
).
830 format(tempus1
, tempus2
, fPattern
, status
);
832 // if the pattern includes just time data or just date date, load the appropriate
833 // pattern string from the resources
834 // setTo() - see DateFormatSymbols::assignArray comments
835 else if (timeStyle
!= kNone
) {
836 UnicodeString ovrStr
;
837 UnicodeString timePattern
= getPatternForTimeStyle(timeStyle
, locale
, dateTimePatterns
.getAlias(), ovrStr
, status
);
838 if (!ovrStr
.isEmpty()) {
839 fDateOverride
= ovrStr
; // is this right? This is what the original code had...
841 fPattern
= timePattern
;
843 else if (dateStyle
!= kNone
) {
844 UnicodeString ovrStr
;
845 UnicodeString datePattern
= getPatternForDateStyle(dateStyle
, dateTimePatterns
.getAlias(), countryDateTimePatterns
.getAlias(), fallingBackByCountry
, ovrStr
, status
);
846 if (!ovrStr
.isEmpty()) {
847 fDateOverride
= ovrStr
;
849 fPattern
= datePattern
;
852 // and if it includes _neither_, that's an error
854 status
= U_INVALID_FORMAT_ERROR
;
856 initialize(locale
, status
);
860 SimpleDateFormat::getPatternForTimeStyle(EStyle timeStyle
,
861 const Locale
& locale
,
862 UResourceBundle
* dateTimePatterns
,
863 UnicodeString
& ovrStr
,
864 UErrorCode
& status
) {
865 UnicodeString timePattern
;
866 if (timeStyle
>= kFull
&& timeStyle
<= kShort
) {
867 const char* baseLoc
= locale
.getBaseName();
868 if (baseLoc
!=NULL
&& baseLoc
[0]!=0 && uprv_strcmp(baseLoc
,"und")!=0) {
869 UErrorCode useStatus
= U_ZERO_ERROR
;
870 const char* validLoc
= getLocaleID(ULOC_VALID_LOCALE
, useStatus
);
871 if (U_SUCCESS(useStatus
) && uprv_strcmp(validLoc
,baseLoc
)!=0) {
872 char minLoc
[kBaseNameMax
];
873 uloc_minimizeSubtags(baseLoc
, minLoc
, kBaseNameMax
, &useStatus
);
874 minLoc
[kBaseNameMax
-1] = 0; // ensure zero term
875 const char* actualLoc
= getLocaleID(ULOC_ACTUAL_LOCALE
, useStatus
);
876 if (U_SUCCESS(useStatus
) && uprv_strcmp(actualLoc
,minLoc
)!=0) {
877 // The standard time formats may have the wrong time cycle, because:
878 // * the valid locale is not the same as the base locale, or
879 // * the actual locale the patterns are coming from is not the same
880 // as the minimized locale.
881 // We could *also* check whether they do actually have a mismatch with
882 // the time cycle preferences for the region, but that is a lot more
883 // work for little or no additional benefit, since just going ahead
884 // and always synthesizing the time format as per the following should
885 // create a locale-appropriate pattern with cycle that matches the
886 // region preferences anyway (for completely unsupported languages,
887 // this will use root patterns for the appropriate cycle for the
888 // likely subtags resion).
889 LocalPointer
<DateTimePatternGenerator
> dtpg(DateTimePatternGenerator::createInstance(locale
, useStatus
, TRUE
));
890 if (U_SUCCESS(useStatus
)) {
891 UnicodeString
timeSkeleton(TRUE
, timeSkeletons
[timeStyle
], -1);
892 timePattern
= dtpg
->getBestPattern(timeSkeleton
, useStatus
);
893 #if DEBUG_SYNTHETIC_TIMEFMTS
894 if (timePattern
.length() != 0) {
896 timePattern
.extract(0,timePattern
.length(),bbuf
,32);
897 printf("\n## for locale %s, validLoc %s, minLoc %s, actualLoc %s, synth timePat %s\n", locale
.getName(), validLoc
, minLoc
, actualLoc
, bbuf
);
906 if (timePattern
.isEmpty()) {
907 timePattern
= getPatternString((int32_t)timeStyle
, dateTimePatterns
, ovrStr
, status
);
913 SimpleDateFormat::getPatternForDateStyle(EStyle dateStyle
,
914 UResourceBundle
* languageDateTimePatterns
,
915 UResourceBundle
* countryDateTimePatterns
,
916 UBool
& fallingBackByCountry
,
917 UnicodeString
& ovrStr
,
918 UErrorCode
& status
) {
919 UnicodeString languageOverride
;
920 UnicodeString languagePattern
= getPatternString((int32_t)dateStyle
, languageDateTimePatterns
, languageOverride
, status
);
922 // by default, we should fetch the pattern from the language resource
923 UnicodeString result
= languagePattern
;
924 ovrStr
= languageOverride
;
926 // but IF the country resource is actually different from the lanuguage resource AND the caller is asking
927 // for a medium or short date format AND that format in the country resource is all-numeric, return the
928 // pattern from the country resource instead
929 if (fallingBackByCountry
) {
930 fallingBackByCountry
= FALSE
;
931 if ((dateStyle
== kDateOffset
+ kMedium
|| dateStyle
== kDateOffset
+ kShort
)) {
932 UnicodeString countryOverride
;
933 UnicodeString countryPattern
= getPatternString((int32_t)dateStyle
, countryDateTimePatterns
, countryOverride
, status
);
934 UBool stripRTLmarks
= uloc_isRightToLeft(ures_getLocaleByType(countryDateTimePatterns
, ULOC_ACTUAL_LOCALE
, &status
)) && !uloc_isRightToLeft(ures_getLocaleByType(languageDateTimePatterns
, ULOC_ACTUAL_LOCALE
, &status
));
936 if (U_SUCCESS(status
)) {
937 if (datePatternHasNumericCore(countryPattern
)) {
938 ovrStr
= countryOverride
;
939 result
= countryPattern
;
940 fallingBackByCountry
= TRUE
;
942 // the date formats for RTL languages often include Unicode right-to-left marks to get the format
943 // to lay out correctly. If we got the pattern from a RTL locale and the requested language is
944 // not a RTL language, we need to strip those out (same comment below)
945 result
.findAndReplace(UnicodeString(u
'\u200f'), UnicodeString());
947 } else if (dateStyle
== kDateOffset
+ kMedium
&& datePatternHasNumericCore(languagePattern
)) {
948 // if the user asked for the MEDIUM format, it's NOT all-numeric in the country resource, but it IS
949 // all-numeric in the language resource, return the SHORT format from the country resource
950 countryPattern
= getPatternString(kDateOffset
+ kShort
, countryDateTimePatterns
, countryOverride
, status
);
951 if (datePatternHasNumericCore(countryPattern
)) {
952 ovrStr
= countryOverride
;
953 result
= countryPattern
;
954 fallingBackByCountry
= TRUE
;
956 result
.findAndReplace(UnicodeString(u
'\u200f'), UnicodeString());
967 SimpleDateFormat::getPatternString(int32_t index
,
968 UResourceBundle
* dateTimePatterns
,
969 UnicodeString
& ovrStr
,
970 UErrorCode
& status
) {
971 UnicodeString resStr
;
972 LocalUResourceBundlePointer
currentBundle(ures_getByIndex(dateTimePatterns
, index
, NULL
, &status
));
974 if (U_FAILURE(status
)) {
975 status
= U_INVALID_FORMAT_ERROR
;
978 switch (ures_getType(currentBundle
.getAlias())) {
980 resStr
= ures_getUnicodeString(currentBundle
.getAlias(), &status
);
984 resStr
= ures_getUnicodeStringByIndex(currentBundle
.getAlias(), 0, &status
);
985 ovrStr
= ures_getUnicodeStringByIndex(currentBundle
.getAlias(), 1, &status
);
989 status
= U_INVALID_FORMAT_ERROR
;
996 //----------------------------------------------------------------------
999 SimpleDateFormat::initializeCalendar(TimeZone
* adoptZone
, const Locale
& locale
, UErrorCode
& status
)
1001 if(!U_FAILURE(status
)) {
1002 fCalendar
= Calendar::createInstance(adoptZone
?adoptZone
:TimeZone::createDefault(), locale
, status
);
1008 SimpleDateFormat::initialize(const Locale
& locale
,
1011 if (U_FAILURE(status
)) return;
1013 parsePattern(); // Need this before initNumberFormatters(), to set fHasHanYearChar
1015 // If the locale has @[....]numbers=hanidays we want to *delete* that (so it
1016 // it is not used for every field) and then set fDateOverride to "d=hanidays"
1017 // (as with std formats for zh@calendar=chinese) to use hanidays for d field.
1018 static const UChar hanidaysOverride
[] = {0x64,0x3D,0x68,0x61,0x6E,0x69,0x64,0x61,0x79,0x73,0}; // "d=hanidays"
1019 char numbersValue
[ULOC_KEYWORDS_CAPACITY
];
1020 UErrorCode numbersStatus
= U_ZERO_ERROR
;
1021 Locale
localeNoHanidays(locale
);
1022 int32_t numbersLen
= localeNoHanidays
.getKeywordValue("numbers", numbersValue
, ULOC_KEYWORDS_CAPACITY
, numbersStatus
);
1023 if ( U_SUCCESS(numbersStatus
) && numbersLen
> 0 ) {
1024 if ( uprv_strcmp(numbersValue
, "hanidays") == 0 ) {
1025 localeNoHanidays
.setKeywordValue("numbers", NULL
, numbersStatus
);
1026 fDateOverride
.setTo(hanidaysOverride
,-1);
1029 // Simple-minded hack to force Gannen year numbering for ja@calendar=japanese
1030 // if format is non-numeric (includes 年) and fDateOverride is not already specified.
1031 // Now this does get updated if applyPattern subsequently changes the pattern type.
1032 if (fDateOverride
.isBogus() && fHasHanYearChar
&&
1033 fCalendar
!= nullptr && uprv_strcmp(fCalendar
->getType(),"japanese") == 0 &&
1034 uprv_strcmp(fLocale
.getLanguage(),"ja") == 0) {
1035 fDateOverride
.setTo(u
"y=jpanyear", -1);
1038 // We don't need to check that the row count is >= 1, since all 2d arrays have at
1040 fNumberFormat
= NumberFormat::createInstance(localeNoHanidays
, status
);
1041 if (fNumberFormat
!= NULL
&& U_SUCCESS(status
))
1043 fixNumberFormatForDates(*fNumberFormat
);
1044 //fNumberFormat->setLenient(TRUE); // Java uses a custom DateNumberFormat to format/parse
1046 initNumberFormatters(locale
, status
);
1047 initFastNumberFormatters(status
);
1050 else if (U_SUCCESS(status
))
1052 status
= U_MISSING_RESOURCE_ERROR
;
1056 /* Initialize the fields we use to disambiguate ambiguous years. Separate
1057 * so we can call it from readObject().
1059 void SimpleDateFormat::initializeDefaultCentury()
1062 fHaveDefaultCentury
= fCalendar
->haveDefaultCentury();
1063 if(fHaveDefaultCentury
) {
1064 fDefaultCenturyStart
= fCalendar
->defaultCenturyStart();
1065 fDefaultCenturyStartYear
= fCalendar
->defaultCenturyStartYear();
1067 fDefaultCenturyStart
= DBL_MIN
;
1068 fDefaultCenturyStartYear
= -1;
1074 * Initialize the boolean attributes. Separate so we can call it from all constructors.
1076 void SimpleDateFormat::initializeBooleanAttributes()
1078 UErrorCode status
= U_ZERO_ERROR
;
1080 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, true, status
);
1081 setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, true, status
);
1082 setBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH
, true, status
);
1083 setBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, true, status
);
1086 /* Define one-century window into which to disambiguate dates using
1087 * two-digit years. Make public in JDK 1.2.
1089 void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate
, UErrorCode
& status
)
1091 if(U_FAILURE(status
)) {
1095 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1099 fCalendar
->setTime(startDate
, status
);
1100 if(U_SUCCESS(status
)) {
1101 fHaveDefaultCentury
= TRUE
;
1102 fDefaultCenturyStart
= startDate
;
1103 fDefaultCenturyStartYear
= fCalendar
->get(UCAL_YEAR
, status
);
1107 //----------------------------------------------------------------------
1110 SimpleDateFormat::format(Calendar
& cal
, UnicodeString
& appendTo
, FieldPosition
& pos
) const
1112 UErrorCode status
= U_ZERO_ERROR
;
1113 FieldPositionOnlyHandler
handler(pos
);
1114 return _format(cal
, appendTo
, handler
, status
);
1117 //----------------------------------------------------------------------
1120 SimpleDateFormat::format(Calendar
& cal
, UnicodeString
& appendTo
,
1121 FieldPositionIterator
* posIter
, UErrorCode
& status
) const
1123 FieldPositionIteratorHandler
handler(posIter
, status
);
1124 return _format(cal
, appendTo
, handler
, status
);
1127 //----------------------------------------------------------------------
1130 SimpleDateFormat::_format(Calendar
& cal
, UnicodeString
& appendTo
,
1131 FieldPositionHandler
& handler
, UErrorCode
& status
) const
1133 if ( U_FAILURE(status
) ) {
1136 Calendar
* workCal
= &cal
;
1137 Calendar
* calClone
= NULL
;
1138 if (&cal
!= fCalendar
&& uprv_strcmp(cal
.getType(), fCalendar
->getType()) != 0) {
1139 // Different calendar type
1140 // We use the time and time zone from the input calendar, but
1141 // do not use the input calendar for field calculation.
1142 calClone
= fCalendar
->clone();
1143 if (calClone
!= NULL
) {
1144 UDate t
= cal
.getTime(status
);
1145 calClone
->setTime(t
, status
);
1146 calClone
->setTimeZone(cal
.getTimeZone());
1149 status
= U_MEMORY_ALLOCATION_ERROR
;
1154 UBool inQuote
= FALSE
;
1157 int32_t fieldNum
= 0;
1158 UDisplayContext capitalizationContext
= getContext(UDISPCTX_TYPE_CAPITALIZATION
, status
);
1160 // loop through the pattern string character by character
1161 for (int32_t i
= 0; i
< fPattern
.length() && U_SUCCESS(status
); ++i
) {
1162 UChar ch
= fPattern
[i
];
1164 // Use subFormat() to format a repeated pattern character
1165 // when a different pattern or non-pattern character is seen
1166 if (ch
!= prevCh
&& count
> 0) {
1167 subFormat(appendTo
, prevCh
, count
, capitalizationContext
, fieldNum
++, handler
, *workCal
, status
);
1171 // Consecutive single quotes are a single quote literal,
1172 // either outside of quotes or between quotes
1173 if ((i
+1) < fPattern
.length() && fPattern
[i
+1] == QUOTE
) {
1174 appendTo
+= (UChar
)QUOTE
;
1177 inQuote
= ! inQuote
;
1180 else if (!inQuote
&& isSyntaxChar(ch
)) {
1181 // ch is a date-time pattern character to be interpreted
1182 // by subFormat(); count the number of times it is repeated
1187 // Append quoted characters and unquoted non-pattern characters
1192 // Format the last item in the pattern, if any
1194 subFormat(appendTo
, prevCh
, count
, capitalizationContext
, fieldNum
++, handler
, *workCal
, status
);
1197 if (calClone
!= NULL
) {
1204 //----------------------------------------------------------------------
1206 /* Map calendar field into calendar field level.
1207 * the larger the level, the smaller the field unit.
1208 * For example, UCAL_ERA level is 0, UCAL_YEAR level is 10,
1209 * UCAL_MONTH level is 20.
1210 * NOTE: if new fields adds in, the table needs to update.
1213 SimpleDateFormat::fgCalendarFieldToLevel
[] =
1217 /*dDEF*/ 30, 20, 30, 30,
1218 /*ahHm*/ 40, 50, 50, 60,
1225 int32_t SimpleDateFormat::getLevelFromChar(UChar ch
) {
1226 // Map date field LETTER into calendar field level.
1227 // the larger the level, the smaller the field unit.
1228 // NOTE: if new fields adds in, the table needs to update.
1229 static const int32_t mapCharToLevel
[] = {
1230 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1232 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1233 // ! " # $ % & ' ( ) * + , - . /
1234 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1235 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1236 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
1237 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1,
1239 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
1240 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1242 // @ A B C D E F G H I J K L M N O
1243 -1, 40, -1, -1, 20, 30, 30, 0, 50, -1, -1, 50, 20, 20, -1, 0,
1244 // P Q R S T U V W X Y Z [ \ ] ^ _
1245 -1, 20, -1, 80, -1, 10, 0, 30, 0, 10, 0, -1, -1, -1, -1, -1,
1246 // ` a b c d e f g h i j k l m n o
1247 -1, 40, -1, 30, 30, 30, -1, 0, 50, -1, -1, 50, 0, 60, -1, -1,
1248 // p q r s t u v w x y z { | } ~
1249 -1, 20, 10, 70, -1, 10, 0, 20, 0, 10, 0, -1, -1, -1, -1, -1
1252 return ch
< UPRV_LENGTHOF(mapCharToLevel
) ? mapCharToLevel
[ch
] : -1;
1255 UBool
SimpleDateFormat::isSyntaxChar(UChar ch
) {
1256 static const UBool mapCharToIsSyntax
[] = {
1258 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1260 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1262 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1264 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1266 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1268 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1270 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1271 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1273 FALSE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1276 FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1279 FALSE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1281 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1283 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1285 TRUE
, TRUE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
,
1287 FALSE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1289 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1291 TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
,
1293 TRUE
, TRUE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
1296 return ch
< UPRV_LENGTHOF(mapCharToIsSyntax
) ? mapCharToIsSyntax
[ch
] : FALSE
;
1299 // Map index into pattern character string to Calendar field number.
1300 const UCalendarDateFields
1301 SimpleDateFormat::fgPatternIndexToCalendarField
[] =
1303 /*GyM*/ UCAL_ERA
, UCAL_YEAR
, UCAL_MONTH
,
1304 /*dkH*/ UCAL_DATE
, UCAL_HOUR_OF_DAY
, UCAL_HOUR_OF_DAY
,
1305 /*msS*/ UCAL_MINUTE
, UCAL_SECOND
, UCAL_MILLISECOND
,
1306 /*EDF*/ UCAL_DAY_OF_WEEK
, UCAL_DAY_OF_YEAR
, UCAL_DAY_OF_WEEK_IN_MONTH
,
1307 /*wWa*/ UCAL_WEEK_OF_YEAR
, UCAL_WEEK_OF_MONTH
, UCAL_AM_PM
,
1308 /*hKz*/ UCAL_HOUR
, UCAL_HOUR
, UCAL_ZONE_OFFSET
,
1309 /*Yeu*/ UCAL_YEAR_WOY
, UCAL_DOW_LOCAL
, UCAL_EXTENDED_YEAR
,
1310 /*gAZ*/ UCAL_JULIAN_DAY
, UCAL_MILLISECONDS_IN_DAY
, UCAL_ZONE_OFFSET
,
1311 /*v*/ UCAL_ZONE_OFFSET
,
1312 /*c*/ UCAL_DOW_LOCAL
,
1316 /*V*/ UCAL_ZONE_OFFSET
,
1318 /*O*/ UCAL_ZONE_OFFSET
,
1319 /*Xx*/ UCAL_ZONE_OFFSET
, UCAL_ZONE_OFFSET
,
1320 /*r*/ UCAL_EXTENDED_YEAR
,
1321 /*bB*/ UCAL_FIELD_COUNT
, UCAL_FIELD_COUNT
, // no mappings to calendar fields
1322 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1323 /*:*/ UCAL_FIELD_COUNT
, /* => no useful mapping to any calendar field */
1325 /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/ UCAL_FIELD_COUNT
, /* => no useful mapping to any calendar field */
1329 // Map index into pattern character string to DateFormat field number
1330 const UDateFormatField
1331 SimpleDateFormat::fgPatternIndexToDateFormatField
[] = {
1332 /*GyM*/ UDAT_ERA_FIELD
, UDAT_YEAR_FIELD
, UDAT_MONTH_FIELD
,
1333 /*dkH*/ UDAT_DATE_FIELD
, UDAT_HOUR_OF_DAY1_FIELD
, UDAT_HOUR_OF_DAY0_FIELD
,
1334 /*msS*/ UDAT_MINUTE_FIELD
, UDAT_SECOND_FIELD
, UDAT_FRACTIONAL_SECOND_FIELD
,
1335 /*EDF*/ UDAT_DAY_OF_WEEK_FIELD
, UDAT_DAY_OF_YEAR_FIELD
, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD
,
1336 /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD
, UDAT_WEEK_OF_MONTH_FIELD
, UDAT_AM_PM_FIELD
,
1337 /*hKz*/ UDAT_HOUR1_FIELD
, UDAT_HOUR0_FIELD
, UDAT_TIMEZONE_FIELD
,
1338 /*Yeu*/ UDAT_YEAR_WOY_FIELD
, UDAT_DOW_LOCAL_FIELD
, UDAT_EXTENDED_YEAR_FIELD
,
1339 /*gAZ*/ UDAT_JULIAN_DAY_FIELD
, UDAT_MILLISECONDS_IN_DAY_FIELD
, UDAT_TIMEZONE_RFC_FIELD
,
1340 /*v*/ UDAT_TIMEZONE_GENERIC_FIELD
,
1341 /*c*/ UDAT_STANDALONE_DAY_FIELD
,
1342 /*L*/ UDAT_STANDALONE_MONTH_FIELD
,
1343 /*Q*/ UDAT_QUARTER_FIELD
,
1344 /*q*/ UDAT_STANDALONE_QUARTER_FIELD
,
1345 /*V*/ UDAT_TIMEZONE_SPECIAL_FIELD
,
1346 /*U*/ UDAT_YEAR_NAME_FIELD
,
1347 /*O*/ UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
,
1348 /*Xx*/ UDAT_TIMEZONE_ISO_FIELD
, UDAT_TIMEZONE_ISO_LOCAL_FIELD
,
1349 /*r*/ UDAT_RELATED_YEAR_FIELD
,
1350 /*bB*/ UDAT_AM_PM_MIDNIGHT_NOON_FIELD
, UDAT_FLEXIBLE_DAY_PERIOD_FIELD
,
1351 #if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
1352 /*:*/ UDAT_TIME_SEPARATOR_FIELD
,
1354 /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/ UDAT_TIME_SEPARATOR_FIELD
,
1358 //----------------------------------------------------------------------
1361 * Append symbols[value] to dst. Make sure the array index is not out
1365 _appendSymbol(UnicodeString
& dst
,
1367 const UnicodeString
* symbols
,
1368 int32_t symbolsCount
) {
1369 U_ASSERT(0 <= value
&& value
< symbolsCount
);
1370 if (0 <= value
&& value
< symbolsCount
) {
1371 dst
+= symbols
[value
];
1376 _appendSymbolWithMonthPattern(UnicodeString
& dst
, int32_t value
, const UnicodeString
* symbols
, int32_t symbolsCount
,
1377 const UnicodeString
* monthPattern
, UErrorCode
& status
) {
1378 U_ASSERT(0 <= value
&& value
< symbolsCount
);
1379 if (0 <= value
&& value
< symbolsCount
) {
1380 if (monthPattern
== NULL
) {
1381 dst
+= symbols
[value
];
1383 SimpleFormatter(*monthPattern
, 1, 1, status
).format(symbols
[value
], dst
, status
);
1388 //----------------------------------------------------------------------
1390 static number::LocalizedNumberFormatter
*
1391 createFastFormatter(const DecimalFormat
* df
, int32_t minInt
, int32_t maxInt
, UErrorCode
& status
) {
1392 const number::LocalizedNumberFormatter
* lnfBase
= df
->toNumberFormatter(status
);
1393 if (U_FAILURE(status
)) {
1396 return lnfBase
->integerWidth(
1397 number::IntegerWidth::zeroFillTo(minInt
).truncateAt(maxInt
)
1401 void SimpleDateFormat::initFastNumberFormatters(UErrorCode
& status
) {
1402 if (U_FAILURE(status
)) {
1405 auto* df
= dynamic_cast<DecimalFormat
*>(fNumberFormat
);
1406 if (df
== nullptr) {
1409 df
->setDFSShallowCopy(TRUE
);
1410 fFastNumberFormatters
[SMPDTFMT_NF_1x10
] = createFastFormatter(df
, 1, 10, status
);
1411 fFastNumberFormatters
[SMPDTFMT_NF_2x10
] = createFastFormatter(df
, 2, 10, status
);
1412 fFastNumberFormatters
[SMPDTFMT_NF_3x10
] = createFastFormatter(df
, 3, 10, status
);
1413 fFastNumberFormatters
[SMPDTFMT_NF_4x10
] = createFastFormatter(df
, 4, 10, status
);
1414 fFastNumberFormatters
[SMPDTFMT_NF_2x2
] = createFastFormatter(df
, 2, 2, status
);
1415 df
->setDFSShallowCopy(FALSE
);
1418 void SimpleDateFormat::freeFastNumberFormatters() {
1419 delete fFastNumberFormatters
[SMPDTFMT_NF_1x10
];
1420 delete fFastNumberFormatters
[SMPDTFMT_NF_2x10
];
1421 delete fFastNumberFormatters
[SMPDTFMT_NF_3x10
];
1422 delete fFastNumberFormatters
[SMPDTFMT_NF_4x10
];
1423 delete fFastNumberFormatters
[SMPDTFMT_NF_2x2
];
1424 fFastNumberFormatters
[SMPDTFMT_NF_1x10
] = nullptr;
1425 fFastNumberFormatters
[SMPDTFMT_NF_2x10
] = nullptr;
1426 fFastNumberFormatters
[SMPDTFMT_NF_3x10
] = nullptr;
1427 fFastNumberFormatters
[SMPDTFMT_NF_4x10
] = nullptr;
1428 fFastNumberFormatters
[SMPDTFMT_NF_2x2
] = nullptr;
1433 SimpleDateFormat::initNumberFormatters(const Locale
&locale
,UErrorCode
&status
) {
1434 if (U_FAILURE(status
)) {
1437 if ( fDateOverride
.isBogus() && fTimeOverride
.isBogus() ) {
1441 if (fSharedNumberFormatters
== NULL
) {
1442 fSharedNumberFormatters
= allocSharedNumberFormatters();
1443 if (fSharedNumberFormatters
== NULL
) {
1444 status
= U_MEMORY_ALLOCATION_ERROR
;
1449 if (U_FAILURE(status
)) {
1453 processOverrideString(locale
,fDateOverride
,kOvrStrDate
,status
);
1454 processOverrideString(locale
,fTimeOverride
,kOvrStrTime
,status
);
1458 SimpleDateFormat::processOverrideString(const Locale
&locale
, const UnicodeString
&str
, int8_t type
, UErrorCode
&status
) {
1459 if (str
.isBogus() || U_FAILURE(status
)) {
1465 UnicodeString nsName
;
1466 UnicodeString ovrField
;
1467 UBool moreToProcess
= TRUE
;
1468 NSOverride
*overrideList
= NULL
;
1470 while (moreToProcess
) {
1471 int32_t delimiterPosition
= str
.indexOf((UChar
)ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE
,start
);
1472 if (delimiterPosition
== -1) {
1473 moreToProcess
= FALSE
;
1474 len
= str
.length() - start
;
1476 len
= delimiterPosition
- start
;
1478 UnicodeString
currentString(str
,start
,len
);
1479 int32_t equalSignPosition
= currentString
.indexOf((UChar
)ULOC_KEYWORD_ASSIGN_UNICODE
,0);
1480 if (equalSignPosition
== -1) { // Simple override string such as "hebrew"
1481 nsName
.setTo(currentString
);
1482 ovrField
.setToBogus();
1483 } else { // Field specific override string such as "y=hebrew"
1484 nsName
.setTo(currentString
,equalSignPosition
+1);
1485 ovrField
.setTo(currentString
,0,1); // We just need the first character.
1488 int32_t nsNameHash
= nsName
.hashCode();
1489 // See if the numbering system is in the override list, if not, then add it.
1490 NSOverride
*curr
= overrideList
;
1491 const SharedNumberFormat
*snf
= NULL
;
1492 UBool found
= FALSE
;
1493 while ( curr
&& !found
) {
1494 if ( curr
->hash
== nsNameHash
) {
1502 LocalPointer
<NSOverride
> cur(new NSOverride
);
1503 if (!cur
.isNull()) {
1504 char kw
[ULOC_KEYWORD_AND_VALUES_CAPACITY
];
1505 uprv_strcpy(kw
,"numbers=");
1506 nsName
.extract(0,len
,kw
+8,ULOC_KEYWORD_AND_VALUES_CAPACITY
-8,US_INV
);
1508 Locale
ovrLoc(locale
.getLanguage(),locale
.getCountry(),locale
.getVariant(),kw
);
1509 cur
->hash
= nsNameHash
;
1510 cur
->next
= overrideList
;
1511 SharedObject::copyPtr(
1512 createSharedNumberFormat(ovrLoc
, status
), cur
->snf
);
1513 if (U_FAILURE(status
)) {
1515 overrideList
->free();
1520 overrideList
= cur
.orphan();
1522 status
= U_MEMORY_ALLOCATION_ERROR
;
1524 overrideList
->free();
1530 // Now that we have an appropriate number formatter, fill in the appropriate spaces in the
1531 // number formatters table.
1532 if (ovrField
.isBogus()) {
1536 for ( int8_t i
=0 ; i
<kDateFieldsCount
; i
++ ) {
1537 SharedObject::copyPtr(snf
, fSharedNumberFormatters
[kDateFields
[i
]]);
1539 if (type
==kOvrStrDate
) {
1544 case kOvrStrTime
: {
1545 for ( int8_t i
=0 ; i
<kTimeFieldsCount
; i
++ ) {
1546 SharedObject::copyPtr(snf
, fSharedNumberFormatters
[kTimeFields
[i
]]);
1552 // if the pattern character is unrecognized, signal an error and bail out
1553 UDateFormatField patternCharIndex
=
1554 DateFormatSymbols::getPatternCharIndex(ovrField
.charAt(0));
1555 if (patternCharIndex
== UDAT_FIELD_COUNT
) {
1556 status
= U_INVALID_FORMAT_ERROR
;
1558 overrideList
->free();
1562 SharedObject::copyPtr(snf
, fSharedNumberFormatters
[patternCharIndex
]);
1565 start
= delimiterPosition
+ 1;
1568 overrideList
->free();
1572 //---------------------------------------------------------------------
1574 SimpleDateFormat::subFormat(UnicodeString
&appendTo
,
1577 UDisplayContext capitalizationContext
,
1579 FieldPositionHandler
& handler
,
1581 UErrorCode
& status
) const
1583 if (U_FAILURE(status
)) {
1587 // this function gets called by format() to produce the appropriate substitution
1588 // text for an individual pattern symbol (e.g., "HH" or "yyyy")
1590 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(ch
);
1591 const int32_t maxIntCount
= 10;
1592 int32_t beginOffset
= appendTo
.length();
1593 const NumberFormat
*currentNumberFormat
;
1594 DateFormatSymbols::ECapitalizationContextUsageType capContextUsageType
= DateFormatSymbols::kCapContextUsageOther
;
1596 UBool isHebrewCalendar
= (uprv_strcmp(cal
.getType(),"hebrew") == 0);
1597 UBool isChineseCalendar
= (uprv_strcmp(cal
.getType(),"chinese") == 0 || uprv_strcmp(cal
.getType(),"dangi") == 0);
1599 // if the pattern character is unrecognized, signal an error and dump out
1600 if (patternCharIndex
== UDAT_FIELD_COUNT
)
1602 if (ch
!= 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
1603 status
= U_INVALID_FORMAT_ERROR
;
1608 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
];
1610 // Don't get value unless it is useful
1611 if (field
< UCAL_FIELD_COUNT
) {
1612 value
= (patternCharIndex
!= UDAT_RELATED_YEAR_FIELD
)? cal
.get(field
, status
): cal
.getRelatedYear(status
);
1614 if (U_FAILURE(status
)) {
1618 currentNumberFormat
= getNumberFormatByIndex(patternCharIndex
);
1619 if (currentNumberFormat
== NULL
) {
1620 status
= U_INTERNAL_PROGRAM_ERROR
;
1623 UnicodeString
hebr("hebr", 4, US_INV
);
1625 switch (patternCharIndex
) {
1627 // for any "G" symbol, write out the appropriate era string
1628 // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name
1629 case UDAT_ERA_FIELD
:
1630 if (isChineseCalendar
) {
1631 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, 1, 9); // as in ICU4J
1634 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowEras
, fSymbols
->fNarrowErasCount
);
1635 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraNarrow
;
1636 } else if (count
== 4) {
1637 _appendSymbol(appendTo
, value
, fSymbols
->fEraNames
, fSymbols
->fEraNamesCount
);
1638 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraWide
;
1640 _appendSymbol(appendTo
, value
, fSymbols
->fEras
, fSymbols
->fErasCount
);
1641 capContextUsageType
= DateFormatSymbols::kCapContextUsageEraAbbrev
;
1646 case UDAT_YEAR_NAME_FIELD
:
1647 if (fSymbols
->fShortYearNames
!= NULL
&& value
<= fSymbols
->fShortYearNamesCount
) {
1648 // the Calendar YEAR field runs 1 through 60 for cyclic years
1649 _appendSymbol(appendTo
, value
- 1, fSymbols
->fShortYearNames
, fSymbols
->fShortYearNamesCount
);
1652 // else fall through to numeric year handling, do not break here
1655 // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits
1657 //Year y yy yyy yyyy yyyyy
1658 //AD 1 1 01 001 0001 00001
1659 //AD 12 12 12 012 0012 00012
1660 //AD 123 123 23 123 0123 00123
1661 //AD 1234 1234 34 1234 1234 01234
1662 //AD 12345 12345 45 12345 12345 12345
1663 case UDAT_YEAR_FIELD
:
1664 case UDAT_YEAR_WOY_FIELD
:
1665 if (fDateOverride
.compare(hebr
)==0 && value
>HEBREW_CAL_CUR_MILLENIUM_START_YEAR
&& value
<HEBREW_CAL_CUR_MILLENIUM_END_YEAR
) {
1666 value
-=HEBREW_CAL_CUR_MILLENIUM_START_YEAR
;
1669 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, 2, 2);
1671 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, count
, maxIntCount
);
1674 // for "MMMM"/"LLLL", write out the whole month name, for "MMM"/"LLL", write out the month
1675 // abbreviation, for "M"/"L" or "MM"/"LL", write out the month as a number with the
1676 // appropriate number of digits
1677 // for "MMMMM"/"LLLLL", use the narrow form
1678 case UDAT_MONTH_FIELD
:
1679 case UDAT_STANDALONE_MONTH_FIELD
:
1680 if ( isHebrewCalendar
) {
1681 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
1682 if (hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
== 6 && count
>= 3 )
1683 value
= 13; // Show alternate form for Adar II in leap years in Hebrew calendar.
1684 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
,status
)) && value
>= 6 && count
< 3 )
1685 value
--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7.
1688 int32_t isLeapMonth
= (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
)?
1689 cal
.get(UCAL_IS_LEAP_MONTH
, status
): 0;
1690 // should consolidate the next section by using arrays of pointers & counts for the right symbols...
1692 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1693 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fNarrowMonths
, fSymbols
->fNarrowMonthsCount
,
1694 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatNarrow
]): NULL
, status
);
1696 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneNarrowMonths
, fSymbols
->fStandaloneNarrowMonthsCount
,
1697 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneNarrow
]): NULL
, status
);
1699 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthNarrow
;
1700 } else if (count
== 4) {
1701 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1702 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fMonths
, fSymbols
->fMonthsCount
,
1703 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatWide
]): NULL
, status
);
1704 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthFormat
;
1706 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneMonths
, fSymbols
->fStandaloneMonthsCount
,
1707 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneWide
]): NULL
, status
);
1708 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthStandalone
;
1710 } else if (count
== 3) {
1711 if (patternCharIndex
== UDAT_MONTH_FIELD
) {
1712 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fShortMonths
, fSymbols
->fShortMonthsCount
,
1713 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatAbbrev
]): NULL
, status
);
1714 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthFormat
;
1716 _appendSymbolWithMonthPattern(appendTo
, value
, fSymbols
->fStandaloneShortMonths
, fSymbols
->fStandaloneShortMonthsCount
,
1717 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev
]): NULL
, status
);
1718 capContextUsageType
= DateFormatSymbols::kCapContextUsageMonthStandalone
;
1721 UnicodeString monthNumber
;
1722 zeroPaddingNumber(currentNumberFormat
,monthNumber
, value
+ 1, count
, maxIntCount
);
1723 _appendSymbolWithMonthPattern(appendTo
, 0, &monthNumber
, 1,
1724 (isLeapMonth
!=0)? &(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternNumeric
]): NULL
, status
);
1729 // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
1730 case UDAT_HOUR_OF_DAY1_FIELD
:
1732 zeroPaddingNumber(currentNumberFormat
,appendTo
, cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1, count
, maxIntCount
);
1734 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1737 case UDAT_FRACTIONAL_SECOND_FIELD
:
1738 // Fractional seconds left-justify
1740 int32_t minDigits
= (count
> 3) ? 3 : count
;
1743 } else if (count
== 2) {
1746 zeroPaddingNumber(currentNumberFormat
, appendTo
, value
, minDigits
, maxIntCount
);
1748 zeroPaddingNumber(currentNumberFormat
, appendTo
, 0, count
- 3, maxIntCount
);
1753 // for "ee" or "e", use local numeric day-of-the-week
1754 // for "EEEEEE" or "eeeeee", write out the short day-of-the-week name
1755 // for "EEEEE" or "eeeee", write out the narrow day-of-the-week name
1756 // for "EEEE" or "eeee", write out the wide day-of-the-week name
1757 // for "EEE" or "EE" or "E" or "eee", write out the abbreviated day-of-the-week name
1758 case UDAT_DOW_LOCAL_FIELD
:
1760 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1763 // fall through to EEEEE-EEE handling, but for that we don't want local day-of-week,
1764 // we want standard day-of-week, so first fix value to work for EEEEE-EEE.
1765 value
= cal
.get(UCAL_DAY_OF_WEEK
, status
);
1766 if (U_FAILURE(status
)) {
1769 // fall through, do not break here
1771 case UDAT_DAY_OF_WEEK_FIELD
:
1773 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowWeekdays
,
1774 fSymbols
->fNarrowWeekdaysCount
);
1775 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayNarrow
;
1776 } else if (count
== 4) {
1777 _appendSymbol(appendTo
, value
, fSymbols
->fWeekdays
,
1778 fSymbols
->fWeekdaysCount
);
1779 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1780 } else if (count
== 6) {
1781 _appendSymbol(appendTo
, value
, fSymbols
->fShorterWeekdays
,
1782 fSymbols
->fShorterWeekdaysCount
);
1783 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1785 _appendSymbol(appendTo
, value
, fSymbols
->fShortWeekdays
,
1786 fSymbols
->fShortWeekdaysCount
);
1787 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayFormat
;
1791 // for "ccc", write out the abbreviated day-of-the-week name
1792 // for "cccc", write out the wide day-of-the-week name
1793 // for "ccccc", use the narrow day-of-the-week name
1794 // for "ccccc", use the short day-of-the-week name
1795 case UDAT_STANDALONE_DAY_FIELD
:
1797 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, 1, maxIntCount
);
1800 // fall through to alpha DOW handling, but for that we don't want local day-of-week,
1801 // we want standard day-of-week, so first fix value.
1802 value
= cal
.get(UCAL_DAY_OF_WEEK
, status
);
1803 if (U_FAILURE(status
)) {
1807 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneNarrowWeekdays
,
1808 fSymbols
->fStandaloneNarrowWeekdaysCount
);
1809 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayNarrow
;
1810 } else if (count
== 4) {
1811 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneWeekdays
,
1812 fSymbols
->fStandaloneWeekdaysCount
);
1813 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1814 } else if (count
== 6) {
1815 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneShorterWeekdays
,
1816 fSymbols
->fStandaloneShorterWeekdaysCount
);
1817 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1818 } else { // count == 3
1819 _appendSymbol(appendTo
, value
, fSymbols
->fStandaloneShortWeekdays
,
1820 fSymbols
->fStandaloneShortWeekdaysCount
);
1821 capContextUsageType
= DateFormatSymbols::kCapContextUsageDayStandalone
;
1825 // for "a" symbol, write out the whole AM/PM string
1826 case UDAT_AM_PM_FIELD
:
1828 _appendSymbol(appendTo
, value
, fSymbols
->fAmPms
,
1829 fSymbols
->fAmPmsCount
);
1831 _appendSymbol(appendTo
, value
, fSymbols
->fNarrowAmPms
,
1832 fSymbols
->fNarrowAmPmsCount
);
1836 // if we see pattern character for UDAT_TIME_SEPARATOR_FIELD (none currently defined),
1837 // write out the time separator string. Leave support in for future definition.
1838 case UDAT_TIME_SEPARATOR_FIELD
:
1840 UnicodeString separator
;
1841 appendTo
+= fSymbols
->getTimeSeparatorString(separator
);
1845 // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
1847 case UDAT_HOUR1_FIELD
:
1849 zeroPaddingNumber(currentNumberFormat
,appendTo
, cal
.getLeastMaximum(UCAL_HOUR
) + 1, count
, maxIntCount
);
1851 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
1854 case UDAT_TIMEZONE_FIELD
: // 'z'
1855 case UDAT_TIMEZONE_RFC_FIELD
: // 'Z'
1856 case UDAT_TIMEZONE_GENERIC_FIELD
: // 'v'
1857 case UDAT_TIMEZONE_SPECIAL_FIELD
: // 'V'
1858 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
: // 'O'
1859 case UDAT_TIMEZONE_ISO_FIELD
: // 'X'
1860 case UDAT_TIMEZONE_ISO_LOCAL_FIELD
: // 'x'
1862 UChar zsbuf
[ZONE_NAME_U16_MAX
];
1863 UnicodeString
zoneString(zsbuf
, 0, UPRV_LENGTHOF(zsbuf
));
1864 const TimeZone
& tz
= cal
.getTimeZone();
1865 UDate date
= cal
.getTime(status
);
1866 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
1867 if (U_SUCCESS(status
)) {
1868 if (patternCharIndex
== UDAT_TIMEZONE_FIELD
) {
1871 tzfmt
->format(UTZFMT_STYLE_SPECIFIC_SHORT
, tz
, date
, zoneString
);
1872 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneShort
;
1875 tzfmt
->format(UTZFMT_STYLE_SPECIFIC_LONG
, tz
, date
, zoneString
);
1876 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneLong
;
1879 else if (patternCharIndex
== UDAT_TIMEZONE_RFC_FIELD
) {
1882 tzfmt
->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
, tz
, date
, zoneString
);
1883 } else if (count
== 5) {
1885 tzfmt
->format(UTZFMT_STYLE_ISO_EXTENDED_FULL
, tz
, date
, zoneString
);
1887 // "ZZ", "ZZZ", "ZZZZ"
1888 tzfmt
->format(UTZFMT_STYLE_LOCALIZED_GMT
, tz
, date
, zoneString
);
1891 else if (patternCharIndex
== UDAT_TIMEZONE_GENERIC_FIELD
) {
1894 tzfmt
->format(UTZFMT_STYLE_GENERIC_SHORT
, tz
, date
, zoneString
);
1895 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneShort
;
1896 } else if (count
== 4) {
1898 tzfmt
->format(UTZFMT_STYLE_GENERIC_LONG
, tz
, date
, zoneString
);
1899 capContextUsageType
= DateFormatSymbols::kCapContextUsageMetazoneLong
;
1902 else if (patternCharIndex
== UDAT_TIMEZONE_SPECIAL_FIELD
) {
1905 tzfmt
->format(UTZFMT_STYLE_ZONE_ID_SHORT
, tz
, date
, zoneString
);
1906 } else if (count
== 2) {
1908 tzfmt
->format(UTZFMT_STYLE_ZONE_ID
, tz
, date
, zoneString
);
1909 } else if (count
== 3) {
1911 tzfmt
->format(UTZFMT_STYLE_EXEMPLAR_LOCATION
, tz
, date
, zoneString
);
1912 } else if (count
== 4) {
1914 tzfmt
->format(UTZFMT_STYLE_GENERIC_LOCATION
, tz
, date
, zoneString
);
1915 capContextUsageType
= DateFormatSymbols::kCapContextUsageZoneLong
;
1918 else if (patternCharIndex
== UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
) {
1921 tzfmt
->format(UTZFMT_STYLE_LOCALIZED_GMT_SHORT
, tz
, date
, zoneString
);
1922 } else if (count
== 4) {
1924 tzfmt
->format(UTZFMT_STYLE_LOCALIZED_GMT
, tz
, date
, zoneString
);
1927 else if (patternCharIndex
== UDAT_TIMEZONE_ISO_FIELD
) {
1930 tzfmt
->format(UTZFMT_STYLE_ISO_BASIC_SHORT
, tz
, date
, zoneString
);
1931 } else if (count
== 2) {
1933 tzfmt
->format(UTZFMT_STYLE_ISO_BASIC_FIXED
, tz
, date
, zoneString
);
1934 } else if (count
== 3) {
1936 tzfmt
->format(UTZFMT_STYLE_ISO_EXTENDED_FIXED
, tz
, date
, zoneString
);
1937 } else if (count
== 4) {
1939 tzfmt
->format(UTZFMT_STYLE_ISO_BASIC_FULL
, tz
, date
, zoneString
);
1940 } else if (count
== 5) {
1942 tzfmt
->format(UTZFMT_STYLE_ISO_EXTENDED_FULL
, tz
, date
, zoneString
);
1945 else if (patternCharIndex
== UDAT_TIMEZONE_ISO_LOCAL_FIELD
) {
1948 tzfmt
->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
, tz
, date
, zoneString
);
1949 } else if (count
== 2) {
1951 tzfmt
->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
, tz
, date
, zoneString
);
1952 } else if (count
== 3) {
1954 tzfmt
->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
, tz
, date
, zoneString
);
1955 } else if (count
== 4) {
1957 tzfmt
->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
, tz
, date
, zoneString
);
1958 } else if (count
== 5) {
1960 tzfmt
->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
, tz
, date
, zoneString
);
1967 appendTo
+= zoneString
;
1971 case UDAT_QUARTER_FIELD
:
1973 _appendSymbol(appendTo
, value
/3, fSymbols
->fQuarters
,
1974 fSymbols
->fQuartersCount
);
1975 else if (count
== 3)
1976 _appendSymbol(appendTo
, value
/3, fSymbols
->fShortQuarters
,
1977 fSymbols
->fShortQuartersCount
);
1979 zeroPaddingNumber(currentNumberFormat
,appendTo
, (value
/3) + 1, count
, maxIntCount
);
1982 case UDAT_STANDALONE_QUARTER_FIELD
:
1984 _appendSymbol(appendTo
, value
/3, fSymbols
->fStandaloneQuarters
,
1985 fSymbols
->fStandaloneQuartersCount
);
1986 else if (count
== 3)
1987 _appendSymbol(appendTo
, value
/3, fSymbols
->fStandaloneShortQuarters
,
1988 fSymbols
->fStandaloneShortQuartersCount
);
1990 zeroPaddingNumber(currentNumberFormat
,appendTo
, (value
/3) + 1, count
, maxIntCount
);
1993 case UDAT_AM_PM_MIDNIGHT_NOON_FIELD
:
1995 const UnicodeString
*toAppend
= NULL
;
1996 int32_t hour
= cal
.get(UCAL_HOUR_OF_DAY
, status
);
1998 // Note: "midnight" can be ambiguous as to whether it refers to beginning of day or end of day.
1999 // For ICU 57 output of "midnight" is temporarily suppressed.
2001 // For "midnight" and "noon":
2002 // Time, as displayed, must be exactly noon or midnight.
2003 // This means minutes and seconds, if present, must be zero.
2004 if ((/*hour == 0 ||*/ hour
== 12) &&
2005 (!fHasMinute
|| cal
.get(UCAL_MINUTE
, status
) == 0) &&
2006 (!fHasSecond
|| cal
.get(UCAL_SECOND
, status
) == 0)) {
2007 // Stealing am/pm value to use as our array index.
2008 // It works out: am/midnight are both 0, pm/noon are both 1,
2009 // 12 am is 12 midnight, and 12 pm is 12 noon.
2010 int32_t val
= cal
.get(UCAL_AM_PM
, status
);
2013 toAppend
= &fSymbols
->fAbbreviatedDayPeriods
[val
];
2014 } else if (count
== 4 || count
> 5) {
2015 toAppend
= &fSymbols
->fWideDayPeriods
[val
];
2016 } else { // count == 5
2017 toAppend
= &fSymbols
->fNarrowDayPeriods
[val
];
2021 // toAppend is NULL if time isn't exactly midnight or noon (as displayed).
2022 // toAppend is bogus if time is midnight or noon, but no localized string exists.
2023 // In either case, fall back to am/pm.
2024 if (toAppend
== NULL
|| toAppend
->isBogus()) {
2025 // Reformat with identical arguments except ch, now changed to 'a'.
2026 subFormat(appendTo
, 0x61, count
, capitalizationContext
, fieldNum
,
2027 handler
, cal
, status
);
2029 appendTo
+= *toAppend
;
2035 case UDAT_FLEXIBLE_DAY_PERIOD_FIELD
:
2037 // TODO: Maybe fetch the DayperiodRules during initialization (instead of at the first
2038 // loading of an instance) if a relevant pattern character (b or B) is used.
2039 const DayPeriodRules
*ruleSet
= DayPeriodRules::getInstance(this->getSmpFmtLocale(), status
);
2040 if (U_FAILURE(status
)) {
2041 // Data doesn't conform to spec, therefore loading failed.
2044 if (ruleSet
== NULL
) {
2045 // Data doesn't exist for the locale we're looking for.
2046 // Falling back to am/pm.
2047 subFormat(appendTo
, 0x61, count
, capitalizationContext
, fieldNum
,
2048 handler
, cal
, status
);
2052 // Get current display time.
2053 int32_t hour
= cal
.get(UCAL_HOUR_OF_DAY
, status
);
2056 minute
= cal
.get(UCAL_MINUTE
, status
);
2060 second
= cal
.get(UCAL_SECOND
, status
);
2063 // Determine day period.
2064 DayPeriodRules::DayPeriod periodType
;
2065 if (hour
== 0 && minute
== 0 && second
== 0 && ruleSet
->hasMidnight()) {
2066 periodType
= DayPeriodRules::DAYPERIOD_MIDNIGHT
;
2067 } else if (hour
== 12 && minute
== 0 && second
== 0 && ruleSet
->hasNoon()) {
2068 periodType
= DayPeriodRules::DAYPERIOD_NOON
;
2070 periodType
= ruleSet
->getDayPeriodForHour(hour
);
2073 // Rule set exists, therefore periodType can't be UNKNOWN.
2074 // Get localized string.
2075 U_ASSERT(periodType
!= DayPeriodRules::DAYPERIOD_UNKNOWN
);
2076 UnicodeString
*toAppend
= NULL
;
2079 // Note: "midnight" can be ambiguous as to whether it refers to beginning of day or end of day.
2080 // For ICU 57 output of "midnight" is temporarily suppressed.
2082 if (periodType
!= DayPeriodRules::DAYPERIOD_AM
&&
2083 periodType
!= DayPeriodRules::DAYPERIOD_PM
&&
2084 periodType
!= DayPeriodRules::DAYPERIOD_MIDNIGHT
) {
2085 index
= (int32_t)periodType
;
2087 toAppend
= &fSymbols
->fAbbreviatedDayPeriods
[index
]; // i.e. short
2088 } else if (count
== 4 || count
> 5) {
2089 toAppend
= &fSymbols
->fWideDayPeriods
[index
];
2090 } else { // count == 5
2091 toAppend
= &fSymbols
->fNarrowDayPeriods
[index
];
2095 // Fallback schedule:
2096 // Midnight/Noon -> General Periods -> AM/PM.
2098 // Midnight/Noon -> General Periods.
2099 if ((toAppend
== NULL
|| toAppend
->isBogus()) &&
2100 (periodType
== DayPeriodRules::DAYPERIOD_MIDNIGHT
||
2101 periodType
== DayPeriodRules::DAYPERIOD_NOON
)) {
2102 periodType
= ruleSet
->getDayPeriodForHour(hour
);
2103 index
= (int32_t)periodType
;
2106 toAppend
= &fSymbols
->fAbbreviatedDayPeriods
[index
]; // i.e. short
2107 } else if (count
== 4 || count
> 5) {
2108 toAppend
= &fSymbols
->fWideDayPeriods
[index
];
2109 } else { // count == 5
2110 toAppend
= &fSymbols
->fNarrowDayPeriods
[index
];
2114 // General Periods -> AM/PM.
2115 if (periodType
== DayPeriodRules::DAYPERIOD_AM
||
2116 periodType
== DayPeriodRules::DAYPERIOD_PM
||
2117 toAppend
->isBogus()) {
2118 subFormat(appendTo
, 0x61, count
, capitalizationContext
, fieldNum
,
2119 handler
, cal
, status
);
2122 appendTo
+= *toAppend
;
2128 // all of the other pattern symbols can be formatted as simple numbers with
2129 // appropriate zero padding
2131 zeroPaddingNumber(currentNumberFormat
,appendTo
, value
, count
, maxIntCount
);
2134 #if !UCONFIG_NO_BREAK_ITERATION
2135 // if first field, check to see whether we need to and are able to titlecase it
2136 if (fieldNum
== 0 && fCapitalizationBrkIter
!= NULL
&& appendTo
.length() > beginOffset
&&
2137 u_islower(appendTo
.char32At(beginOffset
))) {
2138 UBool titlecase
= FALSE
;
2139 switch (capitalizationContext
) {
2140 case UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
:
2143 case UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
:
2144 titlecase
= fSymbols
->fCapitalization
[capContextUsageType
][0];
2146 case UDISPCTX_CAPITALIZATION_FOR_STANDALONE
:
2147 titlecase
= fSymbols
->fCapitalization
[capContextUsageType
][1];
2150 // titlecase = FALSE;
2154 BreakIterator
* const mutableCapitalizationBrkIter
= fCapitalizationBrkIter
->clone();
2155 UnicodeString
firstField(appendTo
, beginOffset
);
2156 firstField
.toTitle(mutableCapitalizationBrkIter
, fLocale
, U_TITLECASE_NO_LOWERCASE
| U_TITLECASE_NO_BREAK_ADJUSTMENT
);
2157 appendTo
.replaceBetween(beginOffset
, appendTo
.length(), firstField
);
2158 delete mutableCapitalizationBrkIter
;
2163 handler
.addAttribute(fgPatternIndexToDateFormatField
[patternCharIndex
], beginOffset
, appendTo
.length());
2166 //----------------------------------------------------------------------
2168 void SimpleDateFormat::adoptNumberFormat(NumberFormat
*formatToAdopt
) {
2169 fixNumberFormatForDates(*formatToAdopt
);
2170 freeFastNumberFormatters(); // deletes refs to fNumberFormat's symbols
2171 delete fNumberFormat
;
2172 fNumberFormat
= formatToAdopt
;
2174 // We successfully set the default number format. Now delete the overrides
2176 if (fSharedNumberFormatters
) {
2177 freeSharedNumberFormatters(fSharedNumberFormatters
);
2178 fSharedNumberFormatters
= NULL
;
2181 // Also re-compute the fast formatters.
2182 UErrorCode localStatus
= U_ZERO_ERROR
;
2183 initFastNumberFormatters(localStatus
);
2186 void SimpleDateFormat::adoptNumberFormat(const UnicodeString
& fields
, NumberFormat
*formatToAdopt
, UErrorCode
&status
){
2187 fixNumberFormatForDates(*formatToAdopt
);
2188 LocalPointer
<NumberFormat
> fmt(formatToAdopt
);
2189 if (U_FAILURE(status
)) {
2193 // We must ensure fSharedNumberFormatters is allocated.
2194 if (fSharedNumberFormatters
== NULL
) {
2195 fSharedNumberFormatters
= allocSharedNumberFormatters();
2196 if (fSharedNumberFormatters
== NULL
) {
2197 status
= U_MEMORY_ALLOCATION_ERROR
;
2201 const SharedNumberFormat
*newFormat
= createSharedNumberFormat(fmt
.orphan());
2202 if (newFormat
== NULL
) {
2203 status
= U_MEMORY_ALLOCATION_ERROR
;
2206 for (int i
=0; i
<fields
.length(); i
++) {
2207 UChar field
= fields
.charAt(i
);
2208 // if the pattern character is unrecognized, signal an error and bail out
2209 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(field
);
2210 if (patternCharIndex
== UDAT_FIELD_COUNT
) {
2211 status
= U_INVALID_FORMAT_ERROR
;
2212 newFormat
->deleteIfZeroRefCount();
2216 // Set the number formatter in the table
2217 SharedObject::copyPtr(
2218 newFormat
, fSharedNumberFormatters
[patternCharIndex
]);
2220 newFormat
->deleteIfZeroRefCount();
2223 const NumberFormat
*
2224 SimpleDateFormat::getNumberFormatForField(UChar field
) const {
2225 UDateFormatField index
= DateFormatSymbols::getPatternCharIndex(field
);
2226 if (index
== UDAT_FIELD_COUNT
) {
2229 return getNumberFormatByIndex(index
);
2232 //----------------------------------------------------------------------
2234 SimpleDateFormat::zeroPaddingNumber(
2235 const NumberFormat
*currentNumberFormat
,
2236 UnicodeString
&appendTo
,
2237 int32_t value
, int32_t minDigits
, int32_t maxDigits
) const
2239 const number::LocalizedNumberFormatter
* fastFormatter
= nullptr;
2240 // NOTE: This uses the heuristic that these five min/max int settings account for the vast majority
2241 // of SimpleDateFormat number formatting cases at the time of writing (ICU 62).
2242 if (currentNumberFormat
== fNumberFormat
) {
2243 if (maxDigits
== 10) {
2244 if (minDigits
== 1) {
2245 fastFormatter
= fFastNumberFormatters
[SMPDTFMT_NF_1x10
];
2246 } else if (minDigits
== 2) {
2247 fastFormatter
= fFastNumberFormatters
[SMPDTFMT_NF_2x10
];
2248 } else if (minDigits
== 3) {
2249 fastFormatter
= fFastNumberFormatters
[SMPDTFMT_NF_3x10
];
2250 } else if (minDigits
== 4) {
2251 fastFormatter
= fFastNumberFormatters
[SMPDTFMT_NF_4x10
];
2253 } else if (maxDigits
== 2) {
2254 if (minDigits
== 2) {
2255 fastFormatter
= fFastNumberFormatters
[SMPDTFMT_NF_2x2
];
2259 if (fastFormatter
!= nullptr) {
2260 // Can use fast path
2261 number::impl::UFormattedNumberData result
;
2262 result
.quantity
.setToInt(value
);
2263 UErrorCode localStatus
= U_ZERO_ERROR
;
2264 fastFormatter
->formatImpl(&result
, localStatus
);
2265 if (U_FAILURE(localStatus
)) {
2268 appendTo
.append(result
.getStringRef().toTempUnicodeString());
2272 // Check for RBNF (no clone necessary)
2273 auto* rbnf
= dynamic_cast<const RuleBasedNumberFormat
*>(currentNumberFormat
);
2274 if (rbnf
!= nullptr) {
2275 FieldPosition
pos(FieldPosition::DONT_CARE
);
2276 rbnf
->format(value
, appendTo
, pos
); // 3rd arg is there to speed up processing
2280 // Fall back to slow path (clone and mutate the NumberFormat)
2281 if (currentNumberFormat
!= nullptr) {
2282 FieldPosition
pos(FieldPosition::DONT_CARE
);
2283 LocalPointer
<NumberFormat
> nf(currentNumberFormat
->clone());
2284 nf
->setMinimumIntegerDigits(minDigits
);
2285 nf
->setMaximumIntegerDigits(maxDigits
);
2286 nf
->format(value
, appendTo
, pos
); // 3rd arg is there to speed up processing
2290 //----------------------------------------------------------------------
2293 * Return true if the given format character, occuring count
2294 * times, represents a numeric field.
2296 UBool
SimpleDateFormat::isNumeric(UChar formatChar
, int32_t count
) {
2297 return DateFormatSymbols::isNumericPatternChar(formatChar
, count
);
2301 SimpleDateFormat::isAtNumericField(const UnicodeString
&pattern
, int32_t patternOffset
) {
2302 if (patternOffset
>= pattern
.length()) {
2306 UChar ch
= pattern
.charAt(patternOffset
);
2307 UDateFormatField f
= DateFormatSymbols::getPatternCharIndex(ch
);
2308 if (f
== UDAT_FIELD_COUNT
) {
2312 int32_t i
= patternOffset
;
2313 while (pattern
.charAt(++i
) == ch
) {}
2314 return DateFormatSymbols::isNumericField(f
, i
- patternOffset
);
2318 SimpleDateFormat::isAfterNonNumericField(const UnicodeString
&pattern
, int32_t patternOffset
) {
2319 if (patternOffset
<= 0) {
2320 // not after any field
2323 UChar ch
= pattern
.charAt(--patternOffset
);
2324 UDateFormatField f
= DateFormatSymbols::getPatternCharIndex(ch
);
2325 if (f
== UDAT_FIELD_COUNT
) {
2326 // not after any field
2329 int32_t i
= patternOffset
;
2330 while (pattern
.charAt(--i
) == ch
) {}
2331 return !DateFormatSymbols::isNumericField(f
, patternOffset
- i
);
2335 SimpleDateFormat::parse(const UnicodeString
& text
, Calendar
& cal
, ParsePosition
& parsePos
) const
2337 UErrorCode status
= U_ZERO_ERROR
;
2338 int32_t pos
= parsePos
.getIndex();
2339 if(parsePos
.getIndex() < 0) {
2340 parsePos
.setErrorIndex(0);
2343 int32_t start
= pos
;
2345 // Hold the day period until everything else is parsed, because we need
2346 // the hour to interpret time correctly.
2347 int32_t dayPeriodInt
= -1;
2349 UBool ambiguousYear
[] = { FALSE
};
2350 int32_t saveHebrewMonth
= -1;
2352 UTimeZoneFormatTimeType tzTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
2354 // For parsing abutting numeric fields. 'abutPat' is the
2355 // offset into 'pattern' of the first of 2 or more abutting
2356 // numeric fields. 'abutStart' is the offset into 'text'
2357 // where parsing the fields begins. 'abutPass' starts off as 0
2358 // and increments each time we try to parse the fields.
2359 int32_t abutPat
= -1; // If >=0, we are in a run of abutting numeric fields
2360 int32_t abutStart
= 0;
2361 int32_t abutPass
= 0;
2362 UBool inQuote
= FALSE
;
2364 MessageFormat
* numericLeapMonthFormatter
= NULL
;
2366 Calendar
* calClone
= NULL
;
2367 Calendar
*workCal
= &cal
;
2368 if (&cal
!= fCalendar
&& uprv_strcmp(cal
.getType(), fCalendar
->getType()) != 0) {
2369 // Different calendar type
2370 // We use the time/zone from the input calendar, but
2371 // do not use the input calendar for field calculation.
2372 calClone
= fCalendar
->clone();
2373 if (calClone
!= NULL
) {
2374 calClone
->setTime(cal
.getTime(status
),status
);
2375 if (U_FAILURE(status
)) {
2378 calClone
->setTimeZone(cal
.getTimeZone());
2381 status
= U_MEMORY_ALLOCATION_ERROR
;
2386 if (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
) {
2387 numericLeapMonthFormatter
= new MessageFormat(fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternNumeric
], fLocale
, status
);
2388 if (numericLeapMonthFormatter
== NULL
) {
2389 status
= U_MEMORY_ALLOCATION_ERROR
;
2391 } else if (U_FAILURE(status
)) {
2392 goto ExitParse
; // this will delete numericLeapMonthFormatter
2396 for (int32_t i
=0; i
<fPattern
.length(); ++i
) {
2397 UChar ch
= fPattern
.charAt(i
);
2399 // Handle alphabetic field characters.
2400 if (!inQuote
&& isSyntaxChar(ch
)) {
2401 int32_t fieldPat
= i
;
2403 // Count the length of this field specifier
2405 while ((i
+1)<fPattern
.length() &&
2406 fPattern
.charAt(i
+1) == ch
) {
2411 if (isNumeric(ch
, count
)) {
2413 // Determine if there is an abutting numeric field.
2414 // Record the start of a set of abutting numeric fields.
2415 if (isAtNumericField(fPattern
, i
+ 1)) {
2422 abutPat
= -1; // End of any abutting fields
2425 // Handle fields within a run of abutting numeric fields. Take
2426 // the pattern "HHmmss" as an example. We will try to parse
2427 // 2/2/2 characters of the input text, then if that fails,
2428 // 1/2/2. We only adjust the width of the leftmost field; the
2429 // others remain fixed. This allows "123456" => 12:34:56, but
2430 // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
2431 // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
2433 // If we are at the start of a run of abutting fields, then
2434 // shorten this field in each pass. If we can't shorten
2435 // this field any more, then the parse of this set of
2436 // abutting numeric fields has failed.
2437 if (fieldPat
== abutPat
) {
2438 count
-= abutPass
++;
2440 status
= U_PARSE_ERROR
;
2445 pos
= subParse(text
, pos
, ch
, count
,
2446 TRUE
, FALSE
, ambiguousYear
, saveHebrewMonth
, *workCal
, i
, numericLeapMonthFormatter
, &tzTimeType
);
2448 // If the parse fails anywhere in the run, back up to the
2449 // start of the run and retry.
2457 // Handle non-numeric fields and non-abutting numeric
2459 else if (ch
!= 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
2460 int32_t s
= subParse(text
, pos
, ch
, count
,
2461 FALSE
, TRUE
, ambiguousYear
, saveHebrewMonth
, *workCal
, i
, numericLeapMonthFormatter
, &tzTimeType
, &dayPeriodInt
);
2464 // era not present, in special cases allow this to continue
2465 // from the position where the era was expected
2468 if (i
+1 < fPattern
.length()) {
2469 // move to next pattern character
2470 UChar c
= fPattern
.charAt(i
+1);
2472 // check for whitespace
2473 if (PatternProps::isWhiteSpace(c
)) {
2475 // Advance over run in pattern
2476 while ((i
+1)<fPattern
.length() &&
2477 PatternProps::isWhiteSpace(fPattern
.charAt(i
+1))) {
2484 status
= U_PARSE_ERROR
;
2491 // Handle literal pattern characters. These are any
2492 // quoted characters and non-alphabetic unquoted
2496 abutPat
= -1; // End of any abutting fields
2498 if (! matchLiterals(fPattern
, i
, text
, pos
, getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, status
), getBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH
, status
), isLenient())) {
2499 status
= U_PARSE_ERROR
;
2505 // Special hack for trailing "." after non-numeric field.
2506 if (text
.charAt(pos
) == 0x2e && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, status
)) {
2507 // only do if the last field is not numeric
2508 if (isAfterNonNumericField(fPattern
, fPattern
.length())) {
2509 pos
++; // skip the extra "."
2513 // If dayPeriod is set, use it in conjunction with hour-of-day to determine am/pm.
2514 if (dayPeriodInt
>= 0) {
2515 DayPeriodRules::DayPeriod dayPeriod
= (DayPeriodRules::DayPeriod
)dayPeriodInt
;
2516 const DayPeriodRules
*ruleSet
= DayPeriodRules::getInstance(this->getSmpFmtLocale(), status
);
2518 if (!cal
.isSet(UCAL_HOUR
) && !cal
.isSet(UCAL_HOUR_OF_DAY
)) {
2519 // If hour is not set, set time to the midpoint of current day period, overwriting
2520 // minutes if it's set.
2521 double midPoint
= ruleSet
->getMidPointForDayPeriod(dayPeriod
, status
);
2523 // If we can't get midPoint we do nothing.
2524 if (U_SUCCESS(status
)) {
2525 // Truncate midPoint toward zero to get the hour.
2526 // Any leftover means it was a half-hour.
2527 int32_t midPointHour
= (int32_t) midPoint
;
2528 int32_t midPointMinute
= (midPoint
- midPointHour
) > 0 ? 30 : 0;
2530 // No need to set am/pm because hour-of-day is set last therefore takes precedence.
2531 cal
.set(UCAL_HOUR_OF_DAY
, midPointHour
);
2532 cal
.set(UCAL_MINUTE
, midPointMinute
);
2537 if (cal
.isSet(UCAL_HOUR_OF_DAY
)) { // Hour is parsed in 24-hour format.
2538 hourOfDay
= cal
.get(UCAL_HOUR_OF_DAY
, status
);
2539 } else { // Hour is parsed in 12-hour format.
2540 hourOfDay
= cal
.get(UCAL_HOUR
, status
);
2541 // cal.get() turns 12 to 0 for 12-hour time; change 0 to 12
2542 // so 0 unambiguously means a 24-hour time from above.
2543 if (hourOfDay
== 0) { hourOfDay
= 12; }
2545 U_ASSERT(0 <= hourOfDay
&& hourOfDay
<= 23);
2548 // If hour-of-day is 0 or 13 thru 23 then input time in unambiguously in 24-hour format.
2549 if (hourOfDay
== 0 || (13 <= hourOfDay
&& hourOfDay
<= 23)) {
2550 // Make hour-of-day take precedence over (hour + am/pm) by setting it again.
2551 cal
.set(UCAL_HOUR_OF_DAY
, hourOfDay
);
2553 // We have a 12-hour time and need to choose between am and pm.
2554 // Behave as if dayPeriod spanned 6 hours each way from its center point.
2555 // This will parse correctly for consistent time + period (e.g. 10 at night) as
2556 // well as provide a reasonable recovery for inconsistent time + period (e.g.
2557 // 9 in the afternoon).
2559 // Assume current time is in the AM.
2560 // - Change 12 back to 0 for easier handling of 12am.
2561 // - Append minutes as fractional hours because e.g. 8:15 and 8:45 could be parsed
2562 // into different half-days if center of dayPeriod is at 14:30.
2563 // - cal.get(MINUTE) will return 0 if MINUTE is unset, which works.
2564 if (hourOfDay
== 12) { hourOfDay
= 0; }
2565 double currentHour
= hourOfDay
+ (cal
.get(UCAL_MINUTE
, status
)) / 60.0;
2566 double midPointHour
= ruleSet
->getMidPointForDayPeriod(dayPeriod
, status
);
2568 if (U_SUCCESS(status
)) {
2569 double hoursAheadMidPoint
= currentHour
- midPointHour
;
2571 // Assume current time is in the AM.
2572 if (-6 <= hoursAheadMidPoint
&& hoursAheadMidPoint
< 6) {
2573 // Assumption holds; set time as such.
2574 cal
.set(UCAL_AM_PM
, 0);
2576 cal
.set(UCAL_AM_PM
, 1);
2583 // At this point the fields of Calendar have been set. Calendar
2584 // will fill in default values for missing fields when the time
2587 parsePos
.setIndex(pos
);
2589 // This part is a problem: When we call parsedDate.after, we compute the time.
2590 // Take the date April 3 2004 at 2:30 am. When this is first set up, the year
2591 // will be wrong if we're parsing a 2-digit year pattern. It will be 1904.
2592 // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am
2593 // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
2594 // on that day. It is therefore parsed out to fields as 3:30 am. Then we
2595 // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is
2596 // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
2598 UDate parsedDate = calendar.getTime();
2599 if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
2600 calendar.add(Calendar.YEAR, 100);
2601 parsedDate = calendar.getTime();
2604 // Because of the above condition, save off the fields in case we need to readjust.
2605 // The procedure we use here is not particularly efficient, but there is no other
2606 // way to do this given the API restrictions present in Calendar. We minimize
2607 // inefficiency by only performing this computation when it might apply, that is,
2608 // when the two-digit year is equal to the start year, and thus might fall at the
2609 // front or the back of the default century. This only works because we adjust
2610 // the year correctly to start with in other cases -- see subParse().
2611 if (ambiguousYear
[0] || tzTimeType
!= UTZFMT_TIME_TYPE_UNKNOWN
) // If this is true then the two-digit year == the default start year
2613 // We need a copy of the fields, and we need to avoid triggering a call to
2614 // complete(), which will recalculate the fields. Since we can't access
2615 // the fields[] array in Calendar, we clone the entire object. This will
2616 // stop working if Calendar.clone() is ever rewritten to call complete().
2618 if (ambiguousYear
[0]) {
2620 // Check for failed cloning.
2622 status
= U_MEMORY_ALLOCATION_ERROR
;
2625 UDate parsedDate
= copy
->getTime(status
);
2626 // {sfb} check internalGetDefaultCenturyStart
2627 if (fHaveDefaultCentury
&& (parsedDate
< fDefaultCenturyStart
)) {
2628 // We can't use add here because that does a complete() first.
2629 cal
.set(UCAL_YEAR
, fDefaultCenturyStartYear
+ 100);
2634 if (tzTimeType
!= UTZFMT_TIME_TYPE_UNKNOWN
) {
2636 // Check for failed cloning.
2638 status
= U_MEMORY_ALLOCATION_ERROR
;
2641 const TimeZone
& tz
= cal
.getTimeZone();
2642 BasicTimeZone
*btz
= NULL
;
2644 if (dynamic_cast<const OlsonTimeZone
*>(&tz
) != NULL
2645 || dynamic_cast<const SimpleTimeZone
*>(&tz
) != NULL
2646 || dynamic_cast<const RuleBasedTimeZone
*>(&tz
) != NULL
2647 || dynamic_cast<const VTimeZone
*>(&tz
) != NULL
) {
2648 btz
= (BasicTimeZone
*)&tz
;
2652 copy
->set(UCAL_ZONE_OFFSET
, 0);
2653 copy
->set(UCAL_DST_OFFSET
, 0);
2654 UDate localMillis
= copy
->getTime(status
);
2656 // Make sure parsed time zone type (Standard or Daylight)
2657 // matches the rule used by the parsed time zone.
2660 if (tzTimeType
== UTZFMT_TIME_TYPE_STANDARD
) {
2661 btz
->getOffsetFromLocal(localMillis
,
2662 BasicTimeZone::kStandard
, BasicTimeZone::kStandard
, raw
, dst
, status
);
2664 btz
->getOffsetFromLocal(localMillis
,
2665 BasicTimeZone::kDaylight
, BasicTimeZone::kDaylight
, raw
, dst
, status
);
2668 // No good way to resolve ambiguous time at transition,
2669 // but following code work in most case.
2670 tz
.getOffset(localMillis
, TRUE
, raw
, dst
, status
);
2673 // Now, compare the results with parsed type, either standard or daylight saving time
2674 int32_t resolvedSavings
= dst
;
2675 if (tzTimeType
== UTZFMT_TIME_TYPE_STANDARD
) {
2677 // Override DST_OFFSET = 0 in the result calendar
2678 resolvedSavings
= 0;
2680 } else { // tztype == TZTYPE_DST
2683 UDate time
= localMillis
+ raw
;
2684 // We use the nearest daylight saving time rule.
2685 TimeZoneTransition beforeTrs
, afterTrs
;
2686 UDate beforeT
= time
, afterT
= time
;
2687 int32_t beforeSav
= 0, afterSav
= 0;
2688 UBool beforeTrsAvail
, afterTrsAvail
;
2690 // Search for DST rule before or on the time
2692 beforeTrsAvail
= btz
->getPreviousTransition(beforeT
, TRUE
, beforeTrs
);
2693 if (!beforeTrsAvail
) {
2696 beforeT
= beforeTrs
.getTime() - 1;
2697 beforeSav
= beforeTrs
.getFrom()->getDSTSavings();
2698 if (beforeSav
!= 0) {
2703 // Search for DST rule after the time
2705 afterTrsAvail
= btz
->getNextTransition(afterT
, FALSE
, afterTrs
);
2706 if (!afterTrsAvail
) {
2709 afterT
= afterTrs
.getTime();
2710 afterSav
= afterTrs
.getTo()->getDSTSavings();
2711 if (afterSav
!= 0) {
2716 if (beforeTrsAvail
&& afterTrsAvail
) {
2717 if (time
- beforeT
> afterT
- time
) {
2718 resolvedSavings
= afterSav
;
2720 resolvedSavings
= beforeSav
;
2722 } else if (beforeTrsAvail
&& beforeSav
!= 0) {
2723 resolvedSavings
= beforeSav
;
2724 } else if (afterTrsAvail
&& afterSav
!= 0) {
2725 resolvedSavings
= afterSav
;
2727 resolvedSavings
= btz
->getDSTSavings();
2730 resolvedSavings
= tz
.getDSTSavings();
2732 if (resolvedSavings
== 0) {
2734 resolvedSavings
= U_MILLIS_PER_HOUR
;
2738 cal
.set(UCAL_ZONE_OFFSET
, raw
);
2739 cal
.set(UCAL_DST_OFFSET
, resolvedSavings
);
2744 // Set the parsed result if local calendar is used
2745 // instead of the input calendar
2746 if (U_SUCCESS(status
) && workCal
!= &cal
) {
2747 cal
.setTimeZone(workCal
->getTimeZone());
2748 cal
.setTime(workCal
->getTime(status
), status
);
2751 if (numericLeapMonthFormatter
!= NULL
) {
2752 delete numericLeapMonthFormatter
;
2754 if (calClone
!= NULL
) {
2758 // If any Calendar calls failed, we pretend that we
2759 // couldn't parse the string, when in reality this isn't quite accurate--
2760 // we did parse it; the Calendar calls just failed.
2761 if (U_FAILURE(status
)) {
2762 parsePos
.setErrorIndex(pos
);
2763 parsePos
.setIndex(start
);
2767 //----------------------------------------------------------------------
2770 matchStringWithOptionalDot(const UnicodeString
&text
,
2772 const UnicodeString
&data
);
2774 int32_t SimpleDateFormat::matchQuarterString(const UnicodeString
& text
,
2776 UCalendarDateFields field
,
2777 const UnicodeString
* data
,
2779 Calendar
& cal
) const
2782 int32_t count
= dataCount
;
2784 // There may be multiple strings in the data[] array which begin with
2785 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2786 // We keep track of the longest match, and return that. Note that this
2787 // unfortunately requires us to test all array elements.
2788 int32_t bestMatchLength
= 0, bestMatch
= -1;
2789 UnicodeString bestMatchName
;
2791 for (; i
< count
; ++i
) {
2792 int32_t matchLength
= 0;
2793 if ((matchLength
= matchStringWithOptionalDot(text
, start
, data
[i
])) > bestMatchLength
) {
2794 bestMatchLength
= matchLength
;
2799 if (bestMatch
>= 0) {
2800 cal
.set(field
, bestMatch
* 3);
2801 return start
+ bestMatchLength
;
2807 int32_t SimpleDateFormat::matchDayPeriodStrings(const UnicodeString
& text
, int32_t start
,
2808 const UnicodeString
* data
, int32_t dataCount
,
2809 int32_t &dayPeriod
) const
2812 int32_t bestMatchLength
= 0, bestMatch
= -1;
2814 for (int32_t i
= 0; i
< dataCount
; ++i
) {
2815 int32_t matchLength
= 0;
2816 if ((matchLength
= matchStringWithOptionalDot(text
, start
, data
[i
])) > bestMatchLength
) {
2817 bestMatchLength
= matchLength
;
2822 if (bestMatch
>= 0) {
2823 dayPeriod
= bestMatch
;
2824 return start
+ bestMatchLength
;
2830 //----------------------------------------------------------------------
2831 #define IS_BIDI_MARK(c) (c==0x200E || c==0x200F || c==0x061C)
2833 UBool
SimpleDateFormat::matchLiterals(const UnicodeString
&pattern
,
2834 int32_t &patternOffset
,
2835 const UnicodeString
&text
,
2836 int32_t &textOffset
,
2837 UBool whitespaceLenient
,
2838 UBool partialMatchLenient
,
2841 UBool inQuote
= FALSE
;
2842 UnicodeString literal
;
2843 int32_t i
= patternOffset
;
2845 // scan pattern looking for contiguous literal characters
2846 for ( ; i
< pattern
.length(); i
+= 1) {
2847 UChar ch
= pattern
.charAt(i
);
2849 if (!inQuote
&& isSyntaxChar(ch
)) {
2854 // Match a quote literal ('') inside OR outside of quotes
2855 if ((i
+ 1) < pattern
.length() && pattern
.charAt(i
+ 1) == QUOTE
) {
2863 if (!IS_BIDI_MARK(ch
)) {
2868 // at this point, literal contains the pattern literal text (without bidi marks)
2869 // and i is the index of the next non-literal pattern character.
2871 int32_t t
= textOffset
;
2873 if (whitespaceLenient
) {
2874 // trim leading, trailing whitespace from the pattern literal
2877 // ignore any leading whitespace (or bidi marks) in the text
2878 while (t
< text
.length()) {
2879 UChar ch
= text
.charAt(t
);
2880 if (!u_isWhitespace(ch
) && !IS_BIDI_MARK(ch
)) {
2887 // Get ignorables, move up here
2888 const UnicodeSet
*ignorables
= NULL
;
2889 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(pattern
.charAt(i
));
2890 if (patternCharIndex
!= UDAT_FIELD_COUNT
) {
2891 ignorables
= SimpleDateFormatStaticSets::getIgnorables(patternCharIndex
);
2894 for (p
= 0; p
< literal
.length() && t
< text
.length();) {
2895 UBool needWhitespace
= FALSE
;
2897 // Skip any whitespace at current position in pattern,
2898 // but remember whether we found whitespace in the pattern
2899 // (we already deleted any bidi marks in the pattern).
2900 while (p
< literal
.length() && PatternProps::isWhiteSpace(literal
.charAt(p
))) {
2901 needWhitespace
= TRUE
;
2905 // If the pattern has whitespace at this point, skip it in text as well
2906 // (if the text does not have any, that may be an error for strict parsing)
2907 if (needWhitespace
) {
2908 UBool whitespaceInText
= FALSE
;
2910 // Skip any whitespace (or bidi marks) at current position in text,
2911 // but remember whether we found whitespace in the text at this point.
2912 while (t
< text
.length()) {
2913 UChar tch
= text
.charAt(t
);
2914 if (u_isUWhiteSpace(tch
) || PatternProps::isWhiteSpace(tch
)) {
2915 whitespaceInText
= TRUE
;
2916 } else if (!IS_BIDI_MARK(tch
)) {
2923 // TODO: should we require internal spaces
2924 // in lenient mode? (There won't be any
2925 // leading or trailing spaces)
2926 if (!whitespaceLenient
&& !whitespaceInText
) {
2927 // didn't find matching whitespace:
2928 // an error in strict mode
2932 // In strict mode, this run of whitespace
2933 // may have been at the end.
2934 if (p
>= literal
.length()) {
2938 // Still need to skip any bidi marks in the text
2939 while (t
< text
.length() && IS_BIDI_MARK(text
.charAt(t
))) {
2943 if (t
>= text
.length() || literal
.charAt(p
) != text
.charAt(t
)) {
2944 // Ran out of text, or found a non-matching character:
2945 // OK in lenient mode, an error in strict mode.
2946 if (whitespaceLenient
) {
2947 if (t
== textOffset
&& text
.charAt(t
) == 0x2e &&
2948 isAfterNonNumericField(pattern
, patternOffset
)) {
2949 // Lenient mode and the literal input text begins with a "." and
2950 // we are after a non-numeric field: We skip the "."
2952 continue; // Do not update p.
2954 // if it is actual whitespace and we're whitespace lenient it's OK
2956 UChar wsc
= text
.charAt(t
);
2957 if(PatternProps::isWhiteSpace(wsc
)) {
2958 // Lenient mode and it's just whitespace we skip it
2960 continue; // Do not update p.
2963 // hack around oldleniency being a bit of a catch-all bucket and we're just adding support specifically for paritial matches
2964 // This fix is for http://bugs.icu-project.org/trac/ticket/10855 and adds "&& oldLeniency"
2965 //if(partialMatchLenient && oldLeniency) {
2966 // However this causes problems for Apple, see <rdar://problem/20692829> regressions in Chinese date parsing
2967 // We don't want to go back to just "if(partialMatchLenient)" as in ICU 53, that is too lenient for strict mode.
2968 // 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.
2969 if( partialMatchLenient
&& ( oldLeniency
||
2970 ( ignorables
!= NULL
&& ignorables
->contains(literal
.charAt(p
)) && (ignorables
->contains(text
.charAt(t
)) || u_isalpha(text
.charAt(t
))) ) )
2981 // At this point if we're in strict mode we have a complete match.
2982 // If we're in lenient mode we may have a partial match, or no
2985 // no match. Pretend it matched a run of whitespace
2986 // and ignorables in the text.
2988 for (t
= textOffset
; t
< text
.length(); t
+= 1) {
2989 UChar ch
= text
.charAt(t
);
2991 if (!IS_BIDI_MARK(ch
) && (ignorables
== NULL
|| !ignorables
->contains(ch
))) {
2997 // if we get here, we've got a complete match.
2998 patternOffset
= i
- 1;
3004 //----------------------------------------------------------------------
3006 int32_t SimpleDateFormat::matchString(const UnicodeString
& text
,
3008 UCalendarDateFields field
,
3009 const UnicodeString
* data
,
3011 const UnicodeString
* monthPattern
,
3012 Calendar
& cal
) const
3015 int32_t count
= dataCount
;
3017 if (field
== UCAL_DAY_OF_WEEK
) i
= 1;
3019 // There may be multiple strings in the data[] array which begin with
3020 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
3021 // We keep track of the longest match, and return that. Note that this
3022 // unfortunately requires us to test all array elements.
3023 int32_t bestMatchLength
= 0, bestMatch
= -1;
3024 UnicodeString bestMatchName
;
3025 int32_t isLeapMonth
= 0;
3027 for (; i
< count
; ++i
) {
3028 int32_t matchLen
= 0;
3029 if ((matchLen
= matchStringWithOptionalDot(text
, start
, data
[i
])) > bestMatchLength
) {
3031 bestMatchLength
= matchLen
;
3034 if (monthPattern
!= NULL
) {
3035 UErrorCode status
= U_ZERO_ERROR
;
3036 UnicodeString leapMonthName
;
3037 SimpleFormatter(*monthPattern
, 1, 1, status
).format(data
[i
], leapMonthName
, status
);
3038 if (U_SUCCESS(status
)) {
3039 if ((matchLen
= matchStringWithOptionalDot(text
, start
, leapMonthName
)) > bestMatchLength
) {
3041 bestMatchLength
= matchLen
;
3048 if (bestMatch
>= 0) {
3049 if (field
< UCAL_FIELD_COUNT
) {
3050 // Adjustment for Hebrew Calendar month Adar II
3051 if (!strcmp(cal
.getType(),"hebrew") && field
==UCAL_MONTH
&& bestMatch
==13) {
3054 if (field
== UCAL_YEAR
) {
3055 bestMatch
++; // only get here for cyclic year names, which match 1-based years 1-60
3057 cal
.set(field
, bestMatch
);
3059 if (monthPattern
!= NULL
) {
3060 cal
.set(UCAL_IS_LEAP_MONTH
, isLeapMonth
);
3064 return start
+ bestMatchLength
;
3071 matchStringWithOptionalDot(const UnicodeString
&text
,
3073 const UnicodeString
&data
) {
3074 UErrorCode sts
= U_ZERO_ERROR
;
3075 int32_t matchLenText
= 0;
3076 int32_t matchLenData
= 0;
3078 u_caseInsensitivePrefixMatch(text
.getBuffer() + index
, text
.length() - index
,
3079 data
.getBuffer(), data
.length(),
3080 0 /* default case option */,
3081 &matchLenText
, &matchLenData
,
3083 U_ASSERT (U_SUCCESS(sts
));
3085 if (matchLenData
== data
.length() /* normal match */
3086 || (data
.charAt(data
.length() - 1) == 0x2e
3087 && matchLenData
== data
.length() - 1 /* match without trailing dot */)) {
3088 return matchLenText
;
3094 //----------------------------------------------------------------------
3097 SimpleDateFormat::set2DigitYearStart(UDate d
, UErrorCode
& status
)
3099 parseAmbiguousDatesAsAfter(d
, status
);
3103 * Private member function that converts the parsed date strings into
3104 * timeFields. Returns -start (for ParsePosition) if failed.
3106 int32_t SimpleDateFormat::subParse(const UnicodeString
& text
, int32_t& start
, UChar ch
, int32_t count
,
3107 UBool obeyCount
, UBool allowNegative
, UBool ambiguousYear
[], int32_t& saveHebrewMonth
, Calendar
& cal
,
3108 int32_t patLoc
, MessageFormat
* numericLeapMonthFormatter
, UTimeZoneFormatTimeType
*tzTimeType
,
3109 int32_t *dayPeriod
) const
3115 UErrorCode status
= U_ZERO_ERROR
;
3116 ParsePosition
pos(0);
3117 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(ch
);
3118 const NumberFormat
*currentNumberFormat
;
3120 int32_t tzParseOptions
= (isLenient())? UTZFMT_PARSE_OPTION_ALL_STYLES
: UTZFMT_PARSE_OPTION_NONE
;
3121 UBool gotNumber
= FALSE
;
3123 #if defined (U_DEBUG_CAL)
3124 //fprintf(stderr, "%s:%d - [%c] st=%d \n", __FILE__, __LINE__, (char) ch, start);
3127 if (patternCharIndex
== UDAT_FIELD_COUNT
) {
3131 currentNumberFormat
= getNumberFormatByIndex(patternCharIndex
);
3132 if (currentNumberFormat
== NULL
) {
3135 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
]; // UCAL_FIELD_COUNT if irrelevant
3136 UnicodeString
hebr("hebr", 4, US_INV
);
3138 if (numericLeapMonthFormatter
!= NULL
) {
3139 numericLeapMonthFormatter
->setFormats((const Format
**)¤tNumberFormat
, 1);
3141 UBool isChineseCalendar
= (uprv_strcmp(cal
.getType(),"chinese") == 0 || uprv_strcmp(cal
.getType(),"dangi") == 0);
3143 // If there are any spaces here, skip over them. If we hit the end
3144 // of the string, then fail.
3146 if (start
>= text
.length()) {
3149 UChar32 c
= text
.char32At(start
);
3150 if (!u_isUWhiteSpace(c
) /*||*/ && !PatternProps::isWhiteSpace(c
)) {
3153 start
+= U16_LENGTH(c
);
3155 pos
.setIndex(start
);
3157 // We handle a few special cases here where we need to parse
3158 // a number value. We handle further, more generic cases below. We need
3159 // to handle some of them here because some fields require extra processing on
3160 // the parsed value.
3161 if (patternCharIndex
== UDAT_HOUR_OF_DAY1_FIELD
|| // k
3162 patternCharIndex
== UDAT_HOUR_OF_DAY0_FIELD
|| // H
3163 patternCharIndex
== UDAT_HOUR1_FIELD
|| // h
3164 patternCharIndex
== UDAT_HOUR0_FIELD
|| // K
3165 (patternCharIndex
== UDAT_DOW_LOCAL_FIELD
&& count
<= 2) || // e
3166 (patternCharIndex
== UDAT_STANDALONE_DAY_FIELD
&& count
<= 2) || // c
3167 (patternCharIndex
== UDAT_MONTH_FIELD
&& count
<= 2) || // M
3168 (patternCharIndex
== UDAT_STANDALONE_MONTH_FIELD
&& count
<= 2) || // L
3169 (patternCharIndex
== UDAT_QUARTER_FIELD
&& count
<= 2) || // Q
3170 (patternCharIndex
== UDAT_STANDALONE_QUARTER_FIELD
&& count
<= 2) || // q
3171 patternCharIndex
== UDAT_YEAR_FIELD
|| // y
3172 patternCharIndex
== UDAT_YEAR_WOY_FIELD
|| // Y
3173 patternCharIndex
== UDAT_YEAR_NAME_FIELD
|| // U (falls back to numeric)
3174 (patternCharIndex
== UDAT_ERA_FIELD
&& isChineseCalendar
) || // G
3175 patternCharIndex
== UDAT_FRACTIONAL_SECOND_FIELD
) // S
3177 int32_t parseStart
= pos
.getIndex();
3178 // It would be good to unify this with the obeyCount logic below,
3179 // but that's going to be difficult.
3180 const UnicodeString
* src
;
3182 UBool parsedNumericLeapMonth
= FALSE
;
3183 if (numericLeapMonthFormatter
!= NULL
&& (patternCharIndex
== UDAT_MONTH_FIELD
|| patternCharIndex
== UDAT_STANDALONE_MONTH_FIELD
)) {
3185 Formattable
* args
= numericLeapMonthFormatter
->parse(text
, pos
, argCount
);
3186 if (args
!= NULL
&& argCount
== 1 && pos
.getIndex() > parseStart
&& args
[0].isNumeric()) {
3187 parsedNumericLeapMonth
= TRUE
;
3188 number
.setLong(args
[0].getLong());
3189 cal
.set(UCAL_IS_LEAP_MONTH
, 1);
3192 pos
.setIndex(parseStart
);
3193 cal
.set(UCAL_IS_LEAP_MONTH
, 0);
3197 if (!parsedNumericLeapMonth
) {
3199 if ((start
+count
) > text
.length()) {
3203 text
.extractBetween(0, start
+ count
, temp
);
3209 parseInt(*src
, number
, pos
, allowNegative
,currentNumberFormat
);
3212 int32_t txtLoc
= pos
.getIndex();
3214 if (txtLoc
> parseStart
) {
3215 value
= number
.getLong();
3218 // suffix processing
3220 txtLoc
= checkIntSuffix(text
, txtLoc
, patLoc
+1, TRUE
);
3221 if (txtLoc
!= pos
.getIndex()) {
3226 txtLoc
= checkIntSuffix(text
, txtLoc
, patLoc
+1, FALSE
);
3229 // Check the range of the value
3230 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, status
)) {
3231 int32_t bias
= gFieldRangeBias
[patternCharIndex
];
3232 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
|| value
< cal
.getMinimum(field
) + bias
)) {
3236 int32_t bias
= gFieldRangeBiasLenient
[patternCharIndex
];
3237 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
)) {
3242 pos
.setIndex(txtLoc
);
3246 // Make sure that we got a number if
3247 // we want one, and didn't get one
3248 // if we don't want one.
3249 switch (patternCharIndex
) {
3250 case UDAT_HOUR_OF_DAY1_FIELD
:
3251 case UDAT_HOUR_OF_DAY0_FIELD
:
3252 case UDAT_HOUR1_FIELD
:
3253 case UDAT_HOUR0_FIELD
:
3254 // special range check for hours:
3255 if (value
< 0 || value
> 24) {
3259 // fall through to gotNumber check
3261 case UDAT_YEAR_FIELD
:
3262 case UDAT_YEAR_WOY_FIELD
:
3263 case UDAT_FRACTIONAL_SECOND_FIELD
:
3264 // these must be a number
3272 // we check the rest of the fields below.
3276 switch (patternCharIndex
) {
3277 case UDAT_ERA_FIELD
:
3278 if (isChineseCalendar
) {
3282 cal
.set(UCAL_ERA
, value
);
3283 return pos
.getIndex();
3286 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fNarrowEras
, fSymbols
->fNarrowErasCount
, NULL
, cal
);
3287 } else if (count
== 4) {
3288 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fEraNames
, fSymbols
->fEraNamesCount
, NULL
, cal
);
3290 ps
= matchString(text
, start
, UCAL_ERA
, fSymbols
->fEras
, fSymbols
->fErasCount
, NULL
, cal
);
3293 // check return position, if it equals -start, then matchString error
3294 // special case the return code so we don't necessarily fail out until we
3295 // verify no year information also
3301 case UDAT_YEAR_FIELD
:
3302 // If there are 3 or more YEAR pattern characters, this indicates
3303 // that the year value is to be treated literally, without any
3304 // two-digit year adjustments (e.g., from "01" to 2001). Otherwise
3305 // we made adjustments to place the 2-digit year in the proper
3306 // century, for parsed strings from "00" to "99". Any other string
3307 // is treated literally: "2250", "-1", "1", "002".
3308 if (fDateOverride
.compare(hebr
)==0 && value
< 1000) {
3309 value
+= HEBREW_CAL_CUR_MILLENIUM_START_YEAR
;
3310 } else if (text
.moveIndex32(start
, 2) == pos
.getIndex() && !isChineseCalendar
3311 && u_isdigit(text
.char32At(start
))
3312 && u_isdigit(text
.char32At(text
.moveIndex32(start
, 1))))
3314 // only adjust year for patterns less than 3.
3316 // Assume for example that the defaultCenturyStart is 6/18/1903.
3317 // This means that two-digit years will be forced into the range
3318 // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
3319 // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
3320 // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
3321 // other fields specify a date before 6/18, or 1903 if they specify a
3322 // date afterwards. As a result, 03 is an ambiguous year. All other
3323 // two-digit years are unambiguous.
3324 if(fHaveDefaultCentury
) { // check if this formatter even has a pivot year
3325 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
3326 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
3327 value
+= (fDefaultCenturyStartYear
/100)*100 +
3328 (value
< ambiguousTwoDigitYear
? 100 : 0);
3332 cal
.set(UCAL_YEAR
, value
);
3334 // Delayed checking for adjustment of Hebrew month numbers in non-leap years.
3335 if (saveHebrewMonth
>= 0) {
3336 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
3337 if (!hc
->isLeapYear(value
) && saveHebrewMonth
>= 6) {
3338 cal
.set(UCAL_MONTH
,saveHebrewMonth
);
3340 cal
.set(UCAL_MONTH
,saveHebrewMonth
-1);
3342 saveHebrewMonth
= -1;
3344 return pos
.getIndex();
3346 case UDAT_YEAR_WOY_FIELD
:
3347 // Comment is the same as for UDAT_Year_FIELDs - look above
3348 if (fDateOverride
.compare(hebr
)==0 && value
< 1000) {
3349 value
+= HEBREW_CAL_CUR_MILLENIUM_START_YEAR
;
3350 } else if (text
.moveIndex32(start
, 2) == pos
.getIndex()
3351 && u_isdigit(text
.char32At(start
))
3352 && u_isdigit(text
.char32At(text
.moveIndex32(start
, 1)))
3353 && fHaveDefaultCentury
)
3355 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
3356 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
3357 value
+= (fDefaultCenturyStartYear
/100)*100 +
3358 (value
< ambiguousTwoDigitYear
? 100 : 0);
3360 cal
.set(UCAL_YEAR_WOY
, value
);
3361 return pos
.getIndex();
3363 case UDAT_YEAR_NAME_FIELD
:
3364 if (fSymbols
->fShortYearNames
!= NULL
) {
3365 int32_t newStart
= matchString(text
, start
, UCAL_YEAR
, fSymbols
->fShortYearNames
, fSymbols
->fShortYearNamesCount
, NULL
, cal
);
3370 if (gotNumber
&& (getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
,status
) || value
> fSymbols
->fShortYearNamesCount
)) {
3371 cal
.set(UCAL_YEAR
, value
);
3372 return pos
.getIndex();
3376 case UDAT_MONTH_FIELD
:
3377 case UDAT_STANDALONE_MONTH_FIELD
:
3378 if (gotNumber
) // i.e., M or MM.
3380 // When parsing month numbers from the Hebrew Calendar, we might need to adjust the month depending on whether
3381 // 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
3382 // the year is parsed.
3383 if (!strcmp(cal
.getType(),"hebrew")) {
3384 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
3385 if (cal
.isSet(UCAL_YEAR
)) {
3386 UErrorCode monthStatus
= U_ZERO_ERROR
;
3387 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
, monthStatus
)) && value
>= 6) {
3388 cal
.set(UCAL_MONTH
, value
);
3390 cal
.set(UCAL_MONTH
, value
- 1);
3393 saveHebrewMonth
= value
;
3396 // Don't want to parse the month if it is a string
3397 // while pattern uses numeric style: M/MM, L/LL
3398 // [We computed 'value' above.]
3399 cal
.set(UCAL_MONTH
, value
- 1);
3401 return pos
.getIndex();
3403 // count >= 3 // i.e., MMM/MMMM, LLL/LLLL
3404 // Want to be able to parse both short and long forms.
3405 // Try count == 4 first:
3406 UnicodeString
* wideMonthPat
= NULL
;
3407 UnicodeString
* shortMonthPat
= NULL
;
3408 if (fSymbols
->fLeapMonthPatterns
!= NULL
&& fSymbols
->fLeapMonthPatternsCount
>= DateFormatSymbols::kMonthPatternsCount
) {
3409 if (patternCharIndex
==UDAT_MONTH_FIELD
) {
3410 wideMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatWide
];
3411 shortMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternFormatAbbrev
];
3413 wideMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneWide
];
3414 shortMonthPat
= &fSymbols
->fLeapMonthPatterns
[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev
];
3417 int32_t newStart
= 0;
3418 if (patternCharIndex
==UDAT_MONTH_FIELD
) {
3419 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3420 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fMonths
, fSymbols
->fMonthsCount
, wideMonthPat
, cal
); // try MMMM
3425 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3426 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fShortMonths
, fSymbols
->fShortMonthsCount
, shortMonthPat
, cal
); // try MMM
3429 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3430 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fStandaloneMonths
, fSymbols
->fStandaloneMonthsCount
, wideMonthPat
, cal
); // try LLLL
3435 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3436 newStart
= matchString(text
, start
, UCAL_MONTH
, fSymbols
->fStandaloneShortMonths
, fSymbols
->fStandaloneShortMonthsCount
, shortMonthPat
, cal
); // try LLL
3439 if (newStart
> 0 || !getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
)) // currently we do not try to parse MMMMM/LLLLL: #8860
3441 // else we allowing parsing as number, below
3445 case UDAT_HOUR_OF_DAY1_FIELD
:
3446 // [We computed 'value' above.]
3447 if (value
== cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1)
3450 // fall through to set field
3452 case UDAT_HOUR_OF_DAY0_FIELD
:
3453 cal
.set(UCAL_HOUR_OF_DAY
, value
);
3454 return pos
.getIndex();
3456 case UDAT_FRACTIONAL_SECOND_FIELD
:
3457 // Fractional seconds left-justify
3458 i
= countDigits(text
, start
, pos
.getIndex());
3472 cal
.set(UCAL_MILLISECOND
, value
);
3473 return pos
.getIndex();
3475 case UDAT_DOW_LOCAL_FIELD
:
3476 if (gotNumber
) // i.e., e or ee
3478 // [We computed 'value' above.]
3479 cal
.set(UCAL_DOW_LOCAL
, value
);
3480 return pos
.getIndex();
3482 // else for eee-eeeee fall through to handling of EEE-EEEEE
3483 // fall through, do not break here
3485 case UDAT_DAY_OF_WEEK_FIELD
:
3487 // Want to be able to parse both short and long forms.
3488 // Try count == 4 (EEEE) wide first:
3489 int32_t newStart
= 0;
3490 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3491 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3492 fSymbols
->fWeekdays
, fSymbols
->fWeekdaysCount
, NULL
, cal
)) > 0)
3495 // EEEE wide failed, now try EEE abbreviated
3496 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3497 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3498 fSymbols
->fShortWeekdays
, fSymbols
->fShortWeekdaysCount
, NULL
, cal
)) > 0)
3501 // EEE abbreviated failed, now try EEEEEE short
3502 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 6) {
3503 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3504 fSymbols
->fShorterWeekdays
, fSymbols
->fShorterWeekdaysCount
, NULL
, cal
)) > 0)
3507 // EEEEEE short failed, now try EEEEE narrow
3508 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 5) {
3509 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3510 fSymbols
->fNarrowWeekdays
, fSymbols
->fNarrowWeekdaysCount
, NULL
, cal
)) > 0)
3513 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
) || patternCharIndex
== UDAT_DAY_OF_WEEK_FIELD
)
3515 // else we allowing parsing as number, below
3519 case UDAT_STANDALONE_DAY_FIELD
:
3521 if (gotNumber
) // c or cc
3523 // [We computed 'value' above.]
3524 cal
.set(UCAL_DOW_LOCAL
, value
);
3525 return pos
.getIndex();
3527 // Want to be able to parse both short and long forms.
3528 // Try count == 4 (cccc) first:
3529 int32_t newStart
= 0;
3530 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3531 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3532 fSymbols
->fStandaloneWeekdays
, fSymbols
->fStandaloneWeekdaysCount
, NULL
, cal
)) > 0)
3535 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3536 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3537 fSymbols
->fStandaloneShortWeekdays
, fSymbols
->fStandaloneShortWeekdaysCount
, NULL
, cal
)) > 0)
3540 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 6) {
3541 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
3542 fSymbols
->fStandaloneShorterWeekdays
, fSymbols
->fStandaloneShorterWeekdaysCount
, NULL
, cal
)) > 0)
3545 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
))
3547 // else we allowing parsing as number, below
3551 case UDAT_AM_PM_FIELD
:
3553 // optionally try both wide/abbrev and narrow forms
3554 int32_t newStart
= 0;
3556 if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
< 5 ) {
3557 if ((newStart
= matchString(text
, start
, UCAL_AM_PM
, fSymbols
->fAmPms
, fSymbols
->fAmPmsCount
, NULL
, cal
)) > 0) {
3562 if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
>= 5 ) {
3563 if ((newStart
= matchString(text
, start
, UCAL_AM_PM
, fSymbols
->fNarrowAmPms
, fSymbols
->fNarrowAmPmsCount
, NULL
, cal
)) > 0) {
3567 // no matches for given options
3571 case UDAT_HOUR1_FIELD
:
3572 // [We computed 'value' above.]
3573 if (value
== cal
.getLeastMaximum(UCAL_HOUR
)+1)
3576 // fall through to set field
3578 case UDAT_HOUR0_FIELD
:
3579 cal
.set(UCAL_HOUR
, value
);
3580 return pos
.getIndex();
3582 case UDAT_QUARTER_FIELD
:
3583 if (gotNumber
) // i.e., Q or QQ.
3585 // Don't want to parse the month if it is a string
3586 // while pattern uses numeric style: Q or QQ.
3587 // [We computed 'value' above.]
3588 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
3589 return pos
.getIndex();
3591 // count >= 3 // i.e., QQQ or QQQQ
3592 // Want to be able to parse both short and long forms.
3593 // Try count == 4 first:
3594 int32_t newStart
= 0;
3596 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3597 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3598 fSymbols
->fQuarters
, fSymbols
->fQuartersCount
, cal
)) > 0)
3601 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3602 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3603 fSymbols
->fShortQuarters
, fSymbols
->fShortQuartersCount
, cal
)) > 0)
3606 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
))
3608 // else we allowing parsing as number, below
3609 if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
))
3614 case UDAT_STANDALONE_QUARTER_FIELD
:
3615 if (gotNumber
) // i.e., q or qq.
3617 // Don't want to parse the month if it is a string
3618 // while pattern uses numeric style: q or q.
3619 // [We computed 'value' above.]
3620 cal
.set(UCAL_MONTH
, (value
- 1) * 3);
3621 return pos
.getIndex();
3623 // count >= 3 // i.e., qqq or qqqq
3624 // Want to be able to parse both short and long forms.
3625 // Try count == 4 first:
3626 int32_t newStart
= 0;
3628 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3629 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3630 fSymbols
->fStandaloneQuarters
, fSymbols
->fStandaloneQuartersCount
, cal
)) > 0)
3633 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3634 if ((newStart
= matchQuarterString(text
, start
, UCAL_MONTH
,
3635 fSymbols
->fStandaloneShortQuarters
, fSymbols
->fStandaloneShortQuartersCount
, cal
)) > 0)
3638 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
))
3640 // else we allowing parsing as number, below
3641 if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
))
3646 case UDAT_TIMEZONE_FIELD
: // 'z'
3648 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_SPECIFIC_SHORT
: UTZFMT_STYLE_SPECIFIC_LONG
;
3649 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
3650 if (U_SUCCESS(status
)) {
3651 TimeZone
*tz
= tzfmt
->parse(style
, text
, pos
, tzParseOptions
, tzTimeType
);
3653 cal
.adoptTimeZone(tz
);
3654 return pos
.getIndex();
3660 case UDAT_TIMEZONE_RFC_FIELD
: // 'Z'
3662 UTimeZoneFormatStyle style
= (count
< 4) ?
3663 UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
: ((count
== 5) ? UTZFMT_STYLE_ISO_EXTENDED_FULL
: UTZFMT_STYLE_LOCALIZED_GMT
);
3664 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
3665 if (U_SUCCESS(status
)) {
3666 TimeZone
*tz
= tzfmt
->parse(style
, text
, pos
, tzTimeType
);
3668 cal
.adoptTimeZone(tz
);
3669 return pos
.getIndex();
3674 case UDAT_TIMEZONE_GENERIC_FIELD
: // 'v'
3676 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_GENERIC_SHORT
: UTZFMT_STYLE_GENERIC_LONG
;
3677 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
3678 if (U_SUCCESS(status
)) {
3679 TimeZone
*tz
= tzfmt
->parse(style
, text
, pos
, tzParseOptions
, tzTimeType
);
3681 cal
.adoptTimeZone(tz
);
3682 return pos
.getIndex();
3687 case UDAT_TIMEZONE_SPECIAL_FIELD
: // 'V'
3689 UTimeZoneFormatStyle style
;
3692 style
= UTZFMT_STYLE_ZONE_ID_SHORT
;
3695 style
= UTZFMT_STYLE_ZONE_ID
;
3698 style
= UTZFMT_STYLE_EXEMPLAR_LOCATION
;
3701 style
= UTZFMT_STYLE_GENERIC_LOCATION
;
3704 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
3705 if (U_SUCCESS(status
)) {
3706 TimeZone
*tz
= tzfmt
->parse(style
, text
, pos
, tzTimeType
);
3708 cal
.adoptTimeZone(tz
);
3709 return pos
.getIndex();
3714 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
: // 'O'
3716 UTimeZoneFormatStyle style
= (count
< 4) ? UTZFMT_STYLE_LOCALIZED_GMT_SHORT
: UTZFMT_STYLE_LOCALIZED_GMT
;
3717 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
3718 if (U_SUCCESS(status
)) {
3719 TimeZone
*tz
= tzfmt
->parse(style
, text
, pos
, tzTimeType
);
3721 cal
.adoptTimeZone(tz
);
3722 return pos
.getIndex();
3727 case UDAT_TIMEZONE_ISO_FIELD
: // 'X'
3729 UTimeZoneFormatStyle style
;
3732 style
= UTZFMT_STYLE_ISO_BASIC_SHORT
;
3735 style
= UTZFMT_STYLE_ISO_BASIC_FIXED
;
3738 style
= UTZFMT_STYLE_ISO_EXTENDED_FIXED
;
3741 style
= UTZFMT_STYLE_ISO_BASIC_FULL
;
3744 style
= UTZFMT_STYLE_ISO_EXTENDED_FULL
;
3747 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
3748 if (U_SUCCESS(status
)) {
3749 TimeZone
*tz
= tzfmt
->parse(style
, text
, pos
, tzTimeType
);
3751 cal
.adoptTimeZone(tz
);
3752 return pos
.getIndex();
3757 case UDAT_TIMEZONE_ISO_LOCAL_FIELD
: // 'x'
3759 UTimeZoneFormatStyle style
;
3762 style
= UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
;
3765 style
= UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
;
3768 style
= UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
;
3771 style
= UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
;
3774 style
= UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
;
3777 const TimeZoneFormat
*tzfmt
= tzFormat(status
);
3778 if (U_SUCCESS(status
)) {
3779 TimeZone
*tz
= tzfmt
->parse(style
, text
, pos
, tzTimeType
);
3781 cal
.adoptTimeZone(tz
);
3782 return pos
.getIndex();
3787 // currently no pattern character is defined for UDAT_TIME_SEPARATOR_FIELD
3788 // so we should not get here. Leave support in for future definition.
3789 case UDAT_TIME_SEPARATOR_FIELD
:
3791 static const UChar def_sep
= DateFormatSymbols::DEFAULT_TIME_SEPARATOR
;
3792 static const UChar alt_sep
= DateFormatSymbols::ALTERNATE_TIME_SEPARATOR
;
3794 // Try matching a time separator.
3795 int32_t count_sep
= 1;
3796 UnicodeString data
[3];
3797 fSymbols
->getTimeSeparatorString(data
[0]);
3799 // Add the default, if different from the locale.
3800 if (data
[0].compare(&def_sep
, 1) != 0) {
3801 data
[count_sep
++].setTo(def_sep
);
3804 // If lenient, add also the alternate, if different from the locale.
3805 if (isLenient() && data
[0].compare(&alt_sep
, 1) != 0) {
3806 data
[count_sep
++].setTo(alt_sep
);
3809 return matchString(text
, start
, UCAL_FIELD_COUNT
/* => nothing to set */, data
, count_sep
, NULL
, cal
);
3812 case UDAT_AM_PM_MIDNIGHT_NOON_FIELD
:
3814 U_ASSERT(dayPeriod
!= NULL
);
3815 int32_t ampmStart
= subParse(text
, start
, 0x61, count
,
3816 obeyCount
, allowNegative
, ambiguousYear
, saveHebrewMonth
, cal
,
3817 patLoc
, numericLeapMonthFormatter
, tzTimeType
);
3819 if (ampmStart
> 0) {
3822 int32_t newStart
= 0;
3824 // Only match the first two strings from the day period strings array.
3825 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3826 if ((newStart
= matchDayPeriodStrings(text
, start
, fSymbols
->fAbbreviatedDayPeriods
,
3827 2, *dayPeriod
)) > 0) {
3831 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 5) {
3832 if ((newStart
= matchDayPeriodStrings(text
, start
, fSymbols
->fNarrowDayPeriods
,
3833 2, *dayPeriod
)) > 0) {
3837 // count == 4, but allow other counts
3838 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
)) {
3839 if ((newStart
= matchDayPeriodStrings(text
, start
, fSymbols
->fWideDayPeriods
,
3840 2, *dayPeriod
)) > 0) {
3849 case UDAT_FLEXIBLE_DAY_PERIOD_FIELD
:
3851 U_ASSERT(dayPeriod
!= NULL
);
3852 int32_t newStart
= 0;
3854 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 3) {
3855 if ((newStart
= matchDayPeriodStrings(text
, start
, fSymbols
->fAbbreviatedDayPeriods
,
3856 fSymbols
->fAbbreviatedDayPeriodsCount
, *dayPeriod
)) > 0) {
3860 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 5) {
3861 if ((newStart
= matchDayPeriodStrings(text
, start
, fSymbols
->fNarrowDayPeriods
,
3862 fSymbols
->fNarrowDayPeriodsCount
, *dayPeriod
)) > 0) {
3866 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH
, status
) || count
== 4) {
3867 if ((newStart
= matchDayPeriodStrings(text
, start
, fSymbols
->fWideDayPeriods
,
3868 fSymbols
->fWideDayPeriodsCount
, *dayPeriod
)) > 0) {
3877 // Handle "generic" fields
3878 // this is now handled below, outside the switch block
3881 // Handle "generic" fields:
3882 // switch default case now handled here (outside switch block) to allow
3883 // parsing of some string fields as digits for lenient case
3885 int32_t parseStart
= pos
.getIndex();
3886 const UnicodeString
* src
;
3888 if ((start
+count
) > text
.length()) {
3891 text
.extractBetween(0, start
+ count
, temp
);
3896 parseInt(*src
, number
, pos
, allowNegative
,currentNumberFormat
);
3897 if (pos
.getIndex() != parseStart
) {
3898 int32_t val
= number
.getLong();
3900 // Don't need suffix processing here (as in number processing at the beginning of the function);
3901 // the new fields being handled as numeric values (month, weekdays, quarters) should not have suffixes.
3903 // Check the range of the value
3904 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
)) {
3905 int32_t bias
= gFieldRangeBias
[patternCharIndex
];
3906 if (bias
>= 0 && (val
> cal
.getMaximum(field
) + bias
|| val
< cal
.getMinimum(field
) + bias
)) {
3910 int32_t bias
= gFieldRangeBiasLenient
[patternCharIndex
];
3911 if (bias
>= 0 && (value
> cal
.getMaximum(field
) + bias
)) {
3916 // For the following, need to repeat some of the "if (gotNumber)" code above:
3917 // UDAT_[STANDALONE_]MONTH_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_STANDALONE_DAY_FIELD,
3918 // UDAT_[STANDALONE_]QUARTER_FIELD
3919 switch (patternCharIndex
) {
3920 case UDAT_MONTH_FIELD
:
3921 // See notes under UDAT_MONTH_FIELD case above
3922 if (!strcmp(cal
.getType(),"hebrew")) {
3923 HebrewCalendar
*hc
= (HebrewCalendar
*)&cal
;
3924 if (cal
.isSet(UCAL_YEAR
)) {
3925 UErrorCode monthStatus
= U_ZERO_ERROR
;
3926 if (!hc
->isLeapYear(hc
->get(UCAL_YEAR
, monthStatus
)) && val
>= 6) {
3927 cal
.set(UCAL_MONTH
, val
);
3929 cal
.set(UCAL_MONTH
, val
- 1);
3932 saveHebrewMonth
= val
;
3935 cal
.set(UCAL_MONTH
, val
- 1);
3938 case UDAT_STANDALONE_MONTH_FIELD
:
3939 cal
.set(UCAL_MONTH
, val
- 1);
3941 case UDAT_DOW_LOCAL_FIELD
:
3942 case UDAT_STANDALONE_DAY_FIELD
:
3943 cal
.set(UCAL_DOW_LOCAL
, val
);
3945 case UDAT_QUARTER_FIELD
:
3946 case UDAT_STANDALONE_QUARTER_FIELD
:
3947 cal
.set(UCAL_MONTH
, (val
- 1) * 3);
3949 case UDAT_RELATED_YEAR_FIELD
:
3950 cal
.setRelatedYear(val
);
3953 cal
.set(field
, val
);
3956 return pos
.getIndex();
3962 * Parse an integer using fNumberFormat. This method is semantically
3963 * const, but actually may modify fNumberFormat.
3965 void SimpleDateFormat::parseInt(const UnicodeString
& text
,
3966 Formattable
& number
,
3968 UBool allowNegative
,
3969 const NumberFormat
*fmt
) const {
3970 parseInt(text
, number
, -1, pos
, allowNegative
,fmt
);
3974 * Parse an integer using fNumberFormat up to maxDigits.
3976 void SimpleDateFormat::parseInt(const UnicodeString
& text
,
3977 Formattable
& number
,
3980 UBool allowNegative
,
3981 const NumberFormat
*fmt
) const {
3982 UnicodeString oldPrefix
;
3983 auto* fmtAsDF
= dynamic_cast<const DecimalFormat
*>(fmt
);
3984 LocalPointer
<DecimalFormat
> df
;
3985 if (!allowNegative
&& fmtAsDF
!= nullptr) {
3986 df
.adoptInstead(fmtAsDF
->clone());
3988 // Memory allocation error
3991 df
->setNegativePrefix(UnicodeString(TRUE
, SUPPRESS_NEGATIVE_PREFIX
, -1));
3992 fmt
= df
.getAlias();
3994 int32_t oldPos
= pos
.getIndex();
3995 fmt
->parse(text
, number
, pos
);
3997 if (maxDigits
> 0) {
3998 // adjust the result to fit into
3999 // the maxDigits and move the position back
4000 int32_t nDigits
= pos
.getIndex() - oldPos
;
4001 if (nDigits
> maxDigits
) {
4002 int32_t val
= number
.getLong();
4003 nDigits
-= maxDigits
;
4004 while (nDigits
> 0) {
4008 pos
.setIndex(oldPos
+ maxDigits
);
4009 number
.setLong(val
);
4014 int32_t SimpleDateFormat::countDigits(const UnicodeString
& text
, int32_t start
, int32_t end
) const {
4015 int32_t numDigits
= 0;
4016 int32_t idx
= start
;
4018 UChar32 cp
= text
.char32At(idx
);
4019 if (u_isdigit(cp
)) {
4022 idx
+= U16_LENGTH(cp
);
4027 //----------------------------------------------------------------------
4029 void SimpleDateFormat::translatePattern(const UnicodeString
& originalPattern
,
4030 UnicodeString
& translatedPattern
,
4031 const UnicodeString
& from
,
4032 const UnicodeString
& to
,
4035 // run through the pattern and convert any pattern symbols from the version
4036 // in "from" to the corresponding character in "to". This code takes
4037 // quoted strings into account (it doesn't try to translate them), and it signals
4038 // an error if a particular "pattern character" doesn't appear in "from".
4039 // Depending on the values of "from" and "to" this can convert from generic
4040 // to localized patterns or localized to generic.
4041 if (U_FAILURE(status
)) {
4045 translatedPattern
.remove();
4046 UBool inQuote
= FALSE
;
4047 for (int32_t i
= 0; i
< originalPattern
.length(); ++i
) {
4048 UChar c
= originalPattern
[i
];
4056 } else if (isSyntaxChar(c
)) {
4057 int32_t ci
= from
.indexOf(c
);
4059 status
= U_INVALID_FORMAT_ERROR
;
4065 translatedPattern
+= c
;
4068 status
= U_INVALID_FORMAT_ERROR
;
4073 //----------------------------------------------------------------------
4076 SimpleDateFormat::toPattern(UnicodeString
& result
) const
4082 //----------------------------------------------------------------------
4085 SimpleDateFormat::toLocalizedPattern(UnicodeString
& result
,
4086 UErrorCode
& status
) const
4088 translatePattern(fPattern
, result
,
4089 UnicodeString(DateFormatSymbols::getPatternUChars()),
4090 fSymbols
->fLocalPatternChars
, status
);
4094 //----------------------------------------------------------------------
4097 SimpleDateFormat::applyPattern(const UnicodeString
& pattern
)
4102 // Hack to update use of Gannen year numbering for ja@calendar=japanese -
4103 // use only if format is non-numeric (includes 年) and no other fDateOverride.
4104 if (fCalendar
!= nullptr && uprv_strcmp(fCalendar
->getType(),"japanese") == 0 &&
4105 uprv_strcmp(fLocale
.getLanguage(),"ja") == 0) {
4106 if (fDateOverride
==UnicodeString(u
"y=jpanyear") && !fHasHanYearChar
) {
4107 // Gannen numbering is set but new pattern should not use it, unset;
4108 // use procedure from adoptNumberFormat to clear overrides
4109 if (fSharedNumberFormatters
) {
4110 freeSharedNumberFormatters(fSharedNumberFormatters
);
4111 fSharedNumberFormatters
= NULL
;
4113 fDateOverride
.setToBogus(); // record status
4114 } else if (fDateOverride
.isBogus() && fHasHanYearChar
) {
4115 // No current override (=> no Gannen numbering) but new pattern needs it;
4116 // use procedures from initNUmberFormatters / adoptNumberFormat
4118 if (fSharedNumberFormatters
== NULL
) {
4119 fSharedNumberFormatters
= allocSharedNumberFormatters();
4122 if (fSharedNumberFormatters
!= NULL
) {
4123 Locale
ovrLoc(fLocale
.getLanguage(),fLocale
.getCountry(),fLocale
.getVariant(),"numbers=jpanyear");
4124 UErrorCode status
= U_ZERO_ERROR
;
4125 const SharedNumberFormat
*snf
= createSharedNumberFormat(ovrLoc
, status
);
4126 if (U_SUCCESS(status
)) {
4127 // Now that we have an appropriate number formatter, fill in the
4128 // appropriate slot in the number formatters table.
4129 UDateFormatField patternCharIndex
= DateFormatSymbols::getPatternCharIndex(u
'y');
4130 SharedObject::copyPtr(snf
, fSharedNumberFormatters
[patternCharIndex
]);
4131 snf
->deleteIfZeroRefCount();
4132 fDateOverride
.setTo(u
"y=jpanyear", -1); // record status
4139 //----------------------------------------------------------------------
4142 SimpleDateFormat::applyLocalizedPattern(const UnicodeString
& pattern
,
4145 translatePattern(pattern
, fPattern
,
4146 fSymbols
->fLocalPatternChars
,
4147 UnicodeString(DateFormatSymbols::getPatternUChars()), status
);
4150 //----------------------------------------------------------------------
4152 const DateFormatSymbols
*
4153 SimpleDateFormat::getDateFormatSymbols() const
4158 //----------------------------------------------------------------------
4161 SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols
* newFormatSymbols
)
4164 fSymbols
= newFormatSymbols
;
4167 //----------------------------------------------------------------------
4169 SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols
& newFormatSymbols
)
4172 fSymbols
= new DateFormatSymbols(newFormatSymbols
);
4175 //----------------------------------------------------------------------
4176 const TimeZoneFormat
*
4177 SimpleDateFormat::getTimeZoneFormat(void) const {
4178 // TimeZoneFormat initialization might fail when out of memory.
4179 // If we always initialize TimeZoneFormat instance, we can return
4180 // such status there. For now, this implementation lazily instantiates
4181 // a TimeZoneFormat for performance optimization reasons, but cannot
4182 // propagate such error (probably just out of memory case) to the caller.
4183 UErrorCode status
= U_ZERO_ERROR
;
4184 return (const TimeZoneFormat
*)tzFormat(status
);
4187 //----------------------------------------------------------------------
4189 SimpleDateFormat::adoptTimeZoneFormat(TimeZoneFormat
* timeZoneFormatToAdopt
)
4191 delete fTimeZoneFormat
;
4192 fTimeZoneFormat
= timeZoneFormatToAdopt
;
4195 //----------------------------------------------------------------------
4197 SimpleDateFormat::setTimeZoneFormat(const TimeZoneFormat
& newTimeZoneFormat
)
4199 delete fTimeZoneFormat
;
4200 fTimeZoneFormat
= new TimeZoneFormat(newTimeZoneFormat
);
4203 //----------------------------------------------------------------------
4206 void SimpleDateFormat::adoptCalendar(Calendar
* calendarToAdopt
)
4208 UErrorCode status
= U_ZERO_ERROR
;
4209 Locale
calLocale(fLocale
);
4210 DateFormatSymbols
*newSymbols
= fSymbols
;
4211 if (!newSymbols
|| fCalendar
->getType() != calendarToAdopt
->getType()) {
4212 calLocale
.setKeywordValue("calendar", calendarToAdopt
->getType(), status
);
4213 newSymbols
= DateFormatSymbols::createForLocale(calLocale
, status
);
4214 if (U_FAILURE(status
)) {
4215 delete calendarToAdopt
;
4219 DateFormat::adoptCalendar(calendarToAdopt
);
4220 if (fSymbols
!= newSymbols
) {
4222 fSymbols
= newSymbols
;
4224 initializeDefaultCentury(); // we need a new century (possibly)
4228 //----------------------------------------------------------------------
4231 // override the DateFormat implementation in order to
4232 // lazily initialize fCapitalizationBrkIter
4234 SimpleDateFormat::setContext(UDisplayContext value
, UErrorCode
& status
)
4236 DateFormat::setContext(value
, status
);
4237 #if !UCONFIG_NO_BREAK_ITERATION
4238 if (U_SUCCESS(status
)) {
4239 if ( fCapitalizationBrkIter
== NULL
&& (value
==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
||
4240 value
==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
|| value
==UDISPCTX_CAPITALIZATION_FOR_STANDALONE
) ) {
4241 status
= U_ZERO_ERROR
;
4242 fCapitalizationBrkIter
= BreakIterator::createSentenceInstance(fLocale
, status
);
4243 if (U_FAILURE(status
)) {
4244 delete fCapitalizationBrkIter
;
4245 fCapitalizationBrkIter
= NULL
;
4253 //----------------------------------------------------------------------
4257 SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field
) const {
4258 return isFieldUnitIgnored(fPattern
, field
);
4263 SimpleDateFormat::isFieldUnitIgnored(const UnicodeString
& pattern
,
4264 UCalendarDateFields field
) {
4265 int32_t fieldLevel
= fgCalendarFieldToLevel
[field
];
4268 UBool inQuote
= FALSE
;
4272 for (int32_t i
= 0; i
< pattern
.length(); ++i
) {
4274 if (ch
!= prevCh
&& count
> 0) {
4275 level
= getLevelFromChar(prevCh
);
4276 // the larger the level, the smaller the field unit.
4277 if (fieldLevel
<= level
) {
4283 if ((i
+1) < pattern
.length() && pattern
[i
+1] == QUOTE
) {
4286 inQuote
= ! inQuote
;
4289 else if (!inQuote
&& isSyntaxChar(ch
)) {
4296 level
= getLevelFromChar(prevCh
);
4297 if (fieldLevel
<= level
) {
4304 //----------------------------------------------------------------------
4307 SimpleDateFormat::getSmpFmtLocale(void) const {
4311 //----------------------------------------------------------------------
4314 SimpleDateFormat::checkIntSuffix(const UnicodeString
& text
, int32_t start
,
4315 int32_t patLoc
, UBool isNegative
) const {
4318 int32_t patternMatch
;
4319 int32_t textPreMatch
;
4320 int32_t textPostMatch
;
4322 // check that we are still in range
4323 if ( (start
> text
.length()) ||
4326 (patLoc
> fPattern
.length())) {
4327 // out of range, don't advance location in text
4332 DecimalFormat
* decfmt
= dynamic_cast<DecimalFormat
*>(fNumberFormat
);
4333 if (decfmt
!= NULL
) {
4335 suf
= decfmt
->getNegativeSuffix(suf
);
4338 suf
= decfmt
->getPositiveSuffix(suf
);
4343 if (suf
.length() <= 0) {
4347 // check suffix will be encountered in the pattern
4348 patternMatch
= compareSimpleAffix(suf
,fPattern
,patLoc
);
4350 // check if a suffix will be encountered in the text
4351 textPreMatch
= compareSimpleAffix(suf
,text
,start
);
4353 // check if a suffix was encountered in the text
4354 textPostMatch
= compareSimpleAffix(suf
,text
,start
-suf
.length());
4356 // check for suffix match
4357 if ((textPreMatch
>= 0) && (patternMatch
>= 0) && (textPreMatch
== patternMatch
)) {
4360 else if ((textPostMatch
>= 0) && (patternMatch
>= 0) && (textPostMatch
== patternMatch
)) {
4361 return start
- suf
.length();
4364 // should not get here
4368 //----------------------------------------------------------------------
4371 SimpleDateFormat::compareSimpleAffix(const UnicodeString
& affix
,
4372 const UnicodeString
& input
,
4373 int32_t pos
) const {
4374 int32_t start
= pos
;
4375 for (int32_t i
=0; i
<affix
.length(); ) {
4376 UChar32 c
= affix
.char32At(i
);
4377 int32_t len
= U16_LENGTH(c
);
4378 if (PatternProps::isWhiteSpace(c
)) {
4379 // We may have a pattern like: \u200F \u0020
4380 // and input text like: \u200F \u0020
4381 // Note that U+200F and U+0020 are Pattern_White_Space but only
4382 // U+0020 is UWhiteSpace. So we have to first do a direct
4383 // match of the run of Pattern_White_Space in the pattern,
4384 // then match any extra characters.
4385 UBool literalMatch
= FALSE
;
4386 while (pos
< input
.length() &&
4387 input
.char32At(pos
) == c
) {
4388 literalMatch
= TRUE
;
4391 if (i
== affix
.length()) {
4394 c
= affix
.char32At(i
);
4395 len
= U16_LENGTH(c
);
4396 if (!PatternProps::isWhiteSpace(c
)) {
4401 // Advance over run in pattern
4402 i
= skipPatternWhiteSpace(affix
, i
);
4404 // Advance over run in input text
4405 // Must see at least one white space char in input,
4406 // unless we've already matched some characters literally.
4408 pos
= skipUWhiteSpace(input
, pos
);
4409 if (pos
== s
&& !literalMatch
) {
4413 // If we skip UWhiteSpace in the input text, we need to skip it in the pattern.
4414 // Otherwise, the previous lines may have skipped over text (such as U+00A0) that
4415 // is also in the affix.
4416 i
= skipUWhiteSpace(affix
, i
);
4418 if (pos
< input
.length() &&
4419 input
.char32At(pos
) == c
) {
4430 //----------------------------------------------------------------------
4433 SimpleDateFormat::skipPatternWhiteSpace(const UnicodeString
& text
, int32_t pos
) const {
4434 const UChar
* s
= text
.getBuffer();
4435 return (int32_t)(PatternProps::skipWhiteSpace(s
+ pos
, text
.length() - pos
) - s
);
4438 //----------------------------------------------------------------------
4441 SimpleDateFormat::skipUWhiteSpace(const UnicodeString
& text
, int32_t pos
) const {
4442 while (pos
< text
.length()) {
4443 UChar32 c
= text
.char32At(pos
);
4444 if (!u_isUWhiteSpace(c
)) {
4447 pos
+= U16_LENGTH(c
);
4452 //----------------------------------------------------------------------
4454 // Lazy TimeZoneFormat instantiation, semantically const.
4456 SimpleDateFormat::tzFormat(UErrorCode
&status
) const {
4457 if (fTimeZoneFormat
== NULL
) {
4460 if (fTimeZoneFormat
== NULL
) {
4461 TimeZoneFormat
*tzfmt
= TimeZoneFormat::createInstance(fLocale
, status
);
4462 if (U_FAILURE(status
)) {
4466 const_cast<SimpleDateFormat
*>(this)->fTimeZoneFormat
= tzfmt
;
4471 return fTimeZoneFormat
;
4474 void SimpleDateFormat::parsePattern() {
4477 fHasHanYearChar
= FALSE
;
4479 int len
= fPattern
.length();
4480 UBool inQuote
= FALSE
;
4481 for (int32_t i
= 0; i
< len
; ++i
) {
4482 UChar ch
= fPattern
[i
];
4486 if (ch
== 0x5E74) { // don't care whether this is inside quotes
4487 fHasHanYearChar
= TRUE
;
4490 if (ch
== 0x6D) { // 0x6D == 'm'
4493 if (ch
== 0x73) { // 0x73 == 's'
4502 #endif /* #if !UCONFIG_NO_FORMATTING */