]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/smpdtfmt.cpp
ICU-64260.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / smpdtfmt.cpp
CommitLineData
f3c0d7a5
A
1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
b75a7d8f
A
3/*
4*******************************************************************************
2ca993e8 5* Copyright (C) 1997-2016, International Business Machines Corporation and *
b75a7d8f
A
6* others. All Rights Reserved. *
7*******************************************************************************
8*
9* File SMPDTFMT.CPP
10*
11* Modification History:
12*
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
21* Removed chk
2ca993e8 22* 02/22/99 stephen Removed character literals for EBCDIC safety
b75a7d8f
A
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********************************************************************************
27*/
28
46f4442e
A
29#define ZID_KEY_MAX 128
30
b75a7d8f
A
31#include "unicode/utypes.h"
32
33#if !UCONFIG_NO_FORMATTING
b75a7d8f
A
34#include "unicode/smpdtfmt.h"
35#include "unicode/dtfmtsym.h"
374ca955 36#include "unicode/ures.h"
b75a7d8f
A
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"
46f4442e 44#include "unicode/uniset.h"
b75a7d8f 45#include "unicode/ustring.h"
46f4442e 46#include "unicode/basictz.h"
2ca993e8 47#include "unicode/simpleformatter.h"
46f4442e
A
48#include "unicode/simpletz.h"
49#include "unicode/rbtz.h"
4388f060 50#include "unicode/tzfmt.h"
f3c0d7a5 51#include "unicode/ucasemap.h"
4388f060 52#include "unicode/utf16.h"
46f4442e 53#include "unicode/vtzone.h"
51004dcb 54#include "unicode/udisplaycontext.h"
57a6839d 55#include "unicode/brkiter.h"
3d1f044b
A
56#include "unicode/rbnf.h"
57#include "unicode/dtptngen.h"
f3c0d7a5 58#include "uresimp.h"
46f4442e 59#include "olsontz.h"
4388f060 60#include "patternprops.h"
729e4ab9 61#include "fphdlimp.h"
729e4ab9 62#include "hebrwcal.h"
374ca955
A
63#include "cstring.h"
64#include "uassert.h"
46f4442e
A
65#include "cmemory.h"
66#include "umutex.h"
b75a7d8f 67#include <float.h>
4388f060 68#include "smpdtfst.h"
b331163b 69#include "sharednumberformat.h"
f3c0d7a5 70#include "ucasemap_imp.h"
b331163b 71#include "ustr_imp.h"
2ca993e8
A
72#include "charstr.h"
73#include "uvector.h"
74#include "cstr.h"
75#include "dayperiodrules.h"
0f5d89e8 76#include "tznames_impl.h" // ZONE_NAME_U16_MAX
3d1f044b 77#include "number_utypes.h"
b75a7d8f 78
3d1f044b
A
79#define DEBUG_SYNTHETIC_TIMEFMTS 0
80
81#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) || DEBUG_SYNTHETIC_TIMEFMTS
374ca955
A
82#include <stdio.h>
83#endif
84
b75a7d8f
A
85// *****************************************************************************
86// class SimpleDateFormat
87// *****************************************************************************
88
89U_NAMESPACE_BEGIN
90
374ca955
A
91/**
92 * Last-resort string to use for "GMT" when constructing time zone strings.
93 */
b75a7d8f
A
94// For time zones that have no names, use strings GMT+minutes and
95// GMT-minutes. For instance, in France the time zone is GMT+60.
96// Also accepted are GMT+H:MM or GMT-H:MM.
51004dcb
A
97// Currently not being used
98//static const UChar gGmt[] = {0x0047, 0x004D, 0x0054, 0x0000}; // "GMT"
99//static const UChar gGmtPlus[] = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+"
100//static const UChar gGmtMinus[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-"
101//static const UChar gDefGmtPat[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
102//static const UChar gDefGmtNegHmsPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */
103//static const UChar gDefGmtNegHmPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */
104//static const UChar gDefGmtPosHmsPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */
105//static const UChar gDefGmtPosHmPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */
106//static const UChar gUt[] = {0x0055, 0x0054, 0x0000}; // "UT"
107//static const UChar gUtc[] = {0x0055, 0x0054, 0x0043, 0x0000}; // "UT"
729e4ab9 108
46f4442e
A
109typedef enum GmtPatSize {
110 kGmtLen = 3,
111 kGmtPatLen = 6,
112 kNegHmsLen = 9,
113 kNegHmLen = 6,
114 kPosHmsLen = 9,
729e4ab9
A
115 kPosHmLen = 6,
116 kUtLen = 2,
117 kUtcLen = 3
46f4442e 118} GmtPatSize;
b75a7d8f 119
729e4ab9
A
120// Stuff needed for numbering system overrides
121
122typedef enum OvrStrType {
123 kOvrStrDate = 0,
124 kOvrStrTime = 1,
125 kOvrStrBoth = 2
126} OvrStrType;
127
128static const UDateFormatField kDateFields[] = {
129 UDAT_YEAR_FIELD,
130 UDAT_MONTH_FIELD,
131 UDAT_DATE_FIELD,
132 UDAT_DAY_OF_YEAR_FIELD,
133 UDAT_DAY_OF_WEEK_IN_MONTH_FIELD,
134 UDAT_WEEK_OF_YEAR_FIELD,
135 UDAT_WEEK_OF_MONTH_FIELD,
136 UDAT_YEAR_WOY_FIELD,
137 UDAT_EXTENDED_YEAR_FIELD,
138 UDAT_JULIAN_DAY_FIELD,
139 UDAT_STANDALONE_DAY_FIELD,
140 UDAT_STANDALONE_MONTH_FIELD,
141 UDAT_QUARTER_FIELD,
4388f060 142 UDAT_STANDALONE_QUARTER_FIELD,
57a6839d
A
143 UDAT_YEAR_NAME_FIELD,
144 UDAT_RELATED_YEAR_FIELD };
145static const int8_t kDateFieldsCount = 16;
729e4ab9
A
146
147static const UDateFormatField kTimeFields[] = {
148 UDAT_HOUR_OF_DAY1_FIELD,
149 UDAT_HOUR_OF_DAY0_FIELD,
150 UDAT_MINUTE_FIELD,
151 UDAT_SECOND_FIELD,
152 UDAT_FRACTIONAL_SECOND_FIELD,
153 UDAT_HOUR1_FIELD,
154 UDAT_HOUR0_FIELD,
155 UDAT_MILLISECONDS_IN_DAY_FIELD,
51004dcb
A
156 UDAT_TIMEZONE_RFC_FIELD,
157 UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD };
158static const int8_t kTimeFieldsCount = 10;
729e4ab9
A
159
160
b75a7d8f
A
161// This is a pattern-of-last-resort used when we can't load a usable pattern out
162// of a resource.
374ca955 163static const UChar gDefaultPattern[] =
b75a7d8f
A
164{
165 0x79, 0x79, 0x79, 0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0
166}; /* "yyyyMMdd hh:mm a" */
167
168// This prefix is designed to NEVER MATCH real text, in order to
169// suppress the parsing of negative numbers. Adjust as needed (if
170// this becomes valid Unicode).
171static const UChar SUPPRESS_NEGATIVE_PREFIX[] = {0xAB00, 0};
172
173/**
174 * These are the tags we expect to see in normal resource bundle files associated
175 * with a locale.
176 */
b75a7d8f 177static const UChar QUOTE = 0x27; // Single quote
46f4442e
A
178
179/*
180 * The field range check bias for each UDateFormatField.
181 * The bias is added to the minimum and maximum values
182 * before they are compared to the parsed number.
183 * For example, the calendar stores zero-based month numbers
184 * but the parsed month numbers start at 1, so the bias is 1.
185 *
186 * A value of -1 means that the value is not checked.
187 */
188static const int32_t gFieldRangeBias[] = {
189 -1, // 'G' - UDAT_ERA_FIELD
190 -1, // 'y' - UDAT_YEAR_FIELD
191 1, // 'M' - UDAT_MONTH_FIELD
192 0, // 'd' - UDAT_DATE_FIELD
193 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
194 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
195 0, // 'm' - UDAT_MINUTE_FIELD
4388f060 196 0, // 's' - UDAT_SECOND_FIELD
46f4442e
A
197 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
198 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
199 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
200 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
201 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
202 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
203 -1, // 'a' - UDAT_AM_PM_FIELD
204 -1, // 'h' - UDAT_HOUR1_FIELD
205 -1, // 'K' - UDAT_HOUR0_FIELD
206 -1, // 'z' - UDAT_TIMEZONE_FIELD
207 -1, // 'Y' - UDAT_YEAR_WOY_FIELD
208 -1, // 'e' - UDAT_DOW_LOCAL_FIELD
209 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
210 -1, // 'g' - UDAT_JULIAN_DAY_FIELD
211 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
212 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
213 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
214 0, // 'c' - UDAT_STANDALONE_DAY_FIELD
215 1, // 'L' - UDAT_STANDALONE_MONTH_FIELD
216 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
217 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
4388f060 218 -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
51004dcb
A
219 -1, // 'U' - UDAT_YEAR_NAME_FIELD
220 -1, // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
221 -1, // 'X' - UDAT_TIMEZONE_ISO_FIELD
222 -1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
57a6839d 223 -1, // 'r' - UDAT_RELATED_YEAR_FIELD
2ca993e8 224#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
b331163b 225 -1, // ':' - UDAT_TIME_SEPARATOR_FIELD
2ca993e8
A
226#else
227 -1, // (no pattern character currently) - UDAT_TIME_SEPARATOR_FIELD
228#endif
4388f060
A
229};
230// A slightly looser range check for lenient parsing
231static const int32_t gFieldRangeBiasLenient[] = {
232 -1, // 'G' - UDAT_ERA_FIELD
233 -1, // 'y' - UDAT_YEAR_FIELD
234 8, // 'M' - UDAT_MONTH_FIELD (allow calendar max + 7, e.g. 19 for grego 1-based month)
235 18, // 'd' - UDAT_DATE_FIELD (allow calendar max + 18, e.g. 49 for grego; tests require at least 40 for grego)
236 -1, // 'k' - UDAT_HOUR_OF_DAY1_FIELD
237 -1, // 'H' - UDAT_HOUR_OF_DAY0_FIELD
238 40, // 'm' - UDAT_MINUTE_FIELD (allow calendar max + 40, e.g. 99)
239 40, // 's' - UDAT_SECOND_FIELD (allow calendar max + 40, e.g. 99)
240 -1, // 'S' - UDAT_FRACTIONAL_SECOND_FIELD (0-999?)
241 -1, // 'E' - UDAT_DAY_OF_WEEK_FIELD (1-7?)
242 -1, // 'D' - UDAT_DAY_OF_YEAR_FIELD (1 - 366?)
243 -1, // 'F' - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD (1-5?)
244 -1, // 'w' - UDAT_WEEK_OF_YEAR_FIELD (1-52?)
245 -1, // 'W' - UDAT_WEEK_OF_MONTH_FIELD (1-5?)
246 -1, // 'a' - UDAT_AM_PM_FIELD
247 -1, // 'h' - UDAT_HOUR1_FIELD
248 -1, // 'K' - UDAT_HOUR0_FIELD
249 -1, // 'z' - UDAT_TIMEZONE_FIELD
250 -1, // 'Y' - UDAT_YEAR_WOY_FIELD
251 -1, // 'e' - UDAT_DOW_LOCAL_FIELD
252 -1, // 'u' - UDAT_EXTENDED_YEAR_FIELD
253 -1, // 'g' - UDAT_JULIAN_DAY_FIELD
254 -1, // 'A' - UDAT_MILLISECONDS_IN_DAY_FIELD
255 -1, // 'Z' - UDAT_TIMEZONE_RFC_FIELD
256 -1, // 'v' - UDAT_TIMEZONE_GENERIC_FIELD
257 18, // 'c' - UDAT_STANDALONE_DAY_FIELD (allow calendar max + 18, e.g. 49 for grego)
258 8, // 'L' - UDAT_STANDALONE_MONTH_FIELD (allow calendar max + 7, e.g. 19 for grego 1-based month)
259 -1, // 'Q' - UDAT_QUARTER_FIELD (1-4?)
260 -1, // 'q' - UDAT_STANDALONE_QUARTER_FIELD
261 -1, // 'V' - UDAT_TIMEZONE_SPECIAL_FIELD
51004dcb
A
262 -1, // 'U' - UDAT_YEAR_NAME_FIELD
263 -1, // 'O' - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
264 -1, // 'X' - UDAT_TIMEZONE_ISO_FIELD
265 -1, // 'x' - UDAT_TIMEZONE_ISO_LOCAL_FIELD
57a6839d 266 -1, // 'r' - UDAT_RELATED_YEAR_FIELD
2ca993e8 267#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
b331163b 268 -1, // ':' - UDAT_TIME_SEPARATOR_FIELD
2ca993e8
A
269#else
270 -1, // (no pattern character currently) - UDAT_TIME_SEPARATOR_FIELD
271#endif
46f4442e
A
272};
273
51004dcb
A
274// When calendar uses hebr numbering (i.e. he@calendar=hebrew),
275// offset the years within the current millenium down to 1-999
276static const int32_t HEBREW_CAL_CUR_MILLENIUM_START_YEAR = 5000;
277static const int32_t HEBREW_CAL_CUR_MILLENIUM_END_YEAR = 6000;
278
3d1f044b
A
279static UMutex *LOCK() {
280 static UMutex *m = STATIC_NEW(UMutex);
281 return m;
282}
46f4442e
A
283
284UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat)
b75a7d8f 285
b331163b
A
286SimpleDateFormat::NSOverride::~NSOverride() {
287 if (snf != NULL) {
288 snf->removeRef();
289 }
290}
291
292
293void SimpleDateFormat::NSOverride::free() {
294 NSOverride *cur = this;
295 while (cur) {
3d1f044b 296 NSOverride *next_temp = cur->next;
b331163b 297 delete cur;
3d1f044b 298 cur = next_temp;
b331163b
A
299 }
300}
301
302// no matter what the locale's default number format looked like, we want
303// to modify it so that it doesn't use thousands separators, doesn't always
304// show the decimal point, and recognizes integers only when parsing
305static void fixNumberFormatForDates(NumberFormat &nf) {
c5116b9f
A
306 // Use new group setter equivalent to
307 // setGroupingUsed(FALSE);
308 // setDecimalSeparatorAlwaysShown(FALSE);
309 // setParseIntegerOnly(TRUE);
310 // setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
311 nf.setDateSettings(); // Apple rdar://50064762
b331163b
A
312}
313
314static const SharedNumberFormat *createSharedNumberFormat(
315 NumberFormat *nfToAdopt) {
316 fixNumberFormatForDates(*nfToAdopt);
317 const SharedNumberFormat *result = new SharedNumberFormat(nfToAdopt);
318 if (result == NULL) {
319 delete nfToAdopt;
320 }
321 return result;
322}
323
324static const SharedNumberFormat *createSharedNumberFormat(
325 const Locale &loc, UErrorCode &status) {
326 NumberFormat *nf = NumberFormat::createInstance(loc, status);
327 if (U_FAILURE(status)) {
328 return NULL;
329 }
330 const SharedNumberFormat *result = createSharedNumberFormat(nf);
331 if (result == NULL) {
332 status = U_MEMORY_ALLOCATION_ERROR;
333 }
334 return result;
335}
336
337static const SharedNumberFormat **allocSharedNumberFormatters() {
338 const SharedNumberFormat **result = (const SharedNumberFormat**)
339 uprv_malloc(UDAT_FIELD_COUNT * sizeof(const SharedNumberFormat*));
340 if (result == NULL) {
341 return NULL;
342 }
343 for (int32_t i = 0; i < UDAT_FIELD_COUNT; ++i) {
344 result[i] = NULL;
345 }
346 return result;
347}
348
349static void freeSharedNumberFormatters(const SharedNumberFormat ** list) {
350 for (int32_t i = 0; i < UDAT_FIELD_COUNT; ++i) {
351 SharedObject::clearPtr(list[i]);
352 }
353 uprv_free(list);
354}
355
356const NumberFormat *SimpleDateFormat::getNumberFormatByIndex(
357 UDateFormatField index) const {
358 if (fSharedNumberFormatters == NULL ||
359 fSharedNumberFormatters[index] == NULL) {
360 return fNumberFormat;
361 }
362 return &(**fSharedNumberFormatters[index]);
363}
364
b331163b
A
365//----------------------------------------------------------------------
366
b75a7d8f
A
367SimpleDateFormat::~SimpleDateFormat()
368{
369 delete fSymbols;
b331163b
A
370 if (fSharedNumberFormatters) {
371 freeSharedNumberFormatters(fSharedNumberFormatters);
729e4ab9 372 }
4388f060
A
373 if (fTimeZoneFormat) {
374 delete fTimeZoneFormat;
375 }
3d1f044b 376 freeFastNumberFormatters();
729e4ab9 377
57a6839d
A
378#if !UCONFIG_NO_BREAK_ITERATION
379 delete fCapitalizationBrkIter;
380#endif
b75a7d8f
A
381}
382
383//----------------------------------------------------------------------
384
385SimpleDateFormat::SimpleDateFormat(UErrorCode& status)
386 : fLocale(Locale::getDefault()),
73c04bcf 387 fSymbols(NULL),
4388f060 388 fTimeZoneFormat(NULL),
b331163b 389 fSharedNumberFormatters(NULL),
57a6839d 390 fCapitalizationBrkIter(NULL)
b75a7d8f 391{
57a6839d 392 initializeBooleanAttributes();
b75a7d8f
A
393 construct(kShort, (EStyle) (kShort + kDateOffset), fLocale, status);
394 initializeDefaultCentury();
395}
396
397//----------------------------------------------------------------------
398
399SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
400 UErrorCode &status)
401: fPattern(pattern),
402 fLocale(Locale::getDefault()),
73c04bcf 403 fSymbols(NULL),
4388f060 404 fTimeZoneFormat(NULL),
b331163b 405 fSharedNumberFormatters(NULL),
57a6839d 406 fCapitalizationBrkIter(NULL)
729e4ab9
A
407{
408 fDateOverride.setToBogus();
409 fTimeOverride.setToBogus();
57a6839d 410 initializeBooleanAttributes();
b331163b
A
411 initializeCalendar(NULL,fLocale,status);
412 fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
729e4ab9
A
413 initialize(fLocale, status);
414 initializeDefaultCentury();
2ca993e8 415
729e4ab9 416}
b331163b 417
729e4ab9
A
418//----------------------------------------------------------------------
419
420SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
421 const UnicodeString& override,
422 UErrorCode &status)
423: fPattern(pattern),
424 fLocale(Locale::getDefault()),
425 fSymbols(NULL),
4388f060 426 fTimeZoneFormat(NULL),
b331163b 427 fSharedNumberFormatters(NULL),
57a6839d 428 fCapitalizationBrkIter(NULL)
b75a7d8f 429{
729e4ab9
A
430 fDateOverride.setTo(override);
431 fTimeOverride.setToBogus();
57a6839d 432 initializeBooleanAttributes();
b331163b
A
433 initializeCalendar(NULL,fLocale,status);
434 fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
b75a7d8f
A
435 initialize(fLocale, status);
436 initializeDefaultCentury();
729e4ab9
A
437
438 processOverrideString(fLocale,override,kOvrStrBoth,status);
439
b75a7d8f
A
440}
441
442//----------------------------------------------------------------------
443
444SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
445 const Locale& locale,
446 UErrorCode& status)
447: fPattern(pattern),
73c04bcf 448 fLocale(locale),
4388f060 449 fTimeZoneFormat(NULL),
b331163b 450 fSharedNumberFormatters(NULL),
57a6839d 451 fCapitalizationBrkIter(NULL)
b75a7d8f 452{
729e4ab9
A
453
454 fDateOverride.setToBogus();
455 fTimeOverride.setToBogus();
57a6839d 456 initializeBooleanAttributes();
729e4ab9 457
b331163b
A
458 initializeCalendar(NULL,fLocale,status);
459 fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
b75a7d8f
A
460 initialize(fLocale, status);
461 initializeDefaultCentury();
462}
463
464//----------------------------------------------------------------------
465
729e4ab9
A
466SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
467 const UnicodeString& override,
468 const Locale& locale,
469 UErrorCode& status)
470: fPattern(pattern),
471 fLocale(locale),
4388f060 472 fTimeZoneFormat(NULL),
b331163b 473 fSharedNumberFormatters(NULL),
57a6839d 474 fCapitalizationBrkIter(NULL)
729e4ab9
A
475{
476
477 fDateOverride.setTo(override);
478 fTimeOverride.setToBogus();
57a6839d 479 initializeBooleanAttributes();
729e4ab9 480
b331163b
A
481 initializeCalendar(NULL,fLocale,status);
482 fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
729e4ab9
A
483 initialize(fLocale, status);
484 initializeDefaultCentury();
485
486 processOverrideString(locale,override,kOvrStrBoth,status);
487
488}
489
490//----------------------------------------------------------------------
491
b75a7d8f
A
492SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
493 DateFormatSymbols* symbolsToAdopt,
494 UErrorCode& status)
495: fPattern(pattern),
496 fLocale(Locale::getDefault()),
73c04bcf 497 fSymbols(symbolsToAdopt),
4388f060 498 fTimeZoneFormat(NULL),
b331163b 499 fSharedNumberFormatters(NULL),
57a6839d 500 fCapitalizationBrkIter(NULL)
b75a7d8f 501{
729e4ab9
A
502
503 fDateOverride.setToBogus();
504 fTimeOverride.setToBogus();
57a6839d 505 initializeBooleanAttributes();
729e4ab9 506
b75a7d8f
A
507 initializeCalendar(NULL,fLocale,status);
508 initialize(fLocale, status);
509 initializeDefaultCentury();
510}
511
512//----------------------------------------------------------------------
513
514SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
515 const DateFormatSymbols& symbols,
516 UErrorCode& status)
517: fPattern(pattern),
518 fLocale(Locale::getDefault()),
73c04bcf 519 fSymbols(new DateFormatSymbols(symbols)),
4388f060 520 fTimeZoneFormat(NULL),
b331163b 521 fSharedNumberFormatters(NULL),
57a6839d 522 fCapitalizationBrkIter(NULL)
b75a7d8f 523{
729e4ab9
A
524
525 fDateOverride.setToBogus();
526 fTimeOverride.setToBogus();
57a6839d 527 initializeBooleanAttributes();
729e4ab9 528
b75a7d8f
A
529 initializeCalendar(NULL, fLocale, status);
530 initialize(fLocale, status);
531 initializeDefaultCentury();
532}
533
534//----------------------------------------------------------------------
535
536// Not for public consumption; used by DateFormat
537SimpleDateFormat::SimpleDateFormat(EStyle timeStyle,
538 EStyle dateStyle,
539 const Locale& locale,
540 UErrorCode& status)
541: fLocale(locale),
73c04bcf 542 fSymbols(NULL),
4388f060 543 fTimeZoneFormat(NULL),
b331163b 544 fSharedNumberFormatters(NULL),
57a6839d 545 fCapitalizationBrkIter(NULL)
b75a7d8f 546{
57a6839d 547 initializeBooleanAttributes();
b75a7d8f
A
548 construct(timeStyle, dateStyle, fLocale, status);
549 if(U_SUCCESS(status)) {
550 initializeDefaultCentury();
551 }
552}
553
554//----------------------------------------------------------------------
555
556/**
557 * Not for public consumption; used by DateFormat. This constructor
558 * never fails. If the resource data is not available, it uses the
559 * the last resort symbols.
560 */
561SimpleDateFormat::SimpleDateFormat(const Locale& locale,
562 UErrorCode& status)
374ca955 563: fPattern(gDefaultPattern),
b75a7d8f 564 fLocale(locale),
73c04bcf 565 fSymbols(NULL),
4388f060 566 fTimeZoneFormat(NULL),
b331163b 567 fSharedNumberFormatters(NULL),
57a6839d 568 fCapitalizationBrkIter(NULL)
b75a7d8f
A
569{
570 if (U_FAILURE(status)) return;
57a6839d 571 initializeBooleanAttributes();
b331163b
A
572 initializeCalendar(NULL, fLocale, status);
573 fSymbols = DateFormatSymbols::createForLocale(fLocale, status);
b75a7d8f
A
574 if (U_FAILURE(status))
575 {
576 status = U_ZERO_ERROR;
577 delete fSymbols;
578 // This constructor doesn't fail; it uses last resort data
579 fSymbols = new DateFormatSymbols(status);
580 /* test for NULL */
581 if (fSymbols == 0) {
582 status = U_MEMORY_ALLOCATION_ERROR;
583 return;
584 }
585 }
586
729e4ab9
A
587 fDateOverride.setToBogus();
588 fTimeOverride.setToBogus();
589
b75a7d8f
A
590 initialize(fLocale, status);
591 if(U_SUCCESS(status)) {
592 initializeDefaultCentury();
593 }
594}
595
596//----------------------------------------------------------------------
597
598SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other)
599: DateFormat(other),
4388f060 600 fLocale(other.fLocale),
73c04bcf 601 fSymbols(NULL),
4388f060 602 fTimeZoneFormat(NULL),
b331163b 603 fSharedNumberFormatters(NULL),
57a6839d 604 fCapitalizationBrkIter(NULL)
b75a7d8f 605{
57a6839d 606 initializeBooleanAttributes();
b75a7d8f
A
607 *this = other;
608}
609
610//----------------------------------------------------------------------
611
612SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other)
613{
46f4442e
A
614 if (this == &other) {
615 return *this;
616 }
3d1f044b 617 freeFastNumberFormatters(); // deletes refs to fNumberFormat's symbols
b75a7d8f 618 DateFormat::operator=(other);
57a6839d
A
619 fDateOverride = other.fDateOverride;
620 fTimeOverride = other.fTimeOverride;
621
b75a7d8f
A
622 delete fSymbols;
623 fSymbols = NULL;
b331163b
A
624
625 if (other.fSymbols)
b75a7d8f 626 fSymbols = new DateFormatSymbols(*other.fSymbols);
57a6839d 627
b331163b
A
628 fDefaultCenturyStart = other.fDefaultCenturyStart;
629 fDefaultCenturyStartYear = other.fDefaultCenturyStartYear;
630 fHaveDefaultCentury = other.fHaveDefaultCentury;
631
632 fPattern = other.fPattern;
2ca993e8
A
633 fHasMinute = other.fHasMinute;
634 fHasSecond = other.fHasSecond;
b331163b
A
635
636 fLocale = other.fLocale;
57a6839d
A
637 // TimeZoneFormat can now be set independently via setter.
638 // If it is NULL, it will be lazily initialized from locale
639 delete fTimeZoneFormat;
640 fTimeZoneFormat = NULL;
641 if (other.fTimeZoneFormat) {
642 fTimeZoneFormat = new TimeZoneFormat(*other.fTimeZoneFormat);
643 }
b75a7d8f 644
57a6839d
A
645#if !UCONFIG_NO_BREAK_ITERATION
646 if (other.fCapitalizationBrkIter != NULL) {
647 fCapitalizationBrkIter = (other.fCapitalizationBrkIter)->clone();
648 }
649#endif
4388f060 650
b331163b
A
651 if (fSharedNumberFormatters != NULL) {
652 freeSharedNumberFormatters(fSharedNumberFormatters);
653 fSharedNumberFormatters = NULL;
654 }
655 if (other.fSharedNumberFormatters != NULL) {
656 fSharedNumberFormatters = allocSharedNumberFormatters();
657 if (fSharedNumberFormatters) {
658 for (int32_t i = 0; i < UDAT_FIELD_COUNT; ++i) {
659 SharedObject::copyPtr(
660 other.fSharedNumberFormatters[i],
661 fSharedNumberFormatters[i]);
662 }
663 }
664 }
665
3d1f044b
A
666 UErrorCode localStatus = U_ZERO_ERROR;
667 initFastNumberFormatters(localStatus);
668
b75a7d8f
A
669 return *this;
670}
671
672//----------------------------------------------------------------------
673
674Format*
675SimpleDateFormat::clone() const
676{
677 return new SimpleDateFormat(*this);
678}
679
680//----------------------------------------------------------------------
681
682UBool
683SimpleDateFormat::operator==(const Format& other) const
684{
374ca955 685 if (DateFormat::operator==(other)) {
57a6839d
A
686 // The DateFormat::operator== check for fCapitalizationContext equality above
687 // is sufficient to check equality of all derived context-related data.
374ca955 688 // DateFormat::operator== guarantees following cast is safe
b75a7d8f 689 SimpleDateFormat* that = (SimpleDateFormat*)&other;
73c04bcf 690 return (fPattern == that->fPattern &&
b75a7d8f 691 fSymbols != NULL && // Check for pathological object
73c04bcf
A
692 that->fSymbols != NULL && // Check for pathological object
693 *fSymbols == *that->fSymbols &&
694 fHaveDefaultCentury == that->fHaveDefaultCentury &&
4388f060 695 fDefaultCenturyStart == that->fDefaultCenturyStart &&
57a6839d
A
696 // Check fTimeZoneFormat, it can be set independently via setter
697 ((fTimeZoneFormat == NULL && that->fTimeZoneFormat == NULL) ||
698 (fTimeZoneFormat != NULL && that->fTimeZoneFormat != NULL && *fTimeZoneFormat == *that->fTimeZoneFormat)) &&
699 // Check override strings (these also indicate any relevant
700 // differences in fNumberFormatters, fOverrideList)
701 fDateOverride == that->fDateOverride &&
702 fTimeOverride == that->fTimeOverride);
b75a7d8f
A
703 }
704 return FALSE;
705}
706
707//----------------------------------------------------------------------
3d1f044b
A
708static const UChar* timeSkeletons[4] = {
709 u"jmmsszzzz", // kFull
710 u"jmmssz", // kLong
711 u"jmmss", // kMedium
712 u"jmm", // kShort
713};
714
715enum { kBaseNameMax = ULOC_LANG_CAPACITY + ULOC_SCRIPT_CAPACITY + ULOC_COUNTRY_CAPACITY }; // includes separators and 0 term
b75a7d8f
A
716
717void SimpleDateFormat::construct(EStyle timeStyle,
718 EStyle dateStyle,
719 const Locale& locale,
720 UErrorCode& status)
721{
722 // called by several constructors to load pattern data from the resources
b75a7d8f
A
723 if (U_FAILURE(status)) return;
724
b75a7d8f
A
725 // We will need the calendar to know what type of symbols to load.
726 initializeCalendar(NULL, locale, status);
73c04bcf 727 if (U_FAILURE(status)) return;
b75a7d8f 728
f3c0d7a5
A
729 // Load date time patterns directly from resources.
730 const char* cType = fCalendar ? fCalendar->getType() : NULL;
731 LocalUResourceBundlePointer bundle(ures_open(NULL, locale.getBaseName(), &status));
732 if (U_FAILURE(status)) return;
733
734 UBool cTypeIsGregorian = TRUE;
735 LocalUResourceBundlePointer dateTimePatterns;
736 if (cType != NULL && uprv_strcmp(cType, "gregorian") != 0) {
737 CharString resourcePath("calendar/", status);
738 resourcePath.append(cType, status).append("/DateTimePatterns", status);
739 dateTimePatterns.adoptInstead(
740 ures_getByKeyWithFallback(bundle.getAlias(), resourcePath.data(),
741 (UResourceBundle*)NULL, &status));
742 cTypeIsGregorian = FALSE;
743 }
729e4ab9 744
f3c0d7a5
A
745 // Check for "gregorian" fallback.
746 if (cTypeIsGregorian || status == U_MISSING_RESOURCE_ERROR) {
747 status = U_ZERO_ERROR;
748 dateTimePatterns.adoptInstead(
749 ures_getByKeyWithFallback(bundle.getAlias(),
750 "calendar/gregorian/DateTimePatterns",
751 (UResourceBundle*)NULL, &status));
752 }
b75a7d8f
A
753 if (U_FAILURE(status)) return;
754
f3c0d7a5
A
755 LocalUResourceBundlePointer currentBundle;
756
757 if (ures_getSize(dateTimePatterns.getAlias()) <= kDateTime)
b75a7d8f
A
758 {
759 status = U_INVALID_FORMAT_ERROR;
760 return;
761 }
762
f3c0d7a5
A
763 setLocaleIDs(ures_getLocaleByType(dateTimePatterns.getAlias(), ULOC_VALID_LOCALE, &status),
764 ures_getLocaleByType(dateTimePatterns.getAlias(), ULOC_ACTUAL_LOCALE, &status));
374ca955 765
b75a7d8f 766 // create a symbols object from the locale
b331163b 767 fSymbols = DateFormatSymbols::createForLocale(locale, status);
b75a7d8f
A
768 if (U_FAILURE(status)) return;
769 /* test for NULL */
770 if (fSymbols == 0) {
771 status = U_MEMORY_ALLOCATION_ERROR;
772 return;
773 }
774
729e4ab9
A
775 const UChar *resStr,*ovrStr;
776 int32_t resStrLen,ovrStrLen = 0;
777 fDateOverride.setToBogus();
778 fTimeOverride.setToBogus();
b75a7d8f 779
3d1f044b
A
780 UnicodeString timePattern;
781 if (timeStyle >= kFull && timeStyle <= kShort) {
782 const char* baseLoc = locale.getBaseName();
783 if (baseLoc!=NULL && baseLoc[0]!=0 && uprv_strcmp(baseLoc,"und")!=0) {
784 UErrorCode useStatus = U_ZERO_ERROR;
785 const char* validLoc = getLocaleID(ULOC_VALID_LOCALE, useStatus);
786 if (U_SUCCESS(useStatus) && uprv_strcmp(validLoc,baseLoc)!=0) {
787 char minLoc[kBaseNameMax];
788 uloc_minimizeSubtags(baseLoc, minLoc, kBaseNameMax, &useStatus);
789 minLoc[kBaseNameMax-1] = 0; // ensure zero term
790 const char* actualLoc = getLocaleID(ULOC_ACTUAL_LOCALE, useStatus);
791 if (U_SUCCESS(useStatus) && uprv_strcmp(actualLoc,minLoc)!=0) {
792 // The standard time formats may have the wrong time cycle, because:
793 // * the valid locale is not the same as the base locale, or
794 // * the actual locale the patterns are coming from is not the same
795 // as the minimized locale.
796 // We could *also* check whether they do actually have a mismatch with
797 // the time cycle preferences for the region, but that is a lot more
798 // work for little or no additional benefit, since just going ahead
799 // and always synthesizing the time format as per the following should
800 // create a locale-appropriate pattern with cycle that matches the
801 // region preferences anyway (for completely unsupported languages,
802 // this will use root patterns for the appropriate cycle for the
803 // likely subtags resion).
804 LocalPointer<DateTimePatternGenerator> dtpg(DateTimePatternGenerator::createInstance(locale, useStatus, TRUE));
805 if (U_SUCCESS(useStatus)) {
806 UnicodeString timeSkeleton(TRUE, timeSkeletons[timeStyle], -1);
807 timePattern = dtpg->getBestPattern(timeSkeleton, useStatus);
808#if DEBUG_SYNTHETIC_TIMEFMTS
809 if (timePattern.length() != 0) {
810 char bbuf[32];
811 timePattern.extract(0,timePattern.length(),bbuf,32);
812 printf("\n## for locale %s, validLoc %s, minLoc %s, actualLoc %s, synth timePat %s\n", locale.getName(), validLoc, minLoc, actualLoc, bbuf);
813 }
814#endif
815 }
816 }
817 }
818 }
819 }
820
b75a7d8f
A
821 // if the pattern should include both date and time information, use the date/time
822 // pattern string as a guide to tell use how to glue together the appropriate date
2ca993e8 823 // and time pattern strings.
374ca955 824 if ((timeStyle != kNone) && (dateStyle != kNone))
b75a7d8f 825 {
3d1f044b
A
826 UnicodeString tempus1(timePattern);
827 if (tempus1.length() == 0) {
828 currentBundle.adoptInstead(
829 ures_getByIndex(dateTimePatterns.getAlias(), (int32_t)timeStyle, NULL, &status));
830 if (U_FAILURE(status)) {
729e4ab9 831 status = U_INVALID_FORMAT_ERROR;
729e4ab9
A
832 return;
833 }
3d1f044b
A
834 switch (ures_getType(currentBundle.getAlias())) {
835 case URES_STRING: {
836 resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status);
837 break;
838 }
839 case URES_ARRAY: {
840 resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status);
841 ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status);
842 fTimeOverride.setTo(TRUE, ovrStr, ovrStrLen);
843 break;
844 }
845 default: {
846 status = U_INVALID_FORMAT_ERROR;
847 return;
848 }
849 }
729e4ab9 850
3d1f044b
A
851 tempus1.setTo(TRUE, resStr, resStrLen);
852 }
729e4ab9 853
f3c0d7a5
A
854 currentBundle.adoptInstead(
855 ures_getByIndex(dateTimePatterns.getAlias(), (int32_t)dateStyle, NULL, &status));
729e4ab9
A
856 if (U_FAILURE(status)) {
857 status = U_INVALID_FORMAT_ERROR;
858 return;
859 }
f3c0d7a5 860 switch (ures_getType(currentBundle.getAlias())) {
729e4ab9 861 case URES_STRING: {
f3c0d7a5 862 resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status);
729e4ab9
A
863 break;
864 }
865 case URES_ARRAY: {
f3c0d7a5
A
866 resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status);
867 ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status);
729e4ab9
A
868 fDateOverride.setTo(TRUE, ovrStr, ovrStrLen);
869 break;
870 }
871 default: {
872 status = U_INVALID_FORMAT_ERROR;
729e4ab9
A
873 return;
874 }
875 }
729e4ab9 876
2ca993e8 877 UnicodeString tempus2(TRUE, resStr, resStrLen);
b75a7d8f 878
729e4ab9 879 int32_t glueIndex = kDateTime;
f3c0d7a5 880 int32_t patternsSize = ures_getSize(dateTimePatterns.getAlias());
729e4ab9
A
881 if (patternsSize >= (kDateTimeOffset + kShort + 1)) {
882 // Get proper date time format
883 glueIndex = (int32_t)(kDateTimeOffset + (dateStyle - kDateOffset));
884 }
885
f3c0d7a5 886 resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), glueIndex, &resStrLen, &status);
2ca993e8
A
887 SimpleFormatter(UnicodeString(TRUE, resStr, resStrLen), 2, 2, status).
888 format(tempus1, tempus2, fPattern, status);
b75a7d8f 889 }
b75a7d8f
A
890 // if the pattern includes just time data or just date date, load the appropriate
891 // pattern string from the resources
374ca955
A
892 // setTo() - see DateFormatSymbols::assignArray comments
893 else if (timeStyle != kNone) {
3d1f044b
A
894 fPattern.setTo(timePattern);
895 if (fPattern.length() == 0) {
896 currentBundle.adoptInstead(
897 ures_getByIndex(dateTimePatterns.getAlias(), (int32_t)timeStyle, NULL, &status));
898 if (U_FAILURE(status)) {
729e4ab9 899 status = U_INVALID_FORMAT_ERROR;
729e4ab9
A
900 return;
901 }
3d1f044b
A
902 switch (ures_getType(currentBundle.getAlias())) {
903 case URES_STRING: {
904 resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status);
905 break;
906 }
907 case URES_ARRAY: {
908 resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status);
909 ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status);
910 fDateOverride.setTo(TRUE, ovrStr, ovrStrLen);
911 break;
912 }
913 default: {
914 status = U_INVALID_FORMAT_ERROR;
915 return;
916 }
917 }
918 fPattern.setTo(TRUE, resStr, resStrLen);
729e4ab9 919 }
374ca955
A
920 }
921 else if (dateStyle != kNone) {
f3c0d7a5
A
922 currentBundle.adoptInstead(
923 ures_getByIndex(dateTimePatterns.getAlias(), (int32_t)dateStyle, NULL, &status));
729e4ab9
A
924 if (U_FAILURE(status)) {
925 status = U_INVALID_FORMAT_ERROR;
926 return;
927 }
f3c0d7a5 928 switch (ures_getType(currentBundle.getAlias())) {
729e4ab9 929 case URES_STRING: {
f3c0d7a5 930 resStr = ures_getString(currentBundle.getAlias(), &resStrLen, &status);
729e4ab9
A
931 break;
932 }
933 case URES_ARRAY: {
f3c0d7a5
A
934 resStr = ures_getStringByIndex(currentBundle.getAlias(), 0, &resStrLen, &status);
935 ovrStr = ures_getStringByIndex(currentBundle.getAlias(), 1, &ovrStrLen, &status);
729e4ab9
A
936 fDateOverride.setTo(TRUE, ovrStr, ovrStrLen);
937 break;
938 }
939 default: {
940 status = U_INVALID_FORMAT_ERROR;
729e4ab9
A
941 return;
942 }
943 }
374ca955
A
944 fPattern.setTo(TRUE, resStr, resStrLen);
945 }
729e4ab9 946
b75a7d8f 947 // and if it includes _neither_, that's an error
374ca955
A
948 else
949 status = U_INVALID_FORMAT_ERROR;
b75a7d8f
A
950
951 // finally, finish initializing by creating a Calendar and a NumberFormat
952 initialize(locale, status);
953}
954
955//----------------------------------------------------------------------
956
957Calendar*
958SimpleDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status)
959{
73c04bcf
A
960 if(!U_FAILURE(status)) {
961 fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status);
962 }
73c04bcf 963 return fCalendar;
b75a7d8f
A
964}
965
b75a7d8f
A
966void
967SimpleDateFormat::initialize(const Locale& locale,
968 UErrorCode& status)
969{
970 if (U_FAILURE(status)) return;
971
1546d4af
A
972 parsePattern(); // Need this before initNumberFormatters(), to set fHasHanYearChar
973
57a6839d
A
974 // If the locale has @[....]numbers=hanidays we want to *delete* that (so it
975 // it is not used for every field) and then set fDateOverride to "d=hanidays"
976 // (as with std formats for zh@calendar=chinese) to use hanidays for d field.
977 static const UChar hanidaysOverride[] = {0x64,0x3D,0x68,0x61,0x6E,0x69,0x64,0x61,0x79,0x73,0}; // "d=hanidays"
978 char numbersValue[ULOC_KEYWORDS_CAPACITY];
979 UErrorCode numbersStatus = U_ZERO_ERROR;
980 Locale localeNoHanidays(locale);
981 int32_t numbersLen = localeNoHanidays.getKeywordValue("numbers", numbersValue, ULOC_KEYWORDS_CAPACITY, numbersStatus);
982 if ( U_SUCCESS(numbersStatus) && numbersLen > 0 ) {
983 if ( uprv_strcmp(numbersValue, "hanidays") == 0 ) {
984 localeNoHanidays.setKeywordValue("numbers", NULL, numbersStatus);
985 fDateOverride.setTo(hanidaysOverride,-1);
986 }
987 }
1546d4af
A
988 // Simple-minded hack to force Gannen year numbering for ja@calendar=japanese
989 // if format is non-numeric (includes 年) and fDateOverride is not already specified.
990 // Now this does get updated if applyPattern subsequently changes the pattern type.
991 if (fDateOverride.isBogus() && fHasHanYearChar &&
992 fCalendar != nullptr && uprv_strcmp(fCalendar->getType(),"japanese") == 0 &&
993 uprv_strcmp(fLocale.getLanguage(),"ja") == 0) {
994 fDateOverride.setTo(u"y=jpanyear", -1);
995 }
57a6839d 996
b75a7d8f
A
997 // We don't need to check that the row count is >= 1, since all 2d arrays have at
998 // least one row
57a6839d 999 fNumberFormat = NumberFormat::createInstance(localeNoHanidays, status);
b75a7d8f
A
1000 if (fNumberFormat != NULL && U_SUCCESS(status))
1001 {
b331163b 1002 fixNumberFormatForDates(*fNumberFormat);
4388f060 1003 //fNumberFormat->setLenient(TRUE); // Java uses a custom DateNumberFormat to format/parse
729e4ab9 1004
3d1f044b
A
1005 initNumberFormatters(locale, status);
1006 initFastNumberFormatters(status);
729e4ab9 1007
b75a7d8f
A
1008 }
1009 else if (U_SUCCESS(status))
1010 {
1011 status = U_MISSING_RESOURCE_ERROR;
1012 }
1013}
1014
1015/* Initialize the fields we use to disambiguate ambiguous years. Separate
1016 * so we can call it from readObject().
1017 */
729e4ab9 1018void SimpleDateFormat::initializeDefaultCentury()
b75a7d8f
A
1019{
1020 if(fCalendar) {
1021 fHaveDefaultCentury = fCalendar->haveDefaultCentury();
1022 if(fHaveDefaultCentury) {
1023 fDefaultCenturyStart = fCalendar->defaultCenturyStart();
1024 fDefaultCenturyStartYear = fCalendar->defaultCenturyStartYear();
1025 } else {
1026 fDefaultCenturyStart = DBL_MIN;
1027 fDefaultCenturyStartYear = -1;
1028 }
1029 }
1030}
1031
57a6839d
A
1032/*
1033 * Initialize the boolean attributes. Separate so we can call it from all constructors.
1034 */
1035void SimpleDateFormat::initializeBooleanAttributes()
1036{
1037 UErrorCode status = U_ZERO_ERROR;
1038
1039 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, true, status);
1040 setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, true, status);
2ca993e8 1041 setBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH, true, status);
57a6839d
A
1042 setBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, true, status);
1043}
1044
b75a7d8f
A
1045/* Define one-century window into which to disambiguate dates using
1046 * two-digit years. Make public in JDK 1.2.
1047 */
729e4ab9 1048void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& status)
b75a7d8f
A
1049{
1050 if(U_FAILURE(status)) {
1051 return;
1052 }
1053 if(!fCalendar) {
1054 status = U_ILLEGAL_ARGUMENT_ERROR;
1055 return;
1056 }
729e4ab9 1057
b75a7d8f
A
1058 fCalendar->setTime(startDate, status);
1059 if(U_SUCCESS(status)) {
1060 fHaveDefaultCentury = TRUE;
1061 fDefaultCenturyStart = startDate;
1062 fDefaultCenturyStartYear = fCalendar->get(UCAL_YEAR, status);
1063 }
1064}
729e4ab9 1065
b75a7d8f
A
1066//----------------------------------------------------------------------
1067
1068UnicodeString&
1069SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition& pos) const
1070{
729e4ab9
A
1071 UErrorCode status = U_ZERO_ERROR;
1072 FieldPositionOnlyHandler handler(pos);
51004dcb 1073 return _format(cal, appendTo, handler, status);
729e4ab9
A
1074}
1075
1076//----------------------------------------------------------------------
1077
1078UnicodeString&
1079SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo,
1080 FieldPositionIterator* posIter, UErrorCode& status) const
1081{
1082 FieldPositionIteratorHandler handler(posIter, status);
51004dcb 1083 return _format(cal, appendTo, handler, status);
729e4ab9
A
1084}
1085
1086//----------------------------------------------------------------------
1087
1088UnicodeString&
51004dcb
A
1089SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo,
1090 FieldPositionHandler& handler, UErrorCode& status) const
729e4ab9 1091{
4388f060 1092 if ( U_FAILURE(status) ) {
2ca993e8 1093 return appendTo;
4388f060
A
1094 }
1095 Calendar* workCal = &cal;
1096 Calendar* calClone = NULL;
729e4ab9
A
1097 if (&cal != fCalendar && uprv_strcmp(cal.getType(), fCalendar->getType()) != 0) {
1098 // Different calendar type
1099 // We use the time and time zone from the input calendar, but
1100 // do not use the input calendar for field calculation.
4388f060
A
1101 calClone = fCalendar->clone();
1102 if (calClone != NULL) {
1103 UDate t = cal.getTime(status);
1104 calClone->setTime(t, status);
1105 calClone->setTimeZone(cal.getTimeZone());
1106 workCal = calClone;
1107 } else {
1108 status = U_MEMORY_ALLOCATION_ERROR;
1109 return appendTo;
1110 }
729e4ab9 1111 }
b75a7d8f
A
1112
1113 UBool inQuote = FALSE;
1114 UChar prevCh = 0;
1115 int32_t count = 0;
4388f060 1116 int32_t fieldNum = 0;
57a6839d 1117 UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status);
729e4ab9 1118
b75a7d8f
A
1119 // loop through the pattern string character by character
1120 for (int32_t i = 0; i < fPattern.length() && U_SUCCESS(status); ++i) {
1121 UChar ch = fPattern[i];
729e4ab9 1122
b75a7d8f
A
1123 // Use subFormat() to format a repeated pattern character
1124 // when a different pattern or non-pattern character is seen
1125 if (ch != prevCh && count > 0) {
3d1f044b 1126 subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, status);
b75a7d8f
A
1127 count = 0;
1128 }
1129 if (ch == QUOTE) {
1130 // Consecutive single quotes are a single quote literal,
1131 // either outside of quotes or between quotes
1132 if ((i+1) < fPattern.length() && fPattern[i+1] == QUOTE) {
1133 appendTo += (UChar)QUOTE;
1134 ++i;
1135 } else {
1136 inQuote = ! inQuote;
1137 }
729e4ab9 1138 }
b331163b 1139 else if (!inQuote && isSyntaxChar(ch)) {
b75a7d8f
A
1140 // ch is a date-time pattern character to be interpreted
1141 // by subFormat(); count the number of times it is repeated
1142 prevCh = ch;
1143 ++count;
1144 }
1145 else {
1146 // Append quoted characters and unquoted non-pattern characters
1147 appendTo += ch;
1148 }
1149 }
1150
1151 // Format the last item in the pattern, if any
1152 if (count > 0) {
3d1f044b 1153 subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, status);
b75a7d8f
A
1154 }
1155
4388f060
A
1156 if (calClone != NULL) {
1157 delete calClone;
b75a7d8f 1158 }
b75a7d8f 1159
729e4ab9 1160 return appendTo;
b75a7d8f
A
1161}
1162
1163//----------------------------------------------------------------------
1164
46f4442e
A
1165/* Map calendar field into calendar field level.
1166 * the larger the level, the smaller the field unit.
1167 * For example, UCAL_ERA level is 0, UCAL_YEAR level is 10,
1168 * UCAL_MONTH level is 20.
1169 * NOTE: if new fields adds in, the table needs to update.
1170 */
1171const int32_t
1172SimpleDateFormat::fgCalendarFieldToLevel[] =
1173{
1174 /*GyM*/ 0, 10, 20,
1175 /*wW*/ 20, 30,
1176 /*dDEF*/ 30, 20, 30, 30,
1177 /*ahHm*/ 40, 50, 50, 60,
57a6839d 1178 /*sS*/ 70, 80,
729e4ab9 1179 /*z?Y*/ 0, 0, 10,
46f4442e 1180 /*eug*/ 30, 10, 0,
57a6839d 1181 /*A?.*/ 40, 0, 0
46f4442e
A
1182};
1183
b331163b
A
1184int32_t SimpleDateFormat::getLevelFromChar(UChar ch) {
1185 // Map date field LETTER into calendar field level.
1186 // the larger the level, the smaller the field unit.
1187 // NOTE: if new fields adds in, the table needs to update.
1188 static const int32_t mapCharToLevel[] = {
1189 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1190 //
1191 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1192 // ! " # $ % & ' ( ) * + , - . /
1193 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2ca993e8 1194#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
b331163b
A
1195 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
1196 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1,
2ca993e8
A
1197#else
1198 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
1199 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1200#endif
b331163b
A
1201 // @ A B C D E F G H I J K L M N O
1202 -1, 40, -1, -1, 20, 30, 30, 0, 50, -1, -1, 50, 20, 20, -1, 0,
1203 // P Q R S T U V W X Y Z [ \ ] ^ _
1204 -1, 20, -1, 80, -1, 10, 0, 30, 0, 10, 0, -1, -1, -1, -1, -1,
1205 // ` a b c d e f g h i j k l m n o
1206 -1, 40, -1, 30, 30, 30, -1, 0, 50, -1, -1, 50, 0, 60, -1, -1,
1207 // p q r s t u v w x y z { | } ~
1208 -1, 20, 10, 70, -1, 10, 0, 20, 0, 10, 0, -1, -1, -1, -1, -1
1209 };
1210
1211 return ch < UPRV_LENGTHOF(mapCharToLevel) ? mapCharToLevel[ch] : -1;
1212}
46f4442e 1213
b331163b
A
1214UBool SimpleDateFormat::isSyntaxChar(UChar ch) {
1215 static const UBool mapCharToIsSyntax[] = {
1216 //
1217 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1218 //
1219 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1220 //
1221 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1222 //
1223 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1224 // ! " # $ % & '
1225 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1226 // ( ) * + , - . /
1227 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1228 // 0 1 2 3 4 5 6 7
1229 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
2ca993e8 1230#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
b331163b
A
1231 // 8 9 : ; < = > ?
1232 FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE,
2ca993e8
A
1233#else
1234 // 8 9 : ; < = > ?
1235 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1236#endif
b331163b
A
1237 // @ A B C D E F G
1238 FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
1239 // H I J K L M N O
1240 TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
1241 // P Q R S T U V W
1242 TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
1243 // X Y Z [ \ ] ^ _
1244 TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE,
1245 // ` a b c d e f g
1246 FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
1247 // h i j k l m n o
1248 TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
1249 // p q r s t u v w
1250 TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
1251 // x y z { | } ~
1252 TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE
1253 };
1254
1255 return ch < UPRV_LENGTHOF(mapCharToIsSyntax) ? mapCharToIsSyntax[ch] : FALSE;
1256}
46f4442e 1257
b75a7d8f
A
1258// Map index into pattern character string to Calendar field number.
1259const UCalendarDateFields
1260SimpleDateFormat::fgPatternIndexToCalendarField[] =
1261{
374ca955
A
1262 /*GyM*/ UCAL_ERA, UCAL_YEAR, UCAL_MONTH,
1263 /*dkH*/ UCAL_DATE, UCAL_HOUR_OF_DAY, UCAL_HOUR_OF_DAY,
1264 /*msS*/ UCAL_MINUTE, UCAL_SECOND, UCAL_MILLISECOND,
1265 /*EDF*/ UCAL_DAY_OF_WEEK, UCAL_DAY_OF_YEAR, UCAL_DAY_OF_WEEK_IN_MONTH,
1266 /*wWa*/ UCAL_WEEK_OF_YEAR, UCAL_WEEK_OF_MONTH, UCAL_AM_PM,
1267 /*hKz*/ UCAL_HOUR, UCAL_HOUR, UCAL_ZONE_OFFSET,
1268 /*Yeu*/ UCAL_YEAR_WOY, UCAL_DOW_LOCAL, UCAL_EXTENDED_YEAR,
73c04bcf
A
1269 /*gAZ*/ UCAL_JULIAN_DAY, UCAL_MILLISECONDS_IN_DAY, UCAL_ZONE_OFFSET,
1270 /*v*/ UCAL_ZONE_OFFSET,
46f4442e 1271 /*c*/ UCAL_DOW_LOCAL,
73c04bcf
A
1272 /*L*/ UCAL_MONTH,
1273 /*Q*/ UCAL_MONTH,
1274 /*q*/ UCAL_MONTH,
46f4442e 1275 /*V*/ UCAL_ZONE_OFFSET,
4388f060 1276 /*U*/ UCAL_YEAR,
51004dcb
A
1277 /*O*/ UCAL_ZONE_OFFSET,
1278 /*Xx*/ UCAL_ZONE_OFFSET, UCAL_ZONE_OFFSET,
57a6839d 1279 /*r*/ UCAL_EXTENDED_YEAR,
2ca993e8
A
1280 /*bB*/ UCAL_FIELD_COUNT, UCAL_FIELD_COUNT, // no mappings to calendar fields
1281#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
b331163b 1282 /*:*/ UCAL_FIELD_COUNT, /* => no useful mapping to any calendar field */
2ca993e8
A
1283#else
1284 /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/ UCAL_FIELD_COUNT, /* => no useful mapping to any calendar field */
1285#endif
b75a7d8f
A
1286};
1287
1288// Map index into pattern character string to DateFormat field number
374ca955 1289const UDateFormatField
b75a7d8f 1290SimpleDateFormat::fgPatternIndexToDateFormatField[] = {
374ca955
A
1291 /*GyM*/ UDAT_ERA_FIELD, UDAT_YEAR_FIELD, UDAT_MONTH_FIELD,
1292 /*dkH*/ UDAT_DATE_FIELD, UDAT_HOUR_OF_DAY1_FIELD, UDAT_HOUR_OF_DAY0_FIELD,
1293 /*msS*/ UDAT_MINUTE_FIELD, UDAT_SECOND_FIELD, UDAT_FRACTIONAL_SECOND_FIELD,
1294 /*EDF*/ UDAT_DAY_OF_WEEK_FIELD, UDAT_DAY_OF_YEAR_FIELD, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD,
1295 /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD, UDAT_WEEK_OF_MONTH_FIELD, UDAT_AM_PM_FIELD,
1296 /*hKz*/ UDAT_HOUR1_FIELD, UDAT_HOUR0_FIELD, UDAT_TIMEZONE_FIELD,
1297 /*Yeu*/ UDAT_YEAR_WOY_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_EXTENDED_YEAR_FIELD,
73c04bcf
A
1298 /*gAZ*/ UDAT_JULIAN_DAY_FIELD, UDAT_MILLISECONDS_IN_DAY_FIELD, UDAT_TIMEZONE_RFC_FIELD,
1299 /*v*/ UDAT_TIMEZONE_GENERIC_FIELD,
1300 /*c*/ UDAT_STANDALONE_DAY_FIELD,
1301 /*L*/ UDAT_STANDALONE_MONTH_FIELD,
1302 /*Q*/ UDAT_QUARTER_FIELD,
1303 /*q*/ UDAT_STANDALONE_QUARTER_FIELD,
46f4442e 1304 /*V*/ UDAT_TIMEZONE_SPECIAL_FIELD,
4388f060 1305 /*U*/ UDAT_YEAR_NAME_FIELD,
51004dcb
A
1306 /*O*/ UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD,
1307 /*Xx*/ UDAT_TIMEZONE_ISO_FIELD, UDAT_TIMEZONE_ISO_LOCAL_FIELD,
57a6839d 1308 /*r*/ UDAT_RELATED_YEAR_FIELD,
2ca993e8
A
1309 /*bB*/ UDAT_AM_PM_MIDNIGHT_NOON_FIELD, UDAT_FLEXIBLE_DAY_PERIOD_FIELD,
1310#if UDAT_HAS_PATTERN_CHAR_FOR_TIME_SEPARATOR
b331163b 1311 /*:*/ UDAT_TIME_SEPARATOR_FIELD,
2ca993e8
A
1312#else
1313 /*no pattern char for UDAT_TIME_SEPARATOR_FIELD*/ UDAT_TIME_SEPARATOR_FIELD,
1314#endif
b75a7d8f
A
1315};
1316
b75a7d8f
A
1317//----------------------------------------------------------------------
1318
374ca955
A
1319/**
1320 * Append symbols[value] to dst. Make sure the array index is not out
1321 * of bounds.
1322 */
73c04bcf 1323static inline void
374ca955
A
1324_appendSymbol(UnicodeString& dst,
1325 int32_t value,
1326 const UnicodeString* symbols,
1327 int32_t symbolsCount) {
73c04bcf
A
1328 U_ASSERT(0 <= value && value < symbolsCount);
1329 if (0 <= value && value < symbolsCount) {
1330 dst += symbols[value];
1331 }
1332}
1333
4388f060
A
1334static inline void
1335_appendSymbolWithMonthPattern(UnicodeString& dst, int32_t value, const UnicodeString* symbols, int32_t symbolsCount,
1336 const UnicodeString* monthPattern, UErrorCode& status) {
1337 U_ASSERT(0 <= value && value < symbolsCount);
1338 if (0 <= value && value < symbolsCount) {
1339 if (monthPattern == NULL) {
1340 dst += symbols[value];
46f4442e 1341 } else {
2ca993e8 1342 SimpleFormatter(*monthPattern, 1, 1, status).format(symbols[value], dst, status);
46f4442e
A
1343 }
1344 }
374ca955
A
1345}
1346
4388f060 1347//----------------------------------------------------------------------
3d1f044b
A
1348
1349static number::LocalizedNumberFormatter*
1350createFastFormatter(const DecimalFormat* df, int32_t minInt, int32_t maxInt) {
1351 return new number::LocalizedNumberFormatter(
1352 df->toNumberFormatter()
1353 .integerWidth(number::IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt)));
1354}
1355
1356void SimpleDateFormat::initFastNumberFormatters(UErrorCode& status) {
1357 if (U_FAILURE(status)) {
1358 return;
1359 }
1360 auto* df = dynamic_cast<DecimalFormat*>(fNumberFormat);
1361 if (df == nullptr) {
1362 return;
1363 }
1364 df->setDFSShallowCopy(TRUE);
1365 fFastNumberFormatters[SMPDTFMT_NF_1x10] = createFastFormatter(df, 1, 10);
1366 fFastNumberFormatters[SMPDTFMT_NF_2x10] = createFastFormatter(df, 2, 10);
1367 fFastNumberFormatters[SMPDTFMT_NF_3x10] = createFastFormatter(df, 3, 10);
1368 fFastNumberFormatters[SMPDTFMT_NF_4x10] = createFastFormatter(df, 4, 10);
1369 fFastNumberFormatters[SMPDTFMT_NF_2x2] = createFastFormatter(df, 2, 2);
1370 df->setDFSShallowCopy(FALSE);
1371}
1372
1373void SimpleDateFormat::freeFastNumberFormatters() {
1374 delete fFastNumberFormatters[SMPDTFMT_NF_1x10];
1375 delete fFastNumberFormatters[SMPDTFMT_NF_2x10];
1376 delete fFastNumberFormatters[SMPDTFMT_NF_3x10];
1377 delete fFastNumberFormatters[SMPDTFMT_NF_4x10];
1378 delete fFastNumberFormatters[SMPDTFMT_NF_2x2];
1379 fFastNumberFormatters[SMPDTFMT_NF_1x10] = nullptr;
1380 fFastNumberFormatters[SMPDTFMT_NF_2x10] = nullptr;
1381 fFastNumberFormatters[SMPDTFMT_NF_3x10] = nullptr;
1382 fFastNumberFormatters[SMPDTFMT_NF_4x10] = nullptr;
1383 fFastNumberFormatters[SMPDTFMT_NF_2x2] = nullptr;
1384}
1385
1386
729e4ab9
A
1387void
1388SimpleDateFormat::initNumberFormatters(const Locale &locale,UErrorCode &status) {
1389 if (U_FAILURE(status)) {
1390 return;
1391 }
1392 if ( fDateOverride.isBogus() && fTimeOverride.isBogus() ) {
1393 return;
1394 }
3d1f044b 1395 umtx_lock(LOCK());
b331163b
A
1396 if (fSharedNumberFormatters == NULL) {
1397 fSharedNumberFormatters = allocSharedNumberFormatters();
1398 if (fSharedNumberFormatters == NULL) {
729e4ab9
A
1399 status = U_MEMORY_ALLOCATION_ERROR;
1400 }
1401 }
3d1f044b 1402 umtx_unlock(LOCK());
729e4ab9 1403
b331163b
A
1404 if (U_FAILURE(status)) {
1405 return;
1406 }
1407
729e4ab9
A
1408 processOverrideString(locale,fDateOverride,kOvrStrDate,status);
1409 processOverrideString(locale,fTimeOverride,kOvrStrTime,status);
729e4ab9
A
1410}
1411
1412void
1413SimpleDateFormat::processOverrideString(const Locale &locale, const UnicodeString &str, int8_t type, UErrorCode &status) {
b331163b 1414 if (str.isBogus() || U_FAILURE(status)) {
729e4ab9
A
1415 return;
1416 }
b331163b 1417
729e4ab9
A
1418 int32_t start = 0;
1419 int32_t len;
1420 UnicodeString nsName;
1421 UnicodeString ovrField;
1422 UBool moreToProcess = TRUE;
b331163b 1423 NSOverride *overrideList = NULL;
729e4ab9
A
1424
1425 while (moreToProcess) {
4388f060 1426 int32_t delimiterPosition = str.indexOf((UChar)ULOC_KEYWORD_ITEM_SEPARATOR_UNICODE,start);
729e4ab9
A
1427 if (delimiterPosition == -1) {
1428 moreToProcess = FALSE;
1429 len = str.length() - start;
1430 } else {
1431 len = delimiterPosition - start;
1432 }
1433 UnicodeString currentString(str,start,len);
4388f060 1434 int32_t equalSignPosition = currentString.indexOf((UChar)ULOC_KEYWORD_ASSIGN_UNICODE,0);
729e4ab9
A
1435 if (equalSignPosition == -1) { // Simple override string such as "hebrew"
1436 nsName.setTo(currentString);
1437 ovrField.setToBogus();
1438 } else { // Field specific override string such as "y=hebrew"
1439 nsName.setTo(currentString,equalSignPosition+1);
1440 ovrField.setTo(currentString,0,1); // We just need the first character.
1441 }
1442
1443 int32_t nsNameHash = nsName.hashCode();
1444 // See if the numbering system is in the override list, if not, then add it.
3d1f044b 1445 NSOverride *curr = overrideList;
b331163b 1446 const SharedNumberFormat *snf = NULL;
729e4ab9 1447 UBool found = FALSE;
3d1f044b
A
1448 while ( curr && !found ) {
1449 if ( curr->hash == nsNameHash ) {
1450 snf = curr->snf;
729e4ab9
A
1451 found = TRUE;
1452 }
3d1f044b 1453 curr = curr->next;
729e4ab9
A
1454 }
1455
1456 if (!found) {
b331163b
A
1457 LocalPointer<NSOverride> cur(new NSOverride);
1458 if (!cur.isNull()) {
729e4ab9
A
1459 char kw[ULOC_KEYWORD_AND_VALUES_CAPACITY];
1460 uprv_strcpy(kw,"numbers=");
1461 nsName.extract(0,len,kw+8,ULOC_KEYWORD_AND_VALUES_CAPACITY-8,US_INV);
1462
1463 Locale ovrLoc(locale.getLanguage(),locale.getCountry(),locale.getVariant(),kw);
b331163b
A
1464 cur->hash = nsNameHash;
1465 cur->next = overrideList;
1466 SharedObject::copyPtr(
1467 createSharedNumberFormat(ovrLoc, status), cur->snf);
1468 if (U_FAILURE(status)) {
1469 if (overrideList) {
1470 overrideList->free();
729e4ab9 1471 }
b331163b 1472 return;
729e4ab9 1473 }
b331163b
A
1474 snf = cur->snf;
1475 overrideList = cur.orphan();
729e4ab9
A
1476 } else {
1477 status = U_MEMORY_ALLOCATION_ERROR;
b331163b
A
1478 if (overrideList) {
1479 overrideList->free();
1480 }
729e4ab9
A
1481 return;
1482 }
1483 }
1484
1485 // Now that we have an appropriate number formatter, fill in the appropriate spaces in the
1486 // number formatters table.
729e4ab9
A
1487 if (ovrField.isBogus()) {
1488 switch (type) {
1489 case kOvrStrDate:
1490 case kOvrStrBoth: {
1491 for ( int8_t i=0 ; i<kDateFieldsCount; i++ ) {
b331163b 1492 SharedObject::copyPtr(snf, fSharedNumberFormatters[kDateFields[i]]);
729e4ab9
A
1493 }
1494 if (type==kOvrStrDate) {
1495 break;
1496 }
2ca993e8 1497 U_FALLTHROUGH;
729e4ab9
A
1498 }
1499 case kOvrStrTime : {
1500 for ( int8_t i=0 ; i<kTimeFieldsCount; i++ ) {
b331163b 1501 SharedObject::copyPtr(snf, fSharedNumberFormatters[kTimeFields[i]]);
729e4ab9
A
1502 }
1503 break;
1504 }
1505 }
1506 } else {
729e4ab9 1507 // if the pattern character is unrecognized, signal an error and bail out
51004dcb
A
1508 UDateFormatField patternCharIndex =
1509 DateFormatSymbols::getPatternCharIndex(ovrField.charAt(0));
1510 if (patternCharIndex == UDAT_FIELD_COUNT) {
729e4ab9 1511 status = U_INVALID_FORMAT_ERROR;
b331163b
A
1512 if (overrideList) {
1513 overrideList->free();
1514 }
729e4ab9
A
1515 return;
1516 }
b331163b 1517 SharedObject::copyPtr(snf, fSharedNumberFormatters[patternCharIndex]);
729e4ab9
A
1518 }
1519
1520 start = delimiterPosition + 1;
1521 }
b331163b
A
1522 if (overrideList) {
1523 overrideList->free();
1524 }
729e4ab9 1525}
51004dcb 1526
73c04bcf 1527//---------------------------------------------------------------------
b75a7d8f
A
1528void
1529SimpleDateFormat::subFormat(UnicodeString &appendTo,
1530 UChar ch,
1531 int32_t count,
51004dcb 1532 UDisplayContext capitalizationContext,
4388f060 1533 int32_t fieldNum,
729e4ab9 1534 FieldPositionHandler& handler,
b75a7d8f
A
1535 Calendar& cal,
1536 UErrorCode& status) const
1537{
374ca955
A
1538 if (U_FAILURE(status)) {
1539 return;
1540 }
1541
b75a7d8f
A
1542 // this function gets called by format() to produce the appropriate substitution
1543 // text for an individual pattern symbol (e.g., "HH" or "yyyy")
1544
51004dcb 1545 UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch);
b75a7d8f
A
1546 const int32_t maxIntCount = 10;
1547 int32_t beginOffset = appendTo.length();
3d1f044b 1548 const NumberFormat *currentNumberFormat;
4388f060 1549 DateFormatSymbols::ECapitalizationContextUsageType capContextUsageType = DateFormatSymbols::kCapContextUsageOther;
729e4ab9 1550
4388f060 1551 UBool isHebrewCalendar = (uprv_strcmp(cal.getType(),"hebrew") == 0);
57a6839d 1552 UBool isChineseCalendar = (uprv_strcmp(cal.getType(),"chinese") == 0 || uprv_strcmp(cal.getType(),"dangi") == 0);
b75a7d8f
A
1553
1554 // if the pattern character is unrecognized, signal an error and dump out
51004dcb 1555 if (patternCharIndex == UDAT_FIELD_COUNT)
b75a7d8f 1556 {
4388f060
A
1557 if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
1558 status = U_INVALID_FORMAT_ERROR;
1559 }
374ca955 1560 return;
b75a7d8f
A
1561 }
1562
b75a7d8f 1563 UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex];
b331163b
A
1564 int32_t value = 0;
1565 // Don't get value unless it is useful
1566 if (field < UCAL_FIELD_COUNT) {
1567 value = (patternCharIndex != UDAT_RELATED_YEAR_FIELD)? cal.get(field, status): cal.getRelatedYear(status);
1568 }
b75a7d8f
A
1569 if (U_FAILURE(status)) {
1570 return;
1571 }
1572
3d1f044b 1573 currentNumberFormat = getNumberFormatByIndex(patternCharIndex);
b331163b 1574 if (currentNumberFormat == NULL) {
3d1f044b 1575 status = U_INTERNAL_PROGRAM_ERROR;
b331163b 1576 return;
51004dcb 1577 }
4388f060 1578 UnicodeString hebr("hebr", 4, US_INV);
2ca993e8 1579
b75a7d8f 1580 switch (patternCharIndex) {
729e4ab9 1581
b75a7d8f 1582 // for any "G" symbol, write out the appropriate era string
46f4442e 1583 // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is abbreviated name
374ca955 1584 case UDAT_ERA_FIELD:
4388f060 1585 if (isChineseCalendar) {
b331163b 1586 zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, 9); // as in ICU4J
4388f060
A
1587 } else {
1588 if (count == 5) {
1589 _appendSymbol(appendTo, value, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount);
1590 capContextUsageType = DateFormatSymbols::kCapContextUsageEraNarrow;
1591 } else if (count == 4) {
1592 _appendSymbol(appendTo, value, fSymbols->fEraNames, fSymbols->fEraNamesCount);
1593 capContextUsageType = DateFormatSymbols::kCapContextUsageEraWide;
1594 } else {
1595 _appendSymbol(appendTo, value, fSymbols->fEras, fSymbols->fErasCount);
1596 capContextUsageType = DateFormatSymbols::kCapContextUsageEraAbbrev;
1597 }
1598 }
b75a7d8f
A
1599 break;
1600
4388f060
A
1601 case UDAT_YEAR_NAME_FIELD:
1602 if (fSymbols->fShortYearNames != NULL && value <= fSymbols->fShortYearNamesCount) {
1603 // the Calendar YEAR field runs 1 through 60 for cyclic years
1604 _appendSymbol(appendTo, value - 1, fSymbols->fShortYearNames, fSymbols->fShortYearNamesCount);
1605 break;
1606 }
1607 // else fall through to numeric year handling, do not break here
2ca993e8 1608 U_FALLTHROUGH;
4388f060
A
1609
1610 // OLD: for "yyyy", write out the whole year; for "yy", write out the last 2 digits
46f4442e
A
1611 // NEW: UTS#35:
1612//Year y yy yyy yyyy yyyyy
1613//AD 1 1 01 001 0001 00001
1614//AD 12 12 12 012 0012 00012
1615//AD 123 123 23 123 0123 00123
1616//AD 1234 1234 34 1234 1234 01234
1617//AD 12345 12345 45 12345 12345 12345
729e4ab9 1618 case UDAT_YEAR_FIELD:
374ca955 1619 case UDAT_YEAR_WOY_FIELD:
51004dcb
A
1620 if (fDateOverride.compare(hebr)==0 && value>HEBREW_CAL_CUR_MILLENIUM_START_YEAR && value<HEBREW_CAL_CUR_MILLENIUM_END_YEAR) {
1621 value-=HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
4388f060 1622 }
46f4442e 1623 if(count == 2)
b331163b 1624 zeroPaddingNumber(currentNumberFormat, appendTo, value, 2, 2);
729e4ab9 1625 else
b331163b 1626 zeroPaddingNumber(currentNumberFormat, appendTo, value, count, maxIntCount);
729e4ab9 1627 break;
b75a7d8f 1628
4388f060
A
1629 // for "MMMM"/"LLLL", write out the whole month name, for "MMM"/"LLL", write out the month
1630 // abbreviation, for "M"/"L" or "MM"/"LL", write out the month as a number with the
b75a7d8f 1631 // appropriate number of digits
4388f060 1632 // for "MMMMM"/"LLLLL", use the narrow form
374ca955 1633 case UDAT_MONTH_FIELD:
4388f060 1634 case UDAT_STANDALONE_MONTH_FIELD:
729e4ab9
A
1635 if ( isHebrewCalendar ) {
1636 HebrewCalendar *hc = (HebrewCalendar*)&cal;
1637 if (hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value == 6 && count >= 3 )
1638 value = 13; // Show alternate form for Adar II in leap years in Hebrew calendar.
1639 if (!hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value >= 6 && count < 3 )
1640 value--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7.
1641 }
4388f060
A
1642 {
1643 int32_t isLeapMonth = (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount)?
1644 cal.get(UCAL_IS_LEAP_MONTH, status): 0;
1645 // should consolidate the next section by using arrays of pointers & counts for the right symbols...
1646 if (count == 5) {
1647 if (patternCharIndex == UDAT_MONTH_FIELD) {
1648 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fNarrowMonths, fSymbols->fNarrowMonthsCount,
1649 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatNarrow]): NULL, status);
1650 } else {
1651 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneNarrowMonths, fSymbols->fStandaloneNarrowMonthsCount,
1652 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneNarrow]): NULL, status);
1653 }
1654 capContextUsageType = DateFormatSymbols::kCapContextUsageMonthNarrow;
1655 } else if (count == 4) {
1656 if (patternCharIndex == UDAT_MONTH_FIELD) {
1657 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fMonths, fSymbols->fMonthsCount,
1658 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatWide]): NULL, status);
1659 capContextUsageType = DateFormatSymbols::kCapContextUsageMonthFormat;
1660 } else {
1661 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount,
1662 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneWide]): NULL, status);
1663 capContextUsageType = DateFormatSymbols::kCapContextUsageMonthStandalone;
1664 }
1665 } else if (count == 3) {
1666 if (patternCharIndex == UDAT_MONTH_FIELD) {
1667 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fShortMonths, fSymbols->fShortMonthsCount,
1668 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatAbbrev]): NULL, status);
1669 capContextUsageType = DateFormatSymbols::kCapContextUsageMonthFormat;
1670 } else {
1671 _appendSymbolWithMonthPattern(appendTo, value, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount,
1672 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev]): NULL, status);
1673 capContextUsageType = DateFormatSymbols::kCapContextUsageMonthStandalone;
1674 }
1675 } else {
1676 UnicodeString monthNumber;
b331163b 1677 zeroPaddingNumber(currentNumberFormat,monthNumber, value + 1, count, maxIntCount);
4388f060
A
1678 _appendSymbolWithMonthPattern(appendTo, 0, &monthNumber, 1,
1679 (isLeapMonth!=0)? &(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric]): NULL, status);
1680 }
1681 }
73c04bcf
A
1682 break;
1683
b75a7d8f 1684 // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
374ca955 1685 case UDAT_HOUR_OF_DAY1_FIELD:
729e4ab9 1686 if (value == 0)
b331163b 1687 zeroPaddingNumber(currentNumberFormat,appendTo, cal.getMaximum(UCAL_HOUR_OF_DAY) + 1, count, maxIntCount);
729e4ab9 1688 else
b331163b 1689 zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
b75a7d8f
A
1690 break;
1691
374ca955
A
1692 case UDAT_FRACTIONAL_SECOND_FIELD:
1693 // Fractional seconds left-justify
1694 {
3d1f044b 1695 int32_t minDigits = (count > 3) ? 3 : count;
374ca955 1696 if (count == 1) {
729e4ab9 1697 value /= 100;
374ca955 1698 } else if (count == 2) {
729e4ab9 1699 value /= 10;
374ca955 1700 }
3d1f044b 1701 zeroPaddingNumber(currentNumberFormat, appendTo, value, minDigits, maxIntCount);
374ca955 1702 if (count > 3) {
3d1f044b 1703 zeroPaddingNumber(currentNumberFormat, appendTo, 0, count - 3, maxIntCount);
374ca955
A
1704 }
1705 }
b75a7d8f
A
1706 break;
1707
46f4442e 1708 // for "ee" or "e", use local numeric day-of-the-week
51004dcb 1709 // for "EEEEEE" or "eeeeee", write out the short day-of-the-week name
46f4442e
A
1710 // for "EEEEE" or "eeeee", write out the narrow day-of-the-week name
1711 // for "EEEE" or "eeee", write out the wide day-of-the-week name
1712 // for "EEE" or "EE" or "E" or "eee", write out the abbreviated day-of-the-week name
1713 case UDAT_DOW_LOCAL_FIELD:
1714 if ( count < 3 ) {
b331163b 1715 zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
46f4442e
A
1716 break;
1717 }
1718 // fall through to EEEEE-EEE handling, but for that we don't want local day-of-week,
1719 // we want standard day-of-week, so first fix value to work for EEEEE-EEE.
1720 value = cal.get(UCAL_DAY_OF_WEEK, status);
1721 if (U_FAILURE(status)) {
1722 return;
1723 }
1724 // fall through, do not break here
2ca993e8 1725 U_FALLTHROUGH;
374ca955 1726 case UDAT_DAY_OF_WEEK_FIELD:
4388f060 1727 if (count == 5) {
73c04bcf
A
1728 _appendSymbol(appendTo, value, fSymbols->fNarrowWeekdays,
1729 fSymbols->fNarrowWeekdaysCount);
4388f060
A
1730 capContextUsageType = DateFormatSymbols::kCapContextUsageDayNarrow;
1731 } else if (count == 4) {
374ca955
A
1732 _appendSymbol(appendTo, value, fSymbols->fWeekdays,
1733 fSymbols->fWeekdaysCount);
4388f060 1734 capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat;
51004dcb
A
1735 } else if (count == 6) {
1736 _appendSymbol(appendTo, value, fSymbols->fShorterWeekdays,
1737 fSymbols->fShorterWeekdaysCount);
1738 capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat;
4388f060 1739 } else {
374ca955
A
1740 _appendSymbol(appendTo, value, fSymbols->fShortWeekdays,
1741 fSymbols->fShortWeekdaysCount);
4388f060
A
1742 capContextUsageType = DateFormatSymbols::kCapContextUsageDayFormat;
1743 }
b75a7d8f
A
1744 break;
1745
73c04bcf
A
1746 // for "ccc", write out the abbreviated day-of-the-week name
1747 // for "cccc", write out the wide day-of-the-week name
1748 // for "ccccc", use the narrow day-of-the-week name
51004dcb 1749 // for "ccccc", use the short day-of-the-week name
73c04bcf 1750 case UDAT_STANDALONE_DAY_FIELD:
46f4442e 1751 if ( count < 3 ) {
b331163b 1752 zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, maxIntCount);
46f4442e
A
1753 break;
1754 }
1755 // fall through to alpha DOW handling, but for that we don't want local day-of-week,
1756 // we want standard day-of-week, so first fix value.
1757 value = cal.get(UCAL_DAY_OF_WEEK, status);
1758 if (U_FAILURE(status)) {
1759 return;
1760 }
4388f060 1761 if (count == 5) {
73c04bcf
A
1762 _appendSymbol(appendTo, value, fSymbols->fStandaloneNarrowWeekdays,
1763 fSymbols->fStandaloneNarrowWeekdaysCount);
4388f060
A
1764 capContextUsageType = DateFormatSymbols::kCapContextUsageDayNarrow;
1765 } else if (count == 4) {
73c04bcf
A
1766 _appendSymbol(appendTo, value, fSymbols->fStandaloneWeekdays,
1767 fSymbols->fStandaloneWeekdaysCount);
4388f060 1768 capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone;
51004dcb
A
1769 } else if (count == 6) {
1770 _appendSymbol(appendTo, value, fSymbols->fStandaloneShorterWeekdays,
1771 fSymbols->fStandaloneShorterWeekdaysCount);
1772 capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone;
4388f060 1773 } else { // count == 3
73c04bcf
A
1774 _appendSymbol(appendTo, value, fSymbols->fStandaloneShortWeekdays,
1775 fSymbols->fStandaloneShortWeekdaysCount);
4388f060
A
1776 capContextUsageType = DateFormatSymbols::kCapContextUsageDayStandalone;
1777 }
73c04bcf
A
1778 break;
1779
b331163b 1780 // for "a" symbol, write out the whole AM/PM string
374ca955 1781 case UDAT_AM_PM_FIELD:
b331163b
A
1782 if (count < 5) {
1783 _appendSymbol(appendTo, value, fSymbols->fAmPms,
1784 fSymbols->fAmPmsCount);
1785 } else {
1786 _appendSymbol(appendTo, value, fSymbols->fNarrowAmPms,
1787 fSymbols->fNarrowAmPmsCount);
1788 }
1789 break;
1790
2ca993e8
A
1791 // if we see pattern character for UDAT_TIME_SEPARATOR_FIELD (none currently defined),
1792 // write out the time separator string. Leave support in for future definition.
b331163b
A
1793 case UDAT_TIME_SEPARATOR_FIELD:
1794 {
1795 UnicodeString separator;
1796 appendTo += fSymbols->getTimeSeparatorString(separator);
1797 }
b75a7d8f
A
1798 break;
1799
1800 // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
1801 // as "12"
374ca955 1802 case UDAT_HOUR1_FIELD:
729e4ab9 1803 if (value == 0)
b331163b 1804 zeroPaddingNumber(currentNumberFormat,appendTo, cal.getLeastMaximum(UCAL_HOUR) + 1, count, maxIntCount);
729e4ab9 1805 else
b331163b 1806 zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
b75a7d8f
A
1807 break;
1808
51004dcb
A
1809 case UDAT_TIMEZONE_FIELD: // 'z'
1810 case UDAT_TIMEZONE_RFC_FIELD: // 'Z'
1811 case UDAT_TIMEZONE_GENERIC_FIELD: // 'v'
1812 case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V'
1813 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: // 'O'
1814 case UDAT_TIMEZONE_ISO_FIELD: // 'X'
1815 case UDAT_TIMEZONE_ISO_LOCAL_FIELD: // 'x'
46f4442e 1816 {
0f5d89e8 1817 UChar zsbuf[ZONE_NAME_U16_MAX];
b331163b 1818 UnicodeString zoneString(zsbuf, 0, UPRV_LENGTHOF(zsbuf));
4388f060
A
1819 const TimeZone& tz = cal.getTimeZone();
1820 UDate date = cal.getTime(status);
3d1f044b 1821 const TimeZoneFormat *tzfmt = tzFormat(status);
4388f060 1822 if (U_SUCCESS(status)) {
51004dcb 1823 if (patternCharIndex == UDAT_TIMEZONE_FIELD) {
46f4442e
A
1824 if (count < 4) {
1825 // "z", "zz", "zzz"
3d1f044b 1826 tzfmt->format(UTZFMT_STYLE_SPECIFIC_SHORT, tz, date, zoneString);
4388f060 1827 capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort;
46f4442e 1828 } else {
51004dcb 1829 // "zzzz" or longer
3d1f044b 1830 tzfmt->format(UTZFMT_STYLE_SPECIFIC_LONG, tz, date, zoneString);
4388f060 1831 capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong;
73c04bcf 1832 }
51004dcb
A
1833 }
1834 else if (patternCharIndex == UDAT_TIMEZONE_RFC_FIELD) {
1835 if (count < 4) {
1836 // "Z"
3d1f044b 1837 tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString);
51004dcb
A
1838 } else if (count == 5) {
1839 // "ZZZZZ"
3d1f044b 1840 tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString);
51004dcb
A
1841 } else {
1842 // "ZZ", "ZZZ", "ZZZZ"
3d1f044b 1843 tzfmt->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString);
51004dcb
A
1844 }
1845 }
1846 else if (patternCharIndex == UDAT_TIMEZONE_GENERIC_FIELD) {
46f4442e
A
1847 if (count == 1) {
1848 // "v"
3d1f044b 1849 tzfmt->format(UTZFMT_STYLE_GENERIC_SHORT, tz, date, zoneString);
4388f060 1850 capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort;
46f4442e
A
1851 } else if (count == 4) {
1852 // "vvvv"
3d1f044b 1853 tzfmt->format(UTZFMT_STYLE_GENERIC_LONG, tz, date, zoneString);
4388f060 1854 capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong;
46f4442e 1855 }
51004dcb
A
1856 }
1857 else if (patternCharIndex == UDAT_TIMEZONE_SPECIAL_FIELD) {
46f4442e
A
1858 if (count == 1) {
1859 // "V"
3d1f044b 1860 tzfmt->format(UTZFMT_STYLE_ZONE_ID_SHORT, tz, date, zoneString);
51004dcb
A
1861 } else if (count == 2) {
1862 // "VV"
3d1f044b 1863 tzfmt->format(UTZFMT_STYLE_ZONE_ID, tz, date, zoneString);
51004dcb
A
1864 } else if (count == 3) {
1865 // "VVV"
3d1f044b 1866 tzfmt->format(UTZFMT_STYLE_EXEMPLAR_LOCATION, tz, date, zoneString);
46f4442e
A
1867 } else if (count == 4) {
1868 // "VVVV"
3d1f044b 1869 tzfmt->format(UTZFMT_STYLE_GENERIC_LOCATION, tz, date, zoneString);
4388f060 1870 capContextUsageType = DateFormatSymbols::kCapContextUsageZoneLong;
73c04bcf
A
1871 }
1872 }
51004dcb
A
1873 else if (patternCharIndex == UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD) {
1874 if (count == 1) {
1875 // "O"
3d1f044b 1876 tzfmt->format(UTZFMT_STYLE_LOCALIZED_GMT_SHORT, tz, date, zoneString);
51004dcb
A
1877 } else if (count == 4) {
1878 // "OOOO"
3d1f044b 1879 tzfmt->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString);
51004dcb
A
1880 }
1881 }
1882 else if (patternCharIndex == UDAT_TIMEZONE_ISO_FIELD) {
1883 if (count == 1) {
1884 // "X"
3d1f044b 1885 tzfmt->format(UTZFMT_STYLE_ISO_BASIC_SHORT, tz, date, zoneString);
51004dcb
A
1886 } else if (count == 2) {
1887 // "XX"
3d1f044b 1888 tzfmt->format(UTZFMT_STYLE_ISO_BASIC_FIXED, tz, date, zoneString);
51004dcb
A
1889 } else if (count == 3) {
1890 // "XXX"
3d1f044b 1891 tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_FIXED, tz, date, zoneString);
51004dcb
A
1892 } else if (count == 4) {
1893 // "XXXX"
3d1f044b 1894 tzfmt->format(UTZFMT_STYLE_ISO_BASIC_FULL, tz, date, zoneString);
51004dcb
A
1895 } else if (count == 5) {
1896 // "XXXXX"
3d1f044b 1897 tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString);
51004dcb
A
1898 }
1899 }
1900 else if (patternCharIndex == UDAT_TIMEZONE_ISO_LOCAL_FIELD) {
1901 if (count == 1) {
1902 // "x"
3d1f044b 1903 tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT, tz, date, zoneString);
51004dcb
A
1904 } else if (count == 2) {
1905 // "xx"
3d1f044b 1906 tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED, tz, date, zoneString);
51004dcb
A
1907 } else if (count == 3) {
1908 // "xxx"
3d1f044b 1909 tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED, tz, date, zoneString);
51004dcb
A
1910 } else if (count == 4) {
1911 // "xxxx"
3d1f044b 1912 tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString);
51004dcb
A
1913 } else if (count == 5) {
1914 // "xxxxx"
3d1f044b 1915 tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL, tz, date, zoneString);
51004dcb
A
1916 }
1917 }
1918 else {
3d1f044b 1919 UPRV_UNREACHABLE;
51004dcb 1920 }
73c04bcf 1921 }
4388f060 1922 appendTo += zoneString;
374ca955
A
1923 }
1924 break;
1925
73c04bcf 1926 case UDAT_QUARTER_FIELD:
729e4ab9 1927 if (count >= 4)
73c04bcf
A
1928 _appendSymbol(appendTo, value/3, fSymbols->fQuarters,
1929 fSymbols->fQuartersCount);
729e4ab9 1930 else if (count == 3)
73c04bcf
A
1931 _appendSymbol(appendTo, value/3, fSymbols->fShortQuarters,
1932 fSymbols->fShortQuartersCount);
729e4ab9 1933 else
b331163b 1934 zeroPaddingNumber(currentNumberFormat,appendTo, (value/3) + 1, count, maxIntCount);
73c04bcf
A
1935 break;
1936
1937 case UDAT_STANDALONE_QUARTER_FIELD:
729e4ab9 1938 if (count >= 4)
73c04bcf
A
1939 _appendSymbol(appendTo, value/3, fSymbols->fStandaloneQuarters,
1940 fSymbols->fStandaloneQuartersCount);
729e4ab9 1941 else if (count == 3)
73c04bcf
A
1942 _appendSymbol(appendTo, value/3, fSymbols->fStandaloneShortQuarters,
1943 fSymbols->fStandaloneShortQuartersCount);
729e4ab9 1944 else
b331163b 1945 zeroPaddingNumber(currentNumberFormat,appendTo, (value/3) + 1, count, maxIntCount);
73c04bcf
A
1946 break;
1947
2ca993e8
A
1948 case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
1949 {
1950 const UnicodeString *toAppend = NULL;
1951 int32_t hour = cal.get(UCAL_HOUR_OF_DAY, status);
1952
1953 // Note: "midnight" can be ambiguous as to whether it refers to beginning of day or end of day.
1954 // For ICU 57 output of "midnight" is temporarily suppressed.
1955
1956 // For "midnight" and "noon":
1957 // Time, as displayed, must be exactly noon or midnight.
1958 // This means minutes and seconds, if present, must be zero.
1959 if ((/*hour == 0 ||*/ hour == 12) &&
1960 (!fHasMinute || cal.get(UCAL_MINUTE, status) == 0) &&
1961 (!fHasSecond || cal.get(UCAL_SECOND, status) == 0)) {
1962 // Stealing am/pm value to use as our array index.
1963 // It works out: am/midnight are both 0, pm/noon are both 1,
1964 // 12 am is 12 midnight, and 12 pm is 12 noon.
3d1f044b 1965 int32_t val = cal.get(UCAL_AM_PM, status);
2ca993e8
A
1966
1967 if (count <= 3) {
3d1f044b 1968 toAppend = &fSymbols->fAbbreviatedDayPeriods[val];
2ca993e8 1969 } else if (count == 4 || count > 5) {
3d1f044b 1970 toAppend = &fSymbols->fWideDayPeriods[val];
2ca993e8 1971 } else { // count == 5
3d1f044b 1972 toAppend = &fSymbols->fNarrowDayPeriods[val];
2ca993e8
A
1973 }
1974 }
1975
1976 // toAppend is NULL if time isn't exactly midnight or noon (as displayed).
1977 // toAppend is bogus if time is midnight or noon, but no localized string exists.
1978 // In either case, fall back to am/pm.
1979 if (toAppend == NULL || toAppend->isBogus()) {
1980 // Reformat with identical arguments except ch, now changed to 'a'.
1981 subFormat(appendTo, 0x61, count, capitalizationContext, fieldNum,
3d1f044b 1982 handler, cal, status);
2ca993e8
A
1983 } else {
1984 appendTo += *toAppend;
1985 }
1986
1987 break;
1988 }
1989
1990 case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
1991 {
1992 // TODO: Maybe fetch the DayperiodRules during initialization (instead of at the first
1993 // loading of an instance) if a relevant pattern character (b or B) is used.
1994 const DayPeriodRules *ruleSet = DayPeriodRules::getInstance(this->getSmpFmtLocale(), status);
1995 if (U_FAILURE(status)) {
1996 // Data doesn't conform to spec, therefore loading failed.
1997 break;
1998 }
1999 if (ruleSet == NULL) {
2000 // Data doesn't exist for the locale we're looking for.
2001 // Falling back to am/pm.
2002 subFormat(appendTo, 0x61, count, capitalizationContext, fieldNum,
3d1f044b 2003 handler, cal, status);
2ca993e8
A
2004 break;
2005 }
2006
2007 // Get current display time.
2008 int32_t hour = cal.get(UCAL_HOUR_OF_DAY, status);
2009 int32_t minute = 0;
2010 if (fHasMinute) {
2011 minute = cal.get(UCAL_MINUTE, status);
2012 }
2013 int32_t second = 0;
2014 if (fHasSecond) {
2015 second = cal.get(UCAL_SECOND, status);
2016 }
2017
2018 // Determine day period.
2019 DayPeriodRules::DayPeriod periodType;
2020 if (hour == 0 && minute == 0 && second == 0 && ruleSet->hasMidnight()) {
2021 periodType = DayPeriodRules::DAYPERIOD_MIDNIGHT;
2022 } else if (hour == 12 && minute == 0 && second == 0 && ruleSet->hasNoon()) {
2023 periodType = DayPeriodRules::DAYPERIOD_NOON;
2024 } else {
2025 periodType = ruleSet->getDayPeriodForHour(hour);
2026 }
2027
2028 // Rule set exists, therefore periodType can't be UNKNOWN.
2029 // Get localized string.
2030 U_ASSERT(periodType != DayPeriodRules::DAYPERIOD_UNKNOWN);
2031 UnicodeString *toAppend = NULL;
2032 int32_t index;
2033
2034 // Note: "midnight" can be ambiguous as to whether it refers to beginning of day or end of day.
2035 // For ICU 57 output of "midnight" is temporarily suppressed.
2036
2037 if (periodType != DayPeriodRules::DAYPERIOD_AM &&
2038 periodType != DayPeriodRules::DAYPERIOD_PM &&
2039 periodType != DayPeriodRules::DAYPERIOD_MIDNIGHT) {
2040 index = (int32_t)periodType;
2041 if (count <= 3) {
2042 toAppend = &fSymbols->fAbbreviatedDayPeriods[index]; // i.e. short
2043 } else if (count == 4 || count > 5) {
2044 toAppend = &fSymbols->fWideDayPeriods[index];
2045 } else { // count == 5
2046 toAppend = &fSymbols->fNarrowDayPeriods[index];
2047 }
2048 }
2049
2050 // Fallback schedule:
2051 // Midnight/Noon -> General Periods -> AM/PM.
2052
2053 // Midnight/Noon -> General Periods.
2054 if ((toAppend == NULL || toAppend->isBogus()) &&
2055 (periodType == DayPeriodRules::DAYPERIOD_MIDNIGHT ||
2056 periodType == DayPeriodRules::DAYPERIOD_NOON)) {
2057 periodType = ruleSet->getDayPeriodForHour(hour);
2058 index = (int32_t)periodType;
2059
2060 if (count <= 3) {
2061 toAppend = &fSymbols->fAbbreviatedDayPeriods[index]; // i.e. short
2062 } else if (count == 4 || count > 5) {
2063 toAppend = &fSymbols->fWideDayPeriods[index];
2064 } else { // count == 5
2065 toAppend = &fSymbols->fNarrowDayPeriods[index];
2066 }
2067 }
2068
2069 // General Periods -> AM/PM.
2070 if (periodType == DayPeriodRules::DAYPERIOD_AM ||
2071 periodType == DayPeriodRules::DAYPERIOD_PM ||
2072 toAppend->isBogus()) {
2073 subFormat(appendTo, 0x61, count, capitalizationContext, fieldNum,
3d1f044b 2074 handler, cal, status);
2ca993e8
A
2075 }
2076 else {
2077 appendTo += *toAppend;
2078 }
2079
2080 break;
2081 }
73c04bcf 2082
b75a7d8f
A
2083 // all of the other pattern symbols can be formatted as simple numbers with
2084 // appropriate zero padding
2085 default:
b331163b 2086 zeroPaddingNumber(currentNumberFormat,appendTo, value, count, maxIntCount);
b75a7d8f
A
2087 break;
2088 }
4388f060 2089#if !UCONFIG_NO_BREAK_ITERATION
57a6839d 2090 // if first field, check to see whether we need to and are able to titlecase it
3d1f044b
A
2091 if (fieldNum == 0 && fCapitalizationBrkIter != NULL && appendTo.length() > beginOffset &&
2092 u_islower(appendTo.char32At(beginOffset))) {
4388f060
A
2093 UBool titlecase = FALSE;
2094 switch (capitalizationContext) {
51004dcb 2095 case UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE:
4388f060
A
2096 titlecase = TRUE;
2097 break;
51004dcb
A
2098 case UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU:
2099 titlecase = fSymbols->fCapitalization[capContextUsageType][0];
2100 break;
2101 case UDISPCTX_CAPITALIZATION_FOR_STANDALONE:
2102 titlecase = fSymbols->fCapitalization[capContextUsageType][1];
2103 break;
4388f060
A
2104 default:
2105 // titlecase = FALSE;
2106 break;
2107 }
2108 if (titlecase) {
2109 UnicodeString firstField(appendTo, beginOffset);
57a6839d 2110 firstField.toTitle(fCapitalizationBrkIter, fLocale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
4388f060
A
2111 appendTo.replaceBetween(beginOffset, appendTo.length(), firstField);
2112 }
2113 }
2114#endif
b75a7d8f 2115
729e4ab9
A
2116 handler.addAttribute(fgPatternIndexToDateFormatField[patternCharIndex], beginOffset, appendTo.length());
2117}
2118
2119//----------------------------------------------------------------------
2120
b331163b
A
2121void SimpleDateFormat::adoptNumberFormat(NumberFormat *formatToAdopt) {
2122 fixNumberFormatForDates(*formatToAdopt);
3d1f044b 2123 freeFastNumberFormatters(); // deletes refs to fNumberFormat's symbols
b331163b
A
2124 delete fNumberFormat;
2125 fNumberFormat = formatToAdopt;
2ca993e8 2126
b331163b
A
2127 // We successfully set the default number format. Now delete the overrides
2128 // (can't fail).
2129 if (fSharedNumberFormatters) {
2130 freeSharedNumberFormatters(fSharedNumberFormatters);
2131 fSharedNumberFormatters = NULL;
b75a7d8f 2132 }
3d1f044b
A
2133
2134 // Also re-compute the fast formatters.
2135 UErrorCode localStatus = U_ZERO_ERROR;
2136 initFastNumberFormatters(localStatus);
b75a7d8f
A
2137}
2138
b331163b
A
2139void SimpleDateFormat::adoptNumberFormat(const UnicodeString& fields, NumberFormat *formatToAdopt, UErrorCode &status){
2140 fixNumberFormatForDates(*formatToAdopt);
2141 LocalPointer<NumberFormat> fmt(formatToAdopt);
2142 if (U_FAILURE(status)) {
2143 return;
2144 }
51004dcb 2145
b331163b
A
2146 // We must ensure fSharedNumberFormatters is allocated.
2147 if (fSharedNumberFormatters == NULL) {
2148 fSharedNumberFormatters = allocSharedNumberFormatters();
2149 if (fSharedNumberFormatters == NULL) {
2150 status = U_MEMORY_ALLOCATION_ERROR;
2151 return;
2152 }
2153 }
2154 const SharedNumberFormat *newFormat = createSharedNumberFormat(fmt.orphan());
2155 if (newFormat == NULL) {
2156 status = U_MEMORY_ALLOCATION_ERROR;
2157 return;
2158 }
2159 for (int i=0; i<fields.length(); i++) {
2160 UChar field = fields.charAt(i);
2161 // if the pattern character is unrecognized, signal an error and bail out
2162 UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(field);
2163 if (patternCharIndex == UDAT_FIELD_COUNT) {
2164 status = U_INVALID_FORMAT_ERROR;
2165 newFormat->deleteIfZeroRefCount();
51004dcb
A
2166 return;
2167 }
b331163b
A
2168
2169 // Set the number formatter in the table
2170 SharedObject::copyPtr(
2171 newFormat, fSharedNumberFormatters[patternCharIndex]);
51004dcb 2172 }
b331163b
A
2173 newFormat->deleteIfZeroRefCount();
2174}
2175
2176const NumberFormat *
2177SimpleDateFormat::getNumberFormatForField(UChar field) const {
2178 UDateFormatField index = DateFormatSymbols::getPatternCharIndex(field);
2179 if (index == UDAT_FIELD_COUNT) {
2180 return NULL;
2181 }
2182 return getNumberFormatByIndex(index);
2183}
2184
2185//----------------------------------------------------------------------
2186void
2187SimpleDateFormat::zeroPaddingNumber(
3d1f044b 2188 const NumberFormat *currentNumberFormat,
b331163b
A
2189 UnicodeString &appendTo,
2190 int32_t value, int32_t minDigits, int32_t maxDigits) const
2191{
3d1f044b
A
2192 const number::LocalizedNumberFormatter* fastFormatter = nullptr;
2193 // NOTE: This uses the heuristic that these five min/max int settings account for the vast majority
2194 // of SimpleDateFormat number formatting cases at the time of writing (ICU 62).
2195 if (currentNumberFormat == fNumberFormat) {
2196 if (maxDigits == 10) {
2197 if (minDigits == 1) {
2198 fastFormatter = fFastNumberFormatters[SMPDTFMT_NF_1x10];
2199 } else if (minDigits == 2) {
2200 fastFormatter = fFastNumberFormatters[SMPDTFMT_NF_2x10];
2201 } else if (minDigits == 3) {
2202 fastFormatter = fFastNumberFormatters[SMPDTFMT_NF_3x10];
2203 } else if (minDigits == 4) {
2204 fastFormatter = fFastNumberFormatters[SMPDTFMT_NF_4x10];
2205 }
2206 } else if (maxDigits == 2) {
2207 if (minDigits == 2) {
2208 fastFormatter = fFastNumberFormatters[SMPDTFMT_NF_2x2];
2209 }
2210 }
2211 }
2212 if (fastFormatter != nullptr) {
2213 // Can use fast path
2214 number::impl::UFormattedNumberData result;
2215 result.quantity.setToInt(value);
2216 UErrorCode localStatus = U_ZERO_ERROR;
2217 fastFormatter->formatImpl(&result, localStatus);
2218 if (U_FAILURE(localStatus)) {
2219 return;
2220 }
2221 appendTo.append(result.getStringRef().toTempUnicodeString());
2222 return;
2223 }
2224
2225 // Check for RBNF (no clone necessary)
2226 auto* rbnf = dynamic_cast<const RuleBasedNumberFormat*>(currentNumberFormat);
2227 if (rbnf != nullptr) {
f3c0d7a5 2228 FieldPosition pos(FieldPosition::DONT_CARE);
3d1f044b
A
2229 rbnf->format(value, appendTo, pos); // 3rd arg is there to speed up processing
2230 return;
2231 }
b75a7d8f 2232
3d1f044b
A
2233 // Fall back to slow path (clone and mutate the NumberFormat)
2234 if (currentNumberFormat != nullptr) {
2235 FieldPosition pos(FieldPosition::DONT_CARE);
2236 LocalPointer<NumberFormat> nf(dynamic_cast<NumberFormat*>(currentNumberFormat->clone()));
2237 nf->setMinimumIntegerDigits(minDigits);
2238 nf->setMaximumIntegerDigits(maxDigits);
2239 nf->format(value, appendTo, pos); // 3rd arg is there to speed up processing
46f4442e 2240 }
b75a7d8f
A
2241}
2242
2243//----------------------------------------------------------------------
2244
b75a7d8f
A
2245/**
2246 * Return true if the given format character, occuring count
2247 * times, represents a numeric field.
2248 */
2249UBool SimpleDateFormat::isNumeric(UChar formatChar, int32_t count) {
51004dcb
A
2250 return DateFormatSymbols::isNumericPatternChar(formatChar, count);
2251}
2252
2253UBool
2254SimpleDateFormat::isAtNumericField(const UnicodeString &pattern, int32_t patternOffset) {
2255 if (patternOffset >= pattern.length()) {
2256 // not at any field
2257 return FALSE;
2258 }
2259 UChar ch = pattern.charAt(patternOffset);
2260 UDateFormatField f = DateFormatSymbols::getPatternCharIndex(ch);
2261 if (f == UDAT_FIELD_COUNT) {
2262 // not at any field
2263 return FALSE;
2264 }
2265 int32_t i = patternOffset;
2266 while (pattern.charAt(++i) == ch) {}
2267 return DateFormatSymbols::isNumericField(f, i - patternOffset);
2268}
2269
2270UBool
2271SimpleDateFormat::isAfterNonNumericField(const UnicodeString &pattern, int32_t patternOffset) {
2272 if (patternOffset <= 0) {
2273 // not after any field
2274 return FALSE;
2275 }
2276 UChar ch = pattern.charAt(--patternOffset);
2277 UDateFormatField f = DateFormatSymbols::getPatternCharIndex(ch);
2278 if (f == UDAT_FIELD_COUNT) {
2279 // not after any field
2280 return FALSE;
2281 }
2282 int32_t i = patternOffset;
2283 while (pattern.charAt(--i) == ch) {}
2284 return !DateFormatSymbols::isNumericField(f, patternOffset - i);
b75a7d8f
A
2285}
2286
2287void
2288SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& parsePos) const
2289{
46f4442e 2290 UErrorCode status = U_ZERO_ERROR;
b75a7d8f 2291 int32_t pos = parsePos.getIndex();
b331163b
A
2292 if(parsePos.getIndex() < 0) {
2293 parsePos.setErrorIndex(0);
2294 return;
2295 }
b75a7d8f 2296 int32_t start = pos;
729e4ab9 2297
2ca993e8
A
2298 // Hold the day period until everything else is parsed, because we need
2299 // the hour to interpret time correctly.
2300 int32_t dayPeriodInt = -1;
b331163b 2301
b75a7d8f 2302 UBool ambiguousYear[] = { FALSE };
729e4ab9 2303 int32_t saveHebrewMonth = -1;
b75a7d8f 2304 int32_t count = 0;
57a6839d 2305 UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
73c04bcf 2306
b75a7d8f
A
2307 // For parsing abutting numeric fields. 'abutPat' is the
2308 // offset into 'pattern' of the first of 2 or more abutting
2309 // numeric fields. 'abutStart' is the offset into 'text'
2310 // where parsing the fields begins. 'abutPass' starts off as 0
2311 // and increments each time we try to parse the fields.
2312 int32_t abutPat = -1; // If >=0, we are in a run of abutting numeric fields
2313 int32_t abutStart = 0;
2314 int32_t abutPass = 0;
2315 UBool inQuote = FALSE;
2316
4388f060 2317 MessageFormat * numericLeapMonthFormatter = NULL;
b75a7d8f 2318
4388f060 2319 Calendar* calClone = NULL;
729e4ab9
A
2320 Calendar *workCal = &cal;
2321 if (&cal != fCalendar && uprv_strcmp(cal.getType(), fCalendar->getType()) != 0) {
2322 // Different calendar type
2323 // We use the time/zone from the input calendar, but
2324 // do not use the input calendar for field calculation.
4388f060
A
2325 calClone = fCalendar->clone();
2326 if (calClone != NULL) {
2327 calClone->setTime(cal.getTime(status),status);
2328 if (U_FAILURE(status)) {
2329 goto ExitParse;
2330 }
2331 calClone->setTimeZone(cal.getTimeZone());
2332 workCal = calClone;
2333 } else {
2334 status = U_MEMORY_ALLOCATION_ERROR;
729e4ab9
A
2335 goto ExitParse;
2336 }
4388f060 2337 }
2ca993e8 2338
4388f060
A
2339 if (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) {
2340 numericLeapMonthFormatter = new MessageFormat(fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternNumeric], fLocale, status);
2341 if (numericLeapMonthFormatter == NULL) {
2342 status = U_MEMORY_ALLOCATION_ERROR;
2343 goto ExitParse;
2344 } else if (U_FAILURE(status)) {
2345 goto ExitParse; // this will delete numericLeapMonthFormatter
2346 }
729e4ab9
A
2347 }
2348
b75a7d8f
A
2349 for (int32_t i=0; i<fPattern.length(); ++i) {
2350 UChar ch = fPattern.charAt(i);
2351
2352 // Handle alphabetic field characters.
b331163b 2353 if (!inQuote && isSyntaxChar(ch)) {
b75a7d8f
A
2354 int32_t fieldPat = i;
2355
2356 // Count the length of this field specifier
2357 count = 1;
2358 while ((i+1)<fPattern.length() &&
2359 fPattern.charAt(i+1) == ch) {
2360 ++count;
2361 ++i;
2362 }
2363
2364 if (isNumeric(ch, count)) {
2365 if (abutPat < 0) {
51004dcb
A
2366 // Determine if there is an abutting numeric field.
2367 // Record the start of a set of abutting numeric fields.
2368 if (isAtNumericField(fPattern, i + 1)) {
2369 abutPat = fieldPat;
2370 abutStart = pos;
2371 abutPass = 0;
b75a7d8f
A
2372 }
2373 }
2374 } else {
2375 abutPat = -1; // End of any abutting fields
2376 }
2377
2378 // Handle fields within a run of abutting numeric fields. Take
2379 // the pattern "HHmmss" as an example. We will try to parse
2380 // 2/2/2 characters of the input text, then if that fails,
2381 // 1/2/2. We only adjust the width of the leftmost field; the
2382 // others remain fixed. This allows "123456" => 12:34:56, but
2383 // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
2384 // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
2385 if (abutPat >= 0) {
2386 // If we are at the start of a run of abutting fields, then
2387 // shorten this field in each pass. If we can't shorten
2388 // this field any more, then the parse of this set of
2389 // abutting numeric fields has failed.
2390 if (fieldPat == abutPat) {
2391 count -= abutPass++;
2392 if (count == 0) {
729e4ab9
A
2393 status = U_PARSE_ERROR;
2394 goto ExitParse;
b75a7d8f
A
2395 }
2396 }
2397
2398 pos = subParse(text, pos, ch, count,
3d1f044b 2399 TRUE, FALSE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType);
b75a7d8f
A
2400
2401 // If the parse fails anywhere in the run, back up to the
2402 // start of the run and retry.
2403 if (pos < 0) {
2404 i = abutPat - 1;
2405 pos = abutStart;
2406 continue;
2407 }
2408 }
2409
2410 // Handle non-numeric fields and non-abutting numeric
2411 // fields.
4388f060 2412 else if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored
46f4442e 2413 int32_t s = subParse(text, pos, ch, count,
3d1f044b 2414 FALSE, TRUE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType, &dayPeriodInt);
729e4ab9
A
2415
2416 if (s == -pos-1) {
2417 // era not present, in special cases allow this to continue
51004dcb
A
2418 // from the position where the era was expected
2419 s = pos;
729e4ab9
A
2420
2421 if (i+1 < fPattern.length()) {
2422 // move to next pattern character
3d1f044b 2423 UChar c = fPattern.charAt(i+1);
729e4ab9
A
2424
2425 // check for whitespace
3d1f044b 2426 if (PatternProps::isWhiteSpace(c)) {
729e4ab9
A
2427 i++;
2428 // Advance over run in pattern
2429 while ((i+1)<fPattern.length() &&
4388f060 2430 PatternProps::isWhiteSpace(fPattern.charAt(i+1))) {
729e4ab9
A
2431 ++i;
2432 }
2433 }
2434 }
2435 }
2436 else if (s <= 0) {
46f4442e
A
2437 status = U_PARSE_ERROR;
2438 goto ExitParse;
b75a7d8f 2439 }
46f4442e 2440 pos = s;
b75a7d8f
A
2441 }
2442 }
2443
2444 // Handle literal pattern characters. These are any
2445 // quoted characters and non-alphabetic unquoted
2446 // characters.
2447 else {
729e4ab9 2448
b75a7d8f 2449 abutPat = -1; // End of any abutting fields
2ca993e8
A
2450
2451 if (! matchLiterals(fPattern, i, text, pos, getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status), getBooleanAttribute(UDAT_PARSE_PARTIAL_LITERAL_MATCH, status), isLenient())) {
46f4442e
A
2452 status = U_PARSE_ERROR;
2453 goto ExitParse;
b75a7d8f 2454 }
b75a7d8f
A
2455 }
2456 }
2457
51004dcb 2458 // Special hack for trailing "." after non-numeric field.
57a6839d 2459 if (text.charAt(pos) == 0x2e && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)) {
51004dcb
A
2460 // only do if the last field is not numeric
2461 if (isAfterNonNumericField(fPattern, fPattern.length())) {
2462 pos++; // skip the extra "."
2463 }
2464 }
2465
2ca993e8
A
2466 // If dayPeriod is set, use it in conjunction with hour-of-day to determine am/pm.
2467 if (dayPeriodInt >= 0) {
2468 DayPeriodRules::DayPeriod dayPeriod = (DayPeriodRules::DayPeriod)dayPeriodInt;
2469 const DayPeriodRules *ruleSet = DayPeriodRules::getInstance(this->getSmpFmtLocale(), status);
2470
2471 if (!cal.isSet(UCAL_HOUR) && !cal.isSet(UCAL_HOUR_OF_DAY)) {
2472 // If hour is not set, set time to the midpoint of current day period, overwriting
2473 // minutes if it's set.
2474 double midPoint = ruleSet->getMidPointForDayPeriod(dayPeriod, status);
2475
2476 // If we can't get midPoint we do nothing.
2477 if (U_SUCCESS(status)) {
2478 // Truncate midPoint toward zero to get the hour.
2479 // Any leftover means it was a half-hour.
2480 int32_t midPointHour = (int32_t) midPoint;
2481 int32_t midPointMinute = (midPoint - midPointHour) > 0 ? 30 : 0;
2482
2483 // No need to set am/pm because hour-of-day is set last therefore takes precedence.
2484 cal.set(UCAL_HOUR_OF_DAY, midPointHour);
2485 cal.set(UCAL_MINUTE, midPointMinute);
2486 }
2487 } else {
2488 int hourOfDay;
2489
2490 if (cal.isSet(UCAL_HOUR_OF_DAY)) { // Hour is parsed in 24-hour format.
2491 hourOfDay = cal.get(UCAL_HOUR_OF_DAY, status);
2492 } else { // Hour is parsed in 12-hour format.
2493 hourOfDay = cal.get(UCAL_HOUR, status);
2494 // cal.get() turns 12 to 0 for 12-hour time; change 0 to 12
2495 // so 0 unambiguously means a 24-hour time from above.
2496 if (hourOfDay == 0) { hourOfDay = 12; }
2497 }
2498 U_ASSERT(0 <= hourOfDay && hourOfDay <= 23);
2499
2500
2501 // If hour-of-day is 0 or 13 thru 23 then input time in unambiguously in 24-hour format.
2502 if (hourOfDay == 0 || (13 <= hourOfDay && hourOfDay <= 23)) {
2503 // Make hour-of-day take precedence over (hour + am/pm) by setting it again.
2504 cal.set(UCAL_HOUR_OF_DAY, hourOfDay);
2505 } else {
2506 // We have a 12-hour time and need to choose between am and pm.
2507 // Behave as if dayPeriod spanned 6 hours each way from its center point.
2508 // This will parse correctly for consistent time + period (e.g. 10 at night) as
2509 // well as provide a reasonable recovery for inconsistent time + period (e.g.
2510 // 9 in the afternoon).
2511
2512 // Assume current time is in the AM.
2513 // - Change 12 back to 0 for easier handling of 12am.
2514 // - Append minutes as fractional hours because e.g. 8:15 and 8:45 could be parsed
2515 // into different half-days if center of dayPeriod is at 14:30.
2516 // - cal.get(MINUTE) will return 0 if MINUTE is unset, which works.
2517 if (hourOfDay == 12) { hourOfDay = 0; }
2518 double currentHour = hourOfDay + (cal.get(UCAL_MINUTE, status)) / 60.0;
2519 double midPointHour = ruleSet->getMidPointForDayPeriod(dayPeriod, status);
2520
2521 if (U_SUCCESS(status)) {
2522 double hoursAheadMidPoint = currentHour - midPointHour;
2523
2524 // Assume current time is in the AM.
2525 if (-6 <= hoursAheadMidPoint && hoursAheadMidPoint < 6) {
2526 // Assumption holds; set time as such.
2527 cal.set(UCAL_AM_PM, 0);
2528 } else {
2529 cal.set(UCAL_AM_PM, 1);
2530 }
2531 }
2532 }
2533 }
2534 }
2535
b75a7d8f
A
2536 // At this point the fields of Calendar have been set. Calendar
2537 // will fill in default values for missing fields when the time
2538 // is computed.
2539
2540 parsePos.setIndex(pos);
2541
2542 // This part is a problem: When we call parsedDate.after, we compute the time.
2543 // Take the date April 3 2004 at 2:30 am. When this is first set up, the year
2544 // will be wrong if we're parsing a 2-digit year pattern. It will be 1904.
2545 // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am
2546 // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
2547 // on that day. It is therefore parsed out to fields as 3:30 am. Then we
2548 // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is
2549 // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
2550 /*
2551 UDate parsedDate = calendar.getTime();
2552 if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
2553 calendar.add(Calendar.YEAR, 100);
2554 parsedDate = calendar.getTime();
2555 }
2556 */
2557 // Because of the above condition, save off the fields in case we need to readjust.
2558 // The procedure we use here is not particularly efficient, but there is no other
2559 // way to do this given the API restrictions present in Calendar. We minimize
2560 // inefficiency by only performing this computation when it might apply, that is,
2561 // when the two-digit year is equal to the start year, and thus might fall at the
2562 // front or the back of the default century. This only works because we adjust
2563 // the year correctly to start with in other cases -- see subParse().
57a6839d 2564 if (ambiguousYear[0] || tzTimeType != UTZFMT_TIME_TYPE_UNKNOWN) // If this is true then the two-digit year == the default start year
b75a7d8f
A
2565 {
2566 // We need a copy of the fields, and we need to avoid triggering a call to
2567 // complete(), which will recalculate the fields. Since we can't access
2568 // the fields[] array in Calendar, we clone the entire object. This will
2569 // stop working if Calendar.clone() is ever rewritten to call complete().
46f4442e 2570 Calendar *copy;
73c04bcf 2571 if (ambiguousYear[0]) {
46f4442e
A
2572 copy = cal.clone();
2573 // Check for failed cloning.
2574 if (copy == NULL) {
729e4ab9
A
2575 status = U_MEMORY_ALLOCATION_ERROR;
2576 goto ExitParse;
46f4442e 2577 }
73c04bcf
A
2578 UDate parsedDate = copy->getTime(status);
2579 // {sfb} check internalGetDefaultCenturyStart
2580 if (fHaveDefaultCentury && (parsedDate < fDefaultCenturyStart)) {
2581 // We can't use add here because that does a complete() first.
2582 cal.set(UCAL_YEAR, fDefaultCenturyStartYear + 100);
2583 }
46f4442e 2584 delete copy;
b75a7d8f 2585 }
73c04bcf 2586
57a6839d 2587 if (tzTimeType != UTZFMT_TIME_TYPE_UNKNOWN) {
46f4442e
A
2588 copy = cal.clone();
2589 // Check for failed cloning.
2590 if (copy == NULL) {
729e4ab9
A
2591 status = U_MEMORY_ALLOCATION_ERROR;
2592 goto ExitParse;
46f4442e
A
2593 }
2594 const TimeZone & tz = cal.getTimeZone();
2595 BasicTimeZone *btz = NULL;
2596
729e4ab9
A
2597 if (dynamic_cast<const OlsonTimeZone *>(&tz) != NULL
2598 || dynamic_cast<const SimpleTimeZone *>(&tz) != NULL
2599 || dynamic_cast<const RuleBasedTimeZone *>(&tz) != NULL
2600 || dynamic_cast<const VTimeZone *>(&tz) != NULL) {
46f4442e
A
2601 btz = (BasicTimeZone*)&tz;
2602 }
73c04bcf 2603
46f4442e
A
2604 // Get local millis
2605 copy->set(UCAL_ZONE_OFFSET, 0);
2606 copy->set(UCAL_DST_OFFSET, 0);
2607 UDate localMillis = copy->getTime(status);
2608
2609 // Make sure parsed time zone type (Standard or Daylight)
2610 // matches the rule used by the parsed time zone.
2611 int32_t raw, dst;
2612 if (btz != NULL) {
57a6839d 2613 if (tzTimeType == UTZFMT_TIME_TYPE_STANDARD) {
46f4442e
A
2614 btz->getOffsetFromLocal(localMillis,
2615 BasicTimeZone::kStandard, BasicTimeZone::kStandard, raw, dst, status);
2616 } else {
2617 btz->getOffsetFromLocal(localMillis,
2618 BasicTimeZone::kDaylight, BasicTimeZone::kDaylight, raw, dst, status);
2619 }
2620 } else {
2621 // No good way to resolve ambiguous time at transition,
2622 // but following code work in most case.
2623 tz.getOffset(localMillis, TRUE, raw, dst, status);
73c04bcf 2624 }
73c04bcf 2625
46f4442e
A
2626 // Now, compare the results with parsed type, either standard or daylight saving time
2627 int32_t resolvedSavings = dst;
57a6839d 2628 if (tzTimeType == UTZFMT_TIME_TYPE_STANDARD) {
46f4442e
A
2629 if (dst != 0) {
2630 // Override DST_OFFSET = 0 in the result calendar
2631 resolvedSavings = 0;
2632 }
2633 } else { // tztype == TZTYPE_DST
2634 if (dst == 0) {
2635 if (btz != NULL) {
2636 UDate time = localMillis + raw;
2637 // We use the nearest daylight saving time rule.
2638 TimeZoneTransition beforeTrs, afterTrs;
2639 UDate beforeT = time, afterT = time;
2640 int32_t beforeSav = 0, afterSav = 0;
2641 UBool beforeTrsAvail, afterTrsAvail;
2642
2643 // Search for DST rule before or on the time
2644 while (TRUE) {
2645 beforeTrsAvail = btz->getPreviousTransition(beforeT, TRUE, beforeTrs);
2646 if (!beforeTrsAvail) {
2647 break;
2648 }
2649 beforeT = beforeTrs.getTime() - 1;
2650 beforeSav = beforeTrs.getFrom()->getDSTSavings();
2651 if (beforeSav != 0) {
2652 break;
2653 }
2654 }
b75a7d8f 2655
46f4442e
A
2656 // Search for DST rule after the time
2657 while (TRUE) {
2658 afterTrsAvail = btz->getNextTransition(afterT, FALSE, afterTrs);
2659 if (!afterTrsAvail) {
2660 break;
2661 }
2662 afterT = afterTrs.getTime();
2663 afterSav = afterTrs.getTo()->getDSTSavings();
2664 if (afterSav != 0) {
2665 break;
2666 }
2667 }
2668
2669 if (beforeTrsAvail && afterTrsAvail) {
2670 if (time - beforeT > afterT - time) {
2671 resolvedSavings = afterSav;
2672 } else {
2673 resolvedSavings = beforeSav;
2674 }
2675 } else if (beforeTrsAvail && beforeSav != 0) {
2676 resolvedSavings = beforeSav;
2677 } else if (afterTrsAvail && afterSav != 0) {
2678 resolvedSavings = afterSav;
2679 } else {
2680 resolvedSavings = btz->getDSTSavings();
2681 }
2682 } else {
2683 resolvedSavings = tz.getDSTSavings();
2684 }
2685 if (resolvedSavings == 0) {
2686 // final fallback
2687 resolvedSavings = U_MILLIS_PER_HOUR;
2688 }
2689 }
2690 }
2691 cal.set(UCAL_ZONE_OFFSET, raw);
2692 cal.set(UCAL_DST_OFFSET, resolvedSavings);
2693 delete copy;
2694 }
2695 }
729e4ab9
A
2696ExitParse:
2697 // Set the parsed result if local calendar is used
2698 // instead of the input calendar
2699 if (U_SUCCESS(status) && workCal != &cal) {
2700 cal.setTimeZone(workCal->getTimeZone());
2701 cal.setTime(workCal->getTime(status), status);
2702 }
2703
4388f060
A
2704 if (numericLeapMonthFormatter != NULL) {
2705 delete numericLeapMonthFormatter;
2706 }
2707 if (calClone != NULL) {
2708 delete calClone;
729e4ab9
A
2709 }
2710
b75a7d8f
A
2711 // If any Calendar calls failed, we pretend that we
2712 // couldn't parse the string, when in reality this isn't quite accurate--
2713 // we did parse it; the Calendar calls just failed.
729e4ab9 2714 if (U_FAILURE(status)) {
b75a7d8f 2715 parsePos.setErrorIndex(pos);
729e4ab9 2716 parsePos.setIndex(start);
b75a7d8f
A
2717 }
2718}
2719
b75a7d8f
A
2720//----------------------------------------------------------------------
2721
b331163b
A
2722static int32_t
2723matchStringWithOptionalDot(const UnicodeString &text,
2724 int32_t index,
2725 const UnicodeString &data);
51004dcb 2726
73c04bcf
A
2727int32_t SimpleDateFormat::matchQuarterString(const UnicodeString& text,
2728 int32_t start,
2729 UCalendarDateFields field,
2730 const UnicodeString* data,
2731 int32_t dataCount,
2732 Calendar& cal) const
2733{
2734 int32_t i = 0;
2735 int32_t count = dataCount;
2736
2737 // There may be multiple strings in the data[] array which begin with
2738 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2739 // We keep track of the longest match, and return that. Note that this
2740 // unfortunately requires us to test all array elements.
2741 int32_t bestMatchLength = 0, bestMatch = -1;
51004dcb 2742 UnicodeString bestMatchName;
73c04bcf 2743
b331163b
A
2744 for (; i < count; ++i) {
2745 int32_t matchLength = 0;
2746 if ((matchLength = matchStringWithOptionalDot(text, start, data[i])) > bestMatchLength) {
2747 bestMatchLength = matchLength;
73c04bcf 2748 bestMatch = i;
73c04bcf
A
2749 }
2750 }
73c04bcf 2751
b331163b
A
2752 if (bestMatch >= 0) {
2753 cal.set(field, bestMatch * 3);
2754 return start + bestMatchLength;
73c04bcf 2755 }
729e4ab9 2756
73c04bcf
A
2757 return -start;
2758}
2759
2ca993e8
A
2760int32_t SimpleDateFormat::matchDayPeriodStrings(const UnicodeString& text, int32_t start,
2761 const UnicodeString* data, int32_t dataCount,
2762 int32_t &dayPeriod) const
2763{
2764
2765 int32_t bestMatchLength = 0, bestMatch = -1;
2766
2767 for (int32_t i = 0; i < dataCount; ++i) {
2768 int32_t matchLength = 0;
2769 if ((matchLength = matchStringWithOptionalDot(text, start, data[i])) > bestMatchLength) {
2770 bestMatchLength = matchLength;
2771 bestMatch = i;
2772 }
2773 }
2774
2775 if (bestMatch >= 0) {
2776 dayPeriod = bestMatch;
2777 return start + bestMatchLength;
2778 }
2779
2780 return -start;
2781}
2782
46f4442e 2783//----------------------------------------------------------------------
08b89b0a
A
2784#define IS_BIDI_MARK(c) (c==0x200E || c==0x200F || c==0x061C)
2785
46f4442e
A
2786UBool SimpleDateFormat::matchLiterals(const UnicodeString &pattern,
2787 int32_t &patternOffset,
2788 const UnicodeString &text,
2789 int32_t &textOffset,
57a6839d 2790 UBool whitespaceLenient,
b331163b
A
2791 UBool partialMatchLenient,
2792 UBool oldLeniency)
46f4442e
A
2793{
2794 UBool inQuote = FALSE;
2ca993e8 2795 UnicodeString literal;
46f4442e 2796 int32_t i = patternOffset;
b331163b 2797
46f4442e
A
2798 // scan pattern looking for contiguous literal characters
2799 for ( ; i < pattern.length(); i += 1) {
2800 UChar ch = pattern.charAt(i);
2ca993e8 2801
b331163b 2802 if (!inQuote && isSyntaxChar(ch)) {
46f4442e
A
2803 break;
2804 }
2ca993e8 2805
46f4442e
A
2806 if (ch == QUOTE) {
2807 // Match a quote literal ('') inside OR outside of quotes
2808 if ((i + 1) < pattern.length() && pattern.charAt(i + 1) == QUOTE) {
2809 i += 1;
2810 } else {
2811 inQuote = !inQuote;
2812 continue;
2813 }
2814 }
2ca993e8 2815
08b89b0a
A
2816 if (!IS_BIDI_MARK(ch)) {
2817 literal += ch;
2818 }
46f4442e 2819 }
2ca993e8 2820
08b89b0a 2821 // at this point, literal contains the pattern literal text (without bidi marks)
46f4442e
A
2822 // and i is the index of the next non-literal pattern character.
2823 int32_t p;
2824 int32_t t = textOffset;
2ca993e8 2825
57a6839d 2826 if (whitespaceLenient) {
08b89b0a 2827 // trim leading, trailing whitespace from the pattern literal
46f4442e 2828 literal.trim();
2ca993e8 2829
08b89b0a
A
2830 // ignore any leading whitespace (or bidi marks) in the text
2831 while (t < text.length()) {
2832 UChar ch = text.charAt(t);
2833 if (!u_isWhitespace(ch) && !IS_BIDI_MARK(ch)) {
2834 break;
2835 }
46f4442e
A
2836 t += 1;
2837 }
2838 }
2ca993e8 2839
b331163b
A
2840 // Get ignorables, move up here
2841 const UnicodeSet *ignorables = NULL;
2842 UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(pattern.charAt(i));
2843 if (patternCharIndex != UDAT_FIELD_COUNT) {
2844 ignorables = SimpleDateFormatStaticSets::getIgnorables(patternCharIndex);
2845 }
2846
51004dcb 2847 for (p = 0; p < literal.length() && t < text.length();) {
46f4442e 2848 UBool needWhitespace = FALSE;
2ca993e8 2849
08b89b0a
A
2850 // Skip any whitespace at current position in pattern,
2851 // but remember whether we found whitespace in the pattern
2852 // (we already deleted any bidi marks in the pattern).
4388f060 2853 while (p < literal.length() && PatternProps::isWhiteSpace(literal.charAt(p))) {
46f4442e
A
2854 needWhitespace = TRUE;
2855 p += 1;
2856 }
2ca993e8 2857
08b89b0a
A
2858 // If the pattern has whitespace at this point, skip it in text as well
2859 // (if the text does not have any, that may be an error for strict parsing)
46f4442e 2860 if (needWhitespace) {
08b89b0a 2861 UBool whitespaceInText = FALSE;
46f4442e 2862
08b89b0a
A
2863 // Skip any whitespace (or bidi marks) at current position in text,
2864 // but remember whether we found whitespace in the text at this point.
46f4442e
A
2865 while (t < text.length()) {
2866 UChar tch = text.charAt(t);
08b89b0a
A
2867 if (u_isUWhiteSpace(tch) || PatternProps::isWhiteSpace(tch)) {
2868 whitespaceInText = TRUE;
2869 } else if (!IS_BIDI_MARK(tch)) {
46f4442e
A
2870 break;
2871 }
2ca993e8 2872
46f4442e
A
2873 t += 1;
2874 }
2ca993e8 2875
46f4442e
A
2876 // TODO: should we require internal spaces
2877 // in lenient mode? (There won't be any
2878 // leading or trailing spaces)
08b89b0a 2879 if (!whitespaceLenient && !whitespaceInText) {
46f4442e
A
2880 // didn't find matching whitespace:
2881 // an error in strict mode
2882 return FALSE;
2883 }
2ca993e8 2884
46f4442e
A
2885 // In strict mode, this run of whitespace
2886 // may have been at the end.
2887 if (p >= literal.length()) {
2888 break;
2889 }
08b89b0a
A
2890 } else {
2891 // Still need to skip any bidi marks in the text
2892 while (t < text.length() && IS_BIDI_MARK(text.charAt(t))) {
2893 ++t;
2894 }
46f4442e 2895 }
46f4442e
A
2896 if (t >= text.length() || literal.charAt(p) != text.charAt(t)) {
2897 // Ran out of text, or found a non-matching character:
2898 // OK in lenient mode, an error in strict mode.
57a6839d 2899 if (whitespaceLenient) {
51004dcb
A
2900 if (t == textOffset && text.charAt(t) == 0x2e &&
2901 isAfterNonNumericField(pattern, patternOffset)) {
2902 // Lenient mode and the literal input text begins with a "." and
2903 // we are after a non-numeric field: We skip the "."
2904 ++t;
2905 continue; // Do not update p.
2906 }
2ca993e8
A
2907 // if it is actual whitespace and we're whitespace lenient it's OK
2908
57a6839d 2909 UChar wsc = text.charAt(t);
b331163b
A
2910 if(PatternProps::isWhiteSpace(wsc)) {
2911 // Lenient mode and it's just whitespace we skip it
2912 ++t;
2913 continue; // Do not update p.
2914 }
2ca993e8 2915 }
b331163b
A
2916 // hack around oldleniency being a bit of a catch-all bucket and we're just adding support specifically for paritial matches
2917 // This fix is for http://bugs.icu-project.org/trac/ticket/10855 and adds "&& oldLeniency"
2918 //if(partialMatchLenient && oldLeniency) {
2919 // However this causes problems for Apple, see <rdar://problem/20692829> regressions in Chinese date parsing
2920 // We don't want to go back to just "if(partialMatchLenient)" as in ICU 53, that is too lenient for strict mode.
2921 // So if the pattern character is in the separator set, we allow the text character to be in that set or be an alpha char.
2922 if( partialMatchLenient && ( oldLeniency ||
2923 ( ignorables != NULL && ignorables->contains(literal.charAt(p)) && (ignorables->contains(text.charAt(t)) || u_isalpha(text.charAt(t))) ) )
2924 ) {
46f4442e
A
2925 break;
2926 }
2ca993e8 2927
46f4442e
A
2928 return FALSE;
2929 }
51004dcb
A
2930 ++p;
2931 ++t;
46f4442e 2932 }
2ca993e8 2933
46f4442e
A
2934 // At this point if we're in strict mode we have a complete match.
2935 // If we're in lenient mode we may have a partial match, or no
2936 // match at all.
2937 if (p <= 0) {
2938 // no match. Pretend it matched a run of whitespace
2939 // and ignorables in the text.
2ca993e8 2940
46f4442e
A
2941 for (t = textOffset; t < text.length(); t += 1) {
2942 UChar ch = text.charAt(t);
2ca993e8 2943
08b89b0a 2944 if (!IS_BIDI_MARK(ch) && (ignorables == NULL || !ignorables->contains(ch))) {
46f4442e
A
2945 break;
2946 }
2947 }
2948 }
2ca993e8 2949
46f4442e
A
2950 // if we get here, we've got a complete match.
2951 patternOffset = i - 1;
2952 textOffset = t;
2ca993e8 2953
46f4442e
A
2954 return TRUE;
2955}
2956
73c04bcf
A
2957//----------------------------------------------------------------------
2958
b75a7d8f
A
2959int32_t SimpleDateFormat::matchString(const UnicodeString& text,
2960 int32_t start,
2961 UCalendarDateFields field,
2962 const UnicodeString* data,
2963 int32_t dataCount,
4388f060 2964 const UnicodeString* monthPattern,
b75a7d8f
A
2965 Calendar& cal) const
2966{
2967 int32_t i = 0;
2968 int32_t count = dataCount;
2969
2970 if (field == UCAL_DAY_OF_WEEK) i = 1;
2971
2972 // There may be multiple strings in the data[] array which begin with
2973 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2974 // We keep track of the longest match, and return that. Note that this
2975 // unfortunately requires us to test all array elements.
2976 int32_t bestMatchLength = 0, bestMatch = -1;
4388f060
A
2977 UnicodeString bestMatchName;
2978 int32_t isLeapMonth = 0;
b75a7d8f 2979
b331163b
A
2980 for (; i < count; ++i) {
2981 int32_t matchLen = 0;
2982 if ((matchLen = matchStringWithOptionalDot(text, start, data[i])) > bestMatchLength) {
b75a7d8f 2983 bestMatch = i;
b331163b 2984 bestMatchLength = matchLen;
4388f060
A
2985 }
2986
2987 if (monthPattern != NULL) {
2988 UErrorCode status = U_ZERO_ERROR;
2989 UnicodeString leapMonthName;
2ca993e8 2990 SimpleFormatter(*monthPattern, 1, 1, status).format(data[i], leapMonthName, status);
4388f060 2991 if (U_SUCCESS(status)) {
b331163b 2992 if ((matchLen = matchStringWithOptionalDot(text, start, leapMonthName)) > bestMatchLength) {
4388f060 2993 bestMatch = i;
b331163b 2994 bestMatchLength = matchLen;
4388f060
A
2995 isLeapMonth = 1;
2996 }
2997 }
b75a7d8f
A
2998 }
2999 }
374ca955 3000
b331163b
A
3001 if (bestMatch >= 0) {
3002 if (field < UCAL_FIELD_COUNT) {
3003 // Adjustment for Hebrew Calendar month Adar II
3004 if (!strcmp(cal.getType(),"hebrew") && field==UCAL_MONTH && bestMatch==13) {
3005 cal.set(field,6);
3006 } else {
3007 if (field == UCAL_YEAR) {
3008 bestMatch++; // only get here for cyclic year names, which match 1-based years 1-60
3009 }
3010 cal.set(field, bestMatch);
374ca955 3011 }
b331163b
A
3012 if (monthPattern != NULL) {
3013 cal.set(UCAL_IS_LEAP_MONTH, isLeapMonth);
374ca955
A
3014 }
3015 }
b331163b
A
3016
3017 return start + bestMatchLength;
b75a7d8f 3018 }
729e4ab9 3019
b75a7d8f
A
3020 return -start;
3021}
3022
b331163b
A
3023static int32_t
3024matchStringWithOptionalDot(const UnicodeString &text,
3025 int32_t index,
3026 const UnicodeString &data) {
3027 UErrorCode sts = U_ZERO_ERROR;
3028 int32_t matchLenText = 0;
3029 int32_t matchLenData = 0;
51004dcb 3030
b331163b
A
3031 u_caseInsensitivePrefixMatch(text.getBuffer() + index, text.length() - index,
3032 data.getBuffer(), data.length(),
3033 0 /* default case option */,
3034 &matchLenText, &matchLenData,
3035 &sts);
3036 U_ASSERT (U_SUCCESS(sts));
3037
3038 if (matchLenData == data.length() /* normal match */
3039 || (data.charAt(data.length() - 1) == 0x2e
3040 && matchLenData == data.length() - 1 /* match without trailing dot */)) {
3041 return matchLenText;
51004dcb 3042 }
b331163b
A
3043
3044 return 0;
51004dcb
A
3045}
3046
b75a7d8f
A
3047//----------------------------------------------------------------------
3048
3049void
3050SimpleDateFormat::set2DigitYearStart(UDate d, UErrorCode& status)
3051{
3052 parseAmbiguousDatesAsAfter(d, status);
3053}
3054
3055/**
3056 * Private member function that converts the parsed date strings into
3057 * timeFields. Returns -start (for ParsePosition) if failed.
b75a7d8f
A
3058 */
3059int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UChar ch, int32_t count,
729e4ab9 3060 UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], int32_t& saveHebrewMonth, Calendar& cal,
3d1f044b 3061 int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType,
2ca993e8 3062 int32_t *dayPeriod) const
b75a7d8f
A
3063{
3064 Formattable number;
3065 int32_t value = 0;
3066 int32_t i;
729e4ab9 3067 int32_t ps = 0;
57a6839d 3068 UErrorCode status = U_ZERO_ERROR;
b75a7d8f 3069 ParsePosition pos(0);
51004dcb 3070 UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch);
3d1f044b 3071 const NumberFormat *currentNumberFormat;
b75a7d8f 3072 UnicodeString temp;
57a6839d 3073 int32_t tzParseOptions = (isLenient())? UTZFMT_PARSE_OPTION_ALL_STYLES: UTZFMT_PARSE_OPTION_NONE;
46f4442e 3074 UBool gotNumber = FALSE;
b75a7d8f 3075
374ca955
A
3076#if defined (U_DEBUG_CAL)
3077 //fprintf(stderr, "%s:%d - [%c] st=%d \n", __FILE__, __LINE__, (char) ch, start);
3078#endif
3079
51004dcb 3080 if (patternCharIndex == UDAT_FIELD_COUNT) {
b75a7d8f
A
3081 return -start;
3082 }
3083
3d1f044b 3084 currentNumberFormat = getNumberFormatByIndex(patternCharIndex);
b331163b
A
3085 if (currentNumberFormat == NULL) {
3086 return -start;
3087 }
3088 UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex]; // UCAL_FIELD_COUNT if irrelevant
4388f060
A
3089 UnicodeString hebr("hebr", 4, US_INV);
3090
3091 if (numericLeapMonthFormatter != NULL) {
3092 numericLeapMonthFormatter->setFormats((const Format **)&currentNumberFormat, 1);
3093 }
57a6839d 3094 UBool isChineseCalendar = (uprv_strcmp(cal.getType(),"chinese") == 0 || uprv_strcmp(cal.getType(),"dangi") == 0);
b75a7d8f
A
3095
3096 // If there are any spaces here, skip over them. If we hit the end
3097 // of the string, then fail.
3098 for (;;) {
3099 if (start >= text.length()) {
3100 return -start;
3101 }
3102 UChar32 c = text.char32At(start);
4388f060 3103 if (!u_isUWhiteSpace(c) /*||*/ && !PatternProps::isWhiteSpace(c)) {
b75a7d8f
A
3104 break;
3105 }
4388f060 3106 start += U16_LENGTH(c);
b75a7d8f
A
3107 }
3108 pos.setIndex(start);
3109
3110 // We handle a few special cases here where we need to parse
3111 // a number value. We handle further, more generic cases below. We need
3112 // to handle some of them here because some fields require extra processing on
3113 // the parsed value.
4388f060
A
3114 if (patternCharIndex == UDAT_HOUR_OF_DAY1_FIELD || // k
3115 patternCharIndex == UDAT_HOUR_OF_DAY0_FIELD || // H
3116 patternCharIndex == UDAT_HOUR1_FIELD || // h
3117 patternCharIndex == UDAT_HOUR0_FIELD || // K
3118 (patternCharIndex == UDAT_DOW_LOCAL_FIELD && count <= 2) || // e
3119 (patternCharIndex == UDAT_STANDALONE_DAY_FIELD && count <= 2) || // c
3120 (patternCharIndex == UDAT_MONTH_FIELD && count <= 2) || // M
3121 (patternCharIndex == UDAT_STANDALONE_MONTH_FIELD && count <= 2) || // L
3122 (patternCharIndex == UDAT_QUARTER_FIELD && count <= 2) || // Q
3123 (patternCharIndex == UDAT_STANDALONE_QUARTER_FIELD && count <= 2) || // q
3124 patternCharIndex == UDAT_YEAR_FIELD || // y
3125 patternCharIndex == UDAT_YEAR_WOY_FIELD || // Y
3126 patternCharIndex == UDAT_YEAR_NAME_FIELD || // U (falls back to numeric)
3127 (patternCharIndex == UDAT_ERA_FIELD && isChineseCalendar) || // G
3128 patternCharIndex == UDAT_FRACTIONAL_SECOND_FIELD) // S
b75a7d8f 3129 {
374ca955 3130 int32_t parseStart = pos.getIndex();
b75a7d8f
A
3131 // It would be good to unify this with the obeyCount logic below,
3132 // but that's going to be difficult.
3133 const UnicodeString* src;
73c04bcf 3134
4388f060
A
3135 UBool parsedNumericLeapMonth = FALSE;
3136 if (numericLeapMonthFormatter != NULL && (patternCharIndex == UDAT_MONTH_FIELD || patternCharIndex == UDAT_STANDALONE_MONTH_FIELD)) {
3137 int32_t argCount;
3138 Formattable * args = numericLeapMonthFormatter->parse(text, pos, argCount);
3139 if (args != NULL && argCount == 1 && pos.getIndex() > parseStart && args[0].isNumeric()) {
3140 parsedNumericLeapMonth = TRUE;
3141 number.setLong(args[0].getLong());
3142 cal.set(UCAL_IS_LEAP_MONTH, 1);
3143 delete[] args;
3144 } else {
3145 pos.setIndex(parseStart);
3146 cal.set(UCAL_IS_LEAP_MONTH, 0);
b75a7d8f 3147 }
b75a7d8f 3148 }
73c04bcf 3149
4388f060
A
3150 if (!parsedNumericLeapMonth) {
3151 if (obeyCount) {
3152 if ((start+count) > text.length()) {
3153 return -start;
3154 }
3155
3156 text.extractBetween(0, start + count, temp);
3157 src = &temp;
3158 } else {
3159 src = &text;
3160 }
3161
3162 parseInt(*src, number, pos, allowNegative,currentNumberFormat);
3163 }
729e4ab9
A
3164
3165 int32_t txtLoc = pos.getIndex();
73c04bcf 3166
729e4ab9 3167 if (txtLoc > parseStart) {
46f4442e
A
3168 value = number.getLong();
3169 gotNumber = TRUE;
2ca993e8 3170
729e4ab9
A
3171 // suffix processing
3172 if (value < 0 ) {
3173 txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, TRUE);
3174 if (txtLoc != pos.getIndex()) {
3175 value *= -1;
3176 }
3177 }
3178 else {
3179 txtLoc = checkIntSuffix(text, txtLoc, patLoc+1, FALSE);
3180 }
4388f060 3181
46f4442e 3182 // Check the range of the value
57a6839d 3183 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)) {
4388f060
A
3184 int32_t bias = gFieldRangeBias[patternCharIndex];
3185 if (bias >= 0 && (value > cal.getMaximum(field) + bias || value < cal.getMinimum(field) + bias)) {
3186 return -start;
3187 }
3188 } else {
3189 int32_t bias = gFieldRangeBiasLenient[patternCharIndex];
3190 if (bias >= 0 && (value > cal.getMaximum(field) + bias)) {
3191 return -start;
3192 }
46f4442e 3193 }
4388f060 3194
729e4ab9
A
3195 pos.setIndex(txtLoc);
3196 }
b75a7d8f 3197 }
2ca993e8 3198
46f4442e
A
3199 // Make sure that we got a number if
3200 // we want one, and didn't get one
3201 // if we don't want one.
3202 switch (patternCharIndex) {
3203 case UDAT_HOUR_OF_DAY1_FIELD:
3204 case UDAT_HOUR_OF_DAY0_FIELD:
3205 case UDAT_HOUR1_FIELD:
3206 case UDAT_HOUR0_FIELD:
3207 // special range check for hours:
3208 if (value < 0 || value > 24) {
3209 return -start;
3210 }
2ca993e8 3211
46f4442e 3212 // fall through to gotNumber check
2ca993e8 3213 U_FALLTHROUGH;
46f4442e
A
3214 case UDAT_YEAR_FIELD:
3215 case UDAT_YEAR_WOY_FIELD:
3216 case UDAT_FRACTIONAL_SECOND_FIELD:
3217 // these must be a number
3218 if (! gotNumber) {
3219 return -start;
3220 }
2ca993e8 3221
46f4442e 3222 break;
2ca993e8 3223
46f4442e
A
3224 default:
3225 // we check the rest of the fields below.
3226 break;
3227 }
729e4ab9 3228
b75a7d8f 3229 switch (patternCharIndex) {
374ca955 3230 case UDAT_ERA_FIELD:
4388f060
A
3231 if (isChineseCalendar) {
3232 if (!gotNumber) {
3233 return -start;
3234 }
3235 cal.set(UCAL_ERA, value);
3236 return pos.getIndex();
3237 }
46f4442e 3238 if (count == 5) {
4388f060 3239 ps = matchString(text, start, UCAL_ERA, fSymbols->fNarrowEras, fSymbols->fNarrowErasCount, NULL, cal);
729e4ab9 3240 } else if (count == 4) {
4388f060 3241 ps = matchString(text, start, UCAL_ERA, fSymbols->fEraNames, fSymbols->fEraNamesCount, NULL, cal);
729e4ab9 3242 } else {
4388f060 3243 ps = matchString(text, start, UCAL_ERA, fSymbols->fEras, fSymbols->fErasCount, NULL, cal);
73c04bcf
A
3244 }
3245
729e4ab9
A
3246 // check return position, if it equals -start, then matchString error
3247 // special case the return code so we don't necessarily fail out until we
3248 // verify no year information also
3249 if (ps == -start)
3250 ps--;
3251
3252 return ps;
73c04bcf 3253
374ca955 3254 case UDAT_YEAR_FIELD:
b75a7d8f
A
3255 // If there are 3 or more YEAR pattern characters, this indicates
3256 // that the year value is to be treated literally, without any
3257 // two-digit year adjustments (e.g., from "01" to 2001). Otherwise
3258 // we made adjustments to place the 2-digit year in the proper
3259 // century, for parsed strings from "00" to "99". Any other string
3260 // is treated literally: "2250", "-1", "1", "002".
4388f060 3261 if (fDateOverride.compare(hebr)==0 && value < 1000) {
51004dcb 3262 value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
0f5d89e8
A
3263 } else if (text.moveIndex32(start, 2) == pos.getIndex() && !isChineseCalendar
3264 && u_isdigit(text.char32At(start))
3265 && u_isdigit(text.char32At(text.moveIndex32(start, 1))))
b75a7d8f 3266 {
57a6839d
A
3267 // only adjust year for patterns less than 3.
3268 if(count < 3) {
3269 // Assume for example that the defaultCenturyStart is 6/18/1903.
3270 // This means that two-digit years will be forced into the range
3271 // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
3272 // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
3273 // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
3274 // other fields specify a date before 6/18, or 1903 if they specify a
3275 // date afterwards. As a result, 03 is an ambiguous year. All other
3276 // two-digit years are unambiguous.
3277 if(fHaveDefaultCentury) { // check if this formatter even has a pivot year
3278 int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
3279 ambiguousYear[0] = (value == ambiguousTwoDigitYear);
3280 value += (fDefaultCenturyStartYear/100)*100 +
3281 (value < ambiguousTwoDigitYear ? 100 : 0);
3282 }
b75a7d8f
A
3283 }
3284 }
3285 cal.set(UCAL_YEAR, value);
729e4ab9
A
3286
3287 // Delayed checking for adjustment of Hebrew month numbers in non-leap years.
3288 if (saveHebrewMonth >= 0) {
3289 HebrewCalendar *hc = (HebrewCalendar*)&cal;
3290 if (!hc->isLeapYear(value) && saveHebrewMonth >= 6) {
3291 cal.set(UCAL_MONTH,saveHebrewMonth);
3292 } else {
3293 cal.set(UCAL_MONTH,saveHebrewMonth-1);
3294 }
3295 saveHebrewMonth = -1;
3296 }
b75a7d8f 3297 return pos.getIndex();
73c04bcf 3298
374ca955
A
3299 case UDAT_YEAR_WOY_FIELD:
3300 // Comment is the same as for UDAT_Year_FIELDs - look above
4388f060 3301 if (fDateOverride.compare(hebr)==0 && value < 1000) {
51004dcb 3302 value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
0f5d89e8
A
3303 } else if (text.moveIndex32(start, 2) == pos.getIndex()
3304 && u_isdigit(text.char32At(start))
3305 && u_isdigit(text.char32At(text.moveIndex32(start, 1)))
b75a7d8f
A
3306 && fHaveDefaultCentury )
3307 {
3308 int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
3309 ambiguousYear[0] = (value == ambiguousTwoDigitYear);
3310 value += (fDefaultCenturyStartYear/100)*100 +
3311 (value < ambiguousTwoDigitYear ? 100 : 0);
3312 }
3313 cal.set(UCAL_YEAR_WOY, value);
3314 return pos.getIndex();
73c04bcf 3315
4388f060
A
3316 case UDAT_YEAR_NAME_FIELD:
3317 if (fSymbols->fShortYearNames != NULL) {
3318 int32_t newStart = matchString(text, start, UCAL_YEAR, fSymbols->fShortYearNames, fSymbols->fShortYearNamesCount, NULL, cal);
3319 if (newStart > 0) {
3320 return newStart;
3321 }
3322 }
57a6839d 3323 if (gotNumber && (getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC,status) || value > fSymbols->fShortYearNamesCount)) {
4388f060
A
3324 cal.set(UCAL_YEAR, value);
3325 return pos.getIndex();
3326 }
3327 return -start;
3328
374ca955 3329 case UDAT_MONTH_FIELD:
4388f060 3330 case UDAT_STANDALONE_MONTH_FIELD:
46f4442e 3331 if (gotNumber) // i.e., M or MM.
b75a7d8f 3332 {
729e4ab9
A
3333 // When parsing month numbers from the Hebrew Calendar, we might need to adjust the month depending on whether
3334 // or not it was a leap year. We may or may not yet know what year it is, so might have to delay checking until
3335 // the year is parsed.
3336 if (!strcmp(cal.getType(),"hebrew")) {
3337 HebrewCalendar *hc = (HebrewCalendar*)&cal;
3338 if (cal.isSet(UCAL_YEAR)) {
3d1f044b
A
3339 UErrorCode monthStatus = U_ZERO_ERROR;
3340 if (!hc->isLeapYear(hc->get(UCAL_YEAR, monthStatus)) && value >= 6) {
729e4ab9
A
3341 cal.set(UCAL_MONTH, value);
3342 } else {
3343 cal.set(UCAL_MONTH, value - 1);
3344 }
3345 } else {
3346 saveHebrewMonth = value;
3347 }
3348 } else {
3349 // Don't want to parse the month if it is a string
4388f060 3350 // while pattern uses numeric style: M/MM, L/LL
729e4ab9
A
3351 // [We computed 'value' above.]
3352 cal.set(UCAL_MONTH, value - 1);
3353 }
b75a7d8f 3354 return pos.getIndex();
73c04bcf 3355 } else {
4388f060 3356 // count >= 3 // i.e., MMM/MMMM, LLL/LLLL
73c04bcf
A
3357 // Want to be able to parse both short and long forms.
3358 // Try count == 4 first:
4388f060
A
3359 UnicodeString * wideMonthPat = NULL;
3360 UnicodeString * shortMonthPat = NULL;
3361 if (fSymbols->fLeapMonthPatterns != NULL && fSymbols->fLeapMonthPatternsCount >= DateFormatSymbols::kMonthPatternsCount) {
3362 if (patternCharIndex==UDAT_MONTH_FIELD) {
3363 wideMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatWide];
3364 shortMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternFormatAbbrev];
3365 } else {
3366 wideMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneWide];
3367 shortMonthPat = &fSymbols->fLeapMonthPatterns[DateFormatSymbols::kLeapMonthPatternStandaloneAbbrev];
3368 }
3369 }
73c04bcf 3370 int32_t newStart = 0;
4388f060 3371 if (patternCharIndex==UDAT_MONTH_FIELD) {
57a6839d
A
3372 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3373 newStart = matchString(text, start, UCAL_MONTH, fSymbols->fMonths, fSymbols->fMonthsCount, wideMonthPat, cal); // try MMMM
3374 if (newStart > 0) {
3375 return newStart;
3376 }
3377 }
3378 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3379 newStart = matchString(text, start, UCAL_MONTH, fSymbols->fShortMonths, fSymbols->fShortMonthsCount, shortMonthPat, cal); // try MMM
4388f060 3380 }
4388f060 3381 } else {
57a6839d
A
3382 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3383 newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount, wideMonthPat, cal); // try LLLL
3384 if (newStart > 0) {
3385 return newStart;
3386 }
3387 }
3388 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3389 newStart = matchString(text, start, UCAL_MONTH, fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount, shortMonthPat, cal); // try LLL
4388f060 3390 }
4388f060 3391 }
57a6839d 3392 if (newStart > 0 || !getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) // currently we do not try to parse MMMMM/LLLLL: #8860
73c04bcf 3393 return newStart;
4388f060 3394 // else we allowing parsing as number, below
73c04bcf 3395 }
4388f060 3396 break;
73c04bcf 3397
374ca955 3398 case UDAT_HOUR_OF_DAY1_FIELD:
b75a7d8f 3399 // [We computed 'value' above.]
729e4ab9 3400 if (value == cal.getMaximum(UCAL_HOUR_OF_DAY) + 1)
b75a7d8f 3401 value = 0;
2ca993e8 3402
46f4442e 3403 // fall through to set field
2ca993e8 3404 U_FALLTHROUGH;
46f4442e 3405 case UDAT_HOUR_OF_DAY0_FIELD:
b75a7d8f
A
3406 cal.set(UCAL_HOUR_OF_DAY, value);
3407 return pos.getIndex();
73c04bcf 3408
374ca955
A
3409 case UDAT_FRACTIONAL_SECOND_FIELD:
3410 // Fractional seconds left-justify
0f5d89e8 3411 i = countDigits(text, start, pos.getIndex());
374ca955
A
3412 if (i < 3) {
3413 while (i < 3) {
3414 value *= 10;
3415 i++;
3416 }
3417 } else {
3418 int32_t a = 1;
3419 while (i > 3) {
3420 a *= 10;
3421 i--;
3422 }
51004dcb 3423 value /= a;
374ca955
A
3424 }
3425 cal.set(UCAL_MILLISECOND, value);
3426 return pos.getIndex();
73c04bcf 3427
46f4442e
A
3428 case UDAT_DOW_LOCAL_FIELD:
3429 if (gotNumber) // i.e., e or ee
3430 {
3431 // [We computed 'value' above.]
3432 cal.set(UCAL_DOW_LOCAL, value);
3433 return pos.getIndex();
3434 }
3435 // else for eee-eeeee fall through to handling of EEE-EEEEE
3436 // fall through, do not break here
2ca993e8 3437 U_FALLTHROUGH;
374ca955 3438 case UDAT_DAY_OF_WEEK_FIELD:
b75a7d8f
A
3439 {
3440 // Want to be able to parse both short and long forms.
51004dcb 3441 // Try count == 4 (EEEE) wide first:
b75a7d8f 3442 int32_t newStart = 0;
57a6839d
A
3443 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3444 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3445 fSymbols->fWeekdays, fSymbols->fWeekdaysCount, NULL, cal)) > 0)
3446 return newStart;
3447 }
51004dcb 3448 // EEEE wide failed, now try EEE abbreviated
57a6839d
A
3449 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3450 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3451 fSymbols->fShortWeekdays, fSymbols->fShortWeekdaysCount, NULL, cal)) > 0)
3452 return newStart;
3453 }
51004dcb 3454 // EEE abbreviated failed, now try EEEEEE short
57a6839d
A
3455 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 6) {
3456 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3457 fSymbols->fShorterWeekdays, fSymbols->fShorterWeekdaysCount, NULL, cal)) > 0)
3458 return newStart;
3459 }
51004dcb 3460 // EEEEEE short failed, now try EEEEE narrow
57a6839d
A
3461 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
3462 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
3463 fSymbols->fNarrowWeekdays, fSymbols->fNarrowWeekdaysCount, NULL, cal)) > 0)
3464 return newStart;
3465 }
3466 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status) || patternCharIndex == UDAT_DAY_OF_WEEK_FIELD)
4388f060
A
3467 return newStart;
3468 // else we allowing parsing as number, below
b75a7d8f 3469 }
4388f060 3470 break;
73c04bcf
A
3471
3472 case UDAT_STANDALONE_DAY_FIELD:
3473 {
46f4442e
A
3474 if (gotNumber) // c or cc
3475 {
3476 // [We computed 'value' above.]
3477 cal.set(UCAL_DOW_LOCAL, value);
3478 return pos.getIndex();
3479 }
73c04bcf 3480 // Want to be able to parse both short and long forms.
46f4442e 3481 // Try count == 4 (cccc) first:
73c04bcf 3482 int32_t newStart = 0;
57a6839d
A
3483 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3484 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
4388f060 3485 fSymbols->fStandaloneWeekdays, fSymbols->fStandaloneWeekdaysCount, NULL, cal)) > 0)
57a6839d
A
3486 return newStart;
3487 }
3488 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3489 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
4388f060 3490 fSymbols->fStandaloneShortWeekdays, fSymbols->fStandaloneShortWeekdaysCount, NULL, cal)) > 0)
57a6839d
A
3491 return newStart;
3492 }
3493 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 6) {
3494 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
51004dcb 3495 fSymbols->fStandaloneShorterWeekdays, fSymbols->fStandaloneShorterWeekdaysCount, NULL, cal)) > 0)
57a6839d
A
3496 return newStart;
3497 }
3498 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))
4388f060
A
3499 return newStart;
3500 // else we allowing parsing as number, below
73c04bcf 3501 }
4388f060 3502 break;
73c04bcf 3503
374ca955 3504 case UDAT_AM_PM_FIELD:
b331163b
A
3505 {
3506 // optionally try both wide/abbrev and narrow forms
3507 int32_t newStart = 0;
3508 // try wide/abbrev
3509 if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count < 5 ) {
3510 if ((newStart = matchString(text, start, UCAL_AM_PM, fSymbols->fAmPms, fSymbols->fAmPmsCount, NULL, cal)) > 0) {
3511 return newStart;
3512 }
3513 }
3514 // try narrow
3515 if( getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count >= 5 ) {
3516 if ((newStart = matchString(text, start, UCAL_AM_PM, fSymbols->fNarrowAmPms, fSymbols->fNarrowAmPmsCount, NULL, cal)) > 0) {
3517 return newStart;
3518 }
3519 }
3520 // no matches for given options
3521 return -start;
3522 }
73c04bcf 3523
374ca955 3524 case UDAT_HOUR1_FIELD:
b75a7d8f 3525 // [We computed 'value' above.]
729e4ab9 3526 if (value == cal.getLeastMaximum(UCAL_HOUR)+1)
b75a7d8f 3527 value = 0;
2ca993e8 3528
46f4442e 3529 // fall through to set field
2ca993e8 3530 U_FALLTHROUGH;
46f4442e 3531 case UDAT_HOUR0_FIELD:
b75a7d8f
A
3532 cal.set(UCAL_HOUR, value);
3533 return pos.getIndex();
4388f060 3534
73c04bcf 3535 case UDAT_QUARTER_FIELD:
46f4442e 3536 if (gotNumber) // i.e., Q or QQ.
73c04bcf
A
3537 {
3538 // Don't want to parse the month if it is a string
3539 // while pattern uses numeric style: Q or QQ.
3540 // [We computed 'value' above.]
3541 cal.set(UCAL_MONTH, (value - 1) * 3);
3542 return pos.getIndex();
3543 } else {
3544 // count >= 3 // i.e., QQQ or QQQQ
3545 // Want to be able to parse both short and long forms.
3546 // Try count == 4 first:
3547 int32_t newStart = 0;
3548
57a6839d
A
3549 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3550 if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
73c04bcf 3551 fSymbols->fQuarters, fSymbols->fQuartersCount, cal)) > 0)
57a6839d
A
3552 return newStart;
3553 }
3554 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3555 if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
4388f060 3556 fSymbols->fShortQuarters, fSymbols->fShortQuartersCount, cal)) > 0)
57a6839d
A
3557 return newStart;
3558 }
3559 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))
4388f060
A
3560 return newStart;
3561 // else we allowing parsing as number, below
57a6839d
A
3562 if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status))
3563 return -start;
73c04bcf 3564 }
4388f060 3565 break;
73c04bcf
A
3566
3567 case UDAT_STANDALONE_QUARTER_FIELD:
46f4442e 3568 if (gotNumber) // i.e., q or qq.
73c04bcf
A
3569 {
3570 // Don't want to parse the month if it is a string
3571 // while pattern uses numeric style: q or q.
3572 // [We computed 'value' above.]
3573 cal.set(UCAL_MONTH, (value - 1) * 3);
3574 return pos.getIndex();
3575 } else {
3576 // count >= 3 // i.e., qqq or qqqq
3577 // Want to be able to parse both short and long forms.
3578 // Try count == 4 first:
3579 int32_t newStart = 0;
3580
57a6839d
A
3581 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3582 if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
73c04bcf 3583 fSymbols->fStandaloneQuarters, fSymbols->fStandaloneQuartersCount, cal)) > 0)
57a6839d
A
3584 return newStart;
3585 }
3586 if(getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3587 if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
4388f060 3588 fSymbols->fStandaloneShortQuarters, fSymbols->fStandaloneShortQuartersCount, cal)) > 0)
57a6839d
A
3589 return newStart;
3590 }
3591 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status))
4388f060
A
3592 return newStart;
3593 // else we allowing parsing as number, below
57a6839d
A
3594 if(!getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status))
3595 return -start;
73c04bcf 3596 }
4388f060 3597 break;
73c04bcf 3598
51004dcb 3599 case UDAT_TIMEZONE_FIELD: // 'z'
b75a7d8f 3600 {
4388f060 3601 UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_SPECIFIC_SHORT : UTZFMT_STYLE_SPECIFIC_LONG;
3d1f044b
A
3602 const TimeZoneFormat *tzfmt = tzFormat(status);
3603 if (U_SUCCESS(status)) {
3604 TimeZone *tz = tzfmt->parse(style, text, pos, tzParseOptions, tzTimeType);
3605 if (tz != NULL) {
3606 cal.adoptTimeZone(tz);
3607 return pos.getIndex();
3608 }
b75a7d8f 3609 }
3d1f044b
A
3610 return -start;
3611 }
4388f060 3612 break;
51004dcb 3613 case UDAT_TIMEZONE_RFC_FIELD: // 'Z'
4388f060 3614 {
51004dcb
A
3615 UTimeZoneFormatStyle style = (count < 4) ?
3616 UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL : ((count == 5) ? UTZFMT_STYLE_ISO_EXTENDED_FULL: UTZFMT_STYLE_LOCALIZED_GMT);
3d1f044b
A
3617 const TimeZoneFormat *tzfmt = tzFormat(status);
3618 if (U_SUCCESS(status)) {
3619 TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
3620 if (tz != NULL) {
3621 cal.adoptTimeZone(tz);
3622 return pos.getIndex();
3623 }
b75a7d8f 3624 }
4388f060
A
3625 return -start;
3626 }
51004dcb 3627 case UDAT_TIMEZONE_GENERIC_FIELD: // 'v'
4388f060 3628 {
4388f060 3629 UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_GENERIC_SHORT : UTZFMT_STYLE_GENERIC_LONG;
3d1f044b
A
3630 const TimeZoneFormat *tzfmt = tzFormat(status);
3631 if (U_SUCCESS(status)) {
3632 TimeZone *tz = tzfmt->parse(style, text, pos, tzParseOptions, tzTimeType);
3633 if (tz != NULL) {
3634 cal.adoptTimeZone(tz);
3635 return pos.getIndex();
3636 }
b75a7d8f 3637 }
4388f060
A
3638 return -start;
3639 }
51004dcb
A
3640 case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V'
3641 {
51004dcb
A
3642 UTimeZoneFormatStyle style;
3643 switch (count) {
3644 case 1:
3645 style = UTZFMT_STYLE_ZONE_ID_SHORT;
3646 break;
3647 case 2:
3648 style = UTZFMT_STYLE_ZONE_ID;
3649 break;
3650 case 3:
3651 style = UTZFMT_STYLE_EXEMPLAR_LOCATION;
3652 break;
3653 default:
3654 style = UTZFMT_STYLE_GENERIC_LOCATION;
3655 break;
3656 }
3d1f044b
A
3657 const TimeZoneFormat *tzfmt = tzFormat(status);
3658 if (U_SUCCESS(status)) {
3659 TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
3660 if (tz != NULL) {
3661 cal.adoptTimeZone(tz);
3662 return pos.getIndex();
3663 }
51004dcb
A
3664 }
3665 return -start;
3666 }
3667 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: // 'O'
3668 {
51004dcb 3669 UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_LOCALIZED_GMT_SHORT : UTZFMT_STYLE_LOCALIZED_GMT;
3d1f044b
A
3670 const TimeZoneFormat *tzfmt = tzFormat(status);
3671 if (U_SUCCESS(status)) {
3672 TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
3673 if (tz != NULL) {
3674 cal.adoptTimeZone(tz);
3675 return pos.getIndex();
3676 }
51004dcb
A
3677 }
3678 return -start;
3679 }
3680 case UDAT_TIMEZONE_ISO_FIELD: // 'X'
3681 {
51004dcb
A
3682 UTimeZoneFormatStyle style;
3683 switch (count) {
3684 case 1:
3685 style = UTZFMT_STYLE_ISO_BASIC_SHORT;
3686 break;
3687 case 2:
3688 style = UTZFMT_STYLE_ISO_BASIC_FIXED;
3689 break;
3690 case 3:
3691 style = UTZFMT_STYLE_ISO_EXTENDED_FIXED;
3692 break;
3693 case 4:
3694 style = UTZFMT_STYLE_ISO_BASIC_FULL;
3695 break;
3696 default:
3697 style = UTZFMT_STYLE_ISO_EXTENDED_FULL;
3698 break;
3699 }
3d1f044b
A
3700 const TimeZoneFormat *tzfmt = tzFormat(status);
3701 if (U_SUCCESS(status)) {
3702 TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
3703 if (tz != NULL) {
3704 cal.adoptTimeZone(tz);
3705 return pos.getIndex();
3706 }
51004dcb
A
3707 }
3708 return -start;
3709 }
3710 case UDAT_TIMEZONE_ISO_LOCAL_FIELD: // 'x'
4388f060 3711 {
51004dcb
A
3712 UTimeZoneFormatStyle style;
3713 switch (count) {
3714 case 1:
3715 style = UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT;
3716 break;
3717 case 2:
3718 style = UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED;
3719 break;
3720 case 3:
3721 style = UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED;
3722 break;
3723 case 4:
3724 style = UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL;
3725 break;
3726 default:
3727 style = UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL;
3728 break;
3729 }
3d1f044b
A
3730 const TimeZoneFormat *tzfmt = tzFormat(status);
3731 if (U_SUCCESS(status)) {
3732 TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType);
3733 if (tz != NULL) {
3734 cal.adoptTimeZone(tz);
3735 return pos.getIndex();
3736 }
729e4ab9 3737 }
46f4442e 3738 return -start;
b75a7d8f 3739 }
2ca993e8
A
3740 // currently no pattern character is defined for UDAT_TIME_SEPARATOR_FIELD
3741 // so we should not get here. Leave support in for future definition.
3742 case UDAT_TIME_SEPARATOR_FIELD:
b331163b
A
3743 {
3744 static const UChar def_sep = DateFormatSymbols::DEFAULT_TIME_SEPARATOR;
3745 static const UChar alt_sep = DateFormatSymbols::ALTERNATE_TIME_SEPARATOR;
3746
3747 // Try matching a time separator.
3d1f044b 3748 int32_t count_sep = 1;
b331163b
A
3749 UnicodeString data[3];
3750 fSymbols->getTimeSeparatorString(data[0]);
3751
3752 // Add the default, if different from the locale.
3753 if (data[0].compare(&def_sep, 1) != 0) {
3d1f044b 3754 data[count_sep++].setTo(def_sep);
b331163b
A
3755 }
3756
3757 // If lenient, add also the alternate, if different from the locale.
3758 if (isLenient() && data[0].compare(&alt_sep, 1) != 0) {
3d1f044b 3759 data[count_sep++].setTo(alt_sep);
b331163b
A
3760 }
3761
3d1f044b 3762 return matchString(text, start, UCAL_FIELD_COUNT /* => nothing to set */, data, count_sep, NULL, cal);
b331163b 3763 }
73c04bcf 3764
2ca993e8
A
3765 case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
3766 {
3767 U_ASSERT(dayPeriod != NULL);
3768 int32_t ampmStart = subParse(text, start, 0x61, count,
3769 obeyCount, allowNegative, ambiguousYear, saveHebrewMonth, cal,
3d1f044b 3770 patLoc, numericLeapMonthFormatter, tzTimeType);
2ca993e8
A
3771
3772 if (ampmStart > 0) {
3773 return ampmStart;
3774 } else {
3775 int32_t newStart = 0;
3776
3777 // Only match the first two strings from the day period strings array.
3778 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3779 if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fAbbreviatedDayPeriods,
3780 2, *dayPeriod)) > 0) {
3781 return newStart;
3782 }
3783 }
3784 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
3785 if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fNarrowDayPeriods,
3786 2, *dayPeriod)) > 0) {
3787 return newStart;
3788 }
3789 }
3790 // count == 4, but allow other counts
3791 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status)) {
3792 if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fWideDayPeriods,
3793 2, *dayPeriod)) > 0) {
3794 return newStart;
3795 }
3796 }
3797
3798 return -start;
3799 }
3800 }
3801
3802 case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
3803 {
3804 U_ASSERT(dayPeriod != NULL);
3805 int32_t newStart = 0;
3806
3807 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 3) {
3808 if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fAbbreviatedDayPeriods,
3809 fSymbols->fAbbreviatedDayPeriodsCount, *dayPeriod)) > 0) {
3810 return newStart;
3811 }
3812 }
3813 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 5) {
3814 if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fNarrowDayPeriods,
3815 fSymbols->fNarrowDayPeriodsCount, *dayPeriod)) > 0) {
3816 return newStart;
3817 }
3818 }
3819 if (getBooleanAttribute(UDAT_PARSE_MULTIPLE_PATTERNS_FOR_MATCH, status) || count == 4) {
3820 if ((newStart = matchDayPeriodStrings(text, start, fSymbols->fWideDayPeriods,
3821 fSymbols->fWideDayPeriodsCount, *dayPeriod)) > 0) {
3822 return newStart;
3823 }
3824 }
3825
3826 return -start;
3827 }
3828
b75a7d8f 3829 default:
b75a7d8f 3830 // Handle "generic" fields
4388f060
A
3831 // this is now handled below, outside the switch block
3832 break;
3833 }
3834 // Handle "generic" fields:
3835 // switch default case now handled here (outside switch block) to allow
3836 // parsing of some string fields as digits for lenient case
3837
3838 int32_t parseStart = pos.getIndex();
3839 const UnicodeString* src;
3840 if (obeyCount) {
3841 if ((start+count) > text.length()) {
3842 return -start;
3843 }
3844 text.extractBetween(0, start + count, temp);
3845 src = &temp;
3846 } else {
3847 src = &text;
3848 }
3849 parseInt(*src, number, pos, allowNegative,currentNumberFormat);
3850 if (pos.getIndex() != parseStart) {
3d1f044b 3851 int32_t val = number.getLong();
4388f060
A
3852
3853 // Don't need suffix processing here (as in number processing at the beginning of the function);
3854 // the new fields being handled as numeric values (month, weekdays, quarters) should not have suffixes.
3855
3856 // Check the range of the value
57a6839d 3857 if (!getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status)) {
4388f060 3858 int32_t bias = gFieldRangeBias[patternCharIndex];
3d1f044b 3859 if (bias >= 0 && (val > cal.getMaximum(field) + bias || val < cal.getMinimum(field) + bias)) {
b75a7d8f
A
3860 return -start;
3861 }
b75a7d8f 3862 } else {
4388f060
A
3863 int32_t bias = gFieldRangeBiasLenient[patternCharIndex];
3864 if (bias >= 0 && (value > cal.getMaximum(field) + bias)) {
3865 return -start;
46f4442e 3866 }
b75a7d8f 3867 }
729e4ab9 3868
4388f060
A
3869 // For the following, need to repeat some of the "if (gotNumber)" code above:
3870 // UDAT_[STANDALONE_]MONTH_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_STANDALONE_DAY_FIELD,
3871 // UDAT_[STANDALONE_]QUARTER_FIELD
3872 switch (patternCharIndex) {
3873 case UDAT_MONTH_FIELD:
3874 // See notes under UDAT_MONTH_FIELD case above
3875 if (!strcmp(cal.getType(),"hebrew")) {
3876 HebrewCalendar *hc = (HebrewCalendar*)&cal;
3877 if (cal.isSet(UCAL_YEAR)) {
3d1f044b
A
3878 UErrorCode monthStatus = U_ZERO_ERROR;
3879 if (!hc->isLeapYear(hc->get(UCAL_YEAR, monthStatus)) && val >= 6) {
3880 cal.set(UCAL_MONTH, val);
4388f060 3881 } else {
3d1f044b 3882 cal.set(UCAL_MONTH, val - 1);
4388f060
A
3883 }
3884 } else {
3d1f044b 3885 saveHebrewMonth = val;
4388f060
A
3886 }
3887 } else {
3d1f044b 3888 cal.set(UCAL_MONTH, val - 1);
4388f060
A
3889 }
3890 break;
3891 case UDAT_STANDALONE_MONTH_FIELD:
3d1f044b 3892 cal.set(UCAL_MONTH, val - 1);
4388f060
A
3893 break;
3894 case UDAT_DOW_LOCAL_FIELD:
3895 case UDAT_STANDALONE_DAY_FIELD:
3d1f044b 3896 cal.set(UCAL_DOW_LOCAL, val);
4388f060
A
3897 break;
3898 case UDAT_QUARTER_FIELD:
3899 case UDAT_STANDALONE_QUARTER_FIELD:
3d1f044b 3900 cal.set(UCAL_MONTH, (val - 1) * 3);
4388f060 3901 break;
57a6839d 3902 case UDAT_RELATED_YEAR_FIELD:
3d1f044b 3903 cal.setRelatedYear(val);
57a6839d 3904 break;
4388f060 3905 default:
3d1f044b 3906 cal.set(field, val);
4388f060
A
3907 break;
3908 }
3909 return pos.getIndex();
b75a7d8f 3910 }
4388f060 3911 return -start;
b75a7d8f
A
3912}
3913
3914/**
3915 * Parse an integer using fNumberFormat. This method is semantically
3916 * const, but actually may modify fNumberFormat.
3917 */
3918void SimpleDateFormat::parseInt(const UnicodeString& text,
3919 Formattable& number,
3920 ParsePosition& pos,
729e4ab9 3921 UBool allowNegative,
3d1f044b 3922 const NumberFormat *fmt) const {
729e4ab9 3923 parseInt(text, number, -1, pos, allowNegative,fmt);
46f4442e
A
3924}
3925
3926/**
3927 * Parse an integer using fNumberFormat up to maxDigits.
3928 */
3929void SimpleDateFormat::parseInt(const UnicodeString& text,
3930 Formattable& number,
3931 int32_t maxDigits,
3932 ParsePosition& pos,
729e4ab9 3933 UBool allowNegative,
3d1f044b 3934 const NumberFormat *fmt) const {
b75a7d8f 3935 UnicodeString oldPrefix;
3d1f044b
A
3936 auto* fmtAsDF = dynamic_cast<const DecimalFormat*>(fmt);
3937 LocalPointer<DecimalFormat> df;
3938 if (!allowNegative && fmtAsDF != nullptr) {
3939 df.adoptInstead(dynamic_cast<DecimalFormat*>(fmtAsDF->clone()));
3940 if (df.isNull()) {
3941 // Memory allocation error
3942 return;
3943 }
4388f060 3944 df->setNegativePrefix(UnicodeString(TRUE, SUPPRESS_NEGATIVE_PREFIX, -1));
3d1f044b 3945 fmt = df.getAlias();
b75a7d8f 3946 }
46f4442e 3947 int32_t oldPos = pos.getIndex();
729e4ab9 3948 fmt->parse(text, number, pos);
46f4442e
A
3949
3950 if (maxDigits > 0) {
3951 // adjust the result to fit into
3952 // the maxDigits and move the position back
3953 int32_t nDigits = pos.getIndex() - oldPos;
3954 if (nDigits > maxDigits) {
3955 int32_t val = number.getLong();
3956 nDigits -= maxDigits;
3957 while (nDigits > 0) {
3958 val /= 10;
3959 nDigits--;
3960 }
3961 pos.setIndex(oldPos + maxDigits);
3962 number.setLong(val);
3963 }
3964 }
b75a7d8f
A
3965}
3966
0f5d89e8
A
3967int32_t SimpleDateFormat::countDigits(const UnicodeString& text, int32_t start, int32_t end) const {
3968 int32_t numDigits = 0;
3969 int32_t idx = start;
3970 while (idx < end) {
3971 UChar32 cp = text.char32At(idx);
3972 if (u_isdigit(cp)) {
3973 numDigits++;
3974 }
3975 idx += U16_LENGTH(cp);
3976 }
3977 return numDigits;
3978}
3979
b75a7d8f
A
3980//----------------------------------------------------------------------
3981
3982void SimpleDateFormat::translatePattern(const UnicodeString& originalPattern,
3983 UnicodeString& translatedPattern,
3984 const UnicodeString& from,
3985 const UnicodeString& to,
3986 UErrorCode& status)
3987{
b331163b 3988 // run through the pattern and convert any pattern symbols from the version
2ca993e8 3989 // in "from" to the corresponding character in "to". This code takes
b331163b
A
3990 // quoted strings into account (it doesn't try to translate them), and it signals
3991 // an error if a particular "pattern character" doesn't appear in "from".
3992 // Depending on the values of "from" and "to" this can convert from generic
3993 // to localized patterns or localized to generic.
3994 if (U_FAILURE(status)) {
3995 return;
b75a7d8f 3996 }
b331163b
A
3997
3998 translatedPattern.remove();
3999 UBool inQuote = FALSE;
4000 for (int32_t i = 0; i < originalPattern.length(); ++i) {
4001 UChar c = originalPattern[i];
4002 if (inQuote) {
4003 if (c == QUOTE) {
4004 inQuote = FALSE;
4005 }
4006 } else {
4007 if (c == QUOTE) {
4008 inQuote = TRUE;
4009 } else if (isSyntaxChar(c)) {
4010 int32_t ci = from.indexOf(c);
4011 if (ci == -1) {
4012 status = U_INVALID_FORMAT_ERROR;
4013 return;
4014 }
4015 c = to[ci];
4016 }
4017 }
4018 translatedPattern += c;
4019 }
4020 if (inQuote) {
4021 status = U_INVALID_FORMAT_ERROR;
4022 return;
b75a7d8f 4023 }
b75a7d8f
A
4024}
4025
4026//----------------------------------------------------------------------
4027
4028UnicodeString&
4029SimpleDateFormat::toPattern(UnicodeString& result) const
4030{
4031 result = fPattern;
4032 return result;
4033}
4034
4035//----------------------------------------------------------------------
4036
4037UnicodeString&
4038SimpleDateFormat::toLocalizedPattern(UnicodeString& result,
4039 UErrorCode& status) const
4040{
4388f060
A
4041 translatePattern(fPattern, result,
4042 UnicodeString(DateFormatSymbols::getPatternUChars()),
4043 fSymbols->fLocalPatternChars, status);
b75a7d8f
A
4044 return result;
4045}
4046
4047//----------------------------------------------------------------------
4048
4049void
4050SimpleDateFormat::applyPattern(const UnicodeString& pattern)
4051{
4052 fPattern = pattern;
2ca993e8 4053 parsePattern();
1546d4af
A
4054
4055 // Hack to update use of Gannen year numbering for ja@calendar=japanese -
4056 // use only if format is non-numeric (includes 年) and no other fDateOverride.
4057 if (fCalendar != nullptr && uprv_strcmp(fCalendar->getType(),"japanese") == 0 &&
4058 uprv_strcmp(fLocale.getLanguage(),"ja") == 0) {
4059 if (fDateOverride==UnicodeString(u"y=jpanyear") && !fHasHanYearChar) {
3d1f044b 4060 // Gannen numbering is set but new pattern should not use it, unset;
1546d4af
A
4061 // use procedure from adoptNumberFormat to clear overrides
4062 if (fSharedNumberFormatters) {
4063 freeSharedNumberFormatters(fSharedNumberFormatters);
4064 fSharedNumberFormatters = NULL;
4065 }
4066 fDateOverride.setToBogus(); // record status
4067 } else if (fDateOverride.isBogus() && fHasHanYearChar) {
4068 // No current override (=> no Gannen numbering) but new pattern needs it;
4069 // use procedures from initNUmberFormatters / adoptNumberFormat
3d1f044b 4070 umtx_lock(LOCK());
1546d4af
A
4071 if (fSharedNumberFormatters == NULL) {
4072 fSharedNumberFormatters = allocSharedNumberFormatters();
4073 }
3d1f044b 4074 umtx_unlock(LOCK());
1546d4af
A
4075 if (fSharedNumberFormatters != NULL) {
4076 Locale ovrLoc(fLocale.getLanguage(),fLocale.getCountry(),fLocale.getVariant(),"numbers=jpanyear");
4077 UErrorCode status = U_ZERO_ERROR;
4078 const SharedNumberFormat *snf = createSharedNumberFormat(ovrLoc, status);
4079 if (U_SUCCESS(status)) {
4080 // Now that we have an appropriate number formatter, fill in the
4081 // appropriate slot in the number formatters table.
4082 UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(u'y');
4083 SharedObject::copyPtr(snf, fSharedNumberFormatters[patternCharIndex]);
4084 snf->deleteIfZeroRefCount();
4085 fDateOverride.setTo(u"y=jpanyear", -1); // record status
4086 }
4087 }
4088 }
4089 }
b75a7d8f
A
4090}
4091
4092//----------------------------------------------------------------------
4093
4094void
4095SimpleDateFormat::applyLocalizedPattern(const UnicodeString& pattern,
4096 UErrorCode &status)
4097{
4388f060
A
4098 translatePattern(pattern, fPattern,
4099 fSymbols->fLocalPatternChars,
4100 UnicodeString(DateFormatSymbols::getPatternUChars()), status);
b75a7d8f
A
4101}
4102
4103//----------------------------------------------------------------------
4104
4105const DateFormatSymbols*
4106SimpleDateFormat::getDateFormatSymbols() const
4107{
4108 return fSymbols;
4109}
4110
4111//----------------------------------------------------------------------
4112
4113void
4114SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols* newFormatSymbols)
4115{
4116 delete fSymbols;
4117 fSymbols = newFormatSymbols;
4118}
4119
4120//----------------------------------------------------------------------
4121void
4122SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols& newFormatSymbols)
4123{
4124 delete fSymbols;
4125 fSymbols = new DateFormatSymbols(newFormatSymbols);
4126}
4127
4388f060
A
4128//----------------------------------------------------------------------
4129const TimeZoneFormat*
4130SimpleDateFormat::getTimeZoneFormat(void) const {
3d1f044b
A
4131 // TimeZoneFormat initialization might fail when out of memory.
4132 // If we always initialize TimeZoneFormat instance, we can return
4133 // such status there. For now, this implementation lazily instantiates
4134 // a TimeZoneFormat for performance optimization reasons, but cannot
4135 // propagate such error (probably just out of memory case) to the caller.
4136 UErrorCode status = U_ZERO_ERROR;
4137 return (const TimeZoneFormat*)tzFormat(status);
4388f060
A
4138}
4139
4140//----------------------------------------------------------------------
4141void
4142SimpleDateFormat::adoptTimeZoneFormat(TimeZoneFormat* timeZoneFormatToAdopt)
4143{
4144 delete fTimeZoneFormat;
4145 fTimeZoneFormat = timeZoneFormatToAdopt;
4146}
4147
4148//----------------------------------------------------------------------
4149void
4150SimpleDateFormat::setTimeZoneFormat(const TimeZoneFormat& newTimeZoneFormat)
4151{
4152 delete fTimeZoneFormat;
4153 fTimeZoneFormat = new TimeZoneFormat(newTimeZoneFormat);
4154}
b75a7d8f
A
4155
4156//----------------------------------------------------------------------
4157
4158
4159void SimpleDateFormat::adoptCalendar(Calendar* calendarToAdopt)
4160{
4161 UErrorCode status = U_ZERO_ERROR;
b331163b 4162 Locale calLocale(fLocale);
0f5d89e8
A
4163 DateFormatSymbols *newSymbols = fSymbols;
4164 if (!newSymbols || fCalendar->getType() != calendarToAdopt->getType()) {
4165 calLocale.setKeywordValue("calendar", calendarToAdopt->getType(), status);
4166 newSymbols = DateFormatSymbols::createForLocale(calLocale, status);
4167 if (U_FAILURE(status)) {
4168 return;
4169 }
b331163b 4170 }
b75a7d8f 4171 DateFormat::adoptCalendar(calendarToAdopt);
0f5d89e8
A
4172 if (fSymbols != newSymbols) {
4173 delete fSymbols;
4174 fSymbols = newSymbols;
4175 }
b75a7d8f
A
4176 initializeDefaultCentury(); // we need a new century (possibly)
4177}
4178
46f4442e
A
4179
4180//----------------------------------------------------------------------
4181
4182
57a6839d
A
4183// override the DateFormat implementation in order to
4184// lazily initialize fCapitalizationBrkIter
4185void
4186SimpleDateFormat::setContext(UDisplayContext value, UErrorCode& status)
4388f060 4187{
57a6839d
A
4188 DateFormat::setContext(value, status);
4189#if !UCONFIG_NO_BREAK_ITERATION
4190 if (U_SUCCESS(status)) {
4191 if ( fCapitalizationBrkIter == NULL && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
4192 value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE) ) {
3d1f044b 4193 status = U_ZERO_ERROR;
57a6839d
A
4194 fCapitalizationBrkIter = BreakIterator::createSentenceInstance(fLocale, status);
4195 if (U_FAILURE(status)) {
4196 delete fCapitalizationBrkIter;
4197 fCapitalizationBrkIter = NULL;
4198 }
4199 }
4388f060 4200 }
57a6839d 4201#endif
4388f060
A
4202}
4203
4204
4205//----------------------------------------------------------------------
4206
4207
46f4442e
A
4208UBool
4209SimpleDateFormat::isFieldUnitIgnored(UCalendarDateFields field) const {
4210 return isFieldUnitIgnored(fPattern, field);
4211}
4212
4213
4214UBool
729e4ab9 4215SimpleDateFormat::isFieldUnitIgnored(const UnicodeString& pattern,
46f4442e
A
4216 UCalendarDateFields field) {
4217 int32_t fieldLevel = fgCalendarFieldToLevel[field];
4218 int32_t level;
4219 UChar ch;
4220 UBool inQuote = FALSE;
4221 UChar prevCh = 0;
4222 int32_t count = 0;
4223
4224 for (int32_t i = 0; i < pattern.length(); ++i) {
4225 ch = pattern[i];
4226 if (ch != prevCh && count > 0) {
b331163b 4227 level = getLevelFromChar(prevCh);
46f4442e 4228 // the larger the level, the smaller the field unit.
b331163b 4229 if (fieldLevel <= level) {
46f4442e
A
4230 return FALSE;
4231 }
4232 count = 0;
4233 }
4234 if (ch == QUOTE) {
4235 if ((i+1) < pattern.length() && pattern[i+1] == QUOTE) {
4236 ++i;
4237 } else {
4238 inQuote = ! inQuote;
4239 }
729e4ab9 4240 }
b331163b 4241 else if (!inQuote && isSyntaxChar(ch)) {
46f4442e
A
4242 prevCh = ch;
4243 ++count;
4244 }
4245 }
b331163b 4246 if (count > 0) {
46f4442e 4247 // last item
b331163b
A
4248 level = getLevelFromChar(prevCh);
4249 if (fieldLevel <= level) {
4250 return FALSE;
4251 }
46f4442e
A
4252 }
4253 return TRUE;
4254}
4255
729e4ab9 4256//----------------------------------------------------------------------
46f4442e
A
4257
4258const Locale&
4259SimpleDateFormat::getSmpFmtLocale(void) const {
4260 return fLocale;
4261}
4262
729e4ab9
A
4263//----------------------------------------------------------------------
4264
4265int32_t
4266SimpleDateFormat::checkIntSuffix(const UnicodeString& text, int32_t start,
4267 int32_t patLoc, UBool isNegative) const {
4268 // local variables
4269 UnicodeString suf;
4270 int32_t patternMatch;
4271 int32_t textPreMatch;
4272 int32_t textPostMatch;
4273
4274 // check that we are still in range
4275 if ( (start > text.length()) ||
4276 (start < 0) ||
4277 (patLoc < 0) ||
4278 (patLoc > fPattern.length())) {
4279 // out of range, don't advance location in text
4280 return start;
4281 }
4282
4283 // get the suffix
4284 DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(fNumberFormat);
4285 if (decfmt != NULL) {
4286 if (isNegative) {
4287 suf = decfmt->getNegativeSuffix(suf);
4288 }
4289 else {
4290 suf = decfmt->getPositiveSuffix(suf);
4291 }
4292 }
4293
4294 // check for suffix
4295 if (suf.length() <= 0) {
4296 return start;
4297 }
4298
4299 // check suffix will be encountered in the pattern
4300 patternMatch = compareSimpleAffix(suf,fPattern,patLoc);
4301
4302 // check if a suffix will be encountered in the text
4303 textPreMatch = compareSimpleAffix(suf,text,start);
4304
4305 // check if a suffix was encountered in the text
4306 textPostMatch = compareSimpleAffix(suf,text,start-suf.length());
46f4442e 4307
729e4ab9
A
4308 // check for suffix match
4309 if ((textPreMatch >= 0) && (patternMatch >= 0) && (textPreMatch == patternMatch)) {
4310 return start;
4311 }
4312 else if ((textPostMatch >= 0) && (patternMatch >= 0) && (textPostMatch == patternMatch)) {
4313 return start - suf.length();
4314 }
4315
4316 // should not get here
4317 return start;
4318}
4319
4320//----------------------------------------------------------------------
4321
4322int32_t
4323SimpleDateFormat::compareSimpleAffix(const UnicodeString& affix,
4324 const UnicodeString& input,
4325 int32_t pos) const {
4326 int32_t start = pos;
4327 for (int32_t i=0; i<affix.length(); ) {
4328 UChar32 c = affix.char32At(i);
4329 int32_t len = U16_LENGTH(c);
4388f060 4330 if (PatternProps::isWhiteSpace(c)) {
729e4ab9
A
4331 // We may have a pattern like: \u200F \u0020
4332 // and input text like: \u200F \u0020
4388f060 4333 // Note that U+200F and U+0020 are Pattern_White_Space but only
729e4ab9 4334 // U+0020 is UWhiteSpace. So we have to first do a direct
4388f060 4335 // match of the run of Pattern_White_Space in the pattern,
729e4ab9
A
4336 // then match any extra characters.
4337 UBool literalMatch = FALSE;
4338 while (pos < input.length() &&
4339 input.char32At(pos) == c) {
4340 literalMatch = TRUE;
4341 i += len;
4342 pos += len;
4343 if (i == affix.length()) {
4344 break;
4345 }
4346 c = affix.char32At(i);
4347 len = U16_LENGTH(c);
4388f060 4348 if (!PatternProps::isWhiteSpace(c)) {
729e4ab9
A
4349 break;
4350 }
4351 }
4352
4353 // Advance over run in pattern
4388f060 4354 i = skipPatternWhiteSpace(affix, i);
729e4ab9
A
4355
4356 // Advance over run in input text
4357 // Must see at least one white space char in input,
4358 // unless we've already matched some characters literally.
4359 int32_t s = pos;
4360 pos = skipUWhiteSpace(input, pos);
4361 if (pos == s && !literalMatch) {
4362 return -1;
4363 }
4364
4365 // If we skip UWhiteSpace in the input text, we need to skip it in the pattern.
4366 // Otherwise, the previous lines may have skipped over text (such as U+00A0) that
4367 // is also in the affix.
4368 i = skipUWhiteSpace(affix, i);
4369 } else {
4370 if (pos < input.length() &&
4371 input.char32At(pos) == c) {
4372 i += len;
4373 pos += len;
4374 } else {
4375 return -1;
4376 }
4377 }
4378 }
4379 return pos - start;
4380}
4381
4382//----------------------------------------------------------------------
4383
4384int32_t
4388f060
A
4385SimpleDateFormat::skipPatternWhiteSpace(const UnicodeString& text, int32_t pos) const {
4386 const UChar* s = text.getBuffer();
4387 return (int32_t)(PatternProps::skipWhiteSpace(s + pos, text.length() - pos) - s);
729e4ab9
A
4388}
4389
4390//----------------------------------------------------------------------
4391
4392int32_t
4393SimpleDateFormat::skipUWhiteSpace(const UnicodeString& text, int32_t pos) const {
4394 while (pos < text.length()) {
4395 UChar32 c = text.char32At(pos);
4396 if (!u_isUWhiteSpace(c)) {
4397 break;
4398 }
4399 pos += U16_LENGTH(c);
4400 }
4401 return pos;
4402}
46f4442e 4403
4388f060
A
4404//----------------------------------------------------------------------
4405
4406// Lazy TimeZoneFormat instantiation, semantically const.
4407TimeZoneFormat *
3d1f044b 4408SimpleDateFormat::tzFormat(UErrorCode &status) const {
4388f060 4409 if (fTimeZoneFormat == NULL) {
3d1f044b 4410 umtx_lock(LOCK());
4388f060
A
4411 {
4412 if (fTimeZoneFormat == NULL) {
4388f060
A
4413 TimeZoneFormat *tzfmt = TimeZoneFormat::createInstance(fLocale, status);
4414 if (U_FAILURE(status)) {
4415 return NULL;
4416 }
4417
4418 const_cast<SimpleDateFormat *>(this)->fTimeZoneFormat = tzfmt;
4419 }
4420 }
3d1f044b 4421 umtx_unlock(LOCK());
4388f060
A
4422 }
4423 return fTimeZoneFormat;
4424}
4425
2ca993e8
A
4426void SimpleDateFormat::parsePattern() {
4427 fHasMinute = FALSE;
4428 fHasSecond = FALSE;
1546d4af 4429 fHasHanYearChar = FALSE;
2ca993e8
A
4430
4431 int len = fPattern.length();
4432 UBool inQuote = FALSE;
4433 for (int32_t i = 0; i < len; ++i) {
4434 UChar ch = fPattern[i];
4435 if (ch == QUOTE) {
4436 inQuote = !inQuote;
4437 }
1546d4af
A
4438 if (ch == 0x5E74) { // don't care whether this is inside quotes
4439 fHasHanYearChar = TRUE;
4440 }
2ca993e8
A
4441 if (!inQuote) {
4442 if (ch == 0x6D) { // 0x6D == 'm'
4443 fHasMinute = TRUE;
4444 }
4445 if (ch == 0x73) { // 0x73 == 's'
4446 fHasSecond = TRUE;
4447 }
4448 }
4449 }
4450}
4451
b75a7d8f
A
4452U_NAMESPACE_END
4453
4454#endif /* #if !UCONFIG_NO_FORMATTING */
4455
4456//eof