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