1 /*
2 *******************************************************************************
3 * Copyright (C) 1997-2007, International Business Machines Corporation and *
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
27 #define ZID_KEY_MAX 128
28
29 #include "unicode/utypes.h"
30
31 #if !UCONFIG_NO_FORMATTING
32
33 #include "unicode/smpdtfmt.h"
34 #include "unicode/dtfmtsym.h"
35 #include "unicode/ures.h"
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"
43 #include "unicode/ustring.h"
44 #include "unicode/basictz.h"
45 #include "unicode/simpletz.h"
46 #include "unicode/rbtz.h"
47 #include "unicode/vtzone.h"
48 #include "olsontz.h"
49 #include "../common/util.h"
50 #include "gregoimp.h"
51 #include "cstring.h"
52 #include "uassert.h"
53 #include "zstrfmt.h"
54 #include "cmemory.h"
55 #include "umutex.h"
56 #include <float.h>
57
58 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
59 #include <stdio.h>
60 #endif
61
62 // *****************************************************************************
63 // class SimpleDateFormat
64 // *****************************************************************************
65
66 U_NAMESPACE_BEGIN
67
68 /**
69 * Last-resort string to use for "GMT" when constructing time zone strings.
70 */
71 // For time zones that have no names, use strings GMT+minutes and
72 // GMT-minutes. For instance, in France the time zone is GMT+60.
73 // Also accepted are GMT+H:MM or GMT-H:MM.
74 static const UChar gGmt[] = {0x0047, 0x004D, 0x0054, 0x0000}; // "GMT"
75 static const UChar gGmtPlus[] = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+"
76 static const UChar gGmtMinus[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-"
77 static const UChar gDefGmtPat[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0x0000}; /* GMT{0} */
78 static const UChar gDefGmtNegHmsPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* -HH:mm:ss */
79 static const UChar gDefGmtNegHmPat[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* -HH:mm */
80 static const UChar gDefGmtPosHmsPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0x0000}; /* +HH:mm:ss */
81 static const UChar gDefGmtPosHmPat[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x0000}; /* +HH:mm */
82 typedef enum GmtPatSize {
83 kGmtLen = 3,
84 kGmtPatLen = 6,
85 kNegHmsLen = 9,
86 kNegHmLen = 6,
87 kPosHmsLen = 9,
88 kPosHmLen = 6
89 } GmtPatSize;
90
91 // This is a pattern-of-last-resort used when we can't load a usable pattern out
92 // of a resource.
93 static const UChar gDefaultPattern[] =
94 {
95 0x79, 0x79, 0x79, 0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0
96 }; /* "yyyyMMdd hh:mm a" */
97
98 // This prefix is designed to NEVER MATCH real text, in order to
99 // suppress the parsing of negative numbers. Adjust as needed (if
100 // this becomes valid Unicode).
101 static const UChar SUPPRESS_NEGATIVE_PREFIX[] = {0xAB00, 0};
102
103 /**
104 * These are the tags we expect to see in normal resource bundle files associated
105 * with a locale.
106 */
107 static const char gDateTimePatternsTag[]="DateTimePatterns";
108
109 static const UChar gEtcUTC[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x54, 0x43, 0x00}; // "Etc/UTC"
110 static const UChar QUOTE = 0x27; // Single quote
111 enum {
112 kGMTNegativeHMS = 0,
113 kGMTNegativeHM,
114 kGMTPositiveHMS,
115 kGMTPositiveHM,
116
117 kNumGMTFormatters
118 };
119
120 static UMTX LOCK;
121
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat)122 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat)
123
124 //----------------------------------------------------------------------
125
126 SimpleDateFormat::~SimpleDateFormat()
127 {
128 delete fSymbols;
129 if (fGMTFormatters) {
130 for (int32_t i = 0; i < kNumGMTFormatters; i++) {
131 if (fGMTFormatters[i]) {
132 delete fGMTFormatters[i];
133 }
134 }
135 uprv_free(fGMTFormatters);
136 }
137 }
138
139 //----------------------------------------------------------------------
140
SimpleDateFormat(UErrorCode & status)141 SimpleDateFormat::SimpleDateFormat(UErrorCode& status)
142 : fLocale(Locale::getDefault()),
143 fSymbols(NULL),
144 fGMTFormatters(NULL)
145 {
146 construct(kShort, (EStyle) (kShort + kDateOffset), fLocale, status);
147 initializeDefaultCentury();
148 }
149
150 //----------------------------------------------------------------------
151
SimpleDateFormat(const UnicodeString & pattern,UErrorCode & status)152 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
153 UErrorCode &status)
154 : fPattern(pattern),
155 fLocale(Locale::getDefault()),
156 fSymbols(NULL),
157 fGMTFormatters(NULL)
158 {
159 initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
160 initialize(fLocale, status);
161 initializeDefaultCentury();
162 }
163
164 //----------------------------------------------------------------------
165
SimpleDateFormat(const UnicodeString & pattern,const Locale & locale,UErrorCode & status)166 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
167 const Locale& locale,
168 UErrorCode& status)
169 : fPattern(pattern),
170 fLocale(locale),
171 fGMTFormatters(NULL)
172 {
173 initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
174 initialize(fLocale, status);
175 initializeDefaultCentury();
176 }
177
178 //----------------------------------------------------------------------
179
SimpleDateFormat(const UnicodeString & pattern,DateFormatSymbols * symbolsToAdopt,UErrorCode & status)180 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
181 DateFormatSymbols* symbolsToAdopt,
182 UErrorCode& status)
183 : fPattern(pattern),
184 fLocale(Locale::getDefault()),
185 fSymbols(symbolsToAdopt),
186 fGMTFormatters(NULL)
187 {
188 initializeCalendar(NULL,fLocale,status);
189 initialize(fLocale, status);
190 initializeDefaultCentury();
191 }
192
193 //----------------------------------------------------------------------
194
SimpleDateFormat(const UnicodeString & pattern,const DateFormatSymbols & symbols,UErrorCode & status)195 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
196 const DateFormatSymbols& symbols,
197 UErrorCode& status)
198 : fPattern(pattern),
199 fLocale(Locale::getDefault()),
200 fSymbols(new DateFormatSymbols(symbols)),
201 fGMTFormatters(NULL)
202 {
203 initializeCalendar(NULL, fLocale, status);
204 initialize(fLocale, status);
205 initializeDefaultCentury();
206 }
207
208 //----------------------------------------------------------------------
209
210 // Not for public consumption; used by DateFormat
SimpleDateFormat(EStyle timeStyle,EStyle dateStyle,const Locale & locale,UErrorCode & status)211 SimpleDateFormat::SimpleDateFormat(EStyle timeStyle,
212 EStyle dateStyle,
213 const Locale& locale,
214 UErrorCode& status)
215 : fLocale(locale),
216 fSymbols(NULL),
217 fGMTFormatters(NULL)
218 {
219 construct(timeStyle, dateStyle, fLocale, status);
220 if(U_SUCCESS(status)) {
221 initializeDefaultCentury();
222 }
223 }
224
225 //----------------------------------------------------------------------
226
227 /**
228 * Not for public consumption; used by DateFormat. This constructor
229 * never fails. If the resource data is not available, it uses the
230 * the last resort symbols.
231 */
SimpleDateFormat(const Locale & locale,UErrorCode & status)232 SimpleDateFormat::SimpleDateFormat(const Locale& locale,
233 UErrorCode& status)
234 : fPattern(gDefaultPattern),
235 fLocale(locale),
236 fSymbols(NULL),
237 fGMTFormatters(NULL)
238 {
239 if (U_FAILURE(status)) return;
240 initializeSymbols(fLocale, initializeCalendar(NULL, fLocale, status),status);
241 if (U_FAILURE(status))
242 {
243 status = U_ZERO_ERROR;
244 delete fSymbols;
245 // This constructor doesn't fail; it uses last resort data
246 fSymbols = new DateFormatSymbols(status);
247 /* test for NULL */
248 if (fSymbols == 0) {
249 status = U_MEMORY_ALLOCATION_ERROR;
250 return;
251 }
252 }
253
254 initialize(fLocale, status);
255 if(U_SUCCESS(status)) {
256 initializeDefaultCentury();
257 }
258 }
259
260 //----------------------------------------------------------------------
261
SimpleDateFormat(const SimpleDateFormat & other)262 SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other)
263 : DateFormat(other),
264 fSymbols(NULL),
265 fGMTFormatters(NULL)
266 {
267 *this = other;
268 }
269
270 //----------------------------------------------------------------------
271
operator =(const SimpleDateFormat & other)272 SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other)
273 {
274 if (this == &other) {
275 return *this;
276 }
277 DateFormat::operator=(other);
278
279 delete fSymbols;
280 fSymbols = NULL;
281
282 if (other.fSymbols)
283 fSymbols = new DateFormatSymbols(*other.fSymbols);
284
285 fDefaultCenturyStart = other.fDefaultCenturyStart;
286 fDefaultCenturyStartYear = other.fDefaultCenturyStartYear;
287 fHaveDefaultCentury = other.fHaveDefaultCentury;
288
289 fPattern = other.fPattern;
290
291 return *this;
292 }
293
294 //----------------------------------------------------------------------
295
296 Format*
clone() const297 SimpleDateFormat::clone() const
298 {
299 return new SimpleDateFormat(*this);
300 }
301
302 //----------------------------------------------------------------------
303
304 UBool
operator ==(const Format & other) const305 SimpleDateFormat::operator==(const Format& other) const
306 {
307 if (DateFormat::operator==(other)) {
308 // DateFormat::operator== guarantees following cast is safe
309 SimpleDateFormat* that = (SimpleDateFormat*)&other;
310 return (fPattern == that->fPattern &&
311 fSymbols != NULL && // Check for pathological object
312 that->fSymbols != NULL && // Check for pathological object
313 *fSymbols == *that->fSymbols &&
314 fHaveDefaultCentury == that->fHaveDefaultCentury &&
315 fDefaultCenturyStart == that->fDefaultCenturyStart);
316 }
317 return FALSE;
318 }
319
320 //----------------------------------------------------------------------
321
construct(EStyle timeStyle,EStyle dateStyle,const Locale & locale,UErrorCode & status)322 void SimpleDateFormat::construct(EStyle timeStyle,
323 EStyle dateStyle,
324 const Locale& locale,
325 UErrorCode& status)
326 {
327 // called by several constructors to load pattern data from the resources
328 if (U_FAILURE(status)) return;
329
330 // We will need the calendar to know what type of symbols to load.
331 initializeCalendar(NULL, locale, status);
332 if (U_FAILURE(status)) return;
333
334 CalendarData calData(locale, fCalendar?fCalendar->getType():NULL, status);
335 UResourceBundle *dateTimePatterns = calData.getByKey(gDateTimePatternsTag, status);
336 if (U_FAILURE(status)) return;
337
338 if (ures_getSize(dateTimePatterns) <= kDateTime)
339 {
340 status = U_INVALID_FORMAT_ERROR;
341 return;
342 }
343
344 setLocaleIDs(ures_getLocaleByType(dateTimePatterns, ULOC_VALID_LOCALE, &status),
345 ures_getLocaleByType(dateTimePatterns, ULOC_ACTUAL_LOCALE, &status));
346
347 // create a symbols object from the locale
348 initializeSymbols(locale,fCalendar, status);
349 if (U_FAILURE(status)) return;
350 /* test for NULL */
351 if (fSymbols == 0) {
352 status = U_MEMORY_ALLOCATION_ERROR;
353 return;
354 }
355
356 const UChar *resStr;
357 int32_t resStrLen = 0;
358
359 // if the pattern should include both date and time information, use the date/time
360 // pattern string as a guide to tell use how to glue together the appropriate date
361 // and time pattern strings. The actual gluing-together is handled by a convenience
362 // method on MessageFormat.
363 if ((timeStyle != kNone) && (dateStyle != kNone))
364 {
365 Formattable timeDateArray[2];
366
367 // use Formattable::adoptString() so that we can use fastCopyFrom()
368 // instead of Formattable::setString()'s unaware, safe, deep string clone
369 // see Jitterbug 2296
370 resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)timeStyle, &resStrLen, &status);
371 timeDateArray[0].adoptString(new UnicodeString(TRUE, resStr, resStrLen));
372 resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)dateStyle, &resStrLen, &status);
373 timeDateArray[1].adoptString(new UnicodeString(TRUE, resStr, resStrLen));
374
375 resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)kDateTime, &resStrLen, &status);
376 MessageFormat::format(UnicodeString(TRUE, resStr, resStrLen), timeDateArray, 2, fPattern, status);
377 }
378 // if the pattern includes just time data or just date date, load the appropriate
379 // pattern string from the resources
380 // setTo() - see DateFormatSymbols::assignArray comments
381 else if (timeStyle != kNone) {
382 resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)timeStyle, &resStrLen, &status);
383 fPattern.setTo(TRUE, resStr, resStrLen);
384 }
385 else if (dateStyle != kNone) {
386 resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)dateStyle, &resStrLen, &status);
387 fPattern.setTo(TRUE, resStr, resStrLen);
388 }
389
390 // and if it includes _neither_, that's an error
391 else
392 status = U_INVALID_FORMAT_ERROR;
393
394 // finally, finish initializing by creating a Calendar and a NumberFormat
395 initialize(locale, status);
396 }
397
398 //----------------------------------------------------------------------
399
400 Calendar*
initializeCalendar(TimeZone * adoptZone,const Locale & locale,UErrorCode & status)401 SimpleDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status)
402 {
403 if(!U_FAILURE(status)) {
404 fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status);
405 }
406 if (U_SUCCESS(status) && fCalendar == NULL) {
407 status = U_MEMORY_ALLOCATION_ERROR;
408 }
409 return fCalendar;
410 }
411
412 void
initializeSymbols(const Locale & locale,Calendar * calendar,UErrorCode & status)413 SimpleDateFormat::initializeSymbols(const Locale& locale, Calendar* calendar, UErrorCode& status)
414 {
415 if(U_FAILURE(status)) {
416 fSymbols = NULL;
417 } else {
418 // pass in calendar type - use NULL (default) if no calendar set (or err).
419 fSymbols = new DateFormatSymbols(locale, calendar?calendar->getType() :NULL , status);
420 }
421 }
422
423 void
initialize(const Locale & locale,UErrorCode & status)424 SimpleDateFormat::initialize(const Locale& locale,
425 UErrorCode& status)
426 {
427 if (U_FAILURE(status)) return;
428
429 // We don't need to check that the row count is >= 1, since all 2d arrays have at
430 // least one row
431 fNumberFormat = NumberFormat::createInstance(locale, status);
432 if (fNumberFormat != NULL && U_SUCCESS(status))
433 {
434 // no matter what the locale's default number format looked like, we want
435 // to modify it so that it doesn't use thousands separators, doesn't always
436 // show the decimal point, and recognizes integers only when parsing
437
438 fNumberFormat->setGroupingUsed(FALSE);
439 if (fNumberFormat->getDynamicClassID() == DecimalFormat::getStaticClassID())
440 ((DecimalFormat*)fNumberFormat)->setDecimalSeparatorAlwaysShown(FALSE);
441 fNumberFormat->setParseIntegerOnly(TRUE);
442 fNumberFormat->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
443 }
444 else if (U_SUCCESS(status))
445 {
446 status = U_MISSING_RESOURCE_ERROR;
447 }
448 }
449
450 /* Initialize the fields we use to disambiguate ambiguous years. Separate
451 * so we can call it from readObject().
452 */
initializeDefaultCentury()453 void SimpleDateFormat::initializeDefaultCentury()
454 {
455 if(fCalendar) {
456 fHaveDefaultCentury = fCalendar->haveDefaultCentury();
457 if(fHaveDefaultCentury) {
458 fDefaultCenturyStart = fCalendar->defaultCenturyStart();
459 fDefaultCenturyStartYear = fCalendar->defaultCenturyStartYear();
460 } else {
461 fDefaultCenturyStart = DBL_MIN;
462 fDefaultCenturyStartYear = -1;
463 }
464 }
465 }
466
467 /* Define one-century window into which to disambiguate dates using
468 * two-digit years. Make public in JDK 1.2.
469 */
parseAmbiguousDatesAsAfter(UDate startDate,UErrorCode & status)470 void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& status)
471 {
472 if(U_FAILURE(status)) {
473 return;
474 }
475 if(!fCalendar) {
476 status = U_ILLEGAL_ARGUMENT_ERROR;
477 return;
478 }
479
480 fCalendar->setTime(startDate, status);
481 if(U_SUCCESS(status)) {
482 fHaveDefaultCentury = TRUE;
483 fDefaultCenturyStart = startDate;
484 fDefaultCenturyStartYear = fCalendar->get(UCAL_YEAR, status);
485 }
486 }
487
488 //----------------------------------------------------------------------
489
490 UnicodeString&
format(Calendar & cal,UnicodeString & appendTo,FieldPosition & pos) const491 SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition& pos) const
492 {
493 UErrorCode status = U_ZERO_ERROR;
494 pos.setBeginIndex(0);
495 pos.setEndIndex(0);
496
497 UBool inQuote = FALSE;
498 UChar prevCh = 0;
499 int32_t count = 0;
500
501 // loop through the pattern string character by character
502 for (int32_t i = 0; i < fPattern.length() && U_SUCCESS(status); ++i) {
503 UChar ch = fPattern[i];
504
505 // Use subFormat() to format a repeated pattern character
506 // when a different pattern or non-pattern character is seen
507 if (ch != prevCh && count > 0) {
508 subFormat(appendTo, prevCh, count, pos, cal, status);
509 count = 0;
510 }
511 if (ch == QUOTE) {
512 // Consecutive single quotes are a single quote literal,
513 // either outside of quotes or between quotes
514 if ((i+1) < fPattern.length() && fPattern[i+1] == QUOTE) {
515 appendTo += (UChar)QUOTE;
516 ++i;
517 } else {
518 inQuote = ! inQuote;
519 }
520 }
521 else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
522 || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
523 // ch is a date-time pattern character to be interpreted
524 // by subFormat(); count the number of times it is repeated
525 prevCh = ch;
526 ++count;
527 }
528 else {
529 // Append quoted characters and unquoted non-pattern characters
530 appendTo += ch;
531 }
532 }
533
534 // Format the last item in the pattern, if any
535 if (count > 0) {
536 subFormat(appendTo, prevCh, count, pos, cal, status);
537 }
538
539 // and if something failed (e.g., an invalid format character), reset our FieldPosition
540 // to (0, 0) to show that
541 // {sfb} look at this later- are these being set correctly?
542 if (U_FAILURE(status)) {
543 pos.setBeginIndex(0);
544 pos.setEndIndex(0);
545 }
546
547 return appendTo;
548 }
549
550 UnicodeString&
format(const Formattable & obj,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const551 SimpleDateFormat::format(const Formattable& obj,
552 UnicodeString& appendTo,
553 FieldPosition& pos,
554 UErrorCode& status) const
555 {
556 // this is just here to get around the hiding problem
557 // (the previous format() override would hide the version of
558 // format() on DateFormat that this function correspond to, so we
559 // have to redefine it here)
560 return DateFormat::format(obj, appendTo, pos, status);
561 }
562
563 //----------------------------------------------------------------------
564
565 // Map index into pattern character string to Calendar field number.
566 const UCalendarDateFields
567 SimpleDateFormat::fgPatternIndexToCalendarField[] =
568 {
569 /*GyM*/ UCAL_ERA, UCAL_YEAR, UCAL_MONTH,
570 /*dkH*/ UCAL_DATE, UCAL_HOUR_OF_DAY, UCAL_HOUR_OF_DAY,
571 /*msS*/ UCAL_MINUTE, UCAL_SECOND, UCAL_MILLISECOND,
572 /*EDF*/ UCAL_DAY_OF_WEEK, UCAL_DAY_OF_YEAR, UCAL_DAY_OF_WEEK_IN_MONTH,
573 /*wWa*/ UCAL_WEEK_OF_YEAR, UCAL_WEEK_OF_MONTH, UCAL_AM_PM,
574 /*hKz*/ UCAL_HOUR, UCAL_HOUR, UCAL_ZONE_OFFSET,
575 /*Yeu*/ UCAL_YEAR_WOY, UCAL_DOW_LOCAL, UCAL_EXTENDED_YEAR,
576 /*gAZ*/ UCAL_JULIAN_DAY, UCAL_MILLISECONDS_IN_DAY, UCAL_ZONE_OFFSET,
577 /*v*/ UCAL_ZONE_OFFSET,
578 /*c*/ UCAL_DAY_OF_WEEK,
579 /*L*/ UCAL_MONTH,
580 /*Q*/ UCAL_MONTH,
581 /*q*/ UCAL_MONTH,
582 /*V*/ UCAL_ZONE_OFFSET,
583 };
584
585 // Map index into pattern character string to DateFormat field number
586 const UDateFormatField
587 SimpleDateFormat::fgPatternIndexToDateFormatField[] = {
588 /*GyM*/ UDAT_ERA_FIELD, UDAT_YEAR_FIELD, UDAT_MONTH_FIELD,
589 /*dkH*/ UDAT_DATE_FIELD, UDAT_HOUR_OF_DAY1_FIELD, UDAT_HOUR_OF_DAY0_FIELD,
590 /*msS*/ UDAT_MINUTE_FIELD, UDAT_SECOND_FIELD, UDAT_FRACTIONAL_SECOND_FIELD,
591 /*EDF*/ UDAT_DAY_OF_WEEK_FIELD, UDAT_DAY_OF_YEAR_FIELD, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD,
592 /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD, UDAT_WEEK_OF_MONTH_FIELD, UDAT_AM_PM_FIELD,
593 /*hKz*/ UDAT_HOUR1_FIELD, UDAT_HOUR0_FIELD, UDAT_TIMEZONE_FIELD,
594 /*Yeu*/ UDAT_YEAR_WOY_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_EXTENDED_YEAR_FIELD,
595 /*gAZ*/ UDAT_JULIAN_DAY_FIELD, UDAT_MILLISECONDS_IN_DAY_FIELD, UDAT_TIMEZONE_RFC_FIELD,
596 /*v*/ UDAT_TIMEZONE_GENERIC_FIELD,
597 /*c*/ UDAT_STANDALONE_DAY_FIELD,
598 /*L*/ UDAT_STANDALONE_MONTH_FIELD,
599 /*Q*/ UDAT_QUARTER_FIELD,
600 /*q*/ UDAT_STANDALONE_QUARTER_FIELD,
601 /*V*/ UDAT_TIMEZONE_SPECIAL_FIELD,
602 };
603
604 //----------------------------------------------------------------------
605
606 /**
607 * Append symbols[value] to dst. Make sure the array index is not out
608 * of bounds.
609 */
610 static inline void
_appendSymbol(UnicodeString & dst,int32_t value,const UnicodeString * symbols,int32_t symbolsCount)611 _appendSymbol(UnicodeString& dst,
612 int32_t value,
613 const UnicodeString* symbols,
614 int32_t symbolsCount) {
615 U_ASSERT(0 <= value && value < symbolsCount);
616 if (0 <= value && value < symbolsCount) {
617 dst += symbols[value];
618 }
619 }
620
621 //---------------------------------------------------------------------
622 void
appendGMT(UnicodeString & appendTo,Calendar & cal,UErrorCode & status) const623 SimpleDateFormat::appendGMT(UnicodeString &appendTo, Calendar& cal, UErrorCode& status) const{
624 int32_t offset = cal.get(UCAL_ZONE_OFFSET, status) + cal.get(UCAL_DST_OFFSET, status);
625 if (U_FAILURE(status)) {
626 return;
627 }
628 if (isDefaultGMTFormat()) {
629 formatGMTDefault(appendTo, offset);
630 } else {
631 ((SimpleDateFormat*)this)->initGMTFormatters(status);
632 if (U_SUCCESS(status)) {
633 int32_t type;
634 if (offset < 0) {
635 offset = -offset;
636 type = (offset % U_MILLIS_PER_MINUTE) == 0 ? kGMTNegativeHM : kGMTNegativeHMS;
637 } else {
638 type = (offset % U_MILLIS_PER_MINUTE) == 0 ? kGMTPositiveHM : kGMTPositiveHMS;
639 }
640 Formattable param(offset, Formattable::kIsDate);
641 FieldPosition fpos(0);
642 fGMTFormatters[type]->format(¶m, 1, appendTo, fpos, status);
643 }
644 }
645 }
646
647 int32_t
parseGMT(const UnicodeString & text,ParsePosition & pos) const648 SimpleDateFormat::parseGMT(const UnicodeString &text, ParsePosition &pos) const {
649 if (!isDefaultGMTFormat()) {
650 int32_t start = pos.getIndex();
651
652 // Quick check
653 UBool prefixMatch = FALSE;
654 int32_t prefixLen = fSymbols->fGmtFormat.indexOf((UChar)0x007B /* '{' */);
655 if (prefixLen > 0 && text.compare(start, prefixLen, fSymbols->fGmtFormat, 0, prefixLen) == 0) {
656 prefixMatch = TRUE;
657 }
658 if (prefixMatch) {
659 // Prefix matched
660 UErrorCode status = U_ZERO_ERROR;
661 ((SimpleDateFormat*)this)->initGMTFormatters(status);
662 if (U_SUCCESS(status)) {
663 Formattable parsed;
664 int32_t parsedCount;
665
666 // Try negative Hms
667 fGMTFormatters[kGMTNegativeHMS]->parseObject(text, parsed, pos);
668 if (pos.getErrorIndex() == -1 && pos.getIndex() > start) {
669 parsed.getArray(parsedCount);
670 if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) {
671 return (int32_t)(-1 * parsed[0].getDate());
672 }
673 }
674
675 // Reset ParsePosition
676 pos.setIndex(start);
677 pos.setErrorIndex(-1);
678
679 // Try positive Hms
680 fGMTFormatters[kGMTPositiveHMS]->parseObject(text, parsed, pos);
681 if (pos.getErrorIndex() == -1 && pos.getIndex() > start) {
682 parsed.getArray(parsedCount);
683 if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) {
684 return (int32_t)parsed[0].getDate();
685 }
686 }
687
688 // Reset ParsePosition
689 pos.setIndex(start);
690 pos.setErrorIndex(-1);
691
692 // Try negative Hm
693 fGMTFormatters[kGMTNegativeHM]->parseObject(text, parsed, pos);
694 if (pos.getErrorIndex() == -1 && pos.getIndex() > start) {
695 parsed.getArray(parsedCount);
696 if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) {
697 return (int32_t)(-1 * parsed[0].getDate());
698 }
699 }
700
701 // Reset ParsePosition
702 pos.setIndex(start);
703 pos.setErrorIndex(-1);
704
705 // Try positive Hm
706 fGMTFormatters[kGMTPositiveHM]->parseObject(text, parsed, pos);
707 if (pos.getErrorIndex() == -1 && pos.getIndex() > start) {
708 parsed.getArray(parsedCount);
709 if (parsedCount == 1 && parsed[0].getType() == Formattable::kDate) {
710 return (int32_t)parsed[0].getDate();
711 }
712 }
713
714 // Reset ParsePosition
715 pos.setIndex(start);
716 pos.setErrorIndex(-1);
717 }
718 // fall through to the default GMT parsing method
719 }
720 }
721 return parseGMTDefault(text, pos);
722 }
723
724 void
formatGMTDefault(UnicodeString & appendTo,int32_t offset) const725 SimpleDateFormat::formatGMTDefault(UnicodeString &appendTo, int32_t offset) const {
726 if (offset < 0) {
727 appendTo += gGmtMinus;
728 offset = -offset; // suppress the '-' sign for text display.
729 }else{
730 appendTo += gGmtPlus;
731 }
732
733 offset /= U_MILLIS_PER_SECOND; // now in seconds
734 int32_t sec = offset % 60;
735 offset /= 60;
736 int32_t min = offset % 60;
737 int32_t hour = offset / 60;
738
739
740 zeroPaddingNumber(appendTo, hour, 2, 2);
741 appendTo += (UChar)0x003A /*':'*/;
742 zeroPaddingNumber(appendTo, min, 2, 2);
743 if (sec != 0) {
744 appendTo += (UChar)0x003A /*':'*/;
745 zeroPaddingNumber(appendTo, sec, 2, 2);
746 }
747 }
748
749 int32_t
parseGMTDefault(const UnicodeString & text,ParsePosition & pos) const750 SimpleDateFormat::parseGMTDefault(const UnicodeString &text, ParsePosition &pos) const {
751 int32_t start = pos.getIndex();
752
753 if (start + kGmtLen + 1 >= text.length()) {
754 pos.setErrorIndex(start);
755 return 0;
756 }
757
758 int32_t cur = start;
759 // "GMT"
760 if (text.compare(start, kGmtLen, gGmt) != 0) {
761 pos.setErrorIndex(start);
762 return 0;
763 }
764 cur += kGmtLen;
765 // Sign
766 UBool negative = FALSE;
767 if (text.charAt(cur) == (UChar)0x002D /* minus */) {
768 negative = TRUE;
769 } else if (text.charAt(cur) != (UChar)0x002B /* plus */) {
770 pos.setErrorIndex(cur);
771 return 0;
772 }
773 cur++;
774
775 // Numbers
776 int32_t numLen;
777 pos.setIndex(cur);
778
779 Formattable number;
780 parseInt(text, number, 6, pos, FALSE);
781 numLen = pos.getIndex() - cur;
782
783 if (numLen <= 0) {
784 pos.setIndex(start);
785 pos.setErrorIndex(cur);
786 return 0;
787 }
788
789 int32_t numVal = number.getLong();
790
791 int32_t hour = 0;
792 int32_t min = 0;
793 int32_t sec = 0;
794
795 if (numLen <= 2) {
796 // H[H][:mm[:ss]]
797 hour = numVal;
798 cur += numLen;
799 if (cur + 2 < text.length() && text.charAt(cur) == (UChar)0x003A /* colon */) {
800 cur++;
801 pos.setIndex(cur);
802 parseInt(text, number, 2, pos, FALSE);
803 numLen = pos.getIndex() - cur;
804 if (numLen == 2) {
805 // got minute field
806 min = number.getLong();
807 cur += numLen;
808 if (cur + 2 < text.length() && text.charAt(cur) == (UChar)0x003A /* colon */) {
809 cur++;
810 pos.setIndex(cur);
811 parseInt(text, number, 2, pos, FALSE);
812 numLen = pos.getIndex() - cur;
813 if (numLen == 2) {
814 // got second field
815 sec = number.getLong();
816 } else {
817 // reset position
818 pos.setIndex(cur - 1);
819 pos.setErrorIndex(-1);
820 }
821 }
822 } else {
823 // reset postion
824 pos.setIndex(cur - 1);
825 pos.setErrorIndex(-1);
826 }
827 }
828 } else if (numLen == 3 || numLen == 4) {
829 // Hmm or HHmm
830 hour = numVal / 100;
831 min = numVal % 100;
832 } else if (numLen == 5 || numLen == 6) {
833 // Hmmss or HHmmss
834 hour = numVal / 10000;
835 min = (numVal % 10000) / 100;
836 sec = numVal % 100;
837 } else {
838 // HHmmss followed by bogus numbers
839 pos.setIndex(cur + 6);
840
841 int32_t shift = numLen - 6;
842 while (shift > 0) {
843 numVal /= 10;
844 shift--;
845 }
846 hour = numVal / 10000;
847 min = (numVal % 10000) / 100;
848 sec = numVal % 100;
849 }
850
851 int32_t offset = ((hour*60 + min)*60 + sec)*1000;
852 if (negative) {
853 offset = -offset;
854 }
855 return offset;
856 }
857
858 UBool
isDefaultGMTFormat() const859 SimpleDateFormat::isDefaultGMTFormat() const {
860 // GMT pattern
861 if (fSymbols->fGmtFormat.length() == 0) {
862 // No GMT pattern is set
863 return TRUE;
864 } else if (fSymbols->fGmtFormat.compare(gDefGmtPat, kGmtPatLen) != 0) {
865 return FALSE;
866 }
867 // Hour patterns
868 if (fSymbols->fGmtHourFormats == NULL || fSymbols->fGmtHourFormatsCount != DateFormatSymbols::GMT_HOUR_COUNT) {
869 // No Hour pattern is set
870 return TRUE;
871 } else if ((fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HMS].compare(gDefGmtNegHmsPat, kNegHmsLen) != 0)
872 || (fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HM].compare(gDefGmtNegHmPat, kNegHmLen) != 0)
873 || (fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HMS].compare(gDefGmtPosHmsPat, kPosHmsLen) != 0)
874 || (fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HM].compare(gDefGmtPosHmPat, kPosHmLen) != 0)) {
875 return FALSE;
876 }
877 return TRUE;
878 }
879
880 void
formatRFC822TZ(UnicodeString & appendTo,int32_t offset) const881 SimpleDateFormat::formatRFC822TZ(UnicodeString &appendTo, int32_t offset) const {
882 UChar sign = 0x002B /* '+' */;
883 if (offset < 0) {
884 offset = -offset;
885 sign = 0x002D /* '-' */;
886 }
887 appendTo.append(sign);
888
889 int32_t offsetH = offset / U_MILLIS_PER_HOUR;
890 offset = offset % U_MILLIS_PER_HOUR;
891 int32_t offsetM = offset / U_MILLIS_PER_MINUTE;
892 offset = offset % U_MILLIS_PER_MINUTE;
893 int32_t offsetS = offset / U_MILLIS_PER_SECOND;
894
895 int32_t num = 0, denom = 0;
896 if (offsetS == 0) {
897 offset = offsetH*100 + offsetM; // HHmm
898 num = offset % 10000;
899 denom = 1000;
900 } else {
901 offset = offsetH*10000 + offsetM*100 + offsetS; // HHmmss
902 num = offset % 1000000;
903 denom = 100000;
904 }
905 while (denom >= 1) {
906 UChar digit = (UChar)0x0030 + (num / denom);
907 appendTo.append(digit);
908 num = num % denom;
909 denom /= 10;
910 }
911 }
912
913 void
initGMTFormatters(UErrorCode & status)914 SimpleDateFormat::initGMTFormatters(UErrorCode &status) {
915 if (U_FAILURE(status)) {
916 return;
917 }
918 umtx_lock(&LOCK);
919 if (fGMTFormatters == NULL) {
920 fGMTFormatters = (MessageFormat**)uprv_malloc(kNumGMTFormatters * sizeof(MessageFormat*));
921 if (fGMTFormatters) {
922 for (int32_t i = 0; i < kNumGMTFormatters; i++) {
923 const UnicodeString *hourPattern;
924 switch (i) {
925 case kGMTNegativeHMS:
926 hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HMS]);
927 break;
928 case kGMTNegativeHM:
929 hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_NEGATIVE_HM]);
930 break;
931 case kGMTPositiveHMS:
932 hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HMS]);
933 break;
934 case kGMTPositiveHM:
935 hourPattern = &(fSymbols->fGmtHourFormats[DateFormatSymbols::GMT_POSITIVE_HM]);
936 break;
937 }
938 fGMTFormatters[i] = new MessageFormat(fSymbols->fGmtFormat, status);
939 if (U_FAILURE(status)) {
940 break;
941 }
942 SimpleDateFormat *sdf = (SimpleDateFormat*)this->clone();
943 sdf->adoptTimeZone(TimeZone::createTimeZone(UnicodeString(gEtcUTC)));
944 sdf->applyPattern(*hourPattern);
945 fGMTFormatters[i]->adoptFormat(0, sdf);
946 }
947 } else {
948 status = U_MEMORY_ALLOCATION_ERROR;
949 }
950 }
951 umtx_unlock(&LOCK);
952 }
953
954 //---------------------------------------------------------------------
955 void
subFormat(UnicodeString & appendTo,UChar ch,int32_t count,FieldPosition & pos,Calendar & cal,UErrorCode & status) const956 SimpleDateFormat::subFormat(UnicodeString &appendTo,
957 UChar ch,
958 int32_t count,
959 FieldPosition& pos,
960 Calendar& cal,
961 UErrorCode& status) const
962 {
963 if (U_FAILURE(status)) {
964 return;
965 }
966
967 // this function gets called by format() to produce the appropriate substitution
968 // text for an individual pattern symbol (e.g., "HH" or "yyyy")
969
970 UChar *patternCharPtr = u_strchr(DateFormatSymbols::getPatternUChars(), ch);
971 UDateFormatField patternCharIndex;
972 const int32_t maxIntCount = 10;
973 int32_t beginOffset = appendTo.length();
974
975 // if the pattern character is unrecognized, signal an error and dump out
976 if (patternCharPtr == NULL)
977 {
978 status = U_INVALID_FORMAT_ERROR;
979 return;
980 }
981
982 patternCharIndex = (UDateFormatField)(patternCharPtr - DateFormatSymbols::getPatternUChars());
983 UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex];
984 int32_t value = cal.get(field, status);
985 if (U_FAILURE(status)) {
986 return;
987 }
988
989 switch (patternCharIndex) {
990
991 // for any "G" symbol, write out the appropriate era string
992 // "GGGG" is wide era name, anything else is abbreviated name
993 case UDAT_ERA_FIELD:
994 if (count >= 4)
995 _appendSymbol(appendTo, value, fSymbols->fEraNames, fSymbols->fEraNamesCount);
996 else
997 _appendSymbol(appendTo, value, fSymbols->fEras, fSymbols->fErasCount);
998 break;
999
1000 // for "yyyy", write out the whole year; for "yy", write out the last 2 digits
1001 case UDAT_YEAR_FIELD:
1002 case UDAT_YEAR_WOY_FIELD:
1003 if (count >= 4)
1004 zeroPaddingNumber(appendTo, value, 4, maxIntCount);
1005 else if(count == 1)
1006 zeroPaddingNumber(appendTo, value, count, maxIntCount);
1007 else
1008 zeroPaddingNumber(appendTo, value, 2, 2);
1009 break; // TODO: this needs to be synced with Java, with GCL/Shanghai's work
1010
1011 // for "MMMM", write out the whole month name, for "MMM", write out the month
1012 // abbreviation, for "M" or "MM", write out the month as a number with the
1013 // appropriate number of digits
1014 // for "MMMMM", use the narrow form
1015 case UDAT_MONTH_FIELD:
1016 if (count == 5)
1017 _appendSymbol(appendTo, value, fSymbols->fNarrowMonths,
1018 fSymbols->fNarrowMonthsCount);
1019 else if (count == 4)
1020 _appendSymbol(appendTo, value, fSymbols->fMonths,
1021 fSymbols->fMonthsCount);
1022 else if (count == 3)
1023 _appendSymbol(appendTo, value, fSymbols->fShortMonths,
1024 fSymbols->fShortMonthsCount);
1025 else
1026 zeroPaddingNumber(appendTo, value + 1, count, maxIntCount);
1027 break;
1028
1029 // for "LLLL", write out the whole month name, for "LLL", write out the month
1030 // abbreviation, for "L" or "LL", write out the month as a number with the
1031 // appropriate number of digits
1032 // for "LLLLL", use the narrow form
1033 case UDAT_STANDALONE_MONTH_FIELD:
1034 if (count == 5)
1035 _appendSymbol(appendTo, value, fSymbols->fStandaloneNarrowMonths,
1036 fSymbols->fStandaloneNarrowMonthsCount);
1037 else if (count == 4)
1038 _appendSymbol(appendTo, value, fSymbols->fStandaloneMonths,
1039 fSymbols->fStandaloneMonthsCount);
1040 else if (count == 3)
1041 _appendSymbol(appendTo, value, fSymbols->fStandaloneShortMonths,
1042 fSymbols->fStandaloneShortMonthsCount);
1043 else
1044 zeroPaddingNumber(appendTo, value + 1, count, maxIntCount);
1045 break;
1046
1047 // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
1048 case UDAT_HOUR_OF_DAY1_FIELD:
1049 if (value == 0)
1050 zeroPaddingNumber(appendTo, cal.getMaximum(UCAL_HOUR_OF_DAY) + 1, count, maxIntCount);
1051 else
1052 zeroPaddingNumber(appendTo, value, count, maxIntCount);
1053 break;
1054
1055 case UDAT_FRACTIONAL_SECOND_FIELD:
1056 // Fractional seconds left-justify
1057 {
1058 fNumberFormat->setMinimumIntegerDigits((count > 3) ? 3 : count);
1059 fNumberFormat->setMaximumIntegerDigits(maxIntCount);
1060 if (count == 1) {
1061 value = (value + 50) / 100;
1062 } else if (count == 2) {
1063 value = (value + 5) / 10;
1064 }
1065 FieldPosition p(0);
1066 fNumberFormat->format(value, appendTo, p);
1067 if (count > 3) {
1068 fNumberFormat->setMinimumIntegerDigits(count - 3);
1069 fNumberFormat->format((int32_t)0, appendTo, p);
1070 }
1071 }
1072 break;
1073
1074 // for "EEE", write out the abbreviated day-of-the-week name
1075 // for "EEEE", write out the wide day-of-the-week name
1076 // for "EEEEE", use the narrow day-of-the-week name
1077 case UDAT_DAY_OF_WEEK_FIELD:
1078 if (count == 5)
1079 _appendSymbol(appendTo, value, fSymbols->fNarrowWeekdays,
1080 fSymbols->fNarrowWeekdaysCount);
1081 else if (count == 4)
1082 _appendSymbol(appendTo, value, fSymbols->fWeekdays,
1083 fSymbols->fWeekdaysCount);
1084 else
1085 _appendSymbol(appendTo, value, fSymbols->fShortWeekdays,
1086 fSymbols->fShortWeekdaysCount);
1087 break;
1088
1089 // for "ccc", write out the abbreviated day-of-the-week name
1090 // for "cccc", write out the wide day-of-the-week name
1091 // for "ccccc", use the narrow day-of-the-week name
1092 case UDAT_STANDALONE_DAY_FIELD:
1093 if (count == 5)
1094 _appendSymbol(appendTo, value, fSymbols->fStandaloneNarrowWeekdays,
1095 fSymbols->fStandaloneNarrowWeekdaysCount);
1096 else if (count == 4)
1097 _appendSymbol(appendTo, value, fSymbols->fStandaloneWeekdays,
1098 fSymbols->fStandaloneWeekdaysCount);
1099 else if (count == 3)
1100 _appendSymbol(appendTo, value, fSymbols->fStandaloneShortWeekdays,
1101 fSymbols->fStandaloneShortWeekdaysCount);
1102 else
1103 zeroPaddingNumber(appendTo, value, 1, maxIntCount);
1104 break;
1105
1106 // for and "a" symbol, write out the whole AM/PM string
1107 case UDAT_AM_PM_FIELD:
1108 _appendSymbol(appendTo, value, fSymbols->fAmPms,
1109 fSymbols->fAmPmsCount);
1110 break;
1111
1112 // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
1113 // as "12"
1114 case UDAT_HOUR1_FIELD:
1115 if (value == 0)
1116 zeroPaddingNumber(appendTo, cal.getLeastMaximum(UCAL_HOUR) + 1, count, maxIntCount);
1117 else
1118 zeroPaddingNumber(appendTo, value, count, maxIntCount);
1119 break;
1120
1121 // for the "z" symbols, we have to check our time zone data first. If we have a
1122 // localized name for the time zone, then "zzzz" / "zzz" indicate whether
1123 // daylight time is in effect (long/short) and "zz" / "z" do not (long/short).
1124 // If we don't have a localized time zone name,
1125 // then the time zone shows up as "GMT+hh:mm" or "GMT-hh:mm" (where "hh:mm" is the
1126 // offset from GMT) regardless of how many z's were in the pattern symbol
1127 case UDAT_TIMEZONE_FIELD:
1128 case UDAT_TIMEZONE_GENERIC_FIELD:
1129 case UDAT_TIMEZONE_SPECIAL_FIELD:
1130 {
1131 UnicodeString zoneString;
1132 const ZoneStringFormat *zsf = fSymbols->getZoneStringFormat();
1133 if (zsf) {
1134 if (patternCharIndex == UDAT_TIMEZONE_FIELD) {
1135 if (count < 4) {
1136 // "z", "zz", "zzz"
1137 zsf->getSpecificShortString(cal, TRUE /*commonly used only*/,
1138 zoneString, status);
1139 } else {
1140 // "zzzz"
1141 zsf->getSpecificLongString(cal, zoneString, status);
1142 }
1143 } else if (patternCharIndex == UDAT_TIMEZONE_GENERIC_FIELD) {
1144 if (count == 1) {
1145 // "v"
1146 zsf->getGenericShortString(cal, TRUE /*commonly used only*/,
1147 zoneString, status);
1148 } else if (count == 4) {
1149 // "vvvv"
1150 zsf->getGenericLongString(cal, zoneString, status);
1151 }
1152 } else { // patternCharIndex == UDAT_TIMEZONE_SPECIAL_FIELD
1153 if (count == 1) {
1154 // "V"
1155 zsf->getSpecificShortString(cal, FALSE /*ignore commonly used*/,
1156 zoneString, status);
1157 } else if (count == 4) {
1158 // "VVVV"
1159 zsf->getGenericLocationString(cal, zoneString, status);
1160 }
1161 }
1162 }
1163 if (zoneString.isEmpty()) {
1164 appendGMT(appendTo, cal, status);
1165 } else {
1166 appendTo += zoneString;
1167 }
1168 }
1169 break;
1170
1171 case UDAT_TIMEZONE_RFC_FIELD: // 'Z' - TIMEZONE_RFC
1172 if (count < 4) {
1173 // RFC822 format, must use ASCII digits
1174 value = (cal.get(UCAL_ZONE_OFFSET, status) + cal.get(UCAL_DST_OFFSET, status));
1175 formatRFC822TZ(appendTo, value);
1176 } else {
1177 // long form, localized GMT pattern
1178 appendGMT(appendTo, cal, status);
1179 }
1180 break;
1181
1182 case UDAT_QUARTER_FIELD:
1183 if (count >= 4)
1184 _appendSymbol(appendTo, value/3, fSymbols->fQuarters,
1185 fSymbols->fQuartersCount);
1186 else if (count == 3)
1187 _appendSymbol(appendTo, value/3, fSymbols->fShortQuarters,
1188 fSymbols->fShortQuartersCount);
1189 else
1190 zeroPaddingNumber(appendTo, (value/3) + 1, count, maxIntCount);
1191 break;
1192
1193 case UDAT_STANDALONE_QUARTER_FIELD:
1194 if (count >= 4)
1195 _appendSymbol(appendTo, value/3, fSymbols->fStandaloneQuarters,
1196 fSymbols->fStandaloneQuartersCount);
1197 else if (count == 3)
1198 _appendSymbol(appendTo, value/3, fSymbols->fStandaloneShortQuarters,
1199 fSymbols->fStandaloneShortQuartersCount);
1200 else
1201 zeroPaddingNumber(appendTo, (value/3) + 1, count, maxIntCount);
1202 break;
1203
1204
1205 // all of the other pattern symbols can be formatted as simple numbers with
1206 // appropriate zero padding
1207 default:
1208 zeroPaddingNumber(appendTo, value, count, maxIntCount);
1209 break;
1210 }
1211
1212 // if the field we're formatting is the one the FieldPosition says it's interested
1213 // in, fill in the FieldPosition with this field's positions
1214 if (pos.getBeginIndex() == pos.getEndIndex() &&
1215 pos.getField() == fgPatternIndexToDateFormatField[patternCharIndex]) {
1216 pos.setBeginIndex(beginOffset);
1217 pos.setEndIndex(appendTo.length());
1218 }
1219 }
1220
1221 //----------------------------------------------------------------------
1222 void
zeroPaddingNumber(UnicodeString & appendTo,int32_t value,int32_t minDigits,int32_t maxDigits) const1223 SimpleDateFormat::zeroPaddingNumber(UnicodeString &appendTo, int32_t value, int32_t minDigits, int32_t maxDigits) const
1224 {
1225 FieldPosition pos(0);
1226
1227 fNumberFormat->setMinimumIntegerDigits(minDigits);
1228 fNumberFormat->setMaximumIntegerDigits(maxDigits);
1229 fNumberFormat->format(value, appendTo, pos); // 3rd arg is there to speed up processing
1230 }
1231
1232 //----------------------------------------------------------------------
1233
1234 /**
1235 * Format characters that indicate numeric fields. The character
1236 * at index 0 is treated specially.
1237 */
1238 static const UChar NUMERIC_FORMAT_CHARS[] = {0x4D, 0x79, 0x75, 0x64, 0x68, 0x48, 0x6D, 0x73, 0x53, 0x44, 0x46, 0x77, 0x57, 0x6B, 0x4B, 0x00}; /* "MyudhHmsSDFwWkK" */
1239
1240 /**
1241 * Return true if the given format character, occuring count
1242 * times, represents a numeric field.
1243 */
isNumeric(UChar formatChar,int32_t count)1244 UBool SimpleDateFormat::isNumeric(UChar formatChar, int32_t count) {
1245 UnicodeString s(NUMERIC_FORMAT_CHARS);
1246 int32_t i = s.indexOf(formatChar);
1247 return (i > 0 || (i == 0 && count < 3));
1248 }
1249
1250 void
parse(const UnicodeString & text,Calendar & cal,ParsePosition & parsePos) const1251 SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& parsePos) const
1252 {
1253 int32_t pos = parsePos.getIndex();
1254 int32_t start = pos;
1255 UBool ambiguousYear[] = { FALSE };
1256 int32_t count = 0;
1257
1258 // hack, reset tztype, cast away const
1259 ((SimpleDateFormat*)this)->tztype = TZTYPE_UNK;
1260
1261 // For parsing abutting numeric fields. 'abutPat' is the
1262 // offset into 'pattern' of the first of 2 or more abutting
1263 // numeric fields. 'abutStart' is the offset into 'text'
1264 // where parsing the fields begins. 'abutPass' starts off as 0
1265 // and increments each time we try to parse the fields.
1266 int32_t abutPat = -1; // If >=0, we are in a run of abutting numeric fields
1267 int32_t abutStart = 0;
1268 int32_t abutPass = 0;
1269 UBool inQuote = FALSE;
1270
1271 const UnicodeString numericFormatChars(NUMERIC_FORMAT_CHARS);
1272
1273 for (int32_t i=0; i<fPattern.length(); ++i) {
1274 UChar ch = fPattern.charAt(i);
1275
1276 // Handle alphabetic field characters.
1277 if (!inQuote && ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A))) { // [A-Za-z]
1278 int32_t fieldPat = i;
1279
1280 // Count the length of this field specifier
1281 count = 1;
1282 while ((i+1)<fPattern.length() &&
1283 fPattern.charAt(i+1) == ch) {
1284 ++count;
1285 ++i;
1286 }
1287
1288 if (isNumeric(ch, count)) {
1289 if (abutPat < 0) {
1290 // Determine if there is an abutting numeric field. For
1291 // most fields we can just look at the next characters,
1292 // but the 'm' field is either numeric or text,
1293 // depending on the count, so we have to look ahead for
1294 // that field.
1295 if ((i+1)<fPattern.length()) {
1296 UBool abutting;
1297 UChar nextCh = fPattern.charAt(i+1);
1298 int32_t k = numericFormatChars.indexOf(nextCh);
1299 if (k == 0) {
1300 int32_t j = i+2;
1301 while (j<fPattern.length() &&
1302 fPattern.charAt(j) == nextCh) {
1303 ++j;
1304 }
1305 abutting = (j-i) < 4; // nextCount < 3
1306 } else {
1307 abutting = k > 0;
1308 }
1309
1310 // Record the start of a set of abutting numeric
1311 // fields.
1312 if (abutting) {
1313 abutPat = fieldPat;
1314 abutStart = pos;
1315 abutPass = 0;
1316 }
1317 }
1318 }
1319 } else {
1320 abutPat = -1; // End of any abutting fields
1321 }
1322
1323 // Handle fields within a run of abutting numeric fields. Take
1324 // the pattern "HHmmss" as an example. We will try to parse
1325 // 2/2/2 characters of the input text, then if that fails,
1326 // 1/2/2. We only adjust the width of the leftmost field; the
1327 // others remain fixed. This allows "123456" => 12:34:56, but
1328 // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
1329 // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
1330 if (abutPat >= 0) {
1331 // If we are at the start of a run of abutting fields, then
1332 // shorten this field in each pass. If we can't shorten
1333 // this field any more, then the parse of this set of
1334 // abutting numeric fields has failed.
1335 if (fieldPat == abutPat) {
1336 count -= abutPass++;
1337 if (count == 0) {
1338 parsePos.setIndex(start);
1339 parsePos.setErrorIndex(pos);
1340 return;
1341 }
1342 }
1343
1344 pos = subParse(text, pos, ch, count,
1345 TRUE, FALSE, ambiguousYear, cal);
1346
1347 // If the parse fails anywhere in the run, back up to the
1348 // start of the run and retry.
1349 if (pos < 0) {
1350 i = abutPat - 1;
1351 pos = abutStart;
1352 continue;
1353 }
1354 }
1355
1356 // Handle non-numeric fields and non-abutting numeric
1357 // fields.
1358 else {
1359 int32_t s = pos;
1360 pos = subParse(text, pos, ch, count,
1361 FALSE, TRUE, ambiguousYear, cal);
1362
1363 if (pos < 0) {
1364 parsePos.setErrorIndex(s);
1365 parsePos.setIndex(start);
1366 return;
1367 }
1368 }
1369 }
1370
1371 // Handle literal pattern characters. These are any
1372 // quoted characters and non-alphabetic unquoted
1373 // characters.
1374 else {
1375
1376 abutPat = -1; // End of any abutting fields
1377
1378 // Handle quotes. Two consecutive quotes is a quote
1379 // literal, inside or outside of quotes. Otherwise a
1380 // quote indicates entry or exit from a quoted region.
1381 if (ch == QUOTE) {
1382 // Match a quote literal '' within OR outside of quotes
1383 if ((i+1)<fPattern.length() && fPattern.charAt(i+1)==ch) {
1384 ++i; // Skip over doubled quote
1385 // Fall through and treat quote as a literal
1386 } else {
1387 // Enter or exit quoted region
1388 inQuote = !inQuote;
1389 continue;
1390 }
1391 }
1392
1393 // A run of white space in the pattern matches a run
1394 // of white space in the input text.
1395 if (uprv_isRuleWhiteSpace(ch)) {
1396 // Advance over run in pattern
1397 while ((i+1)<fPattern.length() &&
1398 uprv_isRuleWhiteSpace(fPattern.charAt(i+1))) {
1399 ++i;
1400 }
1401
1402 // Advance over run in input text
1403 int32_t s = pos;
1404 while (pos<text.length() &&
1405 ( u_isUWhiteSpace(text.charAt(pos)) || uprv_isRuleWhiteSpace(text.charAt(pos)))) {
1406 ++pos;
1407 }
1408
1409 // Must see at least one white space char in input
1410 if (pos > s) {
1411 continue;
1412 }
1413 } else if (pos<text.length() && text.charAt(pos)==ch) {
1414 // Match a literal
1415 ++pos;
1416 continue;
1417 }
1418
1419 // We fall through to this point if the match fails
1420 parsePos.setIndex(start);
1421 parsePos.setErrorIndex(pos);
1422 return;
1423 }
1424 }
1425
1426 // At this point the fields of Calendar have been set. Calendar
1427 // will fill in default values for missing fields when the time
1428 // is computed.
1429
1430 parsePos.setIndex(pos);
1431
1432 // This part is a problem: When we call parsedDate.after, we compute the time.
1433 // Take the date April 3 2004 at 2:30 am. When this is first set up, the year
1434 // will be wrong if we're parsing a 2-digit year pattern. It will be 1904.
1435 // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am
1436 // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
1437 // on that day. It is therefore parsed out to fields as 3:30 am. Then we
1438 // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is
1439 // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
1440 /*
1441 UDate parsedDate = calendar.getTime();
1442 if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
1443 calendar.add(Calendar.YEAR, 100);
1444 parsedDate = calendar.getTime();
1445 }
1446 */
1447 // Because of the above condition, save off the fields in case we need to readjust.
1448 // The procedure we use here is not particularly efficient, but there is no other
1449 // way to do this given the API restrictions present in Calendar. We minimize
1450 // inefficiency by only performing this computation when it might apply, that is,
1451 // when the two-digit year is equal to the start year, and thus might fall at the
1452 // front or the back of the default century. This only works because we adjust
1453 // the year correctly to start with in other cases -- see subParse().
1454 UErrorCode status = U_ZERO_ERROR;
1455 if (ambiguousYear[0] || tztype != TZTYPE_UNK) // If this is true then the two-digit year == the default start year
1456 {
1457 // We need a copy of the fields, and we need to avoid triggering a call to
1458 // complete(), which will recalculate the fields. Since we can't access
1459 // the fields[] array in Calendar, we clone the entire object. This will
1460 // stop working if Calendar.clone() is ever rewritten to call complete().
1461 Calendar *copy;
1462 if (ambiguousYear[0]) {
1463 copy = cal.clone();
1464 UDate parsedDate = copy->getTime(status);
1465 // {sfb} check internalGetDefaultCenturyStart
1466 if (fHaveDefaultCentury && (parsedDate < fDefaultCenturyStart)) {
1467 // We can't use add here because that does a complete() first.
1468 cal.set(UCAL_YEAR, fDefaultCenturyStartYear + 100);
1469 }
1470 delete copy;
1471 }
1472
1473 if (tztype != TZTYPE_UNK) {
1474 copy = cal.clone();
1475 const TimeZone & tz = cal.getTimeZone();
1476 BasicTimeZone *btz = NULL;
1477
1478 if (tz.getDynamicClassID() == OlsonTimeZone::getStaticClassID()
1479 || tz.getDynamicClassID() == SimpleTimeZone::getStaticClassID()
1480 || tz.getDynamicClassID() == RuleBasedTimeZone::getStaticClassID()
1481 || tz.getDynamicClassID() == VTimeZone::getStaticClassID()) {
1482 btz = (BasicTimeZone*)&tz;
1483 }
1484
1485 // Get local millis
1486 copy->set(UCAL_ZONE_OFFSET, 0);
1487 copy->set(UCAL_DST_OFFSET, 0);
1488 UDate localMillis = copy->getTime(status);
1489
1490 // Make sure parsed time zone type (Standard or Daylight)
1491 // matches the rule used by the parsed time zone.
1492 int32_t raw, dst;
1493 if (btz != NULL) {
1494 if (tztype == TZTYPE_STD) {
1495 btz->getOffsetFromLocal(localMillis,
1496 BasicTimeZone::kStandard, BasicTimeZone::kStandard, raw, dst, status);
1497 } else {
1498 btz->getOffsetFromLocal(localMillis,
1499 BasicTimeZone::kDaylight, BasicTimeZone::kDaylight, raw, dst, status);
1500 }
1501 } else {
1502 // No good way to resolve ambiguous time at transition,
1503 // but following code work in most case.
1504 tz.getOffset(localMillis, TRUE, raw, dst, status);
1505 }
1506
1507 // Now, compare the results with parsed type, either standard or daylight saving time
1508 int32_t resolvedSavings = dst;
1509 if (tztype == TZTYPE_STD) {
1510 if (dst != 0) {
1511 // Override DST_OFFSET = 0 in the result calendar
1512 resolvedSavings = 0;
1513 }
1514 } else { // tztype == TZTYPE_DST
1515 if (dst == 0) {
1516 if (btz != NULL) {
1517 UDate time = localMillis + raw;
1518 // We use the nearest daylight saving time rule.
1519 TimeZoneTransition beforeTrs, afterTrs;
1520 UDate beforeT = time, afterT = time;
1521 int32_t beforeSav = 0, afterSav = 0;
1522 UBool beforeTrsAvail, afterTrsAvail;
1523
1524 // Search for DST rule before or on the time
1525 while (TRUE) {
1526 beforeTrsAvail = btz->getPreviousTransition(beforeT, TRUE, beforeTrs);
1527 if (!beforeTrsAvail) {
1528 break;
1529 }
1530 beforeT = beforeTrs.getTime() - 1;
1531 beforeSav = beforeTrs.getFrom()->getDSTSavings();
1532 if (beforeSav != 0) {
1533 break;
1534 }
1535 }
1536
1537 // Search for DST rule after the time
1538 while (TRUE) {
1539 afterTrsAvail = btz->getNextTransition(afterT, FALSE, afterTrs);
1540 if (!afterTrsAvail) {
1541 break;
1542 }
1543 afterT = afterTrs.getTime();
1544 afterSav = afterTrs.getTo()->getDSTSavings();
1545 if (afterSav != 0) {
1546 break;
1547 }
1548 }
1549
1550 if (beforeTrsAvail && afterTrsAvail) {
1551 if (time - beforeT > afterT - time) {
1552 resolvedSavings = afterSav;
1553 } else {
1554 resolvedSavings = beforeSav;
1555 }
1556 } else if (beforeTrsAvail && beforeSav != 0) {
1557 resolvedSavings = beforeSav;
1558 } else if (afterTrsAvail && afterSav != 0) {
1559 resolvedSavings = afterSav;
1560 } else {
1561 resolvedSavings = btz->getDSTSavings();
1562 }
1563 } else {
1564 resolvedSavings = tz.getDSTSavings();
1565 }
1566 if (resolvedSavings == 0) {
1567 // final fallback
1568 resolvedSavings = U_MILLIS_PER_HOUR;
1569 }
1570 }
1571 }
1572 cal.set(UCAL_ZONE_OFFSET, raw);
1573 cal.set(UCAL_DST_OFFSET, resolvedSavings);
1574 delete copy;
1575 }
1576 }
1577
1578 // If any Calendar calls failed, we pretend that we
1579 // couldn't parse the string, when in reality this isn't quite accurate--
1580 // we did parse it; the Calendar calls just failed.
1581 if (U_FAILURE(status)) {
1582 parsePos.setErrorIndex(pos);
1583 parsePos.setIndex(start);
1584 }
1585 }
1586
1587 UDate
parse(const UnicodeString & text,ParsePosition & pos) const1588 SimpleDateFormat::parse( const UnicodeString& text,
1589 ParsePosition& pos) const {
1590 // redefined here because the other parse() function hides this function's
1591 // cunterpart on DateFormat
1592 return DateFormat::parse(text, pos);
1593 }
1594
1595 UDate
parse(const UnicodeString & text,UErrorCode & status) const1596 SimpleDateFormat::parse(const UnicodeString& text, UErrorCode& status) const
1597 {
1598 // redefined here because the other parse() function hides this function's
1599 // counterpart on DateFormat
1600 return DateFormat::parse(text, status);
1601 }
1602 //----------------------------------------------------------------------
1603
matchQuarterString(const UnicodeString & text,int32_t start,UCalendarDateFields field,const UnicodeString * data,int32_t dataCount,Calendar & cal) const1604 int32_t SimpleDateFormat::matchQuarterString(const UnicodeString& text,
1605 int32_t start,
1606 UCalendarDateFields field,
1607 const UnicodeString* data,
1608 int32_t dataCount,
1609 Calendar& cal) const
1610 {
1611 int32_t i = 0;
1612 int32_t count = dataCount;
1613
1614 // There may be multiple strings in the data[] array which begin with
1615 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
1616 // We keep track of the longest match, and return that. Note that this
1617 // unfortunately requires us to test all array elements.
1618 int32_t bestMatchLength = 0, bestMatch = -1;
1619
1620 // {sfb} kludge to support case-insensitive comparison
1621 // {markus 2002oct11} do not just use caseCompareBetween because we do not know
1622 // the length of the match after case folding
1623 // {alan 20040607} don't case change the whole string, since the length
1624 // can change
1625 // TODO we need a case-insensitive startsWith function
1626 UnicodeString lcase, lcaseText;
1627 text.extract(start, INT32_MAX, lcaseText);
1628 lcaseText.foldCase();
1629
1630 for (; i < count; ++i)
1631 {
1632 // Always compare if we have no match yet; otherwise only compare
1633 // against potentially better matches (longer strings).
1634
1635 lcase.fastCopyFrom(data[i]).foldCase();
1636 int32_t length = lcase.length();
1637
1638 if (length > bestMatchLength &&
1639 lcaseText.compareBetween(0, length, lcase, 0, length) == 0)
1640 {
1641 bestMatch = i;
1642 bestMatchLength = length;
1643 }
1644 }
1645 if (bestMatch >= 0)
1646 {
1647 cal.set(field, bestMatch * 3);
1648
1649 // Once we have a match, we have to determine the length of the
1650 // original source string. This will usually be == the length of
1651 // the case folded string, but it may differ (e.g. sharp s).
1652 lcase.fastCopyFrom(data[bestMatch]).foldCase();
1653
1654 // Most of the time, the length will be the same as the length
1655 // of the string from the locale data. Sometimes it will be
1656 // different, in which case we will have to figure it out by
1657 // adding a character at a time, until we have a match. We do
1658 // this all in one loop, where we try 'len' first (at index
1659 // i==0).
1660 int32_t len = data[bestMatch].length(); // 99+% of the time
1661 int32_t n = text.length() - start;
1662 for (i=0; i<=n; ++i) {
1663 int32_t j=i;
1664 if (i == 0) {
1665 j = len;
1666 } else if (i == len) {
1667 continue; // already tried this when i was 0
1668 }
1669 text.extract(start, j, lcaseText);
1670 lcaseText.foldCase();
1671 if (lcase == lcaseText) {
1672 return start + j;
1673 }
1674 }
1675 }
1676
1677 return -start;
1678 }
1679
1680 //----------------------------------------------------------------------
1681
matchString(const UnicodeString & text,int32_t start,UCalendarDateFields field,const UnicodeString * data,int32_t dataCount,Calendar & cal) const1682 int32_t SimpleDateFormat::matchString(const UnicodeString& text,
1683 int32_t start,
1684 UCalendarDateFields field,
1685 const UnicodeString* data,
1686 int32_t dataCount,
1687 Calendar& cal) const
1688 {
1689 int32_t i = 0;
1690 int32_t count = dataCount;
1691
1692 if (field == UCAL_DAY_OF_WEEK) i = 1;
1693
1694 // There may be multiple strings in the data[] array which begin with
1695 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
1696 // We keep track of the longest match, and return that. Note that this
1697 // unfortunately requires us to test all array elements.
1698 int32_t bestMatchLength = 0, bestMatch = -1;
1699
1700 // {sfb} kludge to support case-insensitive comparison
1701 // {markus 2002oct11} do not just use caseCompareBetween because we do not know
1702 // the length of the match after case folding
1703 // {alan 20040607} don't case change the whole string, since the length
1704 // can change
1705 // TODO we need a case-insensitive startsWith function
1706 UnicodeString lcase, lcaseText;
1707 text.extract(start, INT32_MAX, lcaseText);
1708 lcaseText.foldCase();
1709
1710 for (; i < count; ++i)
1711 {
1712 // Always compare if we have no match yet; otherwise only compare
1713 // against potentially better matches (longer strings).
1714
1715 lcase.fastCopyFrom(data[i]).foldCase();
1716 int32_t length = lcase.length();
1717
1718 if (length > bestMatchLength &&
1719 lcaseText.compareBetween(0, length, lcase, 0, length) == 0)
1720 {
1721 bestMatch = i;
1722 bestMatchLength = length;
1723 }
1724 }
1725 if (bestMatch >= 0)
1726 {
1727 cal.set(field, bestMatch);
1728
1729 // Once we have a match, we have to determine the length of the
1730 // original source string. This will usually be == the length of
1731 // the case folded string, but it may differ (e.g. sharp s).
1732 lcase.fastCopyFrom(data[bestMatch]).foldCase();
1733
1734 // Most of the time, the length will be the same as the length
1735 // of the string from the locale data. Sometimes it will be
1736 // different, in which case we will have to figure it out by
1737 // adding a character at a time, until we have a match. We do
1738 // this all in one loop, where we try 'len' first (at index
1739 // i==0).
1740 int32_t len = data[bestMatch].length(); // 99+% of the time
1741 int32_t n = text.length() - start;
1742 for (i=0; i<=n; ++i) {
1743 int32_t j=i;
1744 if (i == 0) {
1745 j = len;
1746 } else if (i == len) {
1747 continue; // already tried this when i was 0
1748 }
1749 text.extract(start, j, lcaseText);
1750 lcaseText.foldCase();
1751 if (lcase == lcaseText) {
1752 return start + j;
1753 }
1754 }
1755 }
1756
1757 return -start;
1758 }
1759
1760 //----------------------------------------------------------------------
1761
1762 void
set2DigitYearStart(UDate d,UErrorCode & status)1763 SimpleDateFormat::set2DigitYearStart(UDate d, UErrorCode& status)
1764 {
1765 parseAmbiguousDatesAsAfter(d, status);
1766 }
1767
1768 /**
1769 * Private member function that converts the parsed date strings into
1770 * timeFields. Returns -start (for ParsePosition) if failed.
1771 * @param text the time text to be parsed.
1772 * @param start where to start parsing.
1773 * @param ch the pattern character for the date field text to be parsed.
1774 * @param count the count of a pattern character.
1775 * @return the new start position if matching succeeded; a negative number
1776 * indicating matching failure, otherwise.
1777 */
subParse(const UnicodeString & text,int32_t & start,UChar ch,int32_t count,UBool obeyCount,UBool allowNegative,UBool ambiguousYear[],Calendar & cal) const1778 int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UChar ch, int32_t count,
1779 UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], Calendar& cal) const
1780 {
1781 Formattable number;
1782 int32_t value = 0;
1783 int32_t i;
1784 ParsePosition pos(0);
1785 int32_t patternCharIndex;
1786 UnicodeString temp;
1787 UChar *patternCharPtr = u_strchr(DateFormatSymbols::getPatternUChars(), ch);
1788
1789 #if defined (U_DEBUG_CAL)
1790 //fprintf(stderr, "%s:%d - [%c] st=%d \n", __FILE__, __LINE__, (char) ch, start);
1791 #endif
1792
1793 if (patternCharPtr == NULL) {
1794 return -start;
1795 }
1796
1797 patternCharIndex = (UDateFormatField)(patternCharPtr - DateFormatSymbols::getPatternUChars());
1798
1799 UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex];
1800
1801 // If there are any spaces here, skip over them. If we hit the end
1802 // of the string, then fail.
1803 for (;;) {
1804 if (start >= text.length()) {
1805 return -start;
1806 }
1807 UChar32 c = text.char32At(start);
1808 if (!u_isUWhiteSpace(c)) {
1809 break;
1810 }
1811 start += UTF_CHAR_LENGTH(c);
1812 }
1813 pos.setIndex(start);
1814
1815 // We handle a few special cases here where we need to parse
1816 // a number value. We handle further, more generic cases below. We need
1817 // to handle some of them here because some fields require extra processing on
1818 // the parsed value.
1819 if (patternCharIndex == UDAT_HOUR_OF_DAY1_FIELD ||
1820 patternCharIndex == UDAT_HOUR1_FIELD ||
1821 (patternCharIndex == UDAT_MONTH_FIELD && count <= 2) ||
1822 (patternCharIndex == UDAT_STANDALONE_MONTH_FIELD && count <= 2) ||
1823 (patternCharIndex == UDAT_QUARTER_FIELD && count <= 2) ||
1824 (patternCharIndex == UDAT_STANDALONE_QUARTER_FIELD && count <= 2) ||
1825 patternCharIndex == UDAT_YEAR_FIELD ||
1826 patternCharIndex == UDAT_YEAR_WOY_FIELD ||
1827 patternCharIndex == UDAT_FRACTIONAL_SECOND_FIELD)
1828 {
1829 int32_t parseStart = pos.getIndex();
1830 // It would be good to unify this with the obeyCount logic below,
1831 // but that's going to be difficult.
1832 const UnicodeString* src;
1833
1834 if (obeyCount) {
1835 if ((start+count) > text.length()) {
1836 return -start;
1837 }
1838
1839 text.extractBetween(0, start + count, temp);
1840 src = &temp;
1841 } else {
1842 src = &text;
1843 }
1844
1845 parseInt(*src, number, pos, allowNegative);
1846
1847 if (pos.getIndex() == parseStart)
1848 return -start;
1849 value = number.getLong();
1850 }
1851
1852 switch (patternCharIndex) {
1853 case UDAT_ERA_FIELD:
1854 if (count == 4) {
1855 return matchString(text, start, UCAL_ERA, fSymbols->fEraNames, fSymbols->fEraNamesCount, cal);
1856 }
1857
1858 return matchString(text, start, UCAL_ERA, fSymbols->fEras, fSymbols->fErasCount, cal);
1859
1860 case UDAT_YEAR_FIELD:
1861 // If there are 3 or more YEAR pattern characters, this indicates
1862 // that the year value is to be treated literally, without any
1863 // two-digit year adjustments (e.g., from "01" to 2001). Otherwise
1864 // we made adjustments to place the 2-digit year in the proper
1865 // century, for parsed strings from "00" to "99". Any other string
1866 // is treated literally: "2250", "-1", "1", "002".
1867 if (count <= 2 && (pos.getIndex() - start) == 2
1868 && u_isdigit(text.charAt(start))
1869 && u_isdigit(text.charAt(start+1)))
1870 {
1871 // Assume for example that the defaultCenturyStart is 6/18/1903.
1872 // This means that two-digit years will be forced into the range
1873 // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
1874 // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
1875 // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
1876 // other fields specify a date before 6/18, or 1903 if they specify a
1877 // date afterwards. As a result, 03 is an ambiguous year. All other
1878 // two-digit years are unambiguous.
1879 if(fHaveDefaultCentury) { // check if this formatter even has a pivot year
1880 int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
1881 ambiguousYear[0] = (value == ambiguousTwoDigitYear);
1882 value += (fDefaultCenturyStartYear/100)*100 +
1883 (value < ambiguousTwoDigitYear ? 100 : 0);
1884 }
1885 }
1886 cal.set(UCAL_YEAR, value);
1887 return pos.getIndex();
1888
1889 case UDAT_YEAR_WOY_FIELD:
1890 // Comment is the same as for UDAT_Year_FIELDs - look above
1891 if (count <= 2 && (pos.getIndex() - start) == 2
1892 && u_isdigit(text.charAt(start))
1893 && u_isdigit(text.charAt(start+1))
1894 && fHaveDefaultCentury )
1895 {
1896 int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
1897 ambiguousYear[0] = (value == ambiguousTwoDigitYear);
1898 value += (fDefaultCenturyStartYear/100)*100 +
1899 (value < ambiguousTwoDigitYear ? 100 : 0);
1900 }
1901 cal.set(UCAL_YEAR_WOY, value);
1902 return pos.getIndex();
1903
1904 case UDAT_MONTH_FIELD:
1905 if (count <= 2) // i.e., M or MM.
1906 {
1907 // Don't want to parse the month if it is a string
1908 // while pattern uses numeric style: M or MM.
1909 // [We computed 'value' above.]
1910 cal.set(UCAL_MONTH, value - 1);
1911 return pos.getIndex();
1912 } else {
1913 // count >= 3 // i.e., MMM or MMMM
1914 // Want to be able to parse both short and long forms.
1915 // Try count == 4 first:
1916 int32_t newStart = 0;
1917
1918 if ((newStart = matchString(text, start, UCAL_MONTH,
1919 fSymbols->fMonths, fSymbols->fMonthsCount, cal)) > 0)
1920 return newStart;
1921 else // count == 4 failed, now try count == 3
1922 return matchString(text, start, UCAL_MONTH,
1923 fSymbols->fShortMonths, fSymbols->fShortMonthsCount, cal);
1924 }
1925
1926 case UDAT_STANDALONE_MONTH_FIELD:
1927 if (count <= 2) // i.e., L or LL.
1928 {
1929 // Don't want to parse the month if it is a string
1930 // while pattern uses numeric style: M or MM.
1931 // [We computed 'value' above.]
1932 cal.set(UCAL_MONTH, value - 1);
1933 return pos.getIndex();
1934 } else {
1935 // count >= 3 // i.e., LLL or LLLL
1936 // Want to be able to parse both short and long forms.
1937 // Try count == 4 first:
1938 int32_t newStart = 0;
1939
1940 if ((newStart = matchString(text, start, UCAL_MONTH,
1941 fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount, cal)) > 0)
1942 return newStart;
1943 else // count == 4 failed, now try count == 3
1944 return matchString(text, start, UCAL_MONTH,
1945 fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount, cal);
1946 }
1947
1948 case UDAT_HOUR_OF_DAY1_FIELD:
1949 // [We computed 'value' above.]
1950 if (value == cal.getMaximum(UCAL_HOUR_OF_DAY) + 1)
1951 value = 0;
1952 cal.set(UCAL_HOUR_OF_DAY, value);
1953 return pos.getIndex();
1954
1955 case UDAT_FRACTIONAL_SECOND_FIELD:
1956 // Fractional seconds left-justify
1957 i = pos.getIndex() - start;
1958 if (i < 3) {
1959 while (i < 3) {
1960 value *= 10;
1961 i++;
1962 }
1963 } else {
1964 int32_t a = 1;
1965 while (i > 3) {
1966 a *= 10;
1967 i--;
1968 }
1969 value = (value + (a>>1)) / a;
1970 }
1971 cal.set(UCAL_MILLISECOND, value);
1972 return pos.getIndex();
1973
1974 case UDAT_DAY_OF_WEEK_FIELD:
1975 {
1976 // Want to be able to parse both short and long forms.
1977 // Try count == 4 (DDDD) first:
1978 int32_t newStart = 0;
1979 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
1980 fSymbols->fWeekdays, fSymbols->fWeekdaysCount, cal)) > 0)
1981 return newStart;
1982 else // DDDD failed, now try DDD
1983 return matchString(text, start, UCAL_DAY_OF_WEEK,
1984 fSymbols->fShortWeekdays, fSymbols->fShortWeekdaysCount, cal);
1985 }
1986
1987 case UDAT_STANDALONE_DAY_FIELD:
1988 {
1989 // Want to be able to parse both short and long forms.
1990 // Try count == 4 (DDDD) first:
1991 int32_t newStart = 0;
1992 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
1993 fSymbols->fStandaloneWeekdays, fSymbols->fStandaloneWeekdaysCount, cal)) > 0)
1994 return newStart;
1995 else // DDDD failed, now try DDD
1996 return matchString(text, start, UCAL_DAY_OF_WEEK,
1997 fSymbols->fStandaloneShortWeekdays, fSymbols->fStandaloneShortWeekdaysCount, cal);
1998 }
1999
2000 case UDAT_AM_PM_FIELD:
2001 return matchString(text, start, UCAL_AM_PM, fSymbols->fAmPms, fSymbols->fAmPmsCount, cal);
2002
2003 case UDAT_HOUR1_FIELD:
2004 // [We computed 'value' above.]
2005 if (value == cal.getLeastMaximum(UCAL_HOUR)+1)
2006 value = 0;
2007 cal.set(UCAL_HOUR, value);
2008 return pos.getIndex();
2009
2010 case UDAT_QUARTER_FIELD:
2011 if (count <= 2) // i.e., Q or QQ.
2012 {
2013 // Don't want to parse the month if it is a string
2014 // while pattern uses numeric style: Q or QQ.
2015 // [We computed 'value' above.]
2016 cal.set(UCAL_MONTH, (value - 1) * 3);
2017 return pos.getIndex();
2018 } else {
2019 // count >= 3 // i.e., QQQ or QQQQ
2020 // Want to be able to parse both short and long forms.
2021 // Try count == 4 first:
2022 int32_t newStart = 0;
2023
2024 if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
2025 fSymbols->fQuarters, fSymbols->fQuartersCount, cal)) > 0)
2026 return newStart;
2027 else // count == 4 failed, now try count == 3
2028 return matchQuarterString(text, start, UCAL_MONTH,
2029 fSymbols->fShortQuarters, fSymbols->fShortQuartersCount, cal);
2030 }
2031
2032 case UDAT_STANDALONE_QUARTER_FIELD:
2033 if (count <= 2) // i.e., q or qq.
2034 {
2035 // Don't want to parse the month if it is a string
2036 // while pattern uses numeric style: q or q.
2037 // [We computed 'value' above.]
2038 cal.set(UCAL_MONTH, (value - 1) * 3);
2039 return pos.getIndex();
2040 } else {
2041 // count >= 3 // i.e., qqq or qqqq
2042 // Want to be able to parse both short and long forms.
2043 // Try count == 4 first:
2044 int32_t newStart = 0;
2045
2046 if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
2047 fSymbols->fStandaloneQuarters, fSymbols->fStandaloneQuartersCount, cal)) > 0)
2048 return newStart;
2049 else // count == 4 failed, now try count == 3
2050 return matchQuarterString(text, start, UCAL_MONTH,
2051 fSymbols->fStandaloneShortQuarters, fSymbols->fStandaloneShortQuartersCount, cal);
2052 }
2053
2054 case UDAT_TIMEZONE_FIELD:
2055 case UDAT_TIMEZONE_RFC_FIELD:
2056 case UDAT_TIMEZONE_GENERIC_FIELD:
2057 case UDAT_TIMEZONE_SPECIAL_FIELD:
2058 {
2059 int32_t offset = 0;
2060 UBool parsed = FALSE;
2061
2062 // Step 1
2063 // Check if this is a long GMT offset string (either localized or default)
2064 offset = parseGMT(text, pos);
2065 if (pos.getIndex() - start > 0) {
2066 parsed = TRUE;
2067 }
2068 if (!parsed) {
2069 // Step 2
2070 // Check if this is an RFC822 time zone offset.
2071 // ICU supports the standard RFC822 format [+|-]HHmm
2072 // and its extended form [+|-]HHmmSS.
2073 do {
2074 int32_t sign = 0;
2075 UChar signChar = text.charAt(start);
2076 if (signChar == (UChar)0x002B /* '+' */) {
2077 sign = 1;
2078 } else if (signChar == (UChar)0x002D /* '-' */) {
2079 sign = -1;
2080 } else {
2081 // Not an RFC822 offset string
2082 break;
2083 }
2084
2085 // Parse digits
2086 int32_t orgPos = start + 1;
2087 pos.setIndex(orgPos);
2088 parseInt(text, number, 6, pos, FALSE);
2089 int32_t numLen = pos.getIndex() - orgPos;
2090 if (numLen <= 0) {
2091 break;
2092 }
2093
2094 // Followings are possible format (excluding sign char)
2095 // HHmmSS
2096 // HmmSS
2097 // HHmm
2098 // Hmm
2099 // HH
2100 // H
2101 int32_t val = number.getLong();
2102 int32_t hour = 0, min = 0, sec = 0;
2103 switch(numLen) {
2104 case 1: // H
2105 case 2: // HH
2106 hour = val;
2107 break;
2108 case 3: // Hmm
2109 case 4: // HHmm
2110 hour = val / 100;
2111 min = val % 100;
2112 break;
2113 case 5: // Hmmss
2114 case 6: // HHmmss
2115 hour = val / 10000;
2116 min = (val % 10000) / 100;
2117 sec = val % 100;
2118 break;
2119 }
2120 if (hour > 23 || min > 59 || sec > 59) {
2121 // Invalid value range
2122 break;
2123 }
2124 offset = (((hour * 60) + min) * 60 + sec) * 1000 * sign;
2125 parsed = TRUE;
2126 } while (FALSE);
2127
2128 if (!parsed) {
2129 // Failed to parse. Reset the position.
2130 pos.setIndex(start);
2131 }
2132 }
2133
2134 if (parsed) {
2135 // offset was successfully parsed as either a long GMT string or RFC822 zone offset
2136 // string. Create normalized zone ID for the offset.
2137
2138 UnicodeString tzID(gGmt);
2139 formatRFC822TZ(tzID, offset);
2140 //TimeZone *customTZ = TimeZone::createTimeZone(tzID);
2141 TimeZone *customTZ = new SimpleTimeZone(offset, tzID); // faster than TimeZone::createTimeZone
2142 cal.adoptTimeZone(customTZ);
2143
2144 return pos.getIndex();
2145 }
2146
2147 // Step 3
2148 // At this point, check for named time zones by looking through
2149 // the locale data from the DateFormatZoneData strings.
2150 // Want to be able to parse both short and long forms.
2151 // optimize for calendar's current time zone
2152 const ZoneStringFormat *zsf = fSymbols->getZoneStringFormat();
2153 if (zsf) {
2154 UErrorCode status = U_ZERO_ERROR;
2155 const ZoneStringInfo *zsinfo = NULL;
2156 int32_t matchLen;
2157
2158 switch (patternCharIndex) {
2159 case UDAT_TIMEZONE_FIELD: // 'z'
2160 if (count < 4) {
2161 zsinfo = zsf->findSpecificShort(text, start, matchLen, status);
2162 } else {
2163 zsinfo = zsf->findSpecificLong(text, start, matchLen, status);
2164 }
2165 break;
2166 case UDAT_TIMEZONE_GENERIC_FIELD: // 'v'
2167 if (count == 1) {
2168 zsinfo = zsf->findGenericShort(text, start, matchLen, status);
2169 } else if (count == 4) {
2170 zsinfo = zsf->findGenericLong(text, start, matchLen, status);
2171 }
2172 break;
2173 case UDAT_TIMEZONE_SPECIAL_FIELD: // 'V'
2174 if (count == 1) {
2175 zsinfo = zsf->findSpecificShort(text, start, matchLen, status);
2176 } else if (count == 4) {
2177 zsinfo = zsf->findGenericLocation(text, start, matchLen, status);
2178 }
2179 break;
2180 }
2181
2182 if (U_SUCCESS(status) && zsinfo != NULL) {
2183 if (zsinfo->isStandard()) {
2184 ((SimpleDateFormat*)this)->tztype = TZTYPE_STD;
2185 } else if (zsinfo->isDaylight()) {
2186 ((SimpleDateFormat*)this)->tztype = TZTYPE_DST;
2187 }
2188 UnicodeString tzid;
2189 zsinfo->getID(tzid);
2190
2191 UnicodeString current;
2192 cal.getTimeZone().getID(current);
2193 if (tzid != current) {
2194 TimeZone *tz = TimeZone::createTimeZone(tzid);
2195 cal.adoptTimeZone(tz);
2196 }
2197 return start + matchLen;
2198 }
2199 }
2200 // complete failure
2201 return -start;
2202 }
2203
2204 default:
2205 // Handle "generic" fields
2206 int32_t parseStart = pos.getIndex();
2207 const UnicodeString* src;
2208 if (obeyCount) {
2209 if ((start+count) > text.length()) {
2210 return -start;
2211 }
2212 text.extractBetween(0, start + count, temp);
2213 src = &temp;
2214 } else {
2215 src = &text;
2216 }
2217 parseInt(*src, number, pos, allowNegative);
2218 if (pos.getIndex() != parseStart) {
2219 cal.set(field, number.getLong());
2220 return pos.getIndex();
2221 }
2222 return -start;
2223 }
2224 }
2225
2226 /**
2227 * Parse an integer using fNumberFormat. This method is semantically
2228 * const, but actually may modify fNumberFormat.
2229 */
parseInt(const UnicodeString & text,Formattable & number,ParsePosition & pos,UBool allowNegative) const2230 void SimpleDateFormat::parseInt(const UnicodeString& text,
2231 Formattable& number,
2232 ParsePosition& pos,
2233 UBool allowNegative) const {
2234 parseInt(text, number, -1, pos, allowNegative);
2235 }
2236
2237 /**
2238 * Parse an integer using fNumberFormat up to maxDigits.
2239 */
parseInt(const UnicodeString & text,Formattable & number,int32_t maxDigits,ParsePosition & pos,UBool allowNegative) const2240 void SimpleDateFormat::parseInt(const UnicodeString& text,
2241 Formattable& number,
2242 int32_t maxDigits,
2243 ParsePosition& pos,
2244 UBool allowNegative) const {
2245 UnicodeString oldPrefix;
2246 DecimalFormat* df = NULL;
2247 if (!allowNegative &&
2248 fNumberFormat->getDynamicClassID() == DecimalFormat::getStaticClassID()) {
2249 df = (DecimalFormat*)fNumberFormat;
2250 df->getNegativePrefix(oldPrefix);
2251 df->setNegativePrefix(SUPPRESS_NEGATIVE_PREFIX);
2252 }
2253 int32_t oldPos = pos.getIndex();
2254 fNumberFormat->parse(text, number, pos);
2255 if (df != NULL) {
2256 df->setNegativePrefix(oldPrefix);
2257 }
2258
2259 if (maxDigits > 0) {
2260 // adjust the result to fit into
2261 // the maxDigits and move the position back
2262 int32_t nDigits = pos.getIndex() - oldPos;
2263 if (nDigits > maxDigits) {
2264 int32_t val = number.getLong();
2265 nDigits -= maxDigits;
2266 while (nDigits > 0) {
2267 val /= 10;
2268 nDigits--;
2269 }
2270 pos.setIndex(oldPos + maxDigits);
2271 number.setLong(val);
2272 }
2273 }
2274 }
2275
2276 //----------------------------------------------------------------------
2277
translatePattern(const UnicodeString & originalPattern,UnicodeString & translatedPattern,const UnicodeString & from,const UnicodeString & to,UErrorCode & status)2278 void SimpleDateFormat::translatePattern(const UnicodeString& originalPattern,
2279 UnicodeString& translatedPattern,
2280 const UnicodeString& from,
2281 const UnicodeString& to,
2282 UErrorCode& status)
2283 {
2284 // run through the pattern and convert any pattern symbols from the version
2285 // in "from" to the corresponding character ion "to". This code takes
2286 // quoted strings into account (it doesn't try to translate them), and it signals
2287 // an error if a particular "pattern character" doesn't appear in "from".
2288 // Depending on the values of "from" and "to" this can convert from generic
2289 // to localized patterns or localized to generic.
2290 if (U_FAILURE(status))
2291 return;
2292
2293 translatedPattern.remove();
2294 UBool inQuote = FALSE;
2295 for (int32_t i = 0; i < originalPattern.length(); ++i) {
2296 UChar c = originalPattern[i];
2297 if (inQuote) {
2298 if (c == QUOTE)
2299 inQuote = FALSE;
2300 }
2301 else {
2302 if (c == QUOTE)
2303 inQuote = TRUE;
2304 else if ((c >= 0x0061 /*'a'*/ && c <= 0x007A) /*'z'*/
2305 || (c >= 0x0041 /*'A'*/ && c <= 0x005A /*'Z'*/)) {
2306 int32_t ci = from.indexOf(c);
2307 if (ci == -1) {
2308 status = U_INVALID_FORMAT_ERROR;
2309 return;
2310 }
2311 c = to[ci];
2312 }
2313 }
2314 translatedPattern += c;
2315 }
2316 if (inQuote) {
2317 status = U_INVALID_FORMAT_ERROR;
2318 return;
2319 }
2320 }
2321
2322 //----------------------------------------------------------------------
2323
2324 UnicodeString&
toPattern(UnicodeString & result) const2325 SimpleDateFormat::toPattern(UnicodeString& result) const
2326 {
2327 result = fPattern;
2328 return result;
2329 }
2330
2331 //----------------------------------------------------------------------
2332
2333 UnicodeString&
toLocalizedPattern(UnicodeString & result,UErrorCode & status) const2334 SimpleDateFormat::toLocalizedPattern(UnicodeString& result,
2335 UErrorCode& status) const
2336 {
2337 translatePattern(fPattern, result, DateFormatSymbols::getPatternUChars(), fSymbols->fLocalPatternChars, status);
2338 return result;
2339 }
2340
2341 //----------------------------------------------------------------------
2342
2343 void
applyPattern(const UnicodeString & pattern)2344 SimpleDateFormat::applyPattern(const UnicodeString& pattern)
2345 {
2346 fPattern = pattern;
2347 }
2348
2349 //----------------------------------------------------------------------
2350
2351 void
applyLocalizedPattern(const UnicodeString & pattern,UErrorCode & status)2352 SimpleDateFormat::applyLocalizedPattern(const UnicodeString& pattern,
2353 UErrorCode &status)
2354 {
2355 translatePattern(pattern, fPattern, fSymbols->fLocalPatternChars, DateFormatSymbols::getPatternUChars(), status);
2356 }
2357
2358 //----------------------------------------------------------------------
2359
2360 const DateFormatSymbols*
getDateFormatSymbols() const2361 SimpleDateFormat::getDateFormatSymbols() const
2362 {
2363 return fSymbols;
2364 }
2365
2366 //----------------------------------------------------------------------
2367
2368 void
adoptDateFormatSymbols(DateFormatSymbols * newFormatSymbols)2369 SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols* newFormatSymbols)
2370 {
2371 delete fSymbols;
2372 fSymbols = newFormatSymbols;
2373 }
2374
2375 //----------------------------------------------------------------------
2376 void
setDateFormatSymbols(const DateFormatSymbols & newFormatSymbols)2377 SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols& newFormatSymbols)
2378 {
2379 delete fSymbols;
2380 fSymbols = new DateFormatSymbols(newFormatSymbols);
2381 }
2382
2383
2384 //----------------------------------------------------------------------
2385
2386
adoptCalendar(Calendar * calendarToAdopt)2387 void SimpleDateFormat::adoptCalendar(Calendar* calendarToAdopt)
2388 {
2389 UErrorCode status = U_ZERO_ERROR;
2390 DateFormat::adoptCalendar(calendarToAdopt);
2391 delete fSymbols;
2392 fSymbols=NULL;
2393 initializeSymbols(fLocale, fCalendar, status); // we need new symbols
2394 initializeDefaultCentury(); // we need a new century (possibly)
2395 }
2396
2397 U_NAMESPACE_END
2398
2399 #endif /* #if !UCONFIG_NO_FORMATTING */
2400
2401 //eof
2402