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