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