]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/smpdtfmt.cpp
ICU-8.11.1.tar.gz
[apple/icu.git] / icuSources / i18n / smpdtfmt.cpp
1 /*
2 *******************************************************************************
3 * Copyright (C) 1997-2006, 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 #include "unicode/utypes.h"
28
29 #if !UCONFIG_NO_FORMATTING
30
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"
42 #include "util.h"
43 #include "gregoimp.h"
44 #include "cstring.h"
45 #include "uassert.h"
46 #include <float.h>
47
48 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
49 #include <stdio.h>
50 #endif
51
52 // *****************************************************************************
53 // class SimpleDateFormat
54 // *****************************************************************************
55
56 U_NAMESPACE_BEGIN
57
58 /**
59 * Last-resort string to use for "GMT" when constructing time zone strings.
60 */
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-"
67
68 // This is a pattern-of-last-resort used when we can't load a usable pattern out
69 // of a resource.
70 static const UChar gDefaultPattern[] =
71 {
72 0x79, 0x79, 0x79, 0x79, 0x4D, 0x4D, 0x64, 0x64, 0x20, 0x68, 0x68, 0x3A, 0x6D, 0x6D, 0x20, 0x61, 0
73 }; /* "yyyyMMdd hh:mm a" */
74
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};
79
80 /**
81 * These are the tags we expect to see in normal resource bundle files associated
82 * with a locale.
83 */
84 static const char gDateTimePatternsTag[]="DateTimePatterns";
85
86 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat)
87
88 static const UChar QUOTE = 0x27; // Single quote
89
90 //----------------------------------------------------------------------
91
92 SimpleDateFormat::~SimpleDateFormat()
93 {
94 delete fSymbols;
95 delete parsedTimeZone; // sanity check
96 }
97
98 //----------------------------------------------------------------------
99
100 SimpleDateFormat::SimpleDateFormat(UErrorCode& status)
101 : fLocale(Locale::getDefault()),
102 fSymbols(NULL),
103 parsedTimeZone(NULL)
104 {
105 construct(kShort, (EStyle) (kShort + kDateOffset), fLocale, status);
106 initializeDefaultCentury();
107 }
108
109 //----------------------------------------------------------------------
110
111 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
112 UErrorCode &status)
113 : fPattern(pattern),
114 fLocale(Locale::getDefault()),
115 fSymbols(NULL),
116 parsedTimeZone(NULL)
117 {
118 initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
119 initialize(fLocale, status);
120 initializeDefaultCentury();
121 }
122
123 //----------------------------------------------------------------------
124
125 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
126 const Locale& locale,
127 UErrorCode& status)
128 : fPattern(pattern),
129 fLocale(locale),
130 parsedTimeZone(NULL)
131 {
132 initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status);
133 initialize(fLocale, status);
134 initializeDefaultCentury();
135 }
136
137 //----------------------------------------------------------------------
138
139 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
140 DateFormatSymbols* symbolsToAdopt,
141 UErrorCode& status)
142 : fPattern(pattern),
143 fLocale(Locale::getDefault()),
144 fSymbols(symbolsToAdopt),
145 parsedTimeZone(NULL)
146 {
147 initializeCalendar(NULL,fLocale,status);
148 initialize(fLocale, status);
149 initializeDefaultCentury();
150 }
151
152 //----------------------------------------------------------------------
153
154 SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern,
155 const DateFormatSymbols& symbols,
156 UErrorCode& status)
157 : fPattern(pattern),
158 fLocale(Locale::getDefault()),
159 fSymbols(new DateFormatSymbols(symbols)),
160 parsedTimeZone(NULL)
161 {
162 initializeCalendar(NULL, fLocale, status);
163 initialize(fLocale, status);
164 initializeDefaultCentury();
165 }
166
167 //----------------------------------------------------------------------
168
169 // Not for public consumption; used by DateFormat
170 SimpleDateFormat::SimpleDateFormat(EStyle timeStyle,
171 EStyle dateStyle,
172 const Locale& locale,
173 UErrorCode& status)
174 : fLocale(locale),
175 fSymbols(NULL),
176 parsedTimeZone(NULL)
177 {
178 construct(timeStyle, dateStyle, fLocale, status);
179 if(U_SUCCESS(status)) {
180 initializeDefaultCentury();
181 }
182 }
183
184 //----------------------------------------------------------------------
185
186 /**
187 * Not for public consumption; used by DateFormat. This constructor
188 * never fails. If the resource data is not available, it uses the
189 * the last resort symbols.
190 */
191 SimpleDateFormat::SimpleDateFormat(const Locale& locale,
192 UErrorCode& status)
193 : fPattern(gDefaultPattern),
194 fLocale(locale),
195 fSymbols(NULL),
196 parsedTimeZone(NULL)
197 {
198 if (U_FAILURE(status)) return;
199 initializeSymbols(fLocale, initializeCalendar(NULL, fLocale, status),status);
200 if (U_FAILURE(status))
201 {
202 status = U_ZERO_ERROR;
203 delete fSymbols;
204 // This constructor doesn't fail; it uses last resort data
205 fSymbols = new DateFormatSymbols(status);
206 /* test for NULL */
207 if (fSymbols == 0) {
208 status = U_MEMORY_ALLOCATION_ERROR;
209 return;
210 }
211 }
212
213 initialize(fLocale, status);
214 if(U_SUCCESS(status)) {
215 initializeDefaultCentury();
216 }
217 }
218
219 //----------------------------------------------------------------------
220
221 SimpleDateFormat::SimpleDateFormat(const SimpleDateFormat& other)
222 : DateFormat(other),
223 fSymbols(NULL),
224 parsedTimeZone(NULL)
225 {
226 *this = other;
227 }
228
229 //----------------------------------------------------------------------
230
231 SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other)
232 {
233 DateFormat::operator=(other);
234
235 delete fSymbols;
236 fSymbols = NULL;
237
238 delete parsedTimeZone; parsedTimeZone = NULL;
239
240 if (other.fSymbols)
241 fSymbols = new DateFormatSymbols(*other.fSymbols);
242
243 fDefaultCenturyStart = other.fDefaultCenturyStart;
244 fDefaultCenturyStartYear = other.fDefaultCenturyStartYear;
245 fHaveDefaultCentury = other.fHaveDefaultCentury;
246
247 fPattern = other.fPattern;
248
249 return *this;
250 }
251
252 //----------------------------------------------------------------------
253
254 Format*
255 SimpleDateFormat::clone() const
256 {
257 return new SimpleDateFormat(*this);
258 }
259
260 //----------------------------------------------------------------------
261
262 UBool
263 SimpleDateFormat::operator==(const Format& other) const
264 {
265 if (DateFormat::operator==(other)) {
266 // DateFormat::operator== guarantees following cast is safe
267 SimpleDateFormat* that = (SimpleDateFormat*)&other;
268 return (fPattern == that->fPattern &&
269 fSymbols != NULL && // Check for pathological object
270 that->fSymbols != NULL && // Check for pathological object
271 *fSymbols == *that->fSymbols &&
272 fHaveDefaultCentury == that->fHaveDefaultCentury &&
273 fDefaultCenturyStart == that->fDefaultCenturyStart);
274 }
275 return FALSE;
276 }
277
278 //----------------------------------------------------------------------
279
280 void SimpleDateFormat::construct(EStyle timeStyle,
281 EStyle dateStyle,
282 const Locale& locale,
283 UErrorCode& status)
284 {
285 // called by several constructors to load pattern data from the resources
286 if (U_FAILURE(status)) return;
287
288 // We will need the calendar to know what type of symbols to load.
289 initializeCalendar(NULL, locale, status);
290 if (U_FAILURE(status)) return;
291
292 CalendarData calData(locale, fCalendar?fCalendar->getType():NULL, status);
293 UResourceBundle *dateTimePatterns = calData.getByKey(gDateTimePatternsTag, status);
294 if (U_FAILURE(status)) return;
295
296 if (ures_getSize(dateTimePatterns) <= kDateTime)
297 {
298 status = U_INVALID_FORMAT_ERROR;
299 return;
300 }
301
302 setLocaleIDs(ures_getLocaleByType(dateTimePatterns, ULOC_VALID_LOCALE, &status),
303 ures_getLocaleByType(dateTimePatterns, ULOC_ACTUAL_LOCALE, &status));
304
305 // create a symbols object from the locale
306 initializeSymbols(locale,fCalendar, status);
307 if (U_FAILURE(status)) return;
308 /* test for NULL */
309 if (fSymbols == 0) {
310 status = U_MEMORY_ALLOCATION_ERROR;
311 return;
312 }
313
314 const UChar *resStr;
315 int32_t resStrLen = 0;
316
317 // if the pattern should include both date and time information, use the date/time
318 // pattern string as a guide to tell use how to glue together the appropriate date
319 // and time pattern strings. The actual gluing-together is handled by a convenience
320 // method on MessageFormat.
321 if ((timeStyle != kNone) && (dateStyle != kNone))
322 {
323 Formattable timeDateArray[2];
324
325 // use Formattable::adoptString() so that we can use fastCopyFrom()
326 // instead of Formattable::setString()'s unaware, safe, deep string clone
327 // see Jitterbug 2296
328 resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)timeStyle, &resStrLen, &status);
329 timeDateArray[0].adoptString(new UnicodeString(TRUE, resStr, resStrLen));
330 resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)dateStyle, &resStrLen, &status);
331 timeDateArray[1].adoptString(new UnicodeString(TRUE, resStr, resStrLen));
332
333 resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)kDateTime, &resStrLen, &status);
334 MessageFormat::format(UnicodeString(TRUE, resStr, resStrLen), timeDateArray, 2, fPattern, status);
335 }
336 // if the pattern includes just time data or just date date, load the appropriate
337 // pattern string from the resources
338 // setTo() - see DateFormatSymbols::assignArray comments
339 else if (timeStyle != kNone) {
340 resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)timeStyle, &resStrLen, &status);
341 fPattern.setTo(TRUE, resStr, resStrLen);
342 }
343 else if (dateStyle != kNone) {
344 resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)dateStyle, &resStrLen, &status);
345 fPattern.setTo(TRUE, resStr, resStrLen);
346 }
347
348 // and if it includes _neither_, that's an error
349 else
350 status = U_INVALID_FORMAT_ERROR;
351
352 // finally, finish initializing by creating a Calendar and a NumberFormat
353 initialize(locale, status);
354 }
355
356 //----------------------------------------------------------------------
357
358 Calendar*
359 SimpleDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status)
360 {
361 if(!U_FAILURE(status)) {
362 fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status);
363 }
364 if (U_SUCCESS(status) && fCalendar == NULL) {
365 status = U_MEMORY_ALLOCATION_ERROR;
366 }
367 return fCalendar;
368 }
369
370 void
371 SimpleDateFormat::initializeSymbols(const Locale& locale, Calendar* calendar, UErrorCode& status)
372 {
373 if(U_FAILURE(status)) {
374 fSymbols = NULL;
375 } else {
376 // pass in calendar type - use NULL (default) if no calendar set (or err).
377 fSymbols = new DateFormatSymbols(locale, calendar?calendar->getType() :NULL , status);
378 }
379 }
380
381 void
382 SimpleDateFormat::initialize(const Locale& locale,
383 UErrorCode& status)
384 {
385 if (U_FAILURE(status)) return;
386
387 // We don't need to check that the row count is >= 1, since all 2d arrays have at
388 // least one row
389 fNumberFormat = NumberFormat::createInstance(locale, status);
390 if (fNumberFormat != NULL && U_SUCCESS(status))
391 {
392 // no matter what the locale's default number format looked like, we want
393 // to modify it so that it doesn't use thousands separators, doesn't always
394 // show the decimal point, and recognizes integers only when parsing
395
396 fNumberFormat->setGroupingUsed(FALSE);
397 if (fNumberFormat->getDynamicClassID() == DecimalFormat::getStaticClassID())
398 ((DecimalFormat*)fNumberFormat)->setDecimalSeparatorAlwaysShown(FALSE);
399 fNumberFormat->setParseIntegerOnly(TRUE);
400 fNumberFormat->setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
401 }
402 else if (U_SUCCESS(status))
403 {
404 status = U_MISSING_RESOURCE_ERROR;
405 }
406 }
407
408 /* Initialize the fields we use to disambiguate ambiguous years. Separate
409 * so we can call it from readObject().
410 */
411 void SimpleDateFormat::initializeDefaultCentury()
412 {
413 if(fCalendar) {
414 fHaveDefaultCentury = fCalendar->haveDefaultCentury();
415 if(fHaveDefaultCentury) {
416 fDefaultCenturyStart = fCalendar->defaultCenturyStart();
417 fDefaultCenturyStartYear = fCalendar->defaultCenturyStartYear();
418 } else {
419 fDefaultCenturyStart = DBL_MIN;
420 fDefaultCenturyStartYear = -1;
421 }
422 }
423 }
424
425 /* Define one-century window into which to disambiguate dates using
426 * two-digit years. Make public in JDK 1.2.
427 */
428 void SimpleDateFormat::parseAmbiguousDatesAsAfter(UDate startDate, UErrorCode& status)
429 {
430 if(U_FAILURE(status)) {
431 return;
432 }
433 if(!fCalendar) {
434 status = U_ILLEGAL_ARGUMENT_ERROR;
435 return;
436 }
437
438 fCalendar->setTime(startDate, status);
439 if(U_SUCCESS(status)) {
440 fHaveDefaultCentury = TRUE;
441 fDefaultCenturyStart = startDate;
442 fDefaultCenturyStartYear = fCalendar->get(UCAL_YEAR, status);
443 }
444 }
445
446 //----------------------------------------------------------------------
447
448 UnicodeString&
449 SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition& pos) const
450 {
451 UErrorCode status = U_ZERO_ERROR;
452 pos.setBeginIndex(0);
453 pos.setEndIndex(0);
454
455 UBool inQuote = FALSE;
456 UChar prevCh = 0;
457 int32_t count = 0;
458
459 // loop through the pattern string character by character
460 for (int32_t i = 0; i < fPattern.length() && U_SUCCESS(status); ++i) {
461 UChar ch = fPattern[i];
462
463 // Use subFormat() to format a repeated pattern character
464 // when a different pattern or non-pattern character is seen
465 if (ch != prevCh && count > 0) {
466 subFormat(appendTo, prevCh, count, pos, cal, status);
467 count = 0;
468 }
469 if (ch == QUOTE) {
470 // Consecutive single quotes are a single quote literal,
471 // either outside of quotes or between quotes
472 if ((i+1) < fPattern.length() && fPattern[i+1] == QUOTE) {
473 appendTo += (UChar)QUOTE;
474 ++i;
475 } else {
476 inQuote = ! inQuote;
477 }
478 }
479 else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
480 || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
481 // ch is a date-time pattern character to be interpreted
482 // by subFormat(); count the number of times it is repeated
483 prevCh = ch;
484 ++count;
485 }
486 else {
487 // Append quoted characters and unquoted non-pattern characters
488 appendTo += ch;
489 }
490 }
491
492 // Format the last item in the pattern, if any
493 if (count > 0) {
494 subFormat(appendTo, prevCh, count, pos, cal, status);
495 }
496
497 // and if something failed (e.g., an invalid format character), reset our FieldPosition
498 // to (0, 0) to show that
499 // {sfb} look at this later- are these being set correctly?
500 if (U_FAILURE(status)) {
501 pos.setBeginIndex(0);
502 pos.setEndIndex(0);
503 }
504
505 return appendTo;
506 }
507
508 UnicodeString&
509 SimpleDateFormat::format(const Formattable& obj,
510 UnicodeString& appendTo,
511 FieldPosition& pos,
512 UErrorCode& status) const
513 {
514 // this is just here to get around the hiding problem
515 // (the previous format() override would hide the version of
516 // format() on DateFormat that this function correspond to, so we
517 // have to redefine it here)
518 return DateFormat::format(obj, appendTo, pos, status);
519 }
520
521 //----------------------------------------------------------------------
522
523 // Map index into pattern character string to Calendar field number.
524 const UCalendarDateFields
525 SimpleDateFormat::fgPatternIndexToCalendarField[] =
526 {
527 /*GyM*/ UCAL_ERA, UCAL_YEAR, UCAL_MONTH,
528 /*dkH*/ UCAL_DATE, UCAL_HOUR_OF_DAY, UCAL_HOUR_OF_DAY,
529 /*msS*/ UCAL_MINUTE, UCAL_SECOND, UCAL_MILLISECOND,
530 /*EDF*/ UCAL_DAY_OF_WEEK, UCAL_DAY_OF_YEAR, UCAL_DAY_OF_WEEK_IN_MONTH,
531 /*wWa*/ UCAL_WEEK_OF_YEAR, UCAL_WEEK_OF_MONTH, UCAL_AM_PM,
532 /*hKz*/ UCAL_HOUR, UCAL_HOUR, UCAL_ZONE_OFFSET,
533 /*Yeu*/ UCAL_YEAR_WOY, UCAL_DOW_LOCAL, UCAL_EXTENDED_YEAR,
534 /*gAZ*/ UCAL_JULIAN_DAY, UCAL_MILLISECONDS_IN_DAY, UCAL_ZONE_OFFSET,
535 /*v*/ UCAL_ZONE_OFFSET,
536 /*c*/ UCAL_DAY_OF_WEEK,
537 /*L*/ UCAL_MONTH,
538 /*Q*/ UCAL_MONTH,
539 /*q*/ UCAL_MONTH,
540 };
541
542 // Map index into pattern character string to DateFormat field number
543 const UDateFormatField
544 SimpleDateFormat::fgPatternIndexToDateFormatField[] = {
545 /*GyM*/ UDAT_ERA_FIELD, UDAT_YEAR_FIELD, UDAT_MONTH_FIELD,
546 /*dkH*/ UDAT_DATE_FIELD, UDAT_HOUR_OF_DAY1_FIELD, UDAT_HOUR_OF_DAY0_FIELD,
547 /*msS*/ UDAT_MINUTE_FIELD, UDAT_SECOND_FIELD, UDAT_FRACTIONAL_SECOND_FIELD,
548 /*EDF*/ UDAT_DAY_OF_WEEK_FIELD, UDAT_DAY_OF_YEAR_FIELD, UDAT_DAY_OF_WEEK_IN_MONTH_FIELD,
549 /*wWa*/ UDAT_WEEK_OF_YEAR_FIELD, UDAT_WEEK_OF_MONTH_FIELD, UDAT_AM_PM_FIELD,
550 /*hKz*/ UDAT_HOUR1_FIELD, UDAT_HOUR0_FIELD, UDAT_TIMEZONE_FIELD,
551 /*Yeu*/ UDAT_YEAR_WOY_FIELD, UDAT_DOW_LOCAL_FIELD, UDAT_EXTENDED_YEAR_FIELD,
552 /*gAZ*/ UDAT_JULIAN_DAY_FIELD, UDAT_MILLISECONDS_IN_DAY_FIELD, UDAT_TIMEZONE_RFC_FIELD,
553 /*v*/ UDAT_TIMEZONE_GENERIC_FIELD,
554 /*c*/ UDAT_STANDALONE_DAY_FIELD,
555 /*L*/ UDAT_STANDALONE_MONTH_FIELD,
556 /*Q*/ UDAT_QUARTER_FIELD,
557 /*q*/ UDAT_STANDALONE_QUARTER_FIELD,
558 };
559
560 //----------------------------------------------------------------------
561
562 /**
563 * Append symbols[value] to dst. Make sure the array index is not out
564 * of bounds.
565 */
566 static inline void
567 _appendSymbol(UnicodeString& dst,
568 int32_t value,
569 const UnicodeString* symbols,
570 int32_t symbolsCount) {
571 U_ASSERT(0 <= value && value < symbolsCount);
572 if (0 <= value && value < symbolsCount) {
573 dst += symbols[value];
574 }
575 }
576
577 //---------------------------------------------------------------------
578 inline void SimpleDateFormat::appendGMT(UnicodeString &appendTo, Calendar& cal, UErrorCode& status) const{
579 int32_t value = cal.get(UCAL_ZONE_OFFSET, status) +
580 cal.get(UCAL_DST_OFFSET, status);
581
582 if (value < 0) {
583 appendTo += gGmtMinus;
584 value = -value; // suppress the '-' sign for text display.
585 }else{
586 appendTo += gGmtPlus;
587 }
588
589 zeroPaddingNumber(appendTo, (int32_t)(value/U_MILLIS_PER_HOUR), 2, 2);
590 appendTo += (UChar)0x003A /*':'*/;
591 zeroPaddingNumber(appendTo, (int32_t)((value%U_MILLIS_PER_HOUR)/U_MILLIS_PER_MINUTE), 2, 2);
592 }
593
594 //---------------------------------------------------------------------
595 void
596 SimpleDateFormat::subFormat(UnicodeString &appendTo,
597 UChar ch,
598 int32_t count,
599 FieldPosition& pos,
600 Calendar& cal,
601 UErrorCode& status) const
602 {
603 if (U_FAILURE(status)) {
604 return;
605 }
606
607 // this function gets called by format() to produce the appropriate substitution
608 // text for an individual pattern symbol (e.g., "HH" or "yyyy")
609
610 UChar *patternCharPtr = u_strchr(DateFormatSymbols::getPatternUChars(), ch);
611 UDateFormatField patternCharIndex;
612 const int32_t maxIntCount = 10;
613 int32_t beginOffset = appendTo.length();
614
615 // if the pattern character is unrecognized, signal an error and dump out
616 if (patternCharPtr == NULL)
617 {
618 status = U_INVALID_FORMAT_ERROR;
619 return;
620 }
621
622 patternCharIndex = (UDateFormatField)(patternCharPtr - DateFormatSymbols::getPatternUChars());
623 UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex];
624 int32_t value = cal.get(field, status);
625 if (U_FAILURE(status)) {
626 return;
627 }
628
629 switch (patternCharIndex) {
630
631 // for any "G" symbol, write out the appropriate era string
632 // "GGGG" is wide era name, anything else is abbreviated name
633 case UDAT_ERA_FIELD:
634 if (count >= 4)
635 _appendSymbol(appendTo, value, fSymbols->fEraNames, fSymbols->fEraNamesCount);
636 else
637 _appendSymbol(appendTo, value, fSymbols->fEras, fSymbols->fErasCount);
638 break;
639
640 // for "yyyy", write out the whole year; for "yy", write out the last 2 digits
641 case UDAT_YEAR_FIELD:
642 case UDAT_YEAR_WOY_FIELD:
643 if (count >= 4)
644 zeroPaddingNumber(appendTo, value, 4, maxIntCount);
645 else if(count == 1)
646 zeroPaddingNumber(appendTo, value, count, maxIntCount);
647 else
648 zeroPaddingNumber(appendTo, value, 2, 2);
649 break; // TODO: this needs to be synced with Java, with GCL/Shanghai's work
650
651 // for "MMMM", write out the whole month name, for "MMM", write out the month
652 // abbreviation, for "M" or "MM", write out the month as a number with the
653 // appropriate number of digits
654 // for "MMMMM", use the narrow form
655 case UDAT_MONTH_FIELD:
656 if (count == 5)
657 _appendSymbol(appendTo, value, fSymbols->fNarrowMonths,
658 fSymbols->fNarrowMonthsCount);
659 else if (count == 4)
660 _appendSymbol(appendTo, value, fSymbols->fMonths,
661 fSymbols->fMonthsCount);
662 else if (count == 3)
663 _appendSymbol(appendTo, value, fSymbols->fShortMonths,
664 fSymbols->fShortMonthsCount);
665 else
666 zeroPaddingNumber(appendTo, value + 1, count, maxIntCount);
667 break;
668
669 // for "LLLL", write out the whole month name, for "LLL", write out the month
670 // abbreviation, for "L" or "LL", write out the month as a number with the
671 // appropriate number of digits
672 // for "LLLLL", use the narrow form
673 case UDAT_STANDALONE_MONTH_FIELD:
674 if (count == 5)
675 _appendSymbol(appendTo, value, fSymbols->fStandaloneNarrowMonths,
676 fSymbols->fStandaloneNarrowMonthsCount);
677 else if (count == 4)
678 _appendSymbol(appendTo, value, fSymbols->fStandaloneMonths,
679 fSymbols->fStandaloneMonthsCount);
680 else if (count == 3)
681 _appendSymbol(appendTo, value, fSymbols->fStandaloneShortMonths,
682 fSymbols->fStandaloneShortMonthsCount);
683 else
684 zeroPaddingNumber(appendTo, value + 1, count, maxIntCount);
685 break;
686
687 // for "k" and "kk", write out the hour, adjusting midnight to appear as "24"
688 case UDAT_HOUR_OF_DAY1_FIELD:
689 if (value == 0)
690 zeroPaddingNumber(appendTo, cal.getMaximum(UCAL_HOUR_OF_DAY) + 1, count, maxIntCount);
691 else
692 zeroPaddingNumber(appendTo, value, count, maxIntCount);
693 break;
694
695 case UDAT_FRACTIONAL_SECOND_FIELD:
696 // Fractional seconds left-justify
697 {
698 fNumberFormat->setMinimumIntegerDigits((count > 3) ? 3 : count);
699 fNumberFormat->setMaximumIntegerDigits(maxIntCount);
700 if (count == 1) {
701 value = (value + 50) / 100;
702 } else if (count == 2) {
703 value = (value + 5) / 10;
704 }
705 FieldPosition p(0);
706 fNumberFormat->format(value, appendTo, p);
707 if (count > 3) {
708 fNumberFormat->setMinimumIntegerDigits(count - 3);
709 fNumberFormat->format((int32_t)0, appendTo, p);
710 }
711 }
712 break;
713
714 // for "EEE", write out the abbreviated day-of-the-week name
715 // for "EEEE", write out the wide day-of-the-week name
716 // for "EEEEE", use the narrow day-of-the-week name
717 case UDAT_DAY_OF_WEEK_FIELD:
718 if (count == 5)
719 _appendSymbol(appendTo, value, fSymbols->fNarrowWeekdays,
720 fSymbols->fNarrowWeekdaysCount);
721 else if (count == 4)
722 _appendSymbol(appendTo, value, fSymbols->fWeekdays,
723 fSymbols->fWeekdaysCount);
724 else
725 _appendSymbol(appendTo, value, fSymbols->fShortWeekdays,
726 fSymbols->fShortWeekdaysCount);
727 break;
728
729 // for "ccc", write out the abbreviated day-of-the-week name
730 // for "cccc", write out the wide day-of-the-week name
731 // for "ccccc", use the narrow day-of-the-week name
732 case UDAT_STANDALONE_DAY_FIELD:
733 if (count == 5)
734 _appendSymbol(appendTo, value, fSymbols->fStandaloneNarrowWeekdays,
735 fSymbols->fStandaloneNarrowWeekdaysCount);
736 else if (count == 4)
737 _appendSymbol(appendTo, value, fSymbols->fStandaloneWeekdays,
738 fSymbols->fStandaloneWeekdaysCount);
739 else if (count == 3)
740 _appendSymbol(appendTo, value, fSymbols->fStandaloneShortWeekdays,
741 fSymbols->fStandaloneShortWeekdaysCount);
742 else
743 zeroPaddingNumber(appendTo, value, 1, maxIntCount);
744 break;
745
746 // for and "a" symbol, write out the whole AM/PM string
747 case UDAT_AM_PM_FIELD:
748 _appendSymbol(appendTo, value, fSymbols->fAmPms,
749 fSymbols->fAmPmsCount);
750 break;
751
752 // for "h" and "hh", write out the hour, adjusting noon and midnight to show up
753 // as "12"
754 case UDAT_HOUR1_FIELD:
755 if (value == 0)
756 zeroPaddingNumber(appendTo, cal.getLeastMaximum(UCAL_HOUR) + 1, count, maxIntCount);
757 else
758 zeroPaddingNumber(appendTo, value, count, maxIntCount);
759 break;
760
761 // for the "z" symbols, we have to check our time zone data first. If we have a
762 // localized name for the time zone, then "zzzz" / "zzz" indicate whether
763 // daylight time is in effect (long/short) and "zz" / "z" do not (long/short).
764 // If we don't have a localized time zone name,
765 // then the time zone shows up as "GMT+hh:mm" or "GMT-hh:mm" (where "hh:mm" is the
766 // offset from GMT) regardless of how many z's were in the pattern symbol
767 case UDAT_TIMEZONE_FIELD:
768 case UDAT_TIMEZONE_GENERIC_FIELD: {
769 UnicodeString str;
770 UnicodeString zid;
771 UnicodeString displayString;
772 zid = fSymbols->getZoneID(cal.getTimeZone().getID(str), zid, status);
773 if(U_FAILURE(status)){
774 break;
775 }
776 if (zid.length() == 0) {
777 appendGMT(appendTo, cal, status);
778 }
779 else {
780
781 if (patternCharIndex == UDAT_TIMEZONE_GENERIC_FIELD) {
782 if(count < 4){
783 fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_GENERIC, displayString, status);
784 }else{
785 fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_LONG_GENERIC, displayString, status);
786 }
787 } else {
788 if (cal.get(UCAL_DST_OFFSET, status) != 0) {
789 if(count < 4){
790 fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_DAYLIGHT, displayString, status);
791 }else{
792 fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_LONG_DAYLIGHT, displayString, status);
793 }
794 }else{
795 if(count < 4){
796 fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_STANDARD, displayString, status);
797 }else{
798 fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_LONG_STANDARD, displayString, status);
799 }
800 }
801 }
802 if(displayString.length()==0){
803 appendGMT(appendTo, cal, status);
804 }else{
805 appendTo += displayString;
806 }
807 }
808 }
809 break;
810
811 case 23: // 'Z' - TIMEZONE_RFC
812 {
813 UChar sign = 43/*'+'*/;
814 value = (cal.get(UCAL_ZONE_OFFSET, status) +
815 cal.get(UCAL_DST_OFFSET, status)) / U_MILLIS_PER_MINUTE;
816 if (value < 0) {
817 value = -value;
818 sign = 45/*'-'*/;
819 }
820 value = (value / 60) * 100 + (value % 60); // minutes => KKmm
821 appendTo += sign;
822 zeroPaddingNumber(appendTo, value, 4, 4);
823 }
824 break;
825
826 case UDAT_QUARTER_FIELD:
827 if (count >= 4)
828 _appendSymbol(appendTo, value/3, fSymbols->fQuarters,
829 fSymbols->fQuartersCount);
830 else if (count == 3)
831 _appendSymbol(appendTo, value/3, fSymbols->fShortQuarters,
832 fSymbols->fShortQuartersCount);
833 else
834 zeroPaddingNumber(appendTo, (value/3) + 1, count, maxIntCount);
835 break;
836
837 case UDAT_STANDALONE_QUARTER_FIELD:
838 if (count >= 4)
839 _appendSymbol(appendTo, value/3, fSymbols->fStandaloneQuarters,
840 fSymbols->fStandaloneQuartersCount);
841 else if (count == 3)
842 _appendSymbol(appendTo, value/3, fSymbols->fStandaloneShortQuarters,
843 fSymbols->fStandaloneShortQuartersCount);
844 else
845 zeroPaddingNumber(appendTo, (value/3) + 1, count, maxIntCount);
846 break;
847
848
849 // all of the other pattern symbols can be formatted as simple numbers with
850 // appropriate zero padding
851 default:
852 zeroPaddingNumber(appendTo, value, count, maxIntCount);
853 break;
854 }
855
856 // if the field we're formatting is the one the FieldPosition says it's interested
857 // in, fill in the FieldPosition with this field's positions
858 if (pos.getBeginIndex() == pos.getEndIndex() &&
859 pos.getField() == fgPatternIndexToDateFormatField[patternCharIndex]) {
860 pos.setBeginIndex(beginOffset);
861 pos.setEndIndex(appendTo.length());
862 }
863 }
864
865 //----------------------------------------------------------------------
866
867 void
868 SimpleDateFormat::zeroPaddingNumber(UnicodeString &appendTo, int32_t value, int32_t minDigits, int32_t maxDigits) const
869 {
870 FieldPosition pos(0);
871
872 fNumberFormat->setMinimumIntegerDigits(minDigits);
873 fNumberFormat->setMaximumIntegerDigits(maxDigits);
874 fNumberFormat->format(value, appendTo, pos); // 3rd arg is there to speed up processing
875 }
876
877 //----------------------------------------------------------------------
878
879 /**
880 * Format characters that indicate numeric fields. The character
881 * at index 0 is treated specially.
882 */
883 static const UChar NUMERIC_FORMAT_CHARS[] = {0x4D, 0x79, 0x75, 0x64, 0x68, 0x48, 0x6D, 0x73, 0x53, 0x44, 0x46, 0x77, 0x57, 0x6B, 0x4B, 0x00}; /* "MyudhHmsSDFwWkK" */
884
885 /**
886 * Return true if the given format character, occuring count
887 * times, represents a numeric field.
888 */
889 UBool SimpleDateFormat::isNumeric(UChar formatChar, int32_t count) {
890 UnicodeString s(NUMERIC_FORMAT_CHARS);
891 int32_t i = s.indexOf(formatChar);
892 return (i > 0 || (i == 0 && count < 3));
893 }
894
895 void
896 SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& parsePos) const
897 {
898 int32_t pos = parsePos.getIndex();
899 int32_t start = pos;
900 UBool ambiguousYear[] = { FALSE };
901 int32_t count = 0;
902
903 // hack, clear parsedTimeZone, cast away const
904 delete parsedTimeZone;
905 ((SimpleDateFormat*)this)->parsedTimeZone = NULL;
906
907 // For parsing abutting numeric fields. 'abutPat' is the
908 // offset into 'pattern' of the first of 2 or more abutting
909 // numeric fields. 'abutStart' is the offset into 'text'
910 // where parsing the fields begins. 'abutPass' starts off as 0
911 // and increments each time we try to parse the fields.
912 int32_t abutPat = -1; // If >=0, we are in a run of abutting numeric fields
913 int32_t abutStart = 0;
914 int32_t abutPass = 0;
915 UBool inQuote = FALSE;
916
917 const UnicodeString numericFormatChars(NUMERIC_FORMAT_CHARS);
918
919 for (int32_t i=0; i<fPattern.length(); ++i) {
920 UChar ch = fPattern.charAt(i);
921
922 // Handle alphabetic field characters.
923 if (!inQuote && ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A))) { // [A-Za-z]
924 int32_t fieldPat = i;
925
926 // Count the length of this field specifier
927 count = 1;
928 while ((i+1)<fPattern.length() &&
929 fPattern.charAt(i+1) == ch) {
930 ++count;
931 ++i;
932 }
933
934 if (isNumeric(ch, count)) {
935 if (abutPat < 0) {
936 // Determine if there is an abutting numeric field. For
937 // most fields we can just look at the next characters,
938 // but the 'm' field is either numeric or text,
939 // depending on the count, so we have to look ahead for
940 // that field.
941 if ((i+1)<fPattern.length()) {
942 UBool abutting;
943 UChar nextCh = fPattern.charAt(i+1);
944 int32_t k = numericFormatChars.indexOf(nextCh);
945 if (k == 0) {
946 int32_t j = i+2;
947 while (j<fPattern.length() &&
948 fPattern.charAt(j) == nextCh) {
949 ++j;
950 }
951 abutting = (j-i) < 4; // nextCount < 3
952 } else {
953 abutting = k > 0;
954 }
955
956 // Record the start of a set of abutting numeric
957 // fields.
958 if (abutting) {
959 abutPat = fieldPat;
960 abutStart = pos;
961 abutPass = 0;
962 }
963 }
964 }
965 } else {
966 abutPat = -1; // End of any abutting fields
967 }
968
969 // Handle fields within a run of abutting numeric fields. Take
970 // the pattern "HHmmss" as an example. We will try to parse
971 // 2/2/2 characters of the input text, then if that fails,
972 // 1/2/2. We only adjust the width of the leftmost field; the
973 // others remain fixed. This allows "123456" => 12:34:56, but
974 // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
975 // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
976 if (abutPat >= 0) {
977 // If we are at the start of a run of abutting fields, then
978 // shorten this field in each pass. If we can't shorten
979 // this field any more, then the parse of this set of
980 // abutting numeric fields has failed.
981 if (fieldPat == abutPat) {
982 count -= abutPass++;
983 if (count == 0) {
984 parsePos.setIndex(start);
985 parsePos.setErrorIndex(pos);
986 return;
987 }
988 }
989
990 pos = subParse(text, pos, ch, count,
991 TRUE, FALSE, ambiguousYear, cal);
992
993 // If the parse fails anywhere in the run, back up to the
994 // start of the run and retry.
995 if (pos < 0) {
996 i = abutPat - 1;
997 pos = abutStart;
998 continue;
999 }
1000 }
1001
1002 // Handle non-numeric fields and non-abutting numeric
1003 // fields.
1004 else {
1005 int32_t s = pos;
1006 pos = subParse(text, pos, ch, count,
1007 FALSE, TRUE, ambiguousYear, cal);
1008
1009 if (pos < 0) {
1010 parsePos.setErrorIndex(s);
1011 parsePos.setIndex(start);
1012 return;
1013 }
1014 }
1015 }
1016
1017 // Handle literal pattern characters. These are any
1018 // quoted characters and non-alphabetic unquoted
1019 // characters.
1020 else {
1021
1022 abutPat = -1; // End of any abutting fields
1023
1024 // Handle quotes. Two consecutive quotes is a quote
1025 // literal, inside or outside of quotes. Otherwise a
1026 // quote indicates entry or exit from a quoted region.
1027 if (ch == QUOTE) {
1028 // Match a quote literal '' within OR outside of quotes
1029 if ((i+1)<fPattern.length() && fPattern.charAt(i+1)==ch) {
1030 ++i; // Skip over doubled quote
1031 // Fall through and treat quote as a literal
1032 } else {
1033 // Enter or exit quoted region
1034 inQuote = !inQuote;
1035 continue;
1036 }
1037 }
1038
1039 // A run of white space in the pattern matches a run
1040 // of white space in the input text.
1041 if (uprv_isRuleWhiteSpace(ch)) {
1042 // Advance over run in pattern
1043 while ((i+1)<fPattern.length() &&
1044 uprv_isRuleWhiteSpace(fPattern.charAt(i+1))) {
1045 ++i;
1046 }
1047
1048 // Advance over run in input text
1049 int32_t s = pos;
1050 while (pos<text.length() &&
1051 u_isUWhiteSpace(text.charAt(pos))) {
1052 ++pos;
1053 }
1054
1055 // Must see at least one white space char in input
1056 if (pos > s) {
1057 continue;
1058 }
1059 } else if (pos<text.length() && text.charAt(pos)==ch) {
1060 // Match a literal
1061 ++pos;
1062 continue;
1063 }
1064
1065 // We fall through to this point if the match fails
1066 parsePos.setIndex(start);
1067 parsePos.setErrorIndex(pos);
1068 return;
1069 }
1070 }
1071
1072 // At this point the fields of Calendar have been set. Calendar
1073 // will fill in default values for missing fields when the time
1074 // is computed.
1075
1076 parsePos.setIndex(pos);
1077
1078 // This part is a problem: When we call parsedDate.after, we compute the time.
1079 // Take the date April 3 2004 at 2:30 am. When this is first set up, the year
1080 // will be wrong if we're parsing a 2-digit year pattern. It will be 1904.
1081 // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am
1082 // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
1083 // on that day. It is therefore parsed out to fields as 3:30 am. Then we
1084 // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is
1085 // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
1086 /*
1087 UDate parsedDate = calendar.getTime();
1088 if( ambiguousYear[0] && !parsedDate.after(fDefaultCenturyStart) ) {
1089 calendar.add(Calendar.YEAR, 100);
1090 parsedDate = calendar.getTime();
1091 }
1092 */
1093 // Because of the above condition, save off the fields in case we need to readjust.
1094 // The procedure we use here is not particularly efficient, but there is no other
1095 // way to do this given the API restrictions present in Calendar. We minimize
1096 // inefficiency by only performing this computation when it might apply, that is,
1097 // when the two-digit year is equal to the start year, and thus might fall at the
1098 // front or the back of the default century. This only works because we adjust
1099 // the year correctly to start with in other cases -- see subParse().
1100 UErrorCode status = U_ZERO_ERROR;
1101 if (ambiguousYear[0] || parsedTimeZone != NULL) // If this is true then the two-digit year == the default start year
1102 {
1103 // We need a copy of the fields, and we need to avoid triggering a call to
1104 // complete(), which will recalculate the fields. Since we can't access
1105 // the fields[] array in Calendar, we clone the entire object. This will
1106 // stop working if Calendar.clone() is ever rewritten to call complete().
1107 Calendar *copy = cal.clone();
1108 if (ambiguousYear[0]) {
1109 UDate parsedDate = copy->getTime(status);
1110 // {sfb} check internalGetDefaultCenturyStart
1111 if (fHaveDefaultCentury && (parsedDate < fDefaultCenturyStart)) {
1112 // We can't use add here because that does a complete() first.
1113 cal.set(UCAL_YEAR, fDefaultCenturyStartYear + 100);
1114 }
1115 }
1116
1117 if (parsedTimeZone != NULL) {
1118 TimeZone *tz = parsedTimeZone;
1119
1120 // the calendar represents the parse as gmt time
1121 // we need to turn this into local time, so we add the raw offset
1122 // then we ask the timezone to handle this local time
1123 int32_t rawOffset = 0;
1124 int32_t dstOffset = 0;
1125 tz->getOffset(copy->getTime(status)+tz->getRawOffset(), TRUE,
1126 rawOffset, dstOffset, status);
1127 if (U_SUCCESS(status)) {
1128 cal.set(UCAL_ZONE_OFFSET, rawOffset);
1129 cal.set(UCAL_DST_OFFSET, dstOffset);
1130 }
1131 }
1132
1133 delete copy;
1134 }
1135
1136 // If any Calendar calls failed, we pretend that we
1137 // couldn't parse the string, when in reality this isn't quite accurate--
1138 // we did parse it; the Calendar calls just failed.
1139 if (U_FAILURE(status)) {
1140 parsePos.setErrorIndex(pos);
1141 parsePos.setIndex(start);
1142 }
1143 }
1144
1145 UDate
1146 SimpleDateFormat::parse( const UnicodeString& text,
1147 ParsePosition& pos) const {
1148 // redefined here because the other parse() function hides this function's
1149 // cunterpart on DateFormat
1150 return DateFormat::parse(text, pos);
1151 }
1152
1153 UDate
1154 SimpleDateFormat::parse(const UnicodeString& text, UErrorCode& status) const
1155 {
1156 // redefined here because the other parse() function hides this function's
1157 // counterpart on DateFormat
1158 return DateFormat::parse(text, status);
1159 }
1160 //----------------------------------------------------------------------
1161
1162 int32_t SimpleDateFormat::matchQuarterString(const UnicodeString& text,
1163 int32_t start,
1164 UCalendarDateFields field,
1165 const UnicodeString* data,
1166 int32_t dataCount,
1167 Calendar& cal) const
1168 {
1169 int32_t i = 0;
1170 int32_t count = dataCount;
1171
1172 // There may be multiple strings in the data[] array which begin with
1173 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
1174 // We keep track of the longest match, and return that. Note that this
1175 // unfortunately requires us to test all array elements.
1176 int32_t bestMatchLength = 0, bestMatch = -1;
1177
1178 // {sfb} kludge to support case-insensitive comparison
1179 // {markus 2002oct11} do not just use caseCompareBetween because we do not know
1180 // the length of the match after case folding
1181 // {alan 20040607} don't case change the whole string, since the length
1182 // can change
1183 // TODO we need a case-insensitive startsWith function
1184 UnicodeString lcase, lcaseText;
1185 text.extract(start, INT32_MAX, lcaseText);
1186 lcaseText.foldCase();
1187
1188 for (; i < count; ++i)
1189 {
1190 // Always compare if we have no match yet; otherwise only compare
1191 // against potentially better matches (longer strings).
1192
1193 lcase.fastCopyFrom(data[i]).foldCase();
1194 int32_t length = lcase.length();
1195
1196 if (length > bestMatchLength &&
1197 lcaseText.compareBetween(0, length, lcase, 0, length) == 0)
1198 {
1199 bestMatch = i;
1200 bestMatchLength = length;
1201 }
1202 }
1203 if (bestMatch >= 0)
1204 {
1205 cal.set(field, bestMatch * 3);
1206
1207 // Once we have a match, we have to determine the length of the
1208 // original source string. This will usually be == the length of
1209 // the case folded string, but it may differ (e.g. sharp s).
1210 lcase.fastCopyFrom(data[bestMatch]).foldCase();
1211
1212 // Most of the time, the length will be the same as the length
1213 // of the string from the locale data. Sometimes it will be
1214 // different, in which case we will have to figure it out by
1215 // adding a character at a time, until we have a match. We do
1216 // this all in one loop, where we try 'len' first (at index
1217 // i==0).
1218 int32_t len = data[bestMatch].length(); // 99+% of the time
1219 int32_t n = text.length() - start;
1220 for (i=0; i<=n; ++i) {
1221 int32_t j=i;
1222 if (i == 0) {
1223 j = len;
1224 } else if (i == len) {
1225 continue; // already tried this when i was 0
1226 }
1227 text.extract(start, j, lcaseText);
1228 lcaseText.foldCase();
1229 if (lcase == lcaseText) {
1230 return start + j;
1231 }
1232 }
1233 }
1234
1235 return -start;
1236 }
1237
1238 //----------------------------------------------------------------------
1239
1240 int32_t SimpleDateFormat::matchString(const UnicodeString& text,
1241 int32_t start,
1242 UCalendarDateFields field,
1243 const UnicodeString* data,
1244 int32_t dataCount,
1245 Calendar& cal) const
1246 {
1247 int32_t i = 0;
1248 int32_t count = dataCount;
1249
1250 if (field == UCAL_DAY_OF_WEEK) i = 1;
1251
1252 // There may be multiple strings in the data[] array which begin with
1253 // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
1254 // We keep track of the longest match, and return that. Note that this
1255 // unfortunately requires us to test all array elements.
1256 int32_t bestMatchLength = 0, bestMatch = -1;
1257
1258 // {sfb} kludge to support case-insensitive comparison
1259 // {markus 2002oct11} do not just use caseCompareBetween because we do not know
1260 // the length of the match after case folding
1261 // {alan 20040607} don't case change the whole string, since the length
1262 // can change
1263 // TODO we need a case-insensitive startsWith function
1264 UnicodeString lcase, lcaseText;
1265 text.extract(start, INT32_MAX, lcaseText);
1266 lcaseText.foldCase();
1267
1268 for (; i < count; ++i)
1269 {
1270 // Always compare if we have no match yet; otherwise only compare
1271 // against potentially better matches (longer strings).
1272
1273 lcase.fastCopyFrom(data[i]).foldCase();
1274 int32_t length = lcase.length();
1275
1276 if (length > bestMatchLength &&
1277 lcaseText.compareBetween(0, length, lcase, 0, length) == 0)
1278 {
1279 bestMatch = i;
1280 bestMatchLength = length;
1281 }
1282 }
1283 if (bestMatch >= 0)
1284 {
1285 cal.set(field, bestMatch);
1286
1287 // Once we have a match, we have to determine the length of the
1288 // original source string. This will usually be == the length of
1289 // the case folded string, but it may differ (e.g. sharp s).
1290 lcase.fastCopyFrom(data[bestMatch]).foldCase();
1291
1292 // Most of the time, the length will be the same as the length
1293 // of the string from the locale data. Sometimes it will be
1294 // different, in which case we will have to figure it out by
1295 // adding a character at a time, until we have a match. We do
1296 // this all in one loop, where we try 'len' first (at index
1297 // i==0).
1298 int32_t len = data[bestMatch].length(); // 99+% of the time
1299 int32_t n = text.length() - start;
1300 for (i=0; i<=n; ++i) {
1301 int32_t j=i;
1302 if (i == 0) {
1303 j = len;
1304 } else if (i == len) {
1305 continue; // already tried this when i was 0
1306 }
1307 text.extract(start, j, lcaseText);
1308 lcaseText.foldCase();
1309 if (lcase == lcaseText) {
1310 return start + j;
1311 }
1312 }
1313 }
1314
1315 return -start;
1316 }
1317
1318 //----------------------------------------------------------------------
1319
1320 void
1321 SimpleDateFormat::set2DigitYearStart(UDate d, UErrorCode& status)
1322 {
1323 parseAmbiguousDatesAsAfter(d, status);
1324 }
1325
1326 /**
1327 * Private member function that converts the parsed date strings into
1328 * timeFields. Returns -start (for ParsePosition) if failed.
1329 * @param text the time text to be parsed.
1330 * @param start where to start parsing.
1331 * @param ch the pattern character for the date field text to be parsed.
1332 * @param count the count of a pattern character.
1333 * @return the new start position if matching succeeded; a negative number
1334 * indicating matching failure, otherwise.
1335 */
1336 int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UChar ch, int32_t count,
1337 UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], Calendar& cal) const
1338 {
1339 Formattable number;
1340 int32_t value = 0;
1341 int32_t i;
1342 ParsePosition pos(0);
1343 int32_t patternCharIndex;
1344 UnicodeString temp;
1345 UChar *patternCharPtr = u_strchr(DateFormatSymbols::getPatternUChars(), ch);
1346
1347 #if defined (U_DEBUG_CAL)
1348 //fprintf(stderr, "%s:%d - [%c] st=%d \n", __FILE__, __LINE__, (char) ch, start);
1349 #endif
1350
1351 if (patternCharPtr == NULL) {
1352 return -start;
1353 }
1354
1355 patternCharIndex = (UDateFormatField)(patternCharPtr - DateFormatSymbols::getPatternUChars());
1356
1357 UCalendarDateFields field = fgPatternIndexToCalendarField[patternCharIndex];
1358
1359 // If there are any spaces here, skip over them. If we hit the end
1360 // of the string, then fail.
1361 for (;;) {
1362 if (start >= text.length()) {
1363 return -start;
1364 }
1365 UChar32 c = text.char32At(start);
1366 if (!u_isUWhiteSpace(c)) {
1367 break;
1368 }
1369 start += UTF_CHAR_LENGTH(c);
1370 }
1371 pos.setIndex(start);
1372
1373 // We handle a few special cases here where we need to parse
1374 // a number value. We handle further, more generic cases below. We need
1375 // to handle some of them here because some fields require extra processing on
1376 // the parsed value.
1377 if (patternCharIndex == UDAT_HOUR_OF_DAY1_FIELD ||
1378 patternCharIndex == UDAT_HOUR1_FIELD ||
1379 (patternCharIndex == UDAT_MONTH_FIELD && count <= 2) ||
1380 (patternCharIndex == UDAT_STANDALONE_MONTH_FIELD && count <= 2) ||
1381 (patternCharIndex == UDAT_QUARTER_FIELD && count <= 2) ||
1382 (patternCharIndex == UDAT_STANDALONE_QUARTER_FIELD && count <= 2) ||
1383 patternCharIndex == UDAT_YEAR_FIELD ||
1384 patternCharIndex == UDAT_YEAR_WOY_FIELD ||
1385 patternCharIndex == UDAT_FRACTIONAL_SECOND_FIELD)
1386 {
1387 int32_t parseStart = pos.getIndex();
1388 // It would be good to unify this with the obeyCount logic below,
1389 // but that's going to be difficult.
1390 const UnicodeString* src;
1391
1392 if (obeyCount) {
1393 if ((start+count) > text.length()) {
1394 return -start;
1395 }
1396
1397 text.extractBetween(0, start + count, temp);
1398 src = &temp;
1399 } else {
1400 src = &text;
1401 }
1402
1403 parseInt(*src, number, pos, allowNegative);
1404
1405 if (pos.getIndex() == parseStart)
1406 return -start;
1407 value = number.getLong();
1408 }
1409
1410 switch (patternCharIndex) {
1411 case UDAT_ERA_FIELD:
1412 if (count == 4) {
1413 return matchString(text, start, UCAL_ERA, fSymbols->fEraNames, fSymbols->fEraNamesCount, cal);
1414 }
1415
1416 return matchString(text, start, UCAL_ERA, fSymbols->fEras, fSymbols->fErasCount, cal);
1417
1418 case UDAT_YEAR_FIELD:
1419 // If there are 3 or more YEAR pattern characters, this indicates
1420 // that the year value is to be treated literally, without any
1421 // two-digit year adjustments (e.g., from "01" to 2001). Otherwise
1422 // we made adjustments to place the 2-digit year in the proper
1423 // century, for parsed strings from "00" to "99". Any other string
1424 // is treated literally: "2250", "-1", "1", "002".
1425 if (count <= 2 && (pos.getIndex() - start) == 2
1426 && u_isdigit(text.charAt(start))
1427 && u_isdigit(text.charAt(start+1)))
1428 {
1429 // Assume for example that the defaultCenturyStart is 6/18/1903.
1430 // This means that two-digit years will be forced into the range
1431 // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
1432 // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
1433 // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
1434 // other fields specify a date before 6/18, or 1903 if they specify a
1435 // date afterwards. As a result, 03 is an ambiguous year. All other
1436 // two-digit years are unambiguous.
1437 if(fHaveDefaultCentury) { // check if this formatter even has a pivot year
1438 int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
1439 ambiguousYear[0] = (value == ambiguousTwoDigitYear);
1440 value += (fDefaultCenturyStartYear/100)*100 +
1441 (value < ambiguousTwoDigitYear ? 100 : 0);
1442 }
1443 }
1444 cal.set(UCAL_YEAR, value);
1445 return pos.getIndex();
1446
1447 case UDAT_YEAR_WOY_FIELD:
1448 // Comment is the same as for UDAT_Year_FIELDs - look above
1449 if (count <= 2 && (pos.getIndex() - start) == 2
1450 && u_isdigit(text.charAt(start))
1451 && u_isdigit(text.charAt(start+1))
1452 && fHaveDefaultCentury )
1453 {
1454 int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100;
1455 ambiguousYear[0] = (value == ambiguousTwoDigitYear);
1456 value += (fDefaultCenturyStartYear/100)*100 +
1457 (value < ambiguousTwoDigitYear ? 100 : 0);
1458 }
1459 cal.set(UCAL_YEAR_WOY, value);
1460 return pos.getIndex();
1461
1462 case UDAT_MONTH_FIELD:
1463 if (count <= 2) // i.e., M or MM.
1464 {
1465 // Don't want to parse the month if it is a string
1466 // while pattern uses numeric style: M or MM.
1467 // [We computed 'value' above.]
1468 cal.set(UCAL_MONTH, value - 1);
1469 return pos.getIndex();
1470 } else {
1471 // count >= 3 // i.e., MMM or MMMM
1472 // Want to be able to parse both short and long forms.
1473 // Try count == 4 first:
1474 int32_t newStart = 0;
1475
1476 if ((newStart = matchString(text, start, UCAL_MONTH,
1477 fSymbols->fMonths, fSymbols->fMonthsCount, cal)) > 0)
1478 return newStart;
1479 else // count == 4 failed, now try count == 3
1480 return matchString(text, start, UCAL_MONTH,
1481 fSymbols->fShortMonths, fSymbols->fShortMonthsCount, cal);
1482 }
1483
1484 case UDAT_STANDALONE_MONTH_FIELD:
1485 if (count <= 2) // i.e., L or LL.
1486 {
1487 // Don't want to parse the month if it is a string
1488 // while pattern uses numeric style: M or MM.
1489 // [We computed 'value' above.]
1490 cal.set(UCAL_MONTH, value - 1);
1491 return pos.getIndex();
1492 } else {
1493 // count >= 3 // i.e., LLL or LLLL
1494 // Want to be able to parse both short and long forms.
1495 // Try count == 4 first:
1496 int32_t newStart = 0;
1497
1498 if ((newStart = matchString(text, start, UCAL_MONTH,
1499 fSymbols->fStandaloneMonths, fSymbols->fStandaloneMonthsCount, cal)) > 0)
1500 return newStart;
1501 else // count == 4 failed, now try count == 3
1502 return matchString(text, start, UCAL_MONTH,
1503 fSymbols->fStandaloneShortMonths, fSymbols->fStandaloneShortMonthsCount, cal);
1504 }
1505
1506 case UDAT_HOUR_OF_DAY1_FIELD:
1507 // [We computed 'value' above.]
1508 if (value == cal.getMaximum(UCAL_HOUR_OF_DAY) + 1)
1509 value = 0;
1510 cal.set(UCAL_HOUR_OF_DAY, value);
1511 return pos.getIndex();
1512
1513 case UDAT_FRACTIONAL_SECOND_FIELD:
1514 // Fractional seconds left-justify
1515 i = pos.getIndex() - start;
1516 if (i < 3) {
1517 while (i < 3) {
1518 value *= 10;
1519 i++;
1520 }
1521 } else {
1522 int32_t a = 1;
1523 while (i > 3) {
1524 a *= 10;
1525 i--;
1526 }
1527 value = (value + (a>>1)) / a;
1528 }
1529 cal.set(UCAL_MILLISECOND, value);
1530 return pos.getIndex();
1531
1532 case UDAT_DAY_OF_WEEK_FIELD:
1533 {
1534 // Want to be able to parse both short and long forms.
1535 // Try count == 4 (DDDD) first:
1536 int32_t newStart = 0;
1537 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
1538 fSymbols->fWeekdays, fSymbols->fWeekdaysCount, cal)) > 0)
1539 return newStart;
1540 else // DDDD failed, now try DDD
1541 return matchString(text, start, UCAL_DAY_OF_WEEK,
1542 fSymbols->fShortWeekdays, fSymbols->fShortWeekdaysCount, cal);
1543 }
1544
1545 case UDAT_STANDALONE_DAY_FIELD:
1546 {
1547 // Want to be able to parse both short and long forms.
1548 // Try count == 4 (DDDD) first:
1549 int32_t newStart = 0;
1550 if ((newStart = matchString(text, start, UCAL_DAY_OF_WEEK,
1551 fSymbols->fStandaloneWeekdays, fSymbols->fStandaloneWeekdaysCount, cal)) > 0)
1552 return newStart;
1553 else // DDDD failed, now try DDD
1554 return matchString(text, start, UCAL_DAY_OF_WEEK,
1555 fSymbols->fStandaloneShortWeekdays, fSymbols->fStandaloneShortWeekdaysCount, cal);
1556 }
1557
1558 case UDAT_AM_PM_FIELD:
1559 return matchString(text, start, UCAL_AM_PM, fSymbols->fAmPms, fSymbols->fAmPmsCount, cal);
1560
1561 case UDAT_HOUR1_FIELD:
1562 // [We computed 'value' above.]
1563 if (value == cal.getLeastMaximum(UCAL_HOUR)+1)
1564 value = 0;
1565 cal.set(UCAL_HOUR, value);
1566 return pos.getIndex();
1567
1568 case UDAT_QUARTER_FIELD:
1569 if (count <= 2) // i.e., Q or QQ.
1570 {
1571 // Don't want to parse the month if it is a string
1572 // while pattern uses numeric style: Q or QQ.
1573 // [We computed 'value' above.]
1574 cal.set(UCAL_MONTH, (value - 1) * 3);
1575 return pos.getIndex();
1576 } else {
1577 // count >= 3 // i.e., QQQ or QQQQ
1578 // Want to be able to parse both short and long forms.
1579 // Try count == 4 first:
1580 int32_t newStart = 0;
1581
1582 if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
1583 fSymbols->fQuarters, fSymbols->fQuartersCount, cal)) > 0)
1584 return newStart;
1585 else // count == 4 failed, now try count == 3
1586 return matchQuarterString(text, start, UCAL_MONTH,
1587 fSymbols->fShortQuarters, fSymbols->fShortQuartersCount, cal);
1588 }
1589
1590 case UDAT_STANDALONE_QUARTER_FIELD:
1591 if (count <= 2) // i.e., q or qq.
1592 {
1593 // Don't want to parse the month if it is a string
1594 // while pattern uses numeric style: q or q.
1595 // [We computed 'value' above.]
1596 cal.set(UCAL_MONTH, (value - 1) * 3);
1597 return pos.getIndex();
1598 } else {
1599 // count >= 3 // i.e., qqq or qqqq
1600 // Want to be able to parse both short and long forms.
1601 // Try count == 4 first:
1602 int32_t newStart = 0;
1603
1604 if ((newStart = matchQuarterString(text, start, UCAL_MONTH,
1605 fSymbols->fStandaloneQuarters, fSymbols->fStandaloneQuartersCount, cal)) > 0)
1606 return newStart;
1607 else // count == 4 failed, now try count == 3
1608 return matchQuarterString(text, start, UCAL_MONTH,
1609 fSymbols->fStandaloneShortQuarters, fSymbols->fStandaloneShortQuartersCount, cal);
1610 }
1611
1612 case UDAT_TIMEZONE_FIELD:
1613 case UDAT_TIMEZONE_RFC_FIELD:
1614 case UDAT_TIMEZONE_GENERIC_FIELD:
1615 {
1616 // First try to parse generic forms such as GMT-07:00. Do this first
1617 // in case localized DateFormatZoneData contains the string "GMT"
1618 // for a zone; in that case, we don't want to match the first three
1619 // characters of GMT+/-HH:MM etc.
1620
1621 int32_t sign = 0;
1622 int32_t offset;
1623 int32_t gmtLen = u_strlen(gGmt);
1624
1625 // For time zones that have no known names, look for strings
1626 // of the form:
1627 // GMT[+-]hours:minutes or
1628 // GMT[+-]hhmm or
1629 // GMT.
1630
1631 if ((text.length() - start) >= gmtLen &&
1632 (text.caseCompare(start, gmtLen, gGmt, 0, gmtLen, U_FOLD_CASE_DEFAULT)) == 0)
1633 {
1634 cal.set(UCAL_DST_OFFSET, 0);
1635
1636 pos.setIndex(start + gmtLen);
1637
1638 if( text[pos.getIndex()] == 0x002B /*'+'*/ )
1639 sign = 1;
1640 else if( text[pos.getIndex()] == 0x002D /*'-'*/ )
1641 sign = -1;
1642 else {
1643 cal.set(UCAL_ZONE_OFFSET, 0 );
1644 return pos.getIndex();
1645 }
1646
1647 // Look for hours:minutes or hhmm.
1648 pos.setIndex(pos.getIndex() + 1);
1649 int32_t parseStart = pos.getIndex();
1650 Formattable tzNumber;
1651 fNumberFormat->parse(text, tzNumber, pos);
1652 if( pos.getIndex() == parseStart) {
1653 return -start;
1654 }
1655 if( text[pos.getIndex()] == 0x003A /*':'*/ ) {
1656 // This is the hours:minutes case
1657 offset = tzNumber.getLong() * 60;
1658 pos.setIndex(pos.getIndex() + 1);
1659 parseStart = pos.getIndex();
1660 fNumberFormat->parse(text, tzNumber, pos);
1661 if( pos.getIndex() == parseStart) {
1662 return -start;
1663 }
1664 offset += tzNumber.getLong();
1665 }
1666 else {
1667 // This is the hhmm case.
1668 offset = tzNumber.getLong();
1669 if( offset < 24 )
1670 offset *= 60;
1671 else
1672 offset = offset % 100 + offset / 100 * 60;
1673 }
1674
1675 // Fall through for final processing below of 'offset' and 'sign'.
1676 }
1677 else {
1678 // At this point, check for named time zones by looking through
1679 // the locale data from the DateFormatZoneData strings.
1680 // Want to be able to parse both short and long forms.
1681 // !!! side effect, might set parsedZoneString
1682 UErrorCode status = U_ZERO_ERROR;
1683 int32_t result = subParseZoneString(text, start, cal, status);
1684 if (result != 0) {
1685 return result;
1686 }
1687
1688 // As a last resort, look for numeric timezones of the form
1689 // [+-]hhmm as specified by RFC 822. This code is actually
1690 // a little more permissive than RFC 822. It will try to do
1691 // its best with numbers that aren't strictly 4 digits long
1692 DecimalFormat fmt(UNICODE_STRING_SIMPLE("+####;-####"), status);
1693 if(U_FAILURE(status))
1694 return -start;
1695 fmt.setParseIntegerOnly(TRUE);
1696 int32_t parseStart = pos.getIndex();
1697 Formattable tzNumber;
1698 fmt.parse( text, tzNumber, pos );
1699 if( pos.getIndex() == parseStart) {
1700 return -start; // Wasn't actually a number.
1701 }
1702 offset = tzNumber.getLong();
1703 sign = 1;
1704 if( offset < 0 ) {
1705 sign = -1;
1706 offset = -offset;
1707 }
1708 if( offset < 24 )
1709 offset = offset * 60;
1710 else
1711 offset = offset % 100 + offset / 100 * 60;
1712
1713 // Fall through for final processing below of 'offset' and 'sign'.
1714 }
1715
1716 // Do the final processing for both of the above cases. We only
1717 // arrive here if the form GMT+/-... or an RFC 822 form was seen.
1718 if (sign != 0)
1719 {
1720 offset *= U_MILLIS_PER_MINUTE * sign;
1721
1722 if (cal.getTimeZone().useDaylightTime())
1723 {
1724 cal.set(UCAL_DST_OFFSET, U_MILLIS_PER_HOUR);
1725 offset -= U_MILLIS_PER_HOUR;
1726 }
1727 cal.set(UCAL_ZONE_OFFSET, offset);
1728
1729 return pos.getIndex();
1730 }
1731
1732 // All efforts to parse a zone failed.
1733 return -start;
1734 }
1735
1736 default:
1737 // Handle "generic" fields
1738 int32_t parseStart = pos.getIndex();
1739 const UnicodeString* src;
1740 if (obeyCount) {
1741 if ((start+count) > text.length()) {
1742 return -start;
1743 }
1744 text.extractBetween(0, start + count, temp);
1745 src = &temp;
1746 } else {
1747 src = &text;
1748 }
1749 parseInt(*src, number, pos, allowNegative);
1750 if (pos.getIndex() != parseStart) {
1751 cal.set(field, number.getLong());
1752 return pos.getIndex();
1753 }
1754 return -start;
1755 }
1756 }
1757
1758 int32_t
1759 SimpleDateFormat::subParseZoneString(const UnicodeString& text, int32_t start, Calendar& cal, UErrorCode& status) const
1760 {
1761 // At this point, check for named time zones by looking through
1762 // the locale data from the DateFormatZoneData strings.
1763 // Want to be able to parse both short and long forms.
1764
1765 // optimize for calendar's current time zone
1766 TimeZone *tz = NULL;
1767 UnicodeString id;
1768 UnicodeString zid, value;
1769 DateFormatSymbols::TimeZoneTranslationType type = DateFormatSymbols::TIMEZONE_COUNT;
1770 fSymbols->getZoneID(getTimeZone().getID(id), zid, status);
1771 if(zid.length() > 0){
1772 fSymbols->findZoneIDTypeValue(zid, text, start, type, value, status);
1773 if(type != DateFormatSymbols::TIMEZONE_COUNT) {
1774 tz = TimeZone::createTimeZone(zid);
1775 }
1776 }
1777
1778 if(U_FAILURE(status)){
1779 return 0;
1780 }
1781 if (tz != NULL) { // Matched any ?
1782 // always set zone offset, needed to get correct hour in wall time
1783 // when checking daylight savings
1784 cal.set(UCAL_ZONE_OFFSET, tz->getRawOffset());
1785 if (type==DateFormatSymbols::TIMEZONE_SHORT_STANDARD || type==DateFormatSymbols::TIMEZONE_LONG_STANDARD ) {
1786 // standard time
1787 cal.set(UCAL_DST_OFFSET, 0);
1788 delete tz; tz = NULL;
1789 } else if (type==DateFormatSymbols::TIMEZONE_SHORT_DAYLIGHT || type==DateFormatSymbols::TIMEZONE_LONG_DAYLIGHT ) {
1790 // daylight time
1791 // !!! todo - no getDSTSavings() in ICU's timezone
1792 // use the correct DST SAVINGS for the zone.
1793 // cal.set(UCAL_DST_OFFSET, tz->getDSTSavings());
1794 cal.set(UCAL_DST_OFFSET, U_MILLIS_PER_HOUR);
1795 delete tz; tz = NULL;
1796 } else {
1797 // either standard or daylight
1798 // need to finish getting the date, then compute dst offset as appropriate
1799
1800 // !!! hack for api compatibility, can't modify subParse(...) so can't
1801 // pass this back any other way. cast away const.
1802 ((SimpleDateFormat*)this)->parsedTimeZone = tz;
1803 }
1804
1805 return start + value.length();
1806 }
1807
1808
1809 // complete failure
1810 return 0;
1811 }
1812
1813 /**
1814 * Parse an integer using fNumberFormat. This method is semantically
1815 * const, but actually may modify fNumberFormat.
1816 */
1817 void SimpleDateFormat::parseInt(const UnicodeString& text,
1818 Formattable& number,
1819 ParsePosition& pos,
1820 UBool allowNegative) const {
1821 UnicodeString oldPrefix;
1822 DecimalFormat* df = NULL;
1823 if (!allowNegative &&
1824 fNumberFormat->getDynamicClassID() == DecimalFormat::getStaticClassID()) {
1825 df = (DecimalFormat*)fNumberFormat;
1826 df->getNegativePrefix(oldPrefix);
1827 df->setNegativePrefix(SUPPRESS_NEGATIVE_PREFIX);
1828 }
1829 fNumberFormat->parse(text, number, pos);
1830 if (df != NULL) {
1831 df->setNegativePrefix(oldPrefix);
1832 }
1833 }
1834
1835 //----------------------------------------------------------------------
1836
1837 void SimpleDateFormat::translatePattern(const UnicodeString& originalPattern,
1838 UnicodeString& translatedPattern,
1839 const UnicodeString& from,
1840 const UnicodeString& to,
1841 UErrorCode& status)
1842 {
1843 // run through the pattern and convert any pattern symbols from the version
1844 // in "from" to the corresponding character ion "to". This code takes
1845 // quoted strings into account (it doesn't try to translate them), and it signals
1846 // an error if a particular "pattern character" doesn't appear in "from".
1847 // Depending on the values of "from" and "to" this can convert from generic
1848 // to localized patterns or localized to generic.
1849 if (U_FAILURE(status))
1850 return;
1851
1852 translatedPattern.remove();
1853 UBool inQuote = FALSE;
1854 for (int32_t i = 0; i < originalPattern.length(); ++i) {
1855 UChar c = originalPattern[i];
1856 if (inQuote) {
1857 if (c == QUOTE)
1858 inQuote = FALSE;
1859 }
1860 else {
1861 if (c == QUOTE)
1862 inQuote = TRUE;
1863 else if ((c >= 0x0061 /*'a'*/ && c <= 0x007A) /*'z'*/
1864 || (c >= 0x0041 /*'A'*/ && c <= 0x005A /*'Z'*/)) {
1865 int32_t ci = from.indexOf(c);
1866 if (ci == -1) {
1867 status = U_INVALID_FORMAT_ERROR;
1868 return;
1869 }
1870 c = to[ci];
1871 }
1872 }
1873 translatedPattern += c;
1874 }
1875 if (inQuote) {
1876 status = U_INVALID_FORMAT_ERROR;
1877 return;
1878 }
1879 }
1880
1881 //----------------------------------------------------------------------
1882
1883 UnicodeString&
1884 SimpleDateFormat::toPattern(UnicodeString& result) const
1885 {
1886 result = fPattern;
1887 return result;
1888 }
1889
1890 //----------------------------------------------------------------------
1891
1892 UnicodeString&
1893 SimpleDateFormat::toLocalizedPattern(UnicodeString& result,
1894 UErrorCode& status) const
1895 {
1896 translatePattern(fPattern, result, DateFormatSymbols::getPatternUChars(), fSymbols->fLocalPatternChars, status);
1897 return result;
1898 }
1899
1900 //----------------------------------------------------------------------
1901
1902 void
1903 SimpleDateFormat::applyPattern(const UnicodeString& pattern)
1904 {
1905 fPattern = pattern;
1906 }
1907
1908 //----------------------------------------------------------------------
1909
1910 void
1911 SimpleDateFormat::applyLocalizedPattern(const UnicodeString& pattern,
1912 UErrorCode &status)
1913 {
1914 translatePattern(pattern, fPattern, fSymbols->fLocalPatternChars, DateFormatSymbols::getPatternUChars(), status);
1915 }
1916
1917 //----------------------------------------------------------------------
1918
1919 const DateFormatSymbols*
1920 SimpleDateFormat::getDateFormatSymbols() const
1921 {
1922 return fSymbols;
1923 }
1924
1925 //----------------------------------------------------------------------
1926
1927 void
1928 SimpleDateFormat::adoptDateFormatSymbols(DateFormatSymbols* newFormatSymbols)
1929 {
1930 delete fSymbols;
1931 fSymbols = newFormatSymbols;
1932 }
1933
1934 //----------------------------------------------------------------------
1935 void
1936 SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols& newFormatSymbols)
1937 {
1938 delete fSymbols;
1939 fSymbols = new DateFormatSymbols(newFormatSymbols);
1940 }
1941
1942
1943 //----------------------------------------------------------------------
1944
1945
1946 void SimpleDateFormat::adoptCalendar(Calendar* calendarToAdopt)
1947 {
1948 UErrorCode status = U_ZERO_ERROR;
1949 DateFormat::adoptCalendar(calendarToAdopt);
1950 delete fSymbols;
1951 fSymbols=NULL;
1952 initializeSymbols(fLocale, fCalendar, status); // we need new symbols
1953 initializeDefaultCentury(); // we need a new century (possibly)
1954 }
1955
1956 U_NAMESPACE_END
1957
1958 #endif /* #if !UCONFIG_NO_FORMATTING */
1959
1960 //eof