2 *******************************************************************************
3 * Copyright (C) 1997-2004, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
9 * Modification History:
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
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 ********************************************************************************
27 #include "unicode/utypes.h"
29 #if !UCONFIG_NO_FORMATTING
31 #include "unicode/smpdtfmt.h"
32 #include "unicode/dtfmtsym.h"
33 #include "unicode/ures.h"
34 #include "unicode/msgfmt.h"
35 #include "unicode/calendar.h"
36 #include "unicode/gregocal.h"
37 #include "unicode/timezone.h"
38 #include "unicode/decimfmt.h"
39 #include "unicode/dcfmtsym.h"
40 #include "unicode/uchar.h"
41 #include "unicode/ustring.h"
48 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
52 // *****************************************************************************
53 // class SimpleDateFormat
54 // *****************************************************************************
59 * Last-resort string to use for "GMT" when constructing time zone strings.
61 // For time zones that have no names, use strings GMT+minutes and
62 // GMT-minutes. For instance, in France the time zone is GMT+60.
63 // Also accepted are GMT+H:MM or GMT-H:MM.
64 static const UChar gGmt
[] = {0x0047, 0x004D, 0x0054, 0x0000}; // "GMT"
65 static const UChar gGmtPlus
[] = {0x0047, 0x004D, 0x0054, 0x002B, 0x0000}; // "GMT+"
66 static const UChar gGmtMinus
[] = {0x0047, 0x004D, 0x0054, 0x002D, 0x0000}; // "GMT-"
68 // This is a pattern-of-last-resort used when we can't load a usable pattern out
70 static const UChar gDefaultPattern
[] =
72 0x79, 0x79, 0x79, 0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0
73 }; /* "yyyyMMdd hh:mm a" */
75 // This prefix is designed to NEVER MATCH real text, in order to
76 // suppress the parsing of negative numbers. Adjust as needed (if
77 // this becomes valid Unicode).
78 static const UChar SUPPRESS_NEGATIVE_PREFIX
[] = {0xAB00, 0};
81 * These are the tags we expect to see in normal resource bundle files associated
84 static const char gDateTimePatternsTag
[]="DateTimePatterns";
86 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat
)
88 static const UChar QUOTE
= 0x27; // Single quote
90 //----------------------------------------------------------------------
92 SimpleDateFormat::~SimpleDateFormat()
97 //----------------------------------------------------------------------
99 SimpleDateFormat::SimpleDateFormat(UErrorCode
& status
)
100 : fLocale(Locale::getDefault()),
103 construct(kShort
, (EStyle
) (kShort
+ kDateOffset
), fLocale
, status
);
104 initializeDefaultCentury();
107 //----------------------------------------------------------------------
109 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
112 fLocale(Locale::getDefault()),
115 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
116 initialize(fLocale
, status
);
117 initializeDefaultCentury();
120 //----------------------------------------------------------------------
122 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
123 const Locale
& locale
,
128 initializeSymbols(fLocale
, initializeCalendar(NULL
,fLocale
,status
), status
);
129 initialize(fLocale
, status
);
130 initializeDefaultCentury();
133 //----------------------------------------------------------------------
135 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
136 DateFormatSymbols
* symbolsToAdopt
,
139 fLocale(Locale::getDefault()),
140 fSymbols(symbolsToAdopt
)
142 initializeCalendar(NULL
,fLocale
,status
);
143 initialize(fLocale
, status
);
144 initializeDefaultCentury();
147 //----------------------------------------------------------------------
149 SimpleDateFormat::SimpleDateFormat(const UnicodeString
& pattern
,
150 const DateFormatSymbols
& symbols
,
153 fLocale(Locale::getDefault()),
154 fSymbols(new DateFormatSymbols(symbols
))
156 initializeCalendar(NULL
, fLocale
, status
);
157 initialize(fLocale
, status
);
158 initializeDefaultCentury();
161 //----------------------------------------------------------------------
163 // Not for public consumption; used by DateFormat
164 SimpleDateFormat::SimpleDateFormat(EStyle timeStyle
,
166 const Locale
& locale
,
171 construct(timeStyle
, dateStyle
, fLocale
, status
);
172 if(U_SUCCESS(status
)) {
173 initializeDefaultCentury();
177 //----------------------------------------------------------------------
180 * Not for public consumption; used by DateFormat. This constructor
181 * never fails. If the resource data is not available, it uses the
182 * the last resort symbols.
184 SimpleDateFormat::SimpleDateFormat(const Locale
& locale
,
186 : fPattern(gDefaultPattern
),
190 if (U_FAILURE(status
)) return;
191 initializeSymbols(fLocale
, initializeCalendar(NULL
, fLocale
, status
),status
);
192 if (U_FAILURE(status
))
194 status
= U_ZERO_ERROR
;
196 // This constructor doesn't fail; it uses last resort data
197 fSymbols
= new DateFormatSymbols(status
);
200 status
= U_MEMORY_ALLOCATION_ERROR
;
205 initialize(fLocale
, status
);
206 if(U_SUCCESS(status
)) {
207 initializeDefaultCentury();
211 //----------------------------------------------------------------------
213 SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat
& other
)
220 //----------------------------------------------------------------------
222 SimpleDateFormat
& SimpleDateFormat::operator=(const SimpleDateFormat
& other
)
224 DateFormat::operator=(other
);
230 fSymbols
= new DateFormatSymbols(*other
.fSymbols
);
232 fDefaultCenturyStart
= other
.fDefaultCenturyStart
;
233 fDefaultCenturyStartYear
= other
.fDefaultCenturyStartYear
;
234 fHaveDefaultCentury
= other
.fHaveDefaultCentury
;
236 fPattern
= other
.fPattern
;
241 //----------------------------------------------------------------------
244 SimpleDateFormat::clone() const
246 return new SimpleDateFormat(*this);
249 //----------------------------------------------------------------------
252 SimpleDateFormat::operator==(const Format
& other
) const
254 if (DateFormat::operator==(other
)) {
255 // DateFormat::operator== guarantees following cast is safe
256 SimpleDateFormat
* that
= (SimpleDateFormat
*)&other
;
257 return (fPattern
== that
->fPattern
&&
258 fSymbols
!= NULL
&& // Check for pathological object
259 that
->fSymbols
!= NULL
&& // Check for pathological object
260 *fSymbols
== *that
->fSymbols
&&
261 fHaveDefaultCentury
== that
->fHaveDefaultCentury
&&
262 fDefaultCenturyStart
== that
->fDefaultCenturyStart
);
267 //----------------------------------------------------------------------
269 void SimpleDateFormat::construct(EStyle timeStyle
,
271 const Locale
& locale
,
274 // called by several constructors to load pattern data from the resources
275 if (U_FAILURE(status
)) return;
277 // We will need the calendar to know what type of symbols to load.
278 initializeCalendar(NULL
, locale
, status
);
280 CalendarData
calData(locale
, fCalendar
?fCalendar
->getType():NULL
, status
);
281 UResourceBundle
*dateTimePatterns
= calData
.getByKey(gDateTimePatternsTag
, status
);
282 if (U_FAILURE(status
)) return;
284 if (ures_getSize(dateTimePatterns
) <= kDateTime
)
286 status
= U_INVALID_FORMAT_ERROR
;
290 setLocaleIDs(ures_getLocaleByType(dateTimePatterns
, ULOC_VALID_LOCALE
, &status
),
291 ures_getLocaleByType(dateTimePatterns
, ULOC_ACTUAL_LOCALE
, &status
));
293 // create a symbols object from the locale
294 initializeSymbols(locale
,fCalendar
, status
);
295 if (U_FAILURE(status
)) return;
298 status
= U_MEMORY_ALLOCATION_ERROR
;
303 int32_t resStrLen
= 0;
305 // if the pattern should include both date and time information, use the date/time
306 // pattern string as a guide to tell use how to glue together the appropriate date
307 // and time pattern strings. The actual gluing-together is handled by a convenience
308 // method on MessageFormat.
309 if ((timeStyle
!= kNone
) && (dateStyle
!= kNone
))
311 Formattable timeDateArray
[2];
313 // use Formattable::adoptString() so that we can use fastCopyFrom()
314 // instead of Formattable::setString()'s unaware, safe, deep string clone
315 // see Jitterbug 2296
316 resStr
= ures_getStringByIndex(dateTimePatterns
, (int32_t)timeStyle
, &resStrLen
, &status
);
317 timeDateArray
[0].adoptString(new UnicodeString(TRUE
, resStr
, resStrLen
));
318 resStr
= ures_getStringByIndex(dateTimePatterns
, (int32_t)dateStyle
, &resStrLen
, &status
);
319 timeDateArray
[1].adoptString(new UnicodeString(TRUE
, resStr
, resStrLen
));
321 resStr
= ures_getStringByIndex(dateTimePatterns
, (int32_t)kDateTime
, &resStrLen
, &status
);
322 MessageFormat::format(UnicodeString(TRUE
, resStr
, resStrLen
), timeDateArray
, 2, fPattern
, status
);
324 // if the pattern includes just time data or just date date, load the appropriate
325 // pattern string from the resources
326 // setTo() - see DateFormatSymbols::assignArray comments
327 else if (timeStyle
!= kNone
) {
328 resStr
= ures_getStringByIndex(dateTimePatterns
, (int32_t)timeStyle
, &resStrLen
, &status
);
329 fPattern
.setTo(TRUE
, resStr
, resStrLen
);
331 else if (dateStyle
!= kNone
) {
332 resStr
= ures_getStringByIndex(dateTimePatterns
, (int32_t)dateStyle
, &resStrLen
, &status
);
333 fPattern
.setTo(TRUE
, resStr
, resStrLen
);
336 // and if it includes _neither_, that's an error
338 status
= U_INVALID_FORMAT_ERROR
;
340 // finally, finish initializing by creating a Calendar and a NumberFormat
341 initialize(locale
, status
);
344 //----------------------------------------------------------------------
347 SimpleDateFormat::initializeCalendar(TimeZone
* adoptZone
, const Locale
& locale
, UErrorCode
& status
)
349 if(!U_FAILURE(status
)) {
350 fCalendar
= Calendar::createInstance(adoptZone
?adoptZone
:TimeZone::createDefault(), locale
, status
);
356 SimpleDateFormat::initializeSymbols(const Locale
& locale
, Calendar
* calendar
, UErrorCode
& status
)
358 if(U_FAILURE(status
)) {
361 // pass in calendar type - use NULL (default) if no calendar set (or err).
362 fSymbols
= new DateFormatSymbols(locale
, calendar
?calendar
->getType() :NULL
, status
);
367 SimpleDateFormat::initialize(const Locale
& locale
,
370 if (U_FAILURE(status
)) return;
372 // {sfb} should this be here?
373 if (fSymbols
->fZoneStringsColCount
< 1)
375 status
= U_INVALID_FORMAT_ERROR
; // Check for bogus locale data
379 // We don't need to check that the row count is >= 1, since all 2d arrays have at
381 fNumberFormat
= NumberFormat::createInstance(locale
, status
);
382 if (fNumberFormat
!= NULL
&& U_SUCCESS(status
))
384 // no matter what the locale's default number format looked like, we want
385 // to modify it so that it doesn't use thousands separators, doesn't always
386 // show the decimal point, and recognizes integers only when parsing
388 fNumberFormat
->setGroupingUsed(FALSE
);
389 if (fNumberFormat
->getDynamicClassID() == DecimalFormat::getStaticClassID())
390 ((DecimalFormat
*)fNumberFormat
)->setDecimalSeparatorAlwaysShown(FALSE
);
391 fNumberFormat
->setParseIntegerOnly(TRUE
);
392 fNumberFormat
->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
394 else if (U_SUCCESS(status
))
396 status
= U_MISSING_RESOURCE_ERROR
;
400 /* Initialize the fields we use to disambiguate ambiguous years. Separate
401 * so we can call it from readObject().
403 void SimpleDateFormat::initializeDefaultCentury()
406 fHaveDefaultCentury
= fCalendar
->haveDefaultCentury();
407 if(fHaveDefaultCentury
) {
408 fDefaultCenturyStart
= fCalendar
->defaultCenturyStart();
409 fDefaultCenturyStartYear
= fCalendar
->defaultCenturyStartYear();
411 fDefaultCenturyStart
= DBL_MIN
;
412 fDefaultCenturyStartYear
= -1;
417 /* Define one-century window into which to disambiguate dates using
418 * two-digit years. Make public in JDK 1.2.
420 void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate
, UErrorCode
& status
)
422 if(U_FAILURE(status
)) {
426 status
= U_ILLEGAL_ARGUMENT_ERROR
;
430 fCalendar
->setTime(startDate
, status
);
431 if(U_SUCCESS(status
)) {
432 fHaveDefaultCentury
= TRUE
;
433 fDefaultCenturyStart
= startDate
;
434 fDefaultCenturyStartYear
= fCalendar
->get(UCAL_YEAR
, status
);
438 //----------------------------------------------------------------------
441 SimpleDateFormat::format(Calendar
& cal
, UnicodeString
& appendTo
, FieldPosition
& pos
) const
443 UErrorCode status
= U_ZERO_ERROR
;
444 pos
.setBeginIndex(0);
447 UBool inQuote
= FALSE
;
451 // loop through the pattern string character by character
452 for (int32_t i
= 0; i
< fPattern
.length() && U_SUCCESS(status
); ++i
) {
453 UChar ch
= fPattern
[i
];
455 // Use subFormat() to format a repeated pattern character
456 // when a different pattern or non-pattern character is seen
457 if (ch
!= prevCh
&& count
> 0) {
458 subFormat(appendTo
, prevCh
, count
, pos
, cal
, status
);
462 // Consecutive single quotes are a single quote literal,
463 // either outside of quotes or between quotes
464 if ((i
+1) < fPattern
.length() && fPattern
[i
+1] == QUOTE
) {
465 appendTo
+= (UChar
)QUOTE
;
471 else if ( ! inQuote
&& ((ch
>= 0x0061 /*'a'*/ && ch
<= 0x007A /*'z'*/)
472 || (ch
>= 0x0041 /*'A'*/ && ch
<= 0x005A /*'Z'*/))) {
473 // ch is a date-time pattern character to be interpreted
474 // by subFormat(); count the number of times it is repeated
479 // Append quoted characters and unquoted non-pattern characters
484 // Format the last item in the pattern, if any
486 subFormat(appendTo
, prevCh
, count
, pos
, cal
, status
);
489 // and if something failed (e.g., an invalid format character), reset our FieldPosition
490 // to (0, 0) to show that
491 // {sfb} look at this later- are these being set correctly?
492 if (U_FAILURE(status
)) {
493 pos
.setBeginIndex(0);
501 SimpleDateFormat::format(const Formattable
& obj
,
502 UnicodeString
& appendTo
,
504 UErrorCode
& status
) const
506 // this is just here to get around the hiding problem
507 // (the previous format() override would hide the version of
508 // format() on DateFormat that this function correspond to, so we
509 // have to redefine it here)
510 return DateFormat::format(obj
, appendTo
, pos
, status
);
513 //----------------------------------------------------------------------
515 // Map index into pattern character string to Calendar field number.
516 const UCalendarDateFields
517 SimpleDateFormat::fgPatternIndexToCalendarField
[] =
519 /*GyM*/ UCAL_ERA
, UCAL_YEAR
, UCAL_MONTH
,
520 /*dkH*/ UCAL_DATE
, UCAL_HOUR_OF_DAY
, UCAL_HOUR_OF_DAY
,
521 /*msS*/ UCAL_MINUTE
, UCAL_SECOND
, UCAL_MILLISECOND
,
522 /*EDF*/ UCAL_DAY_OF_WEEK
, UCAL_DAY_OF_YEAR
, UCAL_DAY_OF_WEEK_IN_MONTH
,
523 /*wWa*/ UCAL_WEEK_OF_YEAR
, UCAL_WEEK_OF_MONTH
, UCAL_AM_PM
,
524 /*hKz*/ UCAL_HOUR
, UCAL_HOUR
, UCAL_ZONE_OFFSET
,
525 /*Yeu*/ UCAL_YEAR_WOY
, UCAL_DOW_LOCAL
, UCAL_EXTENDED_YEAR
,
526 /*gAZ*/ UCAL_JULIAN_DAY
, UCAL_MILLISECONDS_IN_DAY
, UCAL_ZONE_OFFSET
529 // Map index into pattern character string to DateFormat field number
530 const UDateFormatField
531 SimpleDateFormat::fgPatternIndexToDateFormatField
[] = {
532 /*GyM*/ UDAT_ERA_FIELD
, UDAT_YEAR_FIELD
, UDAT_MONTH_FIELD
,
533 /*dkH*/ UDAT_DATE_FIELD
, UDAT_HOUR_OF_DAY1_FIELD
, UDAT_HOUR_OF_DAY0_FIELD
,
534 /*msS*/ UDAT_MINUTE_FIELD
, UDAT_SECOND_FIELD
, UDAT_FRACTIONAL_SECOND_FIELD
,
535 /*EDF*/ UDAT_DAY_OF_WEEK_FIELD
, UDAT_DAY_OF_YEAR_FIELD
, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD
,
536 /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD
, UDAT_WEEK_OF_MONTH_FIELD
, UDAT_AM_PM_FIELD
,
537 /*hKz*/ UDAT_HOUR1_FIELD
, UDAT_HOUR0_FIELD
, UDAT_TIMEZONE_FIELD
,
538 /*Yeu*/ UDAT_YEAR_WOY_FIELD
, UDAT_DOW_LOCAL_FIELD
, UDAT_EXTENDED_YEAR_FIELD
,
539 /*gAZ*/ UDAT_JULIAN_DAY_FIELD
, UDAT_MILLISECONDS_IN_DAY_FIELD
, UDAT_TIMEZONE_RFC_FIELD
542 //----------------------------------------------------------------------
545 * Append symbols[value] to dst. Make sure the array index is not out
549 _appendSymbol(UnicodeString
& dst
,
551 const UnicodeString
* symbols
,
552 int32_t symbolsCount
) {
553 U_ASSERT(value
>= 0 && value
< symbolsCount
);
554 dst
+= symbols
[value
];
558 SimpleDateFormat::subFormat(UnicodeString
&appendTo
,
563 UErrorCode
& status
) const
565 if (U_FAILURE(status
)) {
569 // this function gets called by format() to produce the appropriate substitution
570 // text for an individual pattern symbol (e.g., "HH" or "yyyy")
572 UChar
*patternCharPtr
= u_strchr(DateFormatSymbols::getPatternUChars(), ch
);
573 UDateFormatField patternCharIndex
;
574 const int32_t maxIntCount
= 10;
575 int32_t beginOffset
= appendTo
.length();
577 // if the pattern character is unrecognized, signal an error and dump out
578 if (patternCharPtr
== NULL
)
580 status
= U_INVALID_FORMAT_ERROR
;
584 patternCharIndex
= (UDateFormatField
)(patternCharPtr
- DateFormatSymbols::getPatternUChars());
585 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
];
586 int32_t value
= cal
.get(field
, status
);
587 if (U_FAILURE(status
)) {
591 switch (patternCharIndex
) {
593 // for any "G" symbol, write out the appropriate era string
595 _appendSymbol(appendTo
, value
, fSymbols
->fEras
, fSymbols
->fErasCount
);
598 // for "yyyy", write out the whole year; for "yy", write out the last 2 digits
599 case UDAT_YEAR_FIELD
:
600 case UDAT_YEAR_WOY_FIELD
:
602 zeroPaddingNumber(appendTo
, value
, 4, maxIntCount
);
604 zeroPaddingNumber(appendTo
, value
, count
, maxIntCount
);
606 zeroPaddingNumber(appendTo
, value
, 2, 2);
607 break; // TODO: this needs to be synced with Java, with GCL/Shanghai's work
609 // for "MMMM", write out the whole month name, for "MMM", write out the month
610 // abbreviation, for "M" or "MM", write out the month as a number with the
611 // appropriate number of digits
612 case UDAT_MONTH_FIELD
:
614 _appendSymbol(appendTo
, value
, fSymbols
->fMonths
,
615 fSymbols
->fMonthsCount
);
617 _appendSymbol(appendTo
, value
, fSymbols
->fShortMonths
,
618 fSymbols
->fShortMonthsCount
);
620 zeroPaddingNumber(appendTo
, value
+ 1, count
, maxIntCount
);
623 // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
624 case UDAT_HOUR_OF_DAY1_FIELD
:
626 zeroPaddingNumber(appendTo
, cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1, count
, maxIntCount
);
628 zeroPaddingNumber(appendTo
, value
, count
, maxIntCount
);
631 case UDAT_FRACTIONAL_SECOND_FIELD
:
632 // Fractional seconds left-justify
634 fNumberFormat
->setMinimumIntegerDigits((count
> 3) ? 3 : count
);
635 fNumberFormat
->setMaximumIntegerDigits(maxIntCount
);
637 value
= (value
+ 50) / 100;
638 } else if (count
== 2) {
639 value
= (value
+ 5) / 10;
642 fNumberFormat
->format(value
, appendTo
, p
);
644 fNumberFormat
->setMinimumIntegerDigits(count
- 3);
645 fNumberFormat
->format((int32_t)0, appendTo
, p
);
650 // for "EEEE", write out the day-of-the-week name; otherwise, use the abbreviation
651 case UDAT_DAY_OF_WEEK_FIELD
:
653 _appendSymbol(appendTo
, value
, fSymbols
->fWeekdays
,
654 fSymbols
->fWeekdaysCount
);
656 _appendSymbol(appendTo
, value
, fSymbols
->fShortWeekdays
,
657 fSymbols
->fShortWeekdaysCount
);
660 // for and "a" symbol, write out the whole AM/PM string
661 case UDAT_AM_PM_FIELD
:
662 _appendSymbol(appendTo
, value
, fSymbols
->fAmPms
,
663 fSymbols
->fAmPmsCount
);
666 // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
668 case UDAT_HOUR1_FIELD
:
670 zeroPaddingNumber(appendTo
, cal
.getLeastMaximum(UCAL_HOUR
) + 1, count
, maxIntCount
);
672 zeroPaddingNumber(appendTo
, value
, count
, maxIntCount
);
675 // for the "z" symbols, we have to check our time zone data first. If we have a
676 // localized name for the time zone, then "zzzz" is the whole name and anything
677 // shorter is the abbreviation (we also have to check for daylight savings time
678 // since the name will be different). If we don't have a localized time zone name,
679 // then the time zone shows up as "GMT+hh:mm" or "GMT-hh:mm" (where "hh:mm" is the
680 // offset from GMT) regardless of how many z's were in the pattern symbol
681 case UDAT_TIMEZONE_FIELD
: {
683 int32_t zoneIndex
= fSymbols
->getZoneIndex(cal
.getTimeZone().getID(str
));
684 if (zoneIndex
== -1) {
685 value
= cal
.get(UCAL_ZONE_OFFSET
, status
) +
686 cal
.get(UCAL_DST_OFFSET
, status
);
689 appendTo
+= gGmtMinus
;
690 value
= -value
; // suppress the '-' sign for text display.
693 appendTo
+= gGmtPlus
;
695 zeroPaddingNumber(appendTo
, (int32_t)(value
/U_MILLIS_PER_HOUR
), 2, 2);
696 appendTo
+= (UChar
)0x003A /*':'*/;
697 zeroPaddingNumber(appendTo
, (int32_t)((value%U_MILLIS_PER_HOUR
)/U_MILLIS_PER_MINUTE
), 2, 2);
699 else if (cal
.get(UCAL_DST_OFFSET
, status
) != 0) {
701 appendTo
+= fSymbols
->fZoneStrings
[zoneIndex
][3];
703 appendTo
+= fSymbols
->fZoneStrings
[zoneIndex
][4];
707 appendTo
+= fSymbols
->fZoneStrings
[zoneIndex
][1];
709 appendTo
+= fSymbols
->fZoneStrings
[zoneIndex
][2];
714 case 23: // 'Z' - TIMEZONE_RFC
716 UChar sign
= 43/*'+'*/;
717 value
= (cal
.get(UCAL_ZONE_OFFSET
, status
) +
718 cal
.get(UCAL_DST_OFFSET
, status
)) / U_MILLIS_PER_MINUTE
;
723 value
= (value
/ 3) * 5 + (value
% 60); // minutes => KKmm
725 zeroPaddingNumber(appendTo
, value
, 4, 4);
729 // all of the other pattern symbols can be formatted as simple numbers with
730 // appropriate zero padding
732 zeroPaddingNumber(appendTo
, value
, count
, maxIntCount
);
736 // if the field we're formatting is the one the FieldPosition says it's interested
737 // in, fill in the FieldPosition with this field's positions
738 if (pos
.getBeginIndex() == pos
.getEndIndex() &&
739 pos
.getField() == fgPatternIndexToDateFormatField
[patternCharIndex
]) {
740 pos
.setBeginIndex(beginOffset
);
741 pos
.setEndIndex(appendTo
.length());
745 //----------------------------------------------------------------------
748 SimpleDateFormat::zeroPaddingNumber(UnicodeString
&appendTo
, int32_t value
, int32_t minDigits
, int32_t maxDigits
) const
750 FieldPosition
pos(0);
752 fNumberFormat
->setMinimumIntegerDigits(minDigits
);
753 fNumberFormat
->setMaximumIntegerDigits(maxDigits
);
754 fNumberFormat
->format(value
, appendTo
, pos
); // 3rd arg is there to speed up processing
757 //----------------------------------------------------------------------
760 * Format characters that indicate numeric fields. The character
761 * at index 0 is treated specially.
763 static const UChar NUMERIC_FORMAT_CHARS
[] = {0x4D, 0x79, 0x75, 0x64, 0x68, 0x48, 0x6D, 0x73, 0x53, 0x44, 0x46, 0x77, 0x57, 0x6B, 0x4B, 0x00}; /* "MyudhHmsSDFwWkK" */
766 * Return true if the given format character, occuring count
767 * times, represents a numeric field.
769 UBool
SimpleDateFormat::isNumeric(UChar formatChar
, int32_t count
) {
770 UnicodeString
s(NUMERIC_FORMAT_CHARS
);
771 int32_t i
= s
.indexOf(formatChar
);
772 return (i
> 0 || (i
== 0 && count
< 3));
776 SimpleDateFormat::parse(const UnicodeString
& text
, Calendar
& cal
, ParsePosition
& parsePos
) const
778 int32_t pos
= parsePos
.getIndex();
780 UBool ambiguousYear
[] = { FALSE
};
783 // For parsing abutting numeric fields. 'abutPat' is the
784 // offset into 'pattern' of the first of 2 or more abutting
785 // numeric fields. 'abutStart' is the offset into 'text'
786 // where parsing the fields begins. 'abutPass' starts off as 0
787 // and increments each time we try to parse the fields.
788 int32_t abutPat
= -1; // If >=0, we are in a run of abutting numeric fields
789 int32_t abutStart
= 0;
790 int32_t abutPass
= 0;
791 UBool inQuote
= FALSE
;
793 const UnicodeString
numericFormatChars(NUMERIC_FORMAT_CHARS
);
795 for (int32_t i
=0; i
<fPattern
.length(); ++i
) {
796 UChar ch
= fPattern
.charAt(i
);
798 // Handle alphabetic field characters.
799 if (!inQuote
&& ((ch
>= 0x41 && ch
<= 0x5A) || (ch
>= 0x61 && ch
<= 0x7A))) { // [A-Za-z]
800 int32_t fieldPat
= i
;
802 // Count the length of this field specifier
804 while ((i
+1)<fPattern
.length() &&
805 fPattern
.charAt(i
+1) == ch
) {
810 if (isNumeric(ch
, count
)) {
812 // Determine if there is an abutting numeric field. For
813 // most fields we can just look at the next characters,
814 // but the 'm' field is either numeric or text,
815 // depending on the count, so we have to look ahead for
817 if ((i
+1)<fPattern
.length()) {
819 UChar nextCh
= fPattern
.charAt(i
+1);
820 int32_t k
= numericFormatChars
.indexOf(nextCh
);
823 while (j
<fPattern
.length() &&
824 fPattern
.charAt(j
) == nextCh
) {
827 abutting
= (j
-i
) < 4; // nextCount < 3
832 // Record the start of a set of abutting numeric
842 abutPat
= -1; // End of any abutting fields
845 // Handle fields within a run of abutting numeric fields. Take
846 // the pattern "HHmmss" as an example. We will try to parse
847 // 2/2/2 characters of the input text, then if that fails,
848 // 1/2/2. We only adjust the width of the leftmost field; the
849 // others remain fixed. This allows "123456" => 12:34:56, but
850 // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
851 // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
853 // If we are at the start of a run of abutting fields, then
854 // shorten this field in each pass. If we can't shorten
855 // this field any more, then the parse of this set of
856 // abutting numeric fields has failed.
857 if (fieldPat
== abutPat
) {
860 parsePos
.setIndex(start
);
861 parsePos
.setErrorIndex(pos
);
866 pos
= subParse(text
, pos
, ch
, count
,
867 TRUE
, FALSE
, ambiguousYear
, cal
);
869 // If the parse fails anywhere in the run, back up to the
870 // start of the run and retry.
878 // Handle non-numeric fields and non-abutting numeric
882 pos
= subParse(text
, pos
, ch
, count
,
883 FALSE
, TRUE
, ambiguousYear
, cal
);
886 parsePos
.setErrorIndex(s
);
887 parsePos
.setIndex(start
);
893 // Handle literal pattern characters. These are any
894 // quoted characters and non-alphabetic unquoted
898 abutPat
= -1; // End of any abutting fields
900 // Handle quotes. Two consecutive quotes is a quote
901 // literal, inside or outside of quotes. Otherwise a
902 // quote indicates entry or exit from a quoted region.
904 // Match a quote literal '' within OR outside of quotes
905 if ((i
+1)<fPattern
.length() && fPattern
.charAt(i
+1)==ch
) {
906 ++i
; // Skip over doubled quote
907 // Fall through and treat quote as a literal
909 // Enter or exit quoted region
915 // A run of white space in the pattern matches a run
916 // of white space in the input text.
917 if (uprv_isRuleWhiteSpace(ch
)) {
918 // Advance over run in pattern
919 while ((i
+1)<fPattern
.length() &&
920 uprv_isRuleWhiteSpace(fPattern
.charAt(i
+1))) {
924 // Advance over run in input text
926 while (pos
<text
.length() &&
927 u_isUWhiteSpace(text
.charAt(pos
))) {
931 // Must see at least one white space char in input
935 } else if (pos
<text
.length() && text
.charAt(pos
)==ch
) {
941 // We fall through to this point if the match fails
942 parsePos
.setIndex(start
);
943 parsePos
.setErrorIndex(pos
);
948 // At this point the fields of Calendar have been set. Calendar
949 // will fill in default values for missing fields when the time
952 parsePos
.setIndex(pos
);
954 // This part is a problem: When we call parsedDate.after, we compute the time.
955 // Take the date April 3 2004 at 2:30 am. When this is first set up, the year
956 // will be wrong if we're parsing a 2-digit year pattern. It will be 1904.
957 // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am
958 // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
959 // on that day. It is therefore parsed out to fields as 3:30 am. Then we
960 // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is
961 // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
963 UDate parsedDate = calendar.getTime();
964 if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
965 calendar.add(Calendar.YEAR, 100);
966 parsedDate = calendar.getTime();
969 // Because of the above condition, save off the fields in case we need to readjust.
970 // The procedure we use here is not particularly efficient, but there is no other
971 // way to do this given the API restrictions present in Calendar. We minimize
972 // inefficiency by only performing this computation when it might apply, that is,
973 // when the two-digit year is equal to the start year, and thus might fall at the
974 // front or the back of the default century. This only works because we adjust
975 // the year correctly to start with in other cases -- see subParse().
976 UErrorCode status
= U_ZERO_ERROR
;
977 if (ambiguousYear
[0]) // If this is true then the two-digit year == the default start year
979 // We need a copy of the fields, and we need to avoid triggering a call to
980 // complete(), which will recalculate the fields. Since we can't access
981 // the fields[] array in Calendar, we clone the entire object. This will
982 // stop working if Calendar.clone() is ever rewritten to call complete().
983 Calendar
*copy
= cal
.clone();
984 UDate parsedDate
= copy
->getTime(status
);
985 // {sfb} check internalGetDefaultCenturyStart
986 if (fHaveDefaultCentury
&& (parsedDate
< fDefaultCenturyStart
))
988 // We can't use add here because that does a complete() first.
989 cal
.set(UCAL_YEAR
, fDefaultCenturyStartYear
+ 100);
994 // If any Calendar calls failed, we pretend that we
995 // couldn't parse the string, when in reality this isn't quite accurate--
996 // we did parse it; the Calendar calls just failed.
997 if (U_FAILURE(status
)) {
998 parsePos
.setErrorIndex(pos
);
999 parsePos
.setIndex(start
);
1004 SimpleDateFormat::parse( const UnicodeString
& text
,
1005 ParsePosition
& pos
) const {
1006 // redefined here because the other parse() function hides this function's
1007 // cunterpart on DateFormat
1008 return DateFormat::parse(text
, pos
);
1012 SimpleDateFormat::parse(const UnicodeString
& text
, UErrorCode
& status
) const
1014 // redefined here because the other parse() function hides this function's
1015 // counterpart on DateFormat
1016 return DateFormat::parse(text
, status
);
1018 //----------------------------------------------------------------------
1020 int32_t SimpleDateFormat::matchString(const UnicodeString
& text
,
1022 UCalendarDateFields field
,
1023 const UnicodeString
* data
,
1025 Calendar
& cal
) const
1028 int32_t count
= dataCount
;
1030 if (field
== UCAL_DAY_OF_WEEK
) i
= 1;
1032 // There may be multiple strings in the data[] array which begin with
1033 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
1034 // We keep track of the longest match, and return that. Note that this
1035 // unfortunately requires us to test all array elements.
1036 int32_t bestMatchLength
= 0, bestMatch
= -1;
1038 // {sfb} kludge to support case-insensitive comparison
1039 // {markus 2002oct11} do not just use caseCompareBetween because we do not know
1040 // the length of the match after case folding
1041 // {alan 20040607} don't case change the whole string, since the length
1043 // TODO we need a case-insensitive startsWith function
1044 UnicodeString lcase
, lcaseText
;
1045 text
.extract(start
, INT32_MAX
, lcaseText
);
1046 lcaseText
.foldCase();
1048 for (; i
< count
; ++i
)
1050 // Always compare if we have no match yet; otherwise only compare
1051 // against potentially better matches (longer strings).
1053 lcase
.fastCopyFrom(data
[i
]).foldCase();
1054 int32_t length
= lcase
.length();
1056 if (length
> bestMatchLength
&&
1057 lcaseText
.compareBetween(0, length
, lcase
, 0, length
) == 0)
1060 bestMatchLength
= length
;
1065 cal
.set(field
, bestMatch
);
1067 // Once we have a match, we have to determine the length of the
1068 // original source string. This will usually be == the length of
1069 // the case folded string, but it may differ (e.g. sharp s).
1070 lcase
.fastCopyFrom(data
[bestMatch
]).foldCase();
1072 // Most of the time, the length will be the same as the length
1073 // of the string from the locale data. Sometimes it will be
1074 // different, in which case we will have to figure it out by
1075 // adding a character at a time, until we have a match. We do
1076 // this all in one loop, where we try 'len' first (at index
1078 int32_t len
= data
[bestMatch
].length(); // 99+% of the time
1079 int32_t n
= text
.length() - start
;
1080 for (i
=0; i
<=n
; ++i
) {
1084 } else if (i
== len
) {
1085 continue; // already tried this when i was 0
1087 text
.extract(start
, j
, lcaseText
);
1088 lcaseText
.foldCase();
1089 if (lcase
== lcaseText
) {
1098 //----------------------------------------------------------------------
1101 SimpleDateFormat::set2DigitYearStart(UDate d
, UErrorCode
& status
)
1103 parseAmbiguousDatesAsAfter(d
, status
);
1107 * Private member function that converts the parsed date strings into
1108 * timeFields. Returns -start (for ParsePosition) if failed.
1109 * @param text the time text to be parsed.
1110 * @param start where to start parsing.
1111 * @param ch the pattern character for the date field text to be parsed.
1112 * @param count the count of a pattern character.
1113 * @return the new start position if matching succeeded; a negative number
1114 * indicating matching failure, otherwise.
1116 int32_t SimpleDateFormat::subParse(const UnicodeString
& text
, int32_t& start
, UChar ch
, int32_t count
,
1117 UBool obeyCount
, UBool allowNegative
, UBool ambiguousYear
[], Calendar
& cal
) const
1122 ParsePosition
pos(0);
1123 int32_t patternCharIndex
;
1125 UChar
*patternCharPtr
= u_strchr(DateFormatSymbols::getPatternUChars(), ch
);
1127 #if defined (U_DEBUG_CAL)
1128 //fprintf(stderr, "%s:%d - [%c] st=%d \n", __FILE__, __LINE__, (char) ch, start);
1131 if (patternCharPtr
== NULL
) {
1135 patternCharIndex
= (UDateFormatField
)(patternCharPtr
- DateFormatSymbols::getPatternUChars());
1137 UCalendarDateFields field
= fgPatternIndexToCalendarField
[patternCharIndex
];
1139 // If there are any spaces here, skip over them. If we hit the end
1140 // of the string, then fail.
1142 if (start
>= text
.length()) {
1145 UChar32 c
= text
.char32At(start
);
1146 if (!u_isUWhiteSpace(c
)) {
1149 start
+= UTF_CHAR_LENGTH(c
);
1151 pos
.setIndex(start
);
1153 // We handle a few special cases here where we need to parse
1154 // a number value. We handle further, more generic cases below. We need
1155 // to handle some of them here because some fields require extra processing on
1156 // the parsed value.
1157 if (patternCharIndex
== UDAT_HOUR_OF_DAY1_FIELD
||
1158 patternCharIndex
== UDAT_HOUR1_FIELD
||
1159 (patternCharIndex
== UDAT_MONTH_FIELD
&& count
<= 2) ||
1160 patternCharIndex
== UDAT_YEAR_FIELD
||
1161 patternCharIndex
== UDAT_YEAR_WOY_FIELD
||
1162 patternCharIndex
== UDAT_FRACTIONAL_SECOND_FIELD
)
1164 int32_t parseStart
= pos
.getIndex();
1165 // It would be good to unify this with the obeyCount logic below,
1166 // but that's going to be difficult.
1167 const UnicodeString
* src
;
1169 if ((start
+count
) > text
.length()) {
1172 text
.extractBetween(0, start
+ count
, temp
);
1177 parseInt(*src
, number
, pos
, allowNegative
);
1178 if (pos
.getIndex() == parseStart
)
1180 value
= number
.getLong();
1183 switch (patternCharIndex
) {
1184 case UDAT_ERA_FIELD
:
1185 return matchString(text
, start
, UCAL_ERA
, fSymbols
->fEras
, fSymbols
->fErasCount
, cal
);
1186 case UDAT_YEAR_FIELD
:
1187 // If there are 3 or more YEAR pattern characters, this indicates
1188 // that the year value is to be treated literally, without any
1189 // two-digit year adjustments (e.g., from "01" to 2001). Otherwise
1190 // we made adjustments to place the 2-digit year in the proper
1191 // century, for parsed strings from "00" to "99". Any other string
1192 // is treated literally: "2250", "-1", "1", "002".
1193 if (count
<= 2 && (pos
.getIndex() - start
) == 2
1194 && u_isdigit(text
.charAt(start
))
1195 && u_isdigit(text
.charAt(start
+1)))
1197 // Assume for example that the defaultCenturyStart is 6/18/1903.
1198 // This means that two-digit years will be forced into the range
1199 // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
1200 // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
1201 // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
1202 // other fields specify a date before 6/18, or 1903 if they specify a
1203 // date afterwards. As a result, 03 is an ambiguous year. All other
1204 // two-digit years are unambiguous.
1205 if(fHaveDefaultCentury
) { // check if this formatter even has a pivot year
1206 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
1207 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
1208 value
+= (fDefaultCenturyStartYear
/100)*100 +
1209 (value
< ambiguousTwoDigitYear
? 100 : 0);
1212 cal
.set(UCAL_YEAR
, value
);
1213 return pos
.getIndex();
1214 case UDAT_YEAR_WOY_FIELD
:
1215 // Comment is the same as for UDAT_Year_FIELDs - look above
1216 if (count
<= 2 && (pos
.getIndex() - start
) == 2
1217 && u_isdigit(text
.charAt(start
))
1218 && u_isdigit(text
.charAt(start
+1))
1219 && fHaveDefaultCentury
)
1221 int32_t ambiguousTwoDigitYear
= fDefaultCenturyStartYear
% 100;
1222 ambiguousYear
[0] = (value
== ambiguousTwoDigitYear
);
1223 value
+= (fDefaultCenturyStartYear
/100)*100 +
1224 (value
< ambiguousTwoDigitYear
? 100 : 0);
1226 cal
.set(UCAL_YEAR_WOY
, value
);
1227 return pos
.getIndex();
1228 case UDAT_MONTH_FIELD
:
1229 if (count
<= 2) // i.e., M or MM.
1231 // Don't want to parse the month if it is a string
1232 // while pattern uses numeric style: M or MM.
1233 // [We computed 'value' above.]
1234 cal
.set(UCAL_MONTH
, value
- 1);
1235 return pos
.getIndex();
1239 // count >= 3 // i.e., MMM or MMMM
1240 // Want to be able to parse both short and long forms.
1241 // Try count == 4 first:
1242 int32_t newStart
= 0;
1243 if ((newStart
= matchString(text
, start
, UCAL_MONTH
,
1244 fSymbols
->fMonths
, fSymbols
->fMonthsCount
, cal
)) > 0)
1246 else // count == 4 failed, now try count == 3
1247 return matchString(text
, start
, UCAL_MONTH
,
1248 fSymbols
->fShortMonths
, fSymbols
->fShortMonthsCount
, cal
);
1250 case UDAT_HOUR_OF_DAY1_FIELD
:
1251 // [We computed 'value' above.]
1252 if (value
== cal
.getMaximum(UCAL_HOUR_OF_DAY
) + 1)
1254 cal
.set(UCAL_HOUR_OF_DAY
, value
);
1255 return pos
.getIndex();
1256 case UDAT_FRACTIONAL_SECOND_FIELD
:
1257 // Fractional seconds left-justify
1258 i
= pos
.getIndex() - start
;
1270 value
= (value
+ (a
>>1)) / a
;
1272 cal
.set(UCAL_MILLISECOND
, value
);
1273 return pos
.getIndex();
1274 case UDAT_DAY_OF_WEEK_FIELD
:
1276 // Want to be able to parse both short and long forms.
1277 // Try count == 4 (DDDD) first:
1278 int32_t newStart
= 0;
1279 if ((newStart
= matchString(text
, start
, UCAL_DAY_OF_WEEK
,
1280 fSymbols
->fWeekdays
, fSymbols
->fWeekdaysCount
, cal
)) > 0)
1282 else // DDDD failed, now try DDD
1283 return matchString(text
, start
, UCAL_DAY_OF_WEEK
,
1284 fSymbols
->fShortWeekdays
, fSymbols
->fShortWeekdaysCount
, cal
);
1286 case UDAT_AM_PM_FIELD
:
1287 return matchString(text
, start
, UCAL_AM_PM
, fSymbols
->fAmPms
, fSymbols
->fAmPmsCount
, cal
);
1288 case UDAT_HOUR1_FIELD
:
1289 // [We computed 'value' above.]
1290 if (value
== cal
.getLeastMaximum(UCAL_HOUR
)+1)
1292 cal
.set(UCAL_HOUR
, value
);
1293 return pos
.getIndex();
1294 case UDAT_TIMEZONE_FIELD
:
1295 case UDAT_TIMEZONE_RFC_FIELD
:
1297 // First try to parse generic forms such as GMT-07:00. Do this first
1298 // in case localized DateFormatZoneData contains the string "GMT"
1299 // for a zone; in that case, we don't want to match the first three
1300 // characters of GMT+/-HH:MM etc.
1302 UnicodeString
lcaseText(text
);
1303 UnicodeString
lcaseGMT(gGmt
);
1306 int32_t gmtLen
= lcaseGMT
.length();
1308 // For time zones that have no known names, look for strings
1310 // GMT[+-]hours:minutes or
1314 // {sfb} kludge for case-insensitive compare
1315 lcaseText
.toLower();
1318 if ((text
.length() - start
) > gmtLen
&&
1319 (lcaseText
.compare(start
, gmtLen
, lcaseGMT
, 0, gmtLen
)) == 0)
1321 cal
.set(UCAL_DST_OFFSET
, 0);
1323 pos
.setIndex(start
+ gmtLen
);
1325 if( text
[pos
.getIndex()] == 0x002B /*'+'*/ )
1327 else if( text
[pos
.getIndex()] == 0x002D /*'-'*/ )
1330 cal
.set(UCAL_ZONE_OFFSET
, 0 );
1331 return pos
.getIndex();
1334 // Look for hours:minutes or hhmm.
1335 pos
.setIndex(pos
.getIndex() + 1);
1336 int32_t parseStart
= pos
.getIndex();
1337 Formattable tzNumber
;
1338 fNumberFormat
->parse(text
, tzNumber
, pos
);
1339 if( pos
.getIndex() == parseStart
) {
1342 if( text
[pos
.getIndex()] == 0x003A /*':'*/ ) {
1343 // This is the hours:minutes case
1344 offset
= tzNumber
.getLong() * 60;
1345 pos
.setIndex(pos
.getIndex() + 1);
1346 parseStart
= pos
.getIndex();
1347 fNumberFormat
->parse(text
, tzNumber
, pos
);
1348 if( pos
.getIndex() == parseStart
) {
1351 offset
+= tzNumber
.getLong();
1354 // This is the hhmm case.
1355 offset
= tzNumber
.getLong();
1359 offset
= offset
% 100 + offset
/ 100 * 60;
1362 // Fall through for final processing below of 'offset' and 'sign'.
1365 // At this point, check for named time zones by looking through
1366 // the locale data from the DateFormatZoneData strings.
1367 // Want to be able to parse both short and long forms.
1368 const UnicodeString
*zs
;
1371 for (i
= 0; i
< fSymbols
->fZoneStringsRowCount
; i
++)
1373 // Checking long and short zones [1 & 2],
1374 // and long and short daylight [3 & 4].
1375 for (j
= 1; j
<= 4; ++j
)
1377 zs
= &fSymbols
->fZoneStrings
[i
][j
];
1378 // ### TODO markus 20021014: This use of caseCompare() will fail
1379 // if the text contains a character that case-folds into multiple
1380 // characters. In that case, zs->length() may be too long, and it does not match.
1381 // We need a case-insensitive version of startsWith().
1382 // There are similar cases of such caseCompare() uses elsewhere in ICU.
1383 if (0 == (text
.caseCompare(start
, zs
->length(), *zs
, 0))) {
1384 TimeZone
*tz
= TimeZone::createTimeZone(fSymbols
->fZoneStrings
[i
][0]);
1385 cal
.set(UCAL_ZONE_OFFSET
, tz
->getRawOffset());
1386 // Must call set() with something -- TODO -- Fix this to
1387 // use the correct DST SAVINGS for the zone.
1389 cal
.set(UCAL_DST_OFFSET
, j
>= 3 ? U_MILLIS_PER_HOUR
: 0);
1390 return (start
+ fSymbols
->fZoneStrings
[i
][j
].length());
1395 // As a last resort, look for numeric timezones of the form
1396 // [+-]hhmm as specified by RFC 822. This code is actually
1397 // a little more permissive than RFC 822. It will try to do
1398 // its best with numbers that aren't strictly 4 digits long.
1399 UErrorCode status
= U_ZERO_ERROR
;
1400 DecimalFormat
fmt(UNICODE_STRING_SIMPLE("+####;-####"), status
);
1401 if(U_FAILURE(status
))
1403 fmt
.setParseIntegerOnly(TRUE
);
1404 int32_t parseStart
= pos
.getIndex();
1405 Formattable tzNumber
;
1406 fmt
.parse( text
, tzNumber
, pos
);
1407 if( pos
.getIndex() == parseStart
) {
1408 return -start
; // Wasn't actually a number.
1410 offset
= tzNumber
.getLong();
1417 offset
= offset
* 60;
1419 offset
= offset
% 100 + offset
/ 100 * 60;
1421 // Fall through for final processing below of 'offset' and 'sign'.
1424 // Do the final processing for both of the above cases. We only
1425 // arrive here if the form GMT+/-... or an RFC 822 form was seen.
1428 offset
*= U_MILLIS_PER_MINUTE
* sign
;
1430 if (cal
.getTimeZone().useDaylightTime())
1432 cal
.set(UCAL_DST_OFFSET
, U_MILLIS_PER_HOUR
);
1433 offset
-= U_MILLIS_PER_HOUR
;
1435 cal
.set(UCAL_ZONE_OFFSET
, offset
);
1437 return pos
.getIndex();
1440 // All efforts to parse a zone failed.
1444 // Handle "generic" fields
1445 int32_t parseStart
= pos
.getIndex();
1446 const UnicodeString
* src
;
1448 if ((start
+count
) > text
.length()) {
1451 text
.extractBetween(0, start
+ count
, temp
);
1456 parseInt(*src
, number
, pos
, allowNegative
);
1457 if (pos
.getIndex() != parseStart
) {
1458 cal
.set(field
, number
.getLong());
1459 return pos
.getIndex();
1466 * Parse an integer using fNumberFormat. This method is semantically
1467 * const, but actually may modify fNumberFormat.
1469 void SimpleDateFormat::parseInt(const UnicodeString
& text
,
1470 Formattable
& number
,
1472 UBool allowNegative
) const {
1473 UnicodeString oldPrefix
;
1474 DecimalFormat
* df
= NULL
;
1475 if (!allowNegative
&&
1476 fNumberFormat
->getDynamicClassID() == DecimalFormat::getStaticClassID()) {
1477 df
= (DecimalFormat
*)fNumberFormat
;
1478 df
->getNegativePrefix(oldPrefix
);
1479 df
->setNegativePrefix(SUPPRESS_NEGATIVE_PREFIX
);
1481 fNumberFormat
->parse(text
, number
, pos
);
1483 df
->setNegativePrefix(oldPrefix
);
1487 //----------------------------------------------------------------------
1489 void SimpleDateFormat::translatePattern(const UnicodeString
& originalPattern
,
1490 UnicodeString
& translatedPattern
,
1491 const UnicodeString
& from
,
1492 const UnicodeString
& to
,
1495 // run through the pattern and convert any pattern symbols from the version
1496 // in "from" to the corresponding character ion "to". This code takes
1497 // quoted strings into account (it doesn't try to translate them), and it signals
1498 // an error if a particular "pattern character" doesn't appear in "from".
1499 // Depending on the values of "from" and "to" this can convert from generic
1500 // to localized patterns or localized to generic.
1501 if (U_FAILURE(status
))
1504 translatedPattern
.remove();
1505 UBool inQuote
= FALSE
;
1506 for (int32_t i
= 0; i
< originalPattern
.length(); ++i
) {
1507 UChar c
= originalPattern
[i
];
1515 else if ((c
>= 0x0061 /*'a'*/ && c
<= 0x007A) /*'z'*/
1516 || (c
>= 0x0041 /*'A'*/ && c
<= 0x005A /*'Z'*/)) {
1517 int32_t ci
= from
.indexOf(c
);
1519 status
= U_INVALID_FORMAT_ERROR
;
1525 translatedPattern
+= c
;
1528 status
= U_INVALID_FORMAT_ERROR
;
1533 //----------------------------------------------------------------------
1536 SimpleDateFormat::toPattern(UnicodeString
& result
) const
1542 //----------------------------------------------------------------------
1545 SimpleDateFormat::toLocalizedPattern(UnicodeString
& result
,
1546 UErrorCode
& status
) const
1548 translatePattern(fPattern
, result
, DateFormatSymbols::getPatternUChars(), fSymbols
->fLocalPatternChars
, status
);
1552 //----------------------------------------------------------------------
1555 SimpleDateFormat::applyPattern(const UnicodeString
& pattern
)
1560 //----------------------------------------------------------------------
1563 SimpleDateFormat::applyLocalizedPattern(const UnicodeString
& pattern
,
1566 translatePattern(pattern
, fPattern
, fSymbols
->fLocalPatternChars
, DateFormatSymbols::getPatternUChars(), status
);
1569 //----------------------------------------------------------------------
1571 const DateFormatSymbols
*
1572 SimpleDateFormat::getDateFormatSymbols() const
1577 //----------------------------------------------------------------------
1580 SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols
* newFormatSymbols
)
1583 fSymbols
= newFormatSymbols
;
1586 //----------------------------------------------------------------------
1588 SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols
& newFormatSymbols
)
1591 fSymbols
= new DateFormatSymbols(newFormatSymbols
);
1595 //----------------------------------------------------------------------
1598 void SimpleDateFormat::adoptCalendar(Calendar
* calendarToAdopt
)
1600 UErrorCode status
= U_ZERO_ERROR
;
1601 DateFormat::adoptCalendar(calendarToAdopt
);
1604 initializeSymbols(fLocale
, fCalendar
, status
); // we need new symbols
1605 initializeDefaultCentury(); // we need a new century (possibly)
1610 #endif /* #if !UCONFIG_NO_FORMATTING */