• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&param, 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