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