]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/tzfmt.cpp
ICU-491.11.3.tar.gz
[apple/icu.git] / icuSources / i18n / tzfmt.cpp
CommitLineData
4388f060
A
1/*
2*******************************************************************************
3* Copyright (C) 2011-2012, International Business Machines Corporation and *
4* others. All Rights Reserved. *
5*******************************************************************************
6*/
7
8#include "unicode/utypes.h"
9
10#if !UCONFIG_NO_FORMATTING
11
12#include "unicode/calendar.h"
13#include "unicode/tzfmt.h"
14#include "unicode/numsys.h"
15#include "unicode/uchar.h"
16#include "unicode/udat.h"
17#include "tzgnames.h"
18#include "cmemory.h"
19#include "cstring.h"
20#include "putilimp.h"
21#include "uassert.h"
22#include "ucln_in.h"
23#include "umutex.h"
24#include "uresimp.h"
25#include "ureslocs.h"
26#include "uvector.h"
27#include "zonemeta.h"
28
29U_NAMESPACE_BEGIN
30
31static const char gZoneStringsTag[] = "zoneStrings";
32static const char gGmtFormatTag[]= "gmtFormat";
33static const char gGmtZeroFormatTag[] = "gmtZeroFormat";
34static const char gHourFormatTag[]= "hourFormat";
35
36static const UChar TZID_GMT[] = {0x0045, 0x0074, 0x0063, 0x002F, 0x0047, 0x004D, 0x0054, 0}; // Etc/GMT
37
38static const UChar DEFAULT_GMT_PATTERN[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0}; // GMT{0}
39static const UChar DEFAULT_GMT_ZERO[] = {0x0047, 0x004D, 0x0054, 0}; // GMT
40static const UChar DEFAULT_GMT_POSITIVE_HM[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // +HH:mm
41static const UChar DEFAULT_GMT_POSITIVE_HMS[] = {0x002B, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // +HH:mm:ss
42static const UChar DEFAULT_GMT_NEGATIVE_HM[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // -HH:mm
43static const UChar DEFAULT_GMT_NEGATIVE_HMS[] = {0x002D, 0x0048, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // -HH:mm:ss
44
45static const UChar32 DEFAULT_GMT_DIGITS[] = {
46 0x0030, 0x0031, 0x0032, 0x0033, 0x0034,
47 0x0035, 0x0036, 0x0037, 0x0038, 0x0039
48};
49
50static const UChar DEFAULT_GMT_OFFSET_SEP = 0x003A; // ':'
51
52static const UChar ARG0[] = {0x007B, 0x0030, 0x007D}; // "{0}"
53static const int ARG0_LEN = 3;
54
55static const UChar DEFAULT_GMT_OFFSET_MINUTE_PATTERN[] = {0x006D, 0x006D, 0}; // "mm"
56static const UChar DEFAULT_GMT_OFFSET_SECOND_PATTERN[] = {0x0073, 0x0073, 0}; // "ss"
57
58static const UChar ALT_GMT_STRINGS[][4] = {
59 {0x0047, 0x004D, 0x0054, 0}, // GMT
60 {0x0055, 0x0054, 0x0043, 0}, // UTC
61 {0x0055, 0x0054, 0, 0}, // UT
62 {0, 0, 0, 0}
63};
64
65// Order of GMT offset pattern parsing, *_HMS must be evaluated first
66// because *_HM is most likely a substring of *_HMS
67static const int32_t PARSE_GMT_OFFSET_TYPES[] = {
68 UTZFMT_PAT_POSITIVE_HMS,
69 UTZFMT_PAT_NEGATIVE_HMS,
70 UTZFMT_PAT_POSITIVE_HM,
71 UTZFMT_PAT_NEGATIVE_HM,
72 -1
73};
74
75static const UChar SINGLEQUOTE = 0x0027;
76static const UChar PLUS = 0x002B;
77static const UChar MINUS = 0x002D;
78static const UChar ISO8601_UTC = 0x005A; // 'Z'
79static const UChar ISO8601_SEP = 0x003A; // ':'
80
81static const int32_t MILLIS_PER_HOUR = 60 * 60 * 1000;
82static const int32_t MILLIS_PER_MINUTE = 60 * 1000;
83static const int32_t MILLIS_PER_SECOND = 1000;
84
85// Maximum offset (exclusive) in millisecond supported by offset formats
86static int32_t MAX_OFFSET = 24 * MILLIS_PER_HOUR;
87
88// Maximum values for GMT offset fields
89static const int32_t MAX_OFFSET_HOUR = 23;
90static const int32_t MAX_OFFSET_MINUTE = 59;
91static const int32_t MAX_OFFSET_SECOND = 59;
92
93static const int32_t UNKNOWN_OFFSET = 0x7FFFFFFF;
94
95static const int32_t ALL_SPECIFIC_NAME_TYPES = UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT | UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT;
96static const int32_t ALL_GENERIC_NAME_TYPES = UTZGNM_LOCATION | UTZGNM_LONG | UTZGNM_SHORT;
97
98#define STYLE_FLAG(c) (1 << (c))
99#define DIGIT_VAL(c) (0x0030 <= (c) && (c) <= 0x0039 ? (c) - 0x0030 : -1)
100#define MAX_OFFSET_DIGITS 6
101
102
103// ------------------------------------------------------------------
104// GMTOffsetField
105//
106// This class represents a localized GMT offset pattern
107// item and used by TimeZoneFormat
108// ------------------------------------------------------------------
109class GMTOffsetField : public UMemory {
110public:
111 enum FieldType {
112 TEXT = 0,
113 HOUR = 1,
114 MINUTE = 2,
115 SECOND = 4
116 };
117
118 virtual ~GMTOffsetField();
119
120 static GMTOffsetField* createText(const UnicodeString& text, UErrorCode& status);
121 static GMTOffsetField* createTimeField(FieldType type, uint8_t width, UErrorCode& status);
122 static UBool isValid(FieldType type, int32_t width);
123 static FieldType getTypeByLetter(UChar ch);
124
125 FieldType getType() const;
126 uint8_t getWidth() const;
127 const UChar* getPatternText(void) const;
128
129private:
130 UChar* fText;
131 FieldType fType;
132 uint8_t fWidth;
133
134 GMTOffsetField();
135};
136
137GMTOffsetField::GMTOffsetField()
138: fText(NULL), fType(TEXT), fWidth(0) {
139}
140
141GMTOffsetField::~GMTOffsetField() {
142 if (fText) {
143 uprv_free(fText);
144 }
145}
146
147GMTOffsetField*
148GMTOffsetField::createText(const UnicodeString& text, UErrorCode& status) {
149 if (U_FAILURE(status)) {
150 return NULL;
151 }
152 GMTOffsetField* result = new GMTOffsetField();
153 if (result == NULL) {
154 status = U_MEMORY_ALLOCATION_ERROR;
155 return NULL;
156 }
157
158 int32_t len = text.length();
159 result->fText = (UChar*)uprv_malloc((len + 1) * sizeof(UChar));
160 if (result->fText == NULL) {
161 status = U_MEMORY_ALLOCATION_ERROR;
162 delete result;
163 return NULL;
164 }
165 u_strncpy(result->fText, text.getBuffer(), len);
166 result->fText[len] = 0;
167 result->fType = TEXT;
168
169 return result;
170}
171
172GMTOffsetField*
173GMTOffsetField::createTimeField(FieldType type, uint8_t width, UErrorCode& status) {
174 U_ASSERT(type != TEXT);
175 if (U_FAILURE(status)) {
176 return NULL;
177 }
178 GMTOffsetField* result = new GMTOffsetField();
179 if (result == NULL) {
180 status = U_MEMORY_ALLOCATION_ERROR;
181 return NULL;
182 }
183
184 result->fType = type;
185 result->fWidth = width;
186
187 return result;
188}
189
190UBool
191GMTOffsetField::isValid(FieldType type, int32_t width) {
192 switch (type) {
193 case HOUR:
194 return (width == 1 || width == 2);
195 case MINUTE:
196 case SECOND:
197 return (width == 2);
198 default:
199 U_ASSERT(FALSE);
200 }
201 return (width > 0);
202}
203
204GMTOffsetField::FieldType
205GMTOffsetField::getTypeByLetter(UChar ch) {
206 if (ch == 0x0048 /* H */) {
207 return HOUR;
208 } else if (ch == 0x006D /* m */) {
209 return MINUTE;
210 } else if (ch == 0x0073 /* s */) {
211 return SECOND;
212 }
213 return TEXT;
214}
215
216inline GMTOffsetField::FieldType
217GMTOffsetField::getType() const {
218 return fType;
219 }
220
221inline uint8_t
222GMTOffsetField::getWidth() const {
223 return fWidth;
224}
225
226inline const UChar*
227GMTOffsetField::getPatternText(void) const {
228 return fText;
229}
230
231
232U_CDECL_BEGIN
233static void U_CALLCONV
234deleteGMTOffsetField(void *obj) {
235 delete static_cast<GMTOffsetField *>(obj);
236}
237U_CDECL_END
238
239
240// ------------------------------------------------------------------
241// TimeZoneFormat
242// ------------------------------------------------------------------
243UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneFormat)
244
245TimeZoneFormat::TimeZoneFormat(const Locale& locale, UErrorCode& status)
246: fLock(NULL),fLocale(locale), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL), fDefParseOptionFlags(0) {
247
248 for (int32_t i = 0; i <= UTZFMT_PAT_NEGATIVE_HMS; i++) {
249 fGMTOffsetPatternItems[i] = NULL;
250 }
251
252 const char* region = fLocale.getCountry();
253 int32_t regionLen = uprv_strlen(region);
254 if (regionLen == 0) {
255 char loc[ULOC_FULLNAME_CAPACITY];
256 uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status);
257
258 regionLen = uloc_getCountry(loc, fTargetRegion, sizeof(fTargetRegion), &status);
259 if (U_SUCCESS(status)) {
260 fTargetRegion[regionLen] = 0;
261 } else {
262 return;
263 }
264 } else if (regionLen < (int32_t)sizeof(fTargetRegion)) {
265 uprv_strcpy(fTargetRegion, region);
266 } else {
267 fTargetRegion[0] = 0;
268 }
269
270 fTimeZoneNames = TimeZoneNames::createInstance(locale, status);
271 // fTimeZoneGenericNames is lazily instantiated
272
273 const UChar* gmtPattern = NULL;
274 const UChar* hourFormats = NULL;
275
276 UResourceBundle *zoneBundle = ures_open(U_ICUDATA_ZONE, locale.getName(), &status);
277 UResourceBundle *zoneStringsArray = ures_getByKeyWithFallback(zoneBundle, gZoneStringsTag, NULL, &status);
278 if (U_SUCCESS(status)) {
279 const UChar* resStr;
280 int32_t len;
281 resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtFormatTag, &len, &status);
282 if (len > 0) {
283 gmtPattern = resStr;
284 }
285 resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtZeroFormatTag, &len, &status);
286 if (len > 0) {
287 fGMTZeroFormat.setTo(TRUE, resStr, len);
288 }
289 resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gHourFormatTag, &len, &status);
290 if (len > 0) {
291 hourFormats = resStr;
292 }
293 ures_close(zoneStringsArray);
294 ures_close(zoneBundle);
295 }
296
297 if (gmtPattern == NULL) {
298 gmtPattern = DEFAULT_GMT_PATTERN;
299 }
300 initGMTPattern(UnicodeString(gmtPattern, -1), status);
301
302 UBool useDefHourFmt = TRUE;
303 if (hourFormats) {
304 UChar *sep = u_strchr(hourFormats, (UChar)0x003B /* ';' */);
305 if (sep != NULL) {
306 fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM].setTo(FALSE, hourFormats, (int32_t)(sep - hourFormats));
307 fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM].setTo(TRUE, sep + 1, -1);
308 expandOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HMS]);
309 expandOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HMS]);
310 useDefHourFmt = FALSE;
311 }
312 }
313 if (useDefHourFmt) {
314 fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM].setTo(TRUE, DEFAULT_GMT_POSITIVE_HM, -1);
315 fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HMS].setTo(TRUE, DEFAULT_GMT_POSITIVE_HMS, -1);
316 fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM].setTo(TRUE, DEFAULT_GMT_NEGATIVE_HM, -1);
317 fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HMS].setTo(TRUE, DEFAULT_GMT_NEGATIVE_HMS, -1);
318 }
319 initGMTOffsetPatterns(status);
320
321 NumberingSystem* ns = NumberingSystem::createInstance(locale, status);
322 UBool useDefDigits = TRUE;
323 if (ns && !ns->isAlgorithmic()) {
324 UnicodeString digits = ns->getDescription();
325 useDefDigits = !toCodePoints(digits, fGMTOffsetDigits, 10);
326 }
327 if (useDefDigits) {
328 uprv_memcpy(fGMTOffsetDigits, DEFAULT_GMT_DIGITS, sizeof(UChar32) * 10);
329 }
330 delete ns;
331}
332
333TimeZoneFormat::TimeZoneFormat(const TimeZoneFormat& other)
334: Format(other), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL) {
335
336 for (int32_t i = 0; i <= UTZFMT_PAT_NEGATIVE_HMS; i++) {
337 fGMTOffsetPatternItems[i] = NULL;
338 }
339 *this = other;
340}
341
342
343TimeZoneFormat::~TimeZoneFormat() {
344 delete fTimeZoneNames;
345 delete fTimeZoneGenericNames;
346 for (int32_t i = 0; i <= UTZFMT_PAT_NEGATIVE_HMS; i++) {
347 delete fGMTOffsetPatternItems[i];
348 }
349 umtx_destroy(&fLock);
350}
351
352TimeZoneFormat&
353TimeZoneFormat::operator=(const TimeZoneFormat& other) {
354 if (this == &other) {
355 return *this;
356 }
357
358 delete fTimeZoneNames;
359 delete fTimeZoneGenericNames;
360 fTimeZoneGenericNames = NULL;
361
362 fLocale = other.fLocale;
363 uprv_memcpy(fTargetRegion, other.fTargetRegion, sizeof(fTargetRegion));
364
365 fTimeZoneNames = other.fTimeZoneNames->clone();
366 if (other.fTimeZoneGenericNames) {
367 fTimeZoneGenericNames = other.fTimeZoneGenericNames->clone();
368 }
369
370 fGMTPattern = other.fGMTPattern;
371 fGMTPatternPrefix = other.fGMTPatternPrefix;
372 fGMTPatternSuffix = other.fGMTPatternSuffix;
373
374 UErrorCode status = U_ZERO_ERROR;
375 for (int32_t i = 0; i <= UTZFMT_PAT_NEGATIVE_HMS; i++) {
376 fGMTOffsetPatterns[i] = other.fGMTOffsetPatterns[i];
377 delete fGMTOffsetPatternItems[i];
378 }
379 initGMTOffsetPatterns(status);
380 U_ASSERT(U_SUCCESS(status));
381
382 fGMTZeroFormat = other.fGMTZeroFormat;
383
384 uprv_memcpy(fGMTOffsetDigits, other.fGMTOffsetDigits, sizeof(fGMTOffsetDigits));
385
386 fDefParseOptionFlags = other.fDefParseOptionFlags;
387
388 return *this;
389}
390
391
392UBool
393TimeZoneFormat::operator==(const Format& other) const {
394 TimeZoneFormat* tzfmt = (TimeZoneFormat*)&other;
395
396 UBool isEqual =
397 fLocale == tzfmt->fLocale
398 && fGMTPattern == tzfmt->fGMTPattern
399 && fGMTZeroFormat == tzfmt->fGMTZeroFormat
400 && *fTimeZoneNames == *tzfmt->fTimeZoneNames;
401
402 for (int32_t i = 0; i <= UTZFMT_PAT_NEGATIVE_HMS && isEqual; i++) {
403 isEqual = fGMTOffsetPatterns[i] == tzfmt->fGMTOffsetPatterns[i];
404 }
405 for (int32_t i = 0; i < 10 && isEqual; i++) {
406 isEqual = fGMTOffsetDigits[i] == tzfmt->fGMTOffsetDigits[i];
407 }
408 // TODO
409 // Check fTimeZoneGenericNames. For now,
410 // if fTimeZoneNames is same, fTimeZoneGenericNames should
411 // be also equivalent.
412 return isEqual;
413}
414
415Format*
416TimeZoneFormat::clone() const {
417 return new TimeZoneFormat(*this);
418}
419
420TimeZoneFormat* U_EXPORT2
421TimeZoneFormat::createInstance(const Locale& locale, UErrorCode& status) {
422 TimeZoneFormat* tzfmt = new TimeZoneFormat(locale, status);
423 if (U_SUCCESS(status)) {
424 return tzfmt;
425 }
426 delete tzfmt;
427 return NULL;
428}
429
430// ------------------------------------------------------------------
431// Setter and Getter
432
433const TimeZoneNames*
434TimeZoneFormat::getTimeZoneNames() const {
435 return (const TimeZoneNames*)fTimeZoneNames;
436}
437
438void
439TimeZoneFormat::adoptTimeZoneNames(TimeZoneNames *tznames) {
440 delete fTimeZoneNames;
441 fTimeZoneNames = tznames;
442
443 // TODO - We should also update fTimeZoneGenericNames
444}
445
446void
447TimeZoneFormat::setTimeZoneNames(const TimeZoneNames &tznames) {
448 delete fTimeZoneNames;
449 fTimeZoneNames = tznames.clone();
450
451 // TODO - We should also update fTimeZoneGenericNames
452}
453
454void
455TimeZoneFormat::setDefaultParseOptions(int32_t flags) {
456 fDefParseOptionFlags = flags;
457}
458
459int32_t
460TimeZoneFormat::getDefaultParseOptions(void) const {
461 return fDefParseOptionFlags;
462}
463
464
465UnicodeString&
466TimeZoneFormat::getGMTPattern(UnicodeString& pattern) const {
467 return pattern.setTo(fGMTPattern);
468}
469
470void
471TimeZoneFormat::setGMTPattern(const UnicodeString& pattern, UErrorCode& status) {
472 initGMTPattern(pattern, status);
473}
474
475UnicodeString&
476TimeZoneFormat::getGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, UnicodeString& pattern) const {
477 return pattern.setTo(fGMTOffsetPatterns[type]);
478}
479
480void
481TimeZoneFormat::setGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, const UnicodeString& pattern, UErrorCode& status) {
482 if (U_FAILURE(status)) {
483 return;
484 }
485 if (pattern == fGMTOffsetPatterns[type]) {
486 // No need to reset
487 return;
488 }
489
490 OffsetFields required = (type == UTZFMT_PAT_POSITIVE_HMS || type == UTZFMT_PAT_NEGATIVE_HMS) ? FIELDS_HMS : FIELDS_HM;
491
492 UVector* patternItems = parseOffsetPattern(pattern, required, status);
493 if (patternItems == NULL) {
494 return;
495 }
496
497 fGMTOffsetPatterns[type].setTo(pattern);
498 delete fGMTOffsetPatternItems[type];
499 fGMTOffsetPatternItems[type] = patternItems;
500}
501
502UnicodeString&
503TimeZoneFormat::getGMTOffsetDigits(UnicodeString& digits) const {
504 digits.remove();
505 for (int32_t i = 0; i < 10; i++) {
506 digits.append(fGMTOffsetDigits[i]);
507 }
508 return digits;
509}
510
511void
512TimeZoneFormat::setGMTOffsetDigits(const UnicodeString& digits, UErrorCode& status) {
513 if (U_FAILURE(status)) {
514 return;
515 }
516 UChar32 digitArray[10];
517 if (!toCodePoints(digits, digitArray, 10)) {
518 status = U_ILLEGAL_ARGUMENT_ERROR;
519 return;
520 }
521 uprv_memcpy(fGMTOffsetDigits, digitArray, sizeof(UChar32)*10);
522}
523
524UnicodeString&
525TimeZoneFormat::getGMTZeroFormat(UnicodeString& gmtZeroFormat) const {
526 return gmtZeroFormat.setTo(fGMTZeroFormat);
527}
528
529void
530TimeZoneFormat::setGMTZeroFormat(const UnicodeString& gmtZeroFormat, UErrorCode& status) {
531 if (U_SUCCESS(status)) {
532 if (gmtZeroFormat.isEmpty()) {
533 status = U_ILLEGAL_ARGUMENT_ERROR;
534 } else if (gmtZeroFormat != fGMTZeroFormat) {
535 fGMTZeroFormat.setTo(gmtZeroFormat);
536 }
537 }
538}
539
540// ------------------------------------------------------------------
541// Format and Parse
542
543UnicodeString&
544TimeZoneFormat::format(UTimeZoneFormatStyle style, const TimeZone& tz, UDate date,
545 UnicodeString& name, UTimeZoneFormatTimeType* timeType /* = NULL */) const {
546 if (timeType) {
547 *timeType = UTZFMT_TIME_TYPE_UNKNOWN;
548 }
549 switch (style) {
550 case UTZFMT_STYLE_GENERIC_LOCATION:
551 formatGeneric(tz, UTZGNM_LOCATION, date, name);
552 break;
553 case UTZFMT_STYLE_GENERIC_LONG:
554 formatGeneric(tz, UTZGNM_LONG, date, name);
555 break;
556 case UTZFMT_STYLE_GENERIC_SHORT:
557 formatGeneric(tz, UTZGNM_SHORT, date, name);
558 break;
559 case UTZFMT_STYLE_SPECIFIC_LONG:
560 formatSpecific(tz, UTZNM_LONG_STANDARD, UTZNM_LONG_DAYLIGHT, date, name, timeType);
561 break;
562 case UTZFMT_STYLE_SPECIFIC_SHORT:
563 formatSpecific(tz, UTZNM_SHORT_STANDARD, UTZNM_SHORT_DAYLIGHT, date, name, timeType);
564 break;
565 case UTZFMT_STYLE_RFC822:
566 case UTZFMT_STYLE_ISO8601:
567 case UTZFMT_STYLE_LOCALIZED_GMT:
568 // will be handled below
569 break;
570 }
571
572 if (name.isEmpty()) {
573 UErrorCode status = U_ZERO_ERROR;
574 int32_t rawOffset, dstOffset;
575 tz.getOffset(date, FALSE, rawOffset, dstOffset, status);
576 if (U_SUCCESS(status)) {
577 switch (style) {
578 case UTZFMT_STYLE_RFC822:
579 formatOffsetRFC822(rawOffset + dstOffset, name, status);
580 break;
581 case UTZFMT_STYLE_ISO8601:
582 formatOffsetISO8601(rawOffset + dstOffset, name, status);
583 break;
584 default:
585 formatOffsetLocalizedGMT(rawOffset + dstOffset, name, status);
586 break;
587 }
588 if (timeType) {
589 *timeType = (dstOffset != 0) ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD;
590 }
591 }
592 U_ASSERT(U_SUCCESS(status));
593 }
594
595 return name;
596}
597
598UnicodeString&
599TimeZoneFormat::format(const Formattable& obj, UnicodeString& appendTo,
600 FieldPosition& pos, UErrorCode& status) const {
601 if (U_FAILURE(status)) {
602 return appendTo;
603 }
604 UDate date = Calendar::getNow();
605 if (obj.getType() == Formattable::kObject) {
606 const UObject* formatObj = obj.getObject();
607 const TimeZone* tz = dynamic_cast<const TimeZone*>(formatObj);
608 if (tz == NULL) {
609 const Calendar* cal = dynamic_cast<const Calendar*>(formatObj);
610 if (cal != NULL) {
611 tz = &cal->getTimeZone();
612 date = cal->getTime(status);
613 }
614 }
615 if (tz != NULL) {
616 int32_t rawOffset, dstOffset;
617 tz->getOffset(date, FALSE, rawOffset, dstOffset, status);
618 UnicodeString result;
619 formatOffsetLocalizedGMT(rawOffset + dstOffset, result, status);
620 if (U_SUCCESS(status)) {
621 appendTo.append(result);
622 if (pos.getField() == UDAT_TIMEZONE_FIELD) {
623 pos.setBeginIndex(0);
624 pos.setEndIndex(result.length());
625 }
626 }
627 }
628 }
629 return appendTo;
630}
631
632TimeZone*
633TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos,
634 UTimeZoneFormatTimeType* timeType /*= NULL*/) const {
635 return parse(style, text, pos, getDefaultParseOptions(), timeType);
636}
637
638TimeZone*
639TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos,
640 int32_t parseOptions, UTimeZoneFormatTimeType* timeType /* = NULL */) const {
641 if (timeType) {
642 *timeType = UTZFMT_TIME_TYPE_UNKNOWN;
643 }
644
645 int32_t startIdx = pos.getIndex();
646 int32_t maxPos = text.length();
647 int32_t offset;
648
649 UBool fallbackLocalizedGMT = FALSE;
650 if (style == UTZFMT_STYLE_SPECIFIC_LONG || style == UTZFMT_STYLE_SPECIFIC_SHORT
651 || style == UTZFMT_STYLE_GENERIC_LONG || style == UTZFMT_STYLE_GENERIC_SHORT || style == UTZFMT_STYLE_GENERIC_LOCATION) {
652 // above styles may use localized gmt format as fallback
653 fallbackLocalizedGMT = TRUE;
654 }
655
656 int32_t evaluated = 0;
657 ParsePosition tmpPos(startIdx);
658
659 int32_t parsedOffset = UNKNOWN_OFFSET; // stores successfully parsed offset for later use
660 int32_t parsedPos = -1; // stores successfully parsed offset position for later use
661
662 // Try localized GMT format first if necessary
663 if (fallbackLocalizedGMT) {
664 UBool hasDigitOffset = FALSE;
665 offset = parseOffsetLocalizedGMT(text, tmpPos, &hasDigitOffset);
666 if (tmpPos.getErrorIndex() == -1) {
667 // Even when the input text was successfully parsed as a localized GMT format text,
668 // we may still need to evaluate the specified style if -
669 // 1) GMT zero format was used, and
670 // 2) The input text was not completely processed
671 if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
672 pos.setIndex(tmpPos.getIndex());
673 return createTimeZoneForOffset(offset);
674 }
675 parsedOffset = offset;
676 parsedPos = tmpPos.getIndex();
677 }
678 evaluated |= STYLE_FLAG(UTZFMT_STYLE_LOCALIZED_GMT);
679
680 tmpPos.setIndex(startIdx);
681 tmpPos.setErrorIndex(-1);
682 }
683
684 UErrorCode status = U_ZERO_ERROR;
685 UnicodeString tzID;
686 UTimeZoneFormatTimeType parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
687
688 // Try the specified style
689 switch (style) {
690 case UTZFMT_STYLE_RFC822:
691 {
692 offset = parseOffsetRFC822(text, tmpPos);
693 if (tmpPos.getErrorIndex() == -1) {
694 pos.setIndex(tmpPos.getIndex());
695 return createTimeZoneForOffset(offset);
696 }
697 }
698 break;
699
700 case UTZFMT_STYLE_LOCALIZED_GMT:
701 {
702 offset = parseOffsetLocalizedGMT(text, tmpPos);
703 if (tmpPos.getErrorIndex() == -1) {
704 pos.setIndex(tmpPos.getIndex());
705 return createTimeZoneForOffset(offset);
706 }
707 }
708 break;
709
710 case UTZFMT_STYLE_ISO8601:
711 {
712 offset = parseOffsetISO8601(text, tmpPos);
713 if (tmpPos.getErrorIndex() == -1) {
714 pos.setIndex(tmpPos.getIndex());
715 return createTimeZoneForOffset(offset);
716 }
717 // Note: ISO 8601 parser also support basic format (without ':'),
718 // which is same with RFC 822 format.
719 evaluated |= STYLE_FLAG(UTZFMT_STYLE_RFC822);
720 }
721 break;
722
723 case UTZFMT_STYLE_SPECIFIC_LONG:
724 case UTZFMT_STYLE_SPECIFIC_SHORT:
725 {
726 // Specific styles
727 int32_t nameTypes = 0;
728 if (style == UTZFMT_STYLE_SPECIFIC_LONG) {
729 nameTypes = (UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT);
730 } else {
731 U_ASSERT(style == UTZFMT_STYLE_SPECIFIC_SHORT);
732 nameTypes = (UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT);
733 }
734 LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, nameTypes, status));
735 if (U_FAILURE(status)) {
736 pos.setErrorIndex(startIdx);
737 return NULL;
738 }
739 if (!specificMatches.isNull()) {
740 int32_t matchIdx = -1;
741 int32_t matchPos = -1;
742 for (int32_t i = 0; i < specificMatches->size(); i++) {
743 matchPos = startIdx + specificMatches->getMatchLengthAt(i);
744 if (matchPos > parsedPos) {
745 matchIdx = i;
746 parsedPos = matchPos;
747 }
748 }
749 if (matchIdx >= 0) {
750 if (timeType) {
751 *timeType = getTimeType(specificMatches->getNameTypeAt(matchIdx));
752 }
753 pos.setIndex(matchPos);
754 getTimeZoneID(specificMatches.getAlias(), matchIdx, tzID);
755 U_ASSERT(!tzID.isEmpty());
756 return TimeZone::createTimeZone(tzID);
757 }
758 }
759 }
760 break;
761
762 case UTZFMT_STYLE_GENERIC_LONG:
763 case UTZFMT_STYLE_GENERIC_SHORT:
764 case UTZFMT_STYLE_GENERIC_LOCATION:
765 {
766 int32_t genericNameTypes = 0;
767 switch (style) {
768 case UTZFMT_STYLE_GENERIC_LOCATION:
769 genericNameTypes = UTZGNM_LOCATION;
770 break;
771
772 case UTZFMT_STYLE_GENERIC_LONG:
773 genericNameTypes = UTZGNM_LONG | UTZGNM_LOCATION;
774 break;
775
776 case UTZFMT_STYLE_GENERIC_SHORT:
777 genericNameTypes = UTZGNM_SHORT | UTZGNM_LOCATION;
778 break;
779
780 default:
781 U_ASSERT(FALSE);
782 }
783
784 int32_t len = 0;
785 const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status);
786 if (U_SUCCESS(status)) {
787 len = gnames->findBestMatch(text, startIdx, genericNameTypes, tzID, parsedTimeType, status);
788 }
789 if (U_FAILURE(status)) {
790 pos.setErrorIndex(startIdx);
791 return NULL;
792 }
793 if (len > 0) {
794 // Found a match
795 if (timeType) {
796 *timeType = parsedTimeType;
797 }
798 pos.setIndex(startIdx + len);
799 U_ASSERT(!tzID.isEmpty());
800 return TimeZone::createTimeZone(tzID);
801 }
802 }
803 break;
804 }
805 evaluated |= STYLE_FLAG(style);
806
807
808 if (parsedPos > startIdx) {
809 // When the specified style is one of SPECIFIC_XXX or GENERIC_XXX, we tried to parse the input
810 // as localized GMT format earlier. If parsedOffset is positive, it means it was successfully
811 // parsed as localized GMT format, but offset digits were not detected (more specifically, GMT
812 // zero format). Then, it tried to find a match within the set of display names, but could not
813 // find a match. At this point, we can safely assume the input text contains the localized
814 // GMT format.
815 U_ASSERT(parsedOffset != UNKNOWN_OFFSET);
816 pos.setIndex(parsedPos);
817 return createTimeZoneForOffset(parsedOffset);
818 }
819
820 // Failed to parse the input text as the time zone format in the specified style.
821 // Check the longest match among other styles below.
822 U_ASSERT(parsedPos < 0);
823 U_ASSERT(parsedOffset == UNKNOWN_OFFSET);
824 tmpPos.setIndex(startIdx);
825 tmpPos.setErrorIndex(-1);
826
827 // ISO 8601
828 if ((evaluated & STYLE_FLAG(UTZFMT_STYLE_ISO8601)) == 0) {
829 UBool hasDigitOffset = FALSE;
830 offset = parseOffsetISO8601(text, tmpPos, FALSE, &hasDigitOffset);
831 if (tmpPos.getErrorIndex() == -1) {
832 if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
833 pos.setIndex(tmpPos.getIndex());
834 return createTimeZoneForOffset(offset);
835 }
836 // Note: When ISO 8601 format contains offset digits, it should not
837 // collide with other formats (except RFC 822, which is compatible with
838 // ISO 8601 basic format). However, ISO 8601 UTC format "Z" (single letter)
839 // may collide with other names. In this case, we need to evaluate other
840 // names.
841 parsedOffset = offset;
842 parsedPos = tmpPos.getIndex();
843 U_ASSERT(parsedPos == startIdx + 1); // only when "Z" is used
844 }
845 tmpPos.setIndex(startIdx);
846 tmpPos.setErrorIndex(-1);
847 }
848
849 // RFC 822
850 // Note: ISO 8601 parser supports RFC 822 format. So we do not need to parse
851 // it as RFC 822 here. This might be changed in future when we support
852 // strict format option for ISO 8601 or RFC 822.
853
854 //if ((evaluated & STYLE_FLAG(UTZFMT_STYLE_RFC822)) == 0) {
855 // offset = parseOffsetRFC822(text, tmpPos);
856 // if (tmpPos.getErrorIndex() == -1) {
857 // pos.setIndex(tmpPos.getIndex());
858 // return createTimeZoneForOffset(offset);
859 // }
860 // tmpPos.setIndex(startIdx);
861 // tmpPos.setErrorIndex(-1);
862 //}
863
864 // Localized GMT format
865 if ((evaluated & STYLE_FLAG(UTZFMT_STYLE_LOCALIZED_GMT)) == 0) {
866 UBool hasDigitOffset = FALSE;
867 offset = parseOffsetLocalizedGMT(text, tmpPos, &hasDigitOffset);
868 if (tmpPos.getErrorIndex() == -1) {
869 if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
870 pos.setIndex(tmpPos.getIndex());
871 return createTimeZoneForOffset(offset);
872 }
873 // Evaluate other names - see the comment earlier in this method.
874 parsedOffset = offset;
875 parsedPos = tmpPos.getIndex();
876 }
877 }
878
879 // When ParseOption.ALL_STYLES is available, we also try to look all possible display names.
880 // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never
881 // used for America/New_York. With parseAllStyles true, this code parses "EST"
882 // as America/New_York.
883
884 // Note: Adding all possible names into the trie used by the implementation is quite heavy operation,
885 // which we want to avoid normally (note that we cache the trie, so this is applicable to the
886 // first time only as long as the cache does not expire).
887 if (parseOptions & UTZFMT_PARSE_OPTION_ALL_STYLES) {
888 // Try all specific names first
889 LocalPointer<TimeZoneNames::MatchInfoCollection> spAllMatches(fTimeZoneNames->find(text, startIdx, ALL_SPECIFIC_NAME_TYPES, status));
890 if (U_FAILURE(status)) {
891 pos.setErrorIndex(startIdx);
892 return NULL;
893 }
894 int32_t spMatchIdx = -1;
895 if (!spAllMatches.isNull()) {
896 int32_t matchPos = -1;
897 for (int32_t i = 0; i < spAllMatches->size(); i++) {
898 matchPos = startIdx + spAllMatches->getMatchLengthAt(i);
899 if (matchPos > parsedPos) {
900 spMatchIdx = i;
901 parsedPos = matchPos;
902 }
903 }
904 }
905 int32_t genMatchLen = -1;
906 if (parsedPos < maxPos) {
907 const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status);
908 if (U_SUCCESS(status)) {
909 genMatchLen = gnames->findBestMatch(text, startIdx, ALL_GENERIC_NAME_TYPES, tzID, parsedTimeType, status);
910 }
911 if (U_FAILURE(status)) {
912 pos.setErrorIndex(startIdx);
913 return NULL;
914 }
915 }
916 // Pick up better match
917 if (startIdx + genMatchLen > parsedPos) {
918 // use generic name match
919 parsedPos = startIdx + genMatchLen;
920 if (timeType) {
921 *timeType = parsedTimeType;
922 }
923 pos.setIndex(parsedPos);
924 U_ASSERT(!tzID.isEmpty());
925 return TimeZone::createTimeZone(tzID);
926 } else if (spMatchIdx >= 0) {
927 // use specific name match
928 if (timeType) {
929 *timeType = getTimeType(spAllMatches->getNameTypeAt(spMatchIdx));
930 }
931 pos.setIndex(parsedPos);
932 getTimeZoneID(spAllMatches.getAlias(), spMatchIdx, tzID);
933 U_ASSERT(!tzID.isEmpty());
934 return TimeZone::createTimeZone(tzID);
935 }
936 }
937
938 if (parsedPos > startIdx) {
939 // Parsed successfully as one of 'offset' format
940 U_ASSERT(parsedOffset != UNKNOWN_OFFSET);
941 pos.setIndex(parsedPos);
942 return createTimeZoneForOffset(parsedOffset);
943 }
944
945 pos.setErrorIndex(startIdx);
946 return NULL;
947}
948
949void
950TimeZoneFormat::parseObject(const UnicodeString& source, Formattable& result,
951 ParsePosition& parse_pos) const {
952 result.adoptObject(parse(UTZFMT_STYLE_GENERIC_LOCATION, source, parse_pos, UTZFMT_PARSE_OPTION_ALL_STYLES));
953}
954
955
956// ------------------------------------------------------------------
957// Private zone name format/parse implementation
958
959UnicodeString&
960TimeZoneFormat::formatGeneric(const TimeZone& tz, int32_t genType, UDate date, UnicodeString& name) const {
961 UErrorCode status = U_ZERO_ERROR;
962 const TimeZoneGenericNames* gnames = getTimeZoneGenericNames(status);
963 if (U_FAILURE(status)) {
964 name.setToBogus();
965 return name;
966 }
967
968 if (genType == UTZGNM_LOCATION) {
969 const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
970 if (canonicalID == NULL) {
971 name.setToBogus();
972 return name;
973 }
974 return gnames->getGenericLocationName(UnicodeString(canonicalID), name);
975 }
976 return gnames->getDisplayName(tz, (UTimeZoneGenericNameType)genType, date, name);
977}
978
979UnicodeString&
980TimeZoneFormat::formatSpecific(const TimeZone& tz, UTimeZoneNameType stdType, UTimeZoneNameType dstType,
981 UDate date, UnicodeString& name, UTimeZoneFormatTimeType *timeType) const {
982 if (fTimeZoneNames == NULL) {
983 name.setToBogus();
984 return name;
985 }
986
987 UErrorCode status = U_ZERO_ERROR;
988 UBool isDaylight = tz.inDaylightTime(date, status);
989 const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
990
991 if (U_FAILURE(status) || canonicalID == NULL) {
992 name.setToBogus();
993 return name;
994 }
995
996 if (isDaylight) {
997 fTimeZoneNames->getDisplayName(UnicodeString(canonicalID), dstType, date, name);
998 } else {
999 fTimeZoneNames->getDisplayName(UnicodeString(canonicalID), stdType, date, name);
1000 }
1001
1002 if (timeType && !name.isEmpty()) {
1003 *timeType = isDaylight ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD;
1004 }
1005 return name;
1006}
1007
1008const TimeZoneGenericNames*
1009TimeZoneFormat::getTimeZoneGenericNames(UErrorCode& status) const {
1010 if (U_FAILURE(status)) {
1011 return NULL;
1012 }
1013
1014 UBool create;
1015 UMTX_CHECK(&gZoneMetaLock, (fTimeZoneGenericNames == NULL), create);
1016 if (create) {
1017 TimeZoneFormat *nonConstThis = const_cast<TimeZoneFormat *>(this);
1018 umtx_lock(&nonConstThis->fLock);
1019 {
1020 if (fTimeZoneGenericNames == NULL) {
1021 nonConstThis->fTimeZoneGenericNames = TimeZoneGenericNames::createInstance(fLocale, status);
1022 }
1023 }
1024 umtx_unlock(&nonConstThis->fLock);
1025 }
1026
1027 return fTimeZoneGenericNames;
1028}
1029
1030// ------------------------------------------------------------------
1031// Zone offset format and parse
1032
1033UnicodeString&
1034TimeZoneFormat::formatOffsetRFC822(int32_t offset, UnicodeString& result, UErrorCode& status) const {
1035 if (U_FAILURE(status)) {
1036 result.setToBogus();
1037 return result;
1038 }
1039 if (offset <= -MAX_OFFSET || offset >= MAX_OFFSET) {
1040 result.setToBogus();
1041 status = U_ILLEGAL_ARGUMENT_ERROR;
1042 return result;
1043 }
1044
1045 // Note: FIELDS_HMS as maxFields is an ICU extension. RFC822 specification
1046 // defines exactly 4 digits for the offset field in HHss format.
1047 return formatOffsetWithAsciiDigits(offset, 0, FIELDS_HM, FIELDS_HMS, result);
1048}
1049
1050UnicodeString&
1051TimeZoneFormat::formatOffsetISO8601(int32_t offset, UnicodeString& result, UErrorCode& status) const {
1052 if (U_FAILURE(status)) {
1053 result.setToBogus();
1054 return result;
1055 }
1056 if (offset <= -MAX_OFFSET || offset >= MAX_OFFSET) {
1057 result.setToBogus();
1058 status = U_ILLEGAL_ARGUMENT_ERROR;
1059 return result;
1060 }
1061
1062 if (offset == 0) {
1063 result.setTo(ISO8601_UTC);
1064 return result;
1065 }
1066 return formatOffsetWithAsciiDigits(offset, ISO8601_SEP, FIELDS_HM, FIELDS_HMS, result);
1067}
1068
1069UnicodeString&
1070TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset, UnicodeString& result, UErrorCode& status) const {
1071 if (U_FAILURE(status)) {
1072 result.setToBogus();
1073 return result;
1074 }
1075 if (offset <= -MAX_OFFSET || offset >= MAX_OFFSET) {
1076 result.setToBogus();
1077 status = U_ILLEGAL_ARGUMENT_ERROR;
1078 return result;
1079 }
1080
1081 if (offset == 0) {
1082 result.setTo(fGMTZeroFormat);
1083 return result;
1084 }
1085
1086 UBool positive = TRUE;
1087 if (offset < 0) {
1088 offset = -offset;
1089 positive = FALSE;
1090 }
1091
1092 int32_t offsetH = offset / MILLIS_PER_HOUR;
1093 offset = offset % MILLIS_PER_HOUR;
1094 int32_t offsetM = offset / MILLIS_PER_MINUTE;
1095 offset = offset % MILLIS_PER_MINUTE;
1096 int32_t offsetS = offset / MILLIS_PER_SECOND;
1097
1098 U_ASSERT(offsetH <= MAX_OFFSET_HOUR && offsetM <= MAX_OFFSET_MINUTE && offsetS <= MAX_OFFSET_SECOND);
1099
1100 const UVector* offsetPatternItems = NULL;
1101 if (positive) {
1102 offsetPatternItems = (offsetS == 0) ?
1103 fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HM] :
1104 fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HMS];
1105 } else {
1106 offsetPatternItems = (offsetS == 0) ?
1107 fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HM] :
1108 fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HMS];
1109 }
1110
1111 U_ASSERT(offsetPatternItems != NULL);
1112
1113 // Building the GMT format string
1114 result.setTo(fGMTPatternPrefix);
1115
1116 for (int32_t i = 0; i < offsetPatternItems->size(); i++) {
1117 const GMTOffsetField* item = (GMTOffsetField*)offsetPatternItems->elementAt(i);
1118 GMTOffsetField::FieldType type = item->getType();
1119
1120 switch (type) {
1121 case GMTOffsetField::TEXT:
1122 result.append(item->getPatternText(), -1);
1123 break;
1124
1125 case GMTOffsetField::HOUR:
1126 appendOffsetDigits(result, offsetH, item->getWidth());
1127 break;
1128
1129 case GMTOffsetField::MINUTE:
1130 appendOffsetDigits(result, offsetM, item->getWidth());
1131 break;
1132
1133 case GMTOffsetField::SECOND:
1134 appendOffsetDigits(result, offsetS, item->getWidth());
1135 break;
1136 }
1137 }
1138
1139 result.append(fGMTPatternSuffix);
1140 return result;
1141}
1142
1143int32_t
1144TimeZoneFormat::parseOffsetRFC822(const UnicodeString& text, ParsePosition& pos) const {
1145 int32_t start = pos.getIndex();
1146 if (start >= text.length()) {
1147 pos.setErrorIndex(start);
1148 return 0;
1149 }
1150
1151 int32_t sign = 1;
1152 UChar signChar = text.charAt(start);
1153 if (signChar == PLUS) {
1154 sign = 1;
1155 } else if (signChar == MINUS) {
1156 sign = -1;
1157 } else {
1158 // Not an RFC822 offset string
1159 pos.setErrorIndex(start);
1160 return 0;
1161 }
1162
1163 // Parse digits
1164 pos.setIndex(start + 1);
1165 int32_t offset = parseAbuttingAsciiOffsetFields(text, pos, FIELDS_H, FIELDS_HMS, false);
1166
1167 if (pos.getErrorIndex() != -1) {
1168 pos.setIndex(start); // reset
1169 pos.setErrorIndex(start);
1170 return 0;
1171 }
1172
1173 return sign * offset;
1174}
1175
1176int32_t
1177TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& pos) const {
1178 return parseOffsetISO8601(text, pos, FALSE);
1179}
1180
1181int32_t
1182TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos) const {
1183 return parseOffsetLocalizedGMT(text, pos, NULL);
1184}
1185
1186
1187
1188// ------------------------------------------------------------------
1189// Private zone offset format/parse implementation
1190
1191int32_t
1192TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& pos, UBool extendedOnly, UBool* hasDigitOffset /* = NULL */) const {
1193 if (hasDigitOffset) {
1194 *hasDigitOffset = FALSE;
1195 }
1196 int32_t start = pos.getIndex();
1197 if (start >= text.length()) {
1198 pos.setErrorIndex(start);
1199 return 0;
1200 }
1201
1202 UChar firstChar = text.charAt(start);
1203 if (firstChar == ISO8601_UTC || firstChar == (UChar)(ISO8601_UTC + 0x20)) {
1204 // "Z" (or "z") - indicates UTC
1205 pos.setIndex(start + 1);
1206 return 0;
1207 }
1208
1209 int32_t sign = 1;
1210 if (firstChar == PLUS) {
1211 sign = 1;
1212 } else if (firstChar == MINUS) {
1213 sign = -1;
1214 } else {
1215 // Not an ISO 8601 offset string
1216 pos.setErrorIndex(start);
1217 return 0;
1218 }
1219 ParsePosition posOffset(start + 1);
1220 int32_t offset = parseAsciiOffsetFields(text, posOffset, ISO8601_SEP, FIELDS_H, FIELDS_HMS, FALSE);
1221 if (posOffset.getErrorIndex() == -1 && !extendedOnly && (posOffset.getIndex() - start <= 3)) {
1222 // If the text is successfully parsed as extended format with the options above, it can be also parsed
1223 // as basic format. For example, "0230" can be parsed as offset 2:00 (only first digits are valid for
1224 // extended format), but it can be parsed as offset 2:30 with basic format. We use longer result.
1225 ParsePosition posBasic(start + 1);
1226 int32_t tmpOffset = parseAbuttingAsciiOffsetFields(text, posBasic, FIELDS_H, FIELDS_HMS, FALSE);
1227 if (posBasic.getErrorIndex() == -1 && posBasic.getIndex() > posOffset.getIndex()) {
1228 offset = tmpOffset;
1229 posOffset.setIndex(posBasic.getIndex());
1230 }
1231 }
1232
1233 if (posOffset.getErrorIndex() != -1) {
1234 pos.setErrorIndex(start);
1235 return 0;
1236 }
1237
1238 pos.setIndex(posOffset.getIndex());
1239 if (hasDigitOffset) {
1240 *hasDigitOffset = TRUE;
1241 }
1242 return sign * offset;
1243}
1244
1245int32_t
1246TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos, UBool* hasDigitOffset) const {
1247 int32_t start = pos.getIndex();
1248 int32_t idx = start;
1249 UBool parsed = FALSE;
1250 int32_t offset = 0;
1251
1252 if (hasDigitOffset) {
1253 *hasDigitOffset = FALSE;
1254 }
1255
1256 do {
1257 // Prefix part
1258 int32_t len = fGMTPatternPrefix.length();
1259 if (len > 0 && text.caseCompare(idx, len, fGMTPatternPrefix, 0) != 0) {
1260 // prefix match failed
1261 break;
1262 }
1263 idx += len;
1264
1265 // Offset part
1266 offset = parseOffsetFields(text, idx, FALSE, len);
1267 if (len == 0) {
1268 // offset field match failed
1269 break;
1270 }
1271 idx += len;
1272
1273 // Suffix part
1274 len = fGMTPatternSuffix.length();
1275 if (len > 0 && text.caseCompare(idx, len, fGMTPatternSuffix, 0) != 0) {
1276 // no suffix match
1277 break;
1278 }
1279 idx += len;
1280 parsed = TRUE;
1281
1282 } while (false);
1283
1284 if (parsed) {
1285 if (hasDigitOffset) {
1286 *hasDigitOffset = TRUE;
1287 }
1288 pos.setIndex(idx);
1289 return offset;
1290 }
1291
1292 // Try the default patterns
1293 int32_t parsedLength = 0;
1294 offset = parseOffsetDefaultLocalizedGMT(text, start, parsedLength);
1295 if (parsedLength > 0) {
1296 if (hasDigitOffset) {
1297 *hasDigitOffset = TRUE;
1298 }
1299 pos.setIndex(start + parsedLength);
1300 return offset;
1301 }
1302
1303 // Check if this is a GMT zero format
1304 if (text.caseCompare(start, fGMTZeroFormat.length(), fGMTZeroFormat, 0) == 0) {
1305 pos.setIndex(start + fGMTZeroFormat.length());
1306 return 0;
1307 }
1308
1309 // Check if this is a default GMT zero format
1310 for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) {
1311 const UChar* defGMTZero = ALT_GMT_STRINGS[i];
1312 int32_t defGMTZeroLen = u_strlen(defGMTZero);
1313 if (text.caseCompare(start, defGMTZeroLen, defGMTZero, 0) == 0) {
1314 pos.setIndex(start + defGMTZeroLen);
1315 return 0;
1316 }
1317 }
1318
1319 // Nothing matched
1320 pos.setErrorIndex(start);
1321 return 0;
1322}
1323
1324int32_t
1325TimeZoneFormat::parseOffsetFields(const UnicodeString& text, int32_t start, UBool minimumHourWidth, int32_t& parsedLen) const {
1326 int32_t offset = 0;
1327 UBool sawVarHourAndAbuttingField = FALSE;
1328
1329 parsedLen = 0;
1330
1331 for (int32_t patidx = 0; PARSE_GMT_OFFSET_TYPES[patidx] >= 0; patidx++) {
1332 int32_t gmtPatType = PARSE_GMT_OFFSET_TYPES[patidx];
1333 int32_t offsetH = 0, offsetM = 0, offsetS = 0;
1334 int32_t idx = start;
1335 UVector* items = fGMTOffsetPatternItems[gmtPatType];
1336 U_ASSERT(items != NULL);
1337
1338 UBool failed = FALSE;
1339 for (int32_t i = 0; i < items->size(); i++) {
1340 int32_t tmpParsedLen = 0;
1341 const GMTOffsetField* field = (const GMTOffsetField*)items->elementAt(i);
1342 GMTOffsetField::FieldType fieldType = field->getType();
1343 if (fieldType == GMTOffsetField::TEXT) {
1344 const UChar* patStr = field->getPatternText();
1345 tmpParsedLen = u_strlen(patStr);
1346 if (text.caseCompare(idx, tmpParsedLen, patStr, 0) != 0) {
1347 failed = TRUE;
1348 break;
1349 }
1350 idx += tmpParsedLen;
1351 } else {
1352 if (fieldType == GMTOffsetField::HOUR) {
1353 uint8_t minDigits = 1;
1354 uint8_t maxDigits = minimumHourWidth ? 1 : 2;
1355 if (!minimumHourWidth && !sawVarHourAndAbuttingField) {
1356 if (i + 1 < items->size()) {
1357 const GMTOffsetField* nextField = (const GMTOffsetField*)items->elementAt(i + 1);
1358 if (nextField->getType() != GMTOffsetField::TEXT) {
1359 sawVarHourAndAbuttingField = true;
1360 }
1361 }
1362 }
1363 offsetH = parseOffsetFieldWithLocalizedDigits(text, idx, minDigits, maxDigits, 0, MAX_OFFSET_HOUR, tmpParsedLen);
1364 } else if (fieldType == GMTOffsetField::MINUTE) {
1365 offsetM = parseOffsetFieldWithLocalizedDigits(text, idx, 2, 2, 0, MAX_OFFSET_MINUTE, tmpParsedLen);
1366 } else if (fieldType == GMTOffsetField::SECOND) {
1367 offsetS = parseOffsetFieldWithLocalizedDigits(text, idx, 2, 2, 0, MAX_OFFSET_SECOND, tmpParsedLen);
1368 }
1369
1370 if (tmpParsedLen == 0) {
1371 failed = TRUE;
1372 break;
1373 }
1374 idx += tmpParsedLen;
1375 }
1376 }
1377 if (!failed) {
1378 int32_t sign = (gmtPatType == UTZFMT_PAT_POSITIVE_HM || gmtPatType == UTZFMT_PAT_POSITIVE_HMS) ? 1 : -1;
1379 offset = ((((offsetH * 60) + offsetM) * 60) + offsetS) * 1000 * sign;
1380 parsedLen = idx - start;
1381 break;
1382 }
1383 }
1384
1385 if (parsedLen == 0 && sawVarHourAndAbuttingField && !minimumHourWidth) {
1386 // When hour field is variable width and another non-literal pattern
1387 // field follows, the parse loop above might eat up the digit from
1388 // the abutting field. For example, with pattern "-Hmm" and input "-100",
1389 // the hour is parsed as -10 and fails to parse minute field.
1390 //
1391 // If this is the case, try parsing the text one more time with the arg
1392 // minimumHourWidth = true
1393 //
1394 // Note: This fallback is not applicable when quitAtHourField is true, because
1395 // the option is designed for supporting the case like "GMT+5". In this case,
1396 // we should get better result for parsing hour digits as much as possible.
1397
1398 return parseOffsetFields(text, start, true, parsedLen);
1399 }
1400
1401 return offset;
1402}
1403
1404int32_t
1405TimeZoneFormat::parseAbuttingOffsetFields(const UnicodeString& text, int32_t start, int32_t& parsedLen) const {
1406 int32_t digits[MAX_OFFSET_DIGITS];
1407 int32_t parsed[MAX_OFFSET_DIGITS]; // accumulative offsets
1408
1409 // Parse digits into int[]
1410 int32_t idx = start;
1411 int32_t len = 0;
1412 int32_t numDigits = 0;
1413 for (int32_t i = 0; i < MAX_OFFSET_DIGITS; i++) {
1414 digits[i] = parseSingleLocalizedDigit(text, idx, len);
1415 if (digits[i] < 0) {
1416 break;
1417 }
1418 idx += len;
1419 parsed[i] = idx - start;
1420 numDigits++;
1421 }
1422
1423 if (numDigits == 0) {
1424 parsedLen = 0;
1425 return 0;
1426 }
1427
1428 int32_t offset = 0;
1429 while (numDigits > 0) {
1430 int32_t hour = 0;
1431 int32_t min = 0;
1432 int32_t sec = 0;
1433
1434 U_ASSERT(numDigits > 0 && numDigits <= MAX_OFFSET_DIGITS);
1435 switch (numDigits) {
1436 case 1: // H
1437 hour = digits[0];
1438 break;
1439 case 2: // HH
1440 hour = digits[0] * 10 + digits[1];
1441 break;
1442 case 3: // Hmm
1443 hour = digits[0];
1444 min = digits[1] * 10 + digits[2];
1445 break;
1446 case 4: // HHmm
1447 hour = digits[0] * 10 + digits[1];
1448 min = digits[2] * 10 + digits[3];
1449 break;
1450 case 5: // Hmmss
1451 hour = digits[0];
1452 min = digits[1] * 10 + digits[2];
1453 sec = digits[3] * 10 + digits[4];
1454 break;
1455 case 6: // HHmmss
1456 hour = digits[0] * 10 + digits[1];
1457 min = digits[2] * 10 + digits[3];
1458 sec = digits[4] * 10 + digits[5];
1459 break;
1460 }
1461 if (hour <= MAX_OFFSET_HOUR && min <= MAX_OFFSET_MINUTE && sec <= MAX_OFFSET_SECOND) {
1462 // found a valid combination
1463 offset = hour * MILLIS_PER_HOUR + min * MILLIS_PER_MINUTE + sec * MILLIS_PER_SECOND;
1464 parsedLen = parsed[numDigits - 1];
1465 break;
1466 }
1467 numDigits--;
1468 }
1469 return offset;
1470}
1471
1472int32_t
1473TimeZoneFormat::parseOffsetDefaultLocalizedGMT(const UnicodeString& text, int start, int32_t& parsedLen) const {
1474 int32_t idx = start;
1475 int32_t offset = 0;
1476 int32_t parsed = 0;
1477
1478 do {
1479 // check global default GMT alternatives
1480 int32_t gmtLen = 0;
1481
1482 for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) {
1483 const UChar* gmt = ALT_GMT_STRINGS[i];
1484 int32_t len = u_strlen(gmt);
1485 if (text.caseCompare(start, len, gmt, 0) == 0) {
1486 gmtLen = len;
1487 break;
1488 }
1489 }
1490 if (gmtLen == 0) {
1491 break;
1492 }
1493 idx += gmtLen;
1494
1495 // offset needs a sign char and a digit at minimum
1496 if (idx + 1 >= text.length()) {
1497 break;
1498 }
1499
1500 // parse sign
1501 int32_t sign = 1;
1502 UChar c = text.charAt(idx);
1503 if (c == PLUS) {
1504 sign = 1;
1505 } else if (c == MINUS) {
1506 sign = -1;
1507 } else {
1508 break;
1509 }
1510 idx++;
1511
1512 // offset part
1513 // try the default pattern with the separator first
1514 int32_t lenWithSep = 0;
1515 int32_t offsetWithSep = parseDefaultOffsetFields(text, idx, DEFAULT_GMT_OFFSET_SEP, lenWithSep);
1516 if (lenWithSep == text.length() - idx) {
1517 // maximum match
1518 offset = offsetWithSep * sign;
1519 idx += lenWithSep;
1520 } else {
1521 // try abutting field pattern
1522 int32_t lenAbut = 0;
1523 int32_t offsetAbut = parseAbuttingOffsetFields(text, idx, lenAbut);
1524
1525 if (lenWithSep > lenAbut) {
1526 offset = offsetWithSep * sign;
1527 idx += lenWithSep;
1528 } else {
1529 offset = offsetAbut * sign;
1530 idx += lenAbut;
1531 }
1532 }
1533 parsed = idx - start;
1534 } while (false);
1535
1536 parsedLen = parsed;
1537 return offset;
1538}
1539
1540int32_t
1541TimeZoneFormat::parseDefaultOffsetFields(const UnicodeString& text, int32_t start, UChar separator, int32_t& parsedLen) const {
1542 int32_t max = text.length();
1543 int32_t idx = start;
1544 int32_t len = 0;
1545 int32_t hour = 0, min = 0, sec = 0;
1546
1547 parsedLen = 0;
1548
1549 do {
1550 hour = parseOffsetFieldWithLocalizedDigits(text, idx, 1, 2, 0, MAX_OFFSET_HOUR, len);
1551 if (len == 0) {
1552 break;
1553 }
1554 idx += len;
1555
1556 if (idx + 1 < max && text.charAt(idx) == separator) {
1557 min = parseOffsetFieldWithLocalizedDigits(text, idx + 1, 2, 2, 0, MAX_OFFSET_MINUTE, len);
1558 if (len == 0) {
1559 break;
1560 }
1561 idx += (1 + len);
1562
1563 if (idx + 1 < max && text.charAt(idx) == separator) {
1564 sec = parseOffsetFieldWithLocalizedDigits(text, idx + 1, 2, 2, 0, MAX_OFFSET_SECOND, len);
1565 if (len == 0) {
1566 break;
1567 }
1568 idx += (1 + len);
1569 }
1570 }
1571 } while (FALSE);
1572
1573 if (idx == start) {
1574 return 0;
1575 }
1576
1577 parsedLen = idx - start;
1578 return hour * MILLIS_PER_HOUR + min * MILLIS_PER_MINUTE + sec * MILLIS_PER_SECOND;
1579}
1580
1581int32_t
1582TimeZoneFormat::parseOffsetFieldWithLocalizedDigits(const UnicodeString& text, int32_t start, uint8_t minDigits, uint8_t maxDigits, uint16_t minVal, uint16_t maxVal, int32_t& parsedLen) const {
1583 parsedLen = 0;
1584
1585 int32_t decVal = 0;
1586 int32_t numDigits = 0;
1587 int32_t idx = start;
1588 int32_t digitLen = 0;
1589
1590 while (idx < text.length() && numDigits < maxDigits) {
1591 int32_t digit = parseSingleLocalizedDigit(text, idx, digitLen);
1592 if (digit < 0) {
1593 break;
1594 }
1595 int32_t tmpVal = decVal * 10 + digit;
1596 if (tmpVal > maxVal) {
1597 break;
1598 }
1599 decVal = tmpVal;
1600 numDigits++;
1601 idx += digitLen;
1602 }
1603
1604 // Note: maxVal is checked in the while loop
1605 if (numDigits < minDigits || decVal < minVal) {
1606 decVal = -1;
1607 numDigits = 0;
1608 } else {
1609 parsedLen = idx - start;
1610 }
1611
1612 return decVal;
1613}
1614
1615int32_t
1616TimeZoneFormat::parseSingleLocalizedDigit(const UnicodeString& text, int32_t start, int32_t& len) const {
1617 int32_t digit = -1;
1618 len = 0;
1619 if (start < text.length()) {
1620 UChar32 cp = text.char32At(start);
1621
1622 // First, try digits configured for this instance
1623 for (int32_t i = 0; i < 10; i++) {
1624 if (cp == fGMTOffsetDigits[i]) {
1625 digit = i;
1626 break;
1627 }
1628 }
1629 // If failed, check if this is a Unicode digit
1630 if (digit < 0) {
1631 int32_t tmp = u_charDigitValue(cp);
1632 digit = (tmp >= 0 && tmp <= 9) ? tmp : -1;
1633 }
1634
1635 if (digit >= 0) {
1636 int32_t next = text.moveIndex32(start, 1);
1637 len = next - start;
1638 }
1639 }
1640 return digit;
1641}
1642
1643UnicodeString&
1644TimeZoneFormat::formatOffsetWithAsciiDigits(int32_t offset, UChar sep, OffsetFields minFields, OffsetFields maxFields, UnicodeString& result) {
1645 U_ASSERT(maxFields >= minFields);
1646 U_ASSERT(offset > -MAX_OFFSET && offset < MAX_OFFSET);
1647
1648 UChar sign = PLUS;
1649 if (offset < 0) {
1650 sign = MINUS;
1651 offset = -offset;
1652 }
1653 result.setTo(sign);
1654
1655 int fields[3];
1656 fields[0] = offset / MILLIS_PER_HOUR;
1657 offset = offset % MILLIS_PER_HOUR;
1658 fields[1] = offset / MILLIS_PER_MINUTE;
1659 offset = offset % MILLIS_PER_MINUTE;
1660 fields[2] = offset / MILLIS_PER_SECOND;
1661
1662 U_ASSERT(fields[0] >= 0 && fields[0] <= MAX_OFFSET_HOUR);
1663 U_ASSERT(fields[1] >= 0 && fields[1] <= MAX_OFFSET_MINUTE);
1664 U_ASSERT(fields[2] >= 0 && fields[2] <= MAX_OFFSET_SECOND);
1665
1666 int32_t lastIdx = maxFields;
1667 while (lastIdx > minFields) {
1668 if (fields[lastIdx] != 0) {
1669 break;
1670 }
1671 lastIdx--;
1672 }
1673
1674 for (int32_t idx = 0; idx <= lastIdx; idx++) {
1675 if (sep && idx != 0) {
1676 result.append(sep);
1677 }
1678 result.append((UChar)(0x0030 + fields[idx]/10));
1679 result.append((UChar)(0x0030 + fields[idx]%10));
1680 }
1681
1682 return result;
1683}
1684
1685int32_t
1686TimeZoneFormat::parseAbuttingAsciiOffsetFields(const UnicodeString& text, ParsePosition& pos, OffsetFields minFields, OffsetFields maxFields, UBool fixedHourWidth) {
1687 int32_t start = pos.getIndex();
1688
1689 int32_t minDigits = 2 * (minFields + 1) - (fixedHourWidth ? 0 : 1);
1690 int32_t maxDigits = 2 * (maxFields + 1);
1691
1692 U_ASSERT(maxDigits <= MAX_OFFSET_DIGITS);
1693
1694 int32_t digits[MAX_OFFSET_DIGITS];
1695 int32_t numDigits = 0;
1696 int32_t idx = start;
1697 while (numDigits < maxDigits && idx < text.length()) {
1698 UChar uch = text.charAt(idx);
1699 int32_t digit = DIGIT_VAL(uch);
1700 if (digit < 0) {
1701 break;
1702 }
1703 digits[numDigits] = digit;
1704 numDigits++;
1705 idx++;
1706 }
1707
1708 if (fixedHourWidth && (numDigits & 1)) {
1709 // Fixed digits, so the number of digits must be even number. Truncating.
1710 numDigits--;
1711 }
1712
1713 if (numDigits < minDigits) {
1714 pos.setErrorIndex(start);
1715 return 0;
1716 }
1717
1718 int32_t hour = 0, min = 0, sec = 0;
1719 UBool bParsed = FALSE;
1720 while (numDigits >= minDigits) {
1721 switch (numDigits) {
1722 case 1: //H
1723 hour = digits[0];
1724 break;
1725 case 2: //HH
1726 hour = digits[0] * 10 + digits[1];
1727 break;
1728 case 3: //Hmm
1729 hour = digits[0];
1730 min = digits[1] * 10 + digits[2];
1731 break;
1732 case 4: //HHmm
1733 hour = digits[0] * 10 + digits[1];
1734 min = digits[2] * 10 + digits[3];
1735 break;
1736 case 5: //Hmmss
1737 hour = digits[0];
1738 min = digits[1] * 10 + digits[2];
1739 sec = digits[3] * 10 + digits[4];
1740 break;
1741 case 6: //HHmmss
1742 hour = digits[0] * 10 + digits[1];
1743 min = digits[2] * 10 + digits[3];
1744 sec = digits[4] * 10 + digits[5];
1745 break;
1746 }
1747
1748 if (hour <= MAX_OFFSET_HOUR && min <= MAX_OFFSET_MINUTE && sec <= MAX_OFFSET_SECOND) {
1749 // Successfully parsed
1750 bParsed = true;
1751 break;
1752 }
1753
1754 // Truncating
1755 numDigits -= (fixedHourWidth ? 2 : 1);
1756 hour = min = sec = 0;
1757 }
1758
1759 if (!bParsed) {
1760 pos.setErrorIndex(start);
1761 return 0;
1762 }
1763 pos.setIndex(start + numDigits);
1764 return ((((hour * 60) + min) * 60) + sec) * 1000;
1765}
1766
1767int32_t
1768TimeZoneFormat::parseAsciiOffsetFields(const UnicodeString& text, ParsePosition& pos, UChar sep, OffsetFields minFields, OffsetFields maxFields, UBool fixedHourWidth) {
1769 int32_t start = pos.getIndex();
1770 int32_t fieldVal[] = {0, 0, 0};
1771 int32_t fieldLen[] = {0, -1, -1};
1772 for (int32_t idx = start, fieldIdx = 0; idx < text.length() && fieldIdx <= maxFields; idx++) {
1773 UChar c = text.charAt(idx);
1774 if (c == sep) {
1775 if (fieldLen[fieldIdx] < 0) {
1776 // next field - expected
1777 fieldLen[fieldIdx] = 0;
1778 } else if (fieldIdx == 0 && !fixedHourWidth) {
1779 // 1 digit hour, move to next field
1780 fieldIdx++;
1781 fieldLen[fieldIdx] = 0;
1782 } else {
1783 // otherwise, premature field
1784 break;
1785 }
1786 continue;
1787 }
1788 int32_t digit = DIGIT_VAL(c);
1789 if (digit < 0) {
1790 // not a digit
1791 break;
1792 }
1793 fieldVal[fieldIdx] = fieldVal[fieldIdx] * 10 + digit;
1794 fieldLen[fieldIdx]++;
1795 if (fieldLen[fieldIdx] >= 2) {
1796 // parsed 2 digits, move to next field
1797 fieldIdx++;
1798 }
1799 }
1800
1801 int32_t offset = 0;
1802 int32_t parsedLen = 0;
1803 int32_t parsedFields = -1;
1804 do {
1805 // hour
1806 if (fieldLen[0] == 0 || (fieldLen[0] == 1 && fixedHourWidth)) {
1807 break;
1808 }
1809 if (fieldVal[0] > MAX_OFFSET_HOUR) {
1810 if (fixedHourWidth) {
1811 break;
1812 }
1813 offset = (fieldVal[0] / 10) * MILLIS_PER_HOUR;
1814 parsedFields = FIELDS_H;
1815 parsedLen = 1;
1816 break;
1817 }
1818 offset = fieldVal[0] * MILLIS_PER_HOUR;
1819 parsedLen = fieldLen[0];
1820 parsedFields = FIELDS_H;
1821
1822 // minute
1823 if (fieldLen[1] != 2 || fieldVal[1] > MAX_OFFSET_MINUTE) {
1824 break;
1825 }
1826 offset += fieldVal[1] * MILLIS_PER_MINUTE;
1827 parsedLen += (1 + fieldLen[1]);
1828 parsedFields = FIELDS_HM;
1829
1830 // second
1831 if (fieldLen[2] != 2 || fieldVal[2] > MAX_OFFSET_SECOND) {
1832 break;
1833 }
1834 offset += fieldVal[2] * MILLIS_PER_SECOND;
1835 parsedLen += (1 + fieldLen[2]);
1836 parsedFields = FIELDS_HMS;
1837 } while (false);
1838
1839 if (parsedFields < minFields) {
1840 pos.setErrorIndex(start);
1841 return 0;
1842 }
1843
1844 pos.setIndex(start + parsedLen);
1845 return offset;
1846}
1847
1848void
1849TimeZoneFormat::appendOffsetDigits(UnicodeString& buf, int32_t n, uint8_t minDigits) const {
1850 U_ASSERT(n >= 0 && n < 60);
1851 int32_t numDigits = n >= 10 ? 2 : 1;
1852 for (int32_t i = 0; i < minDigits - numDigits; i++) {
1853 buf.append(fGMTOffsetDigits[0]);
1854 }
1855 if (numDigits == 2) {
1856 buf.append(fGMTOffsetDigits[n / 10]);
1857 }
1858 buf.append(fGMTOffsetDigits[n % 10]);
1859}
1860
1861// ------------------------------------------------------------------
1862// Private misc
1863void
1864TimeZoneFormat::initGMTPattern(const UnicodeString& gmtPattern, UErrorCode& status) {
1865 if (U_FAILURE(status)) {
1866 return;
1867 }
1868 // This implementation not perfect, but sufficient practically.
1869 int32_t idx = gmtPattern.indexOf(ARG0, ARG0_LEN, 0);
1870 if (idx < 0) {
1871 status = U_ILLEGAL_ARGUMENT_ERROR;
1872 return;
1873 }
1874 fGMTPattern.setTo(gmtPattern);
1875 unquote(gmtPattern.tempSubString(0, idx), fGMTPatternPrefix);
1876 unquote(gmtPattern.tempSubString(idx + ARG0_LEN), fGMTPatternSuffix);
1877}
1878
1879UnicodeString&
1880TimeZoneFormat::unquote(const UnicodeString& pattern, UnicodeString& result) {
1881 if (pattern.indexOf(SINGLEQUOTE) < 0) {
1882 result.setTo(pattern);
1883 return result;
1884 }
1885 result.remove();
1886 UBool isPrevQuote = FALSE;
1887 UBool inQuote = FALSE;
1888 for (int32_t i = 0; i < pattern.length(); i++) {
1889 UChar c = pattern.charAt(i);
1890 if (c == SINGLEQUOTE) {
1891 if (isPrevQuote) {
1892 result.append(c);
1893 isPrevQuote = FALSE;
1894 } else {
1895 isPrevQuote = TRUE;
1896 }
1897 inQuote = !inQuote;
1898 } else {
1899 isPrevQuote = FALSE;
1900 result.append(c);
1901 }
1902 }
1903 return result;
1904}
1905
1906UVector*
1907TimeZoneFormat::parseOffsetPattern(const UnicodeString& pattern, OffsetFields required, UErrorCode& status) {
1908 if (U_FAILURE(status)) {
1909 return NULL;
1910 }
1911 UVector* result = new UVector(deleteGMTOffsetField, NULL, status);
1912 if (result == NULL) {
1913 status = U_MEMORY_ALLOCATION_ERROR;
1914 return NULL;
1915 }
1916
1917 int32_t checkBits = 0;
1918 UBool isPrevQuote = FALSE;
1919 UBool inQuote = FALSE;
1920 UnicodeString text;
1921 GMTOffsetField::FieldType itemType = GMTOffsetField::TEXT;
1922 int32_t itemLength = 1;
1923
1924 for (int32_t i = 0; i < pattern.length(); i++) {
1925 UChar ch = pattern.charAt(i);
1926 if (ch == SINGLEQUOTE) {
1927 if (isPrevQuote) {
1928 text.append(SINGLEQUOTE);
1929 isPrevQuote = FALSE;
1930 } else {
1931 isPrevQuote = TRUE;
1932 if (itemType != GMTOffsetField::TEXT) {
1933 if (GMTOffsetField::isValid(itemType, itemLength)) {
1934 GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, (uint8_t)itemLength, status);
1935 result->addElement(fld, status);
1936 if (U_FAILURE(status)) {
1937 break;
1938 }
1939 } else {
1940 status = U_ILLEGAL_ARGUMENT_ERROR;
1941 break;
1942 }
1943 itemType = GMTOffsetField::TEXT;
1944 }
1945 }
1946 inQuote = !inQuote;
1947 } else {
1948 isPrevQuote = FALSE;
1949 if (inQuote) {
1950 text.append(ch);
1951 } else {
1952 GMTOffsetField::FieldType tmpType = GMTOffsetField::getTypeByLetter(ch);
1953 if (tmpType != GMTOffsetField::TEXT) {
1954 // an offset time pattern character
1955 if (tmpType == itemType) {
1956 itemLength++;
1957 } else {
1958 if (itemType == GMTOffsetField::TEXT) {
1959 if (text.length() > 0) {
1960 GMTOffsetField* textfld = GMTOffsetField::createText(text, status);
1961 result->addElement(textfld, status);
1962 if (U_FAILURE(status)) {
1963 break;
1964 }
1965 text.remove();
1966 }
1967 } else {
1968 if (GMTOffsetField::isValid(itemType, itemLength)) {
1969 GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
1970 result->addElement(fld, status);
1971 if (U_FAILURE(status)) {
1972 break;
1973 }
1974 } else {
1975 status = U_ILLEGAL_ARGUMENT_ERROR;
1976 break;
1977 }
1978 }
1979 itemType = tmpType;
1980 itemLength = 1;
1981 checkBits |= tmpType;
1982 }
1983 } else {
1984 // a string literal
1985 if (itemType != GMTOffsetField::TEXT) {
1986 if (GMTOffsetField::isValid(itemType, itemLength)) {
1987 GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
1988 result->addElement(fld, status);
1989 if (U_FAILURE(status)) {
1990 break;
1991 }
1992 } else {
1993 status = U_ILLEGAL_ARGUMENT_ERROR;
1994 break;
1995 }
1996 itemType = GMTOffsetField::TEXT;
1997 }
1998 text.append(ch);
1999 }
2000 }
2001 }
2002 }
2003 // handle last item
2004 if (U_SUCCESS(status)) {
2005 if (itemType == GMTOffsetField::TEXT) {
2006 if (text.length() > 0) {
2007 GMTOffsetField* tfld = GMTOffsetField::createText(text, status);
2008 result->addElement(tfld, status);
2009 }
2010 } else {
2011 if (GMTOffsetField::isValid(itemType, itemLength)) {
2012 GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
2013 result->addElement(fld, status);
2014 } else {
2015 status = U_ILLEGAL_ARGUMENT_ERROR;
2016 }
2017 }
2018
2019 // Check all required fields are set
2020 if (U_SUCCESS(status)) {
2021 int32_t reqBits = 0;
2022 switch (required) {
2023 case FIELDS_H:
2024 reqBits = GMTOffsetField::HOUR;
2025 break;
2026 case FIELDS_HM:
2027 reqBits = GMTOffsetField::HOUR | GMTOffsetField::MINUTE;
2028 break;
2029 case FIELDS_HMS:
2030 reqBits = GMTOffsetField::HOUR | GMTOffsetField::MINUTE | GMTOffsetField::SECOND;
2031 break;
2032 }
2033 if (checkBits == reqBits) {
2034 // all required fields are set, no extra fields
2035 return result;
2036 }
2037 }
2038 }
2039
2040 // error
2041 delete result;
2042 return NULL;
2043}
2044
2045UnicodeString&
2046TimeZoneFormat::expandOffsetPattern(const UnicodeString& offsetHM, UnicodeString& result) {
2047 U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN) == 2);
2048
2049 int32_t idx_mm = offsetHM.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN, 2, 0);
2050 if (idx_mm < 0) {
2051 // we cannot do anything with this...
2052 result.setTo(offsetHM);
2053 result.append(DEFAULT_GMT_OFFSET_SEP);
2054 result.append(DEFAULT_GMT_OFFSET_SECOND_PATTERN, -1);
2055 return result;
2056 }
2057
2058 UnicodeString sep;
2059 int32_t idx_H = offsetHM.tempSubString(0, idx_mm).lastIndexOf(0x0048 /* H */);
2060 if (idx_H >= 0) {
2061 sep = offsetHM.tempSubString(idx_H + 1, idx_mm - (idx_H + 1));
2062 }
2063 result.setTo(offsetHM.tempSubString(0, idx_mm + 2));
2064 result.append(sep);
2065 result.append(DEFAULT_GMT_OFFSET_SECOND_PATTERN, -1);
2066 result.append(offsetHM.tempSubString(idx_mm + 2));
2067 return result;
2068}
2069
2070void
2071TimeZoneFormat::initGMTOffsetPatterns(UErrorCode& status) {
2072 for (int32_t type = 0; type <= UTZFMT_PAT_NEGATIVE_HMS; type++) {
2073 switch (type) {
2074 case UTZFMT_PAT_POSITIVE_HM:
2075 case UTZFMT_PAT_NEGATIVE_HM:
2076 fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_HM, status);
2077 break;
2078 case UTZFMT_PAT_POSITIVE_HMS:
2079 case UTZFMT_PAT_NEGATIVE_HMS:
2080 fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_HMS, status);
2081 break;
2082 }
2083 }
2084}
2085
2086UBool
2087TimeZoneFormat::toCodePoints(const UnicodeString& str, UChar32* codeArray, int32_t size) {
2088 int32_t count = str.countChar32();
2089 if (count != size) {
2090 return FALSE;
2091 }
2092
2093 for (int32_t idx = 0, start = 0; idx < size; idx++) {
2094 codeArray[idx] = str.char32At(start);
2095 start = str.moveIndex32(start, 1);
2096 }
2097
2098 return TRUE;
2099}
2100
2101TimeZone*
2102TimeZoneFormat::createTimeZoneForOffset(int32_t offset) const {
2103 if (offset == 0) {
2104 // when offset is 0, we should use "Etc/GMT"
2105 return TimeZone::createTimeZone(UnicodeString(TZID_GMT));
2106 }
2107 return ZoneMeta::createCustomTimeZone(offset);
2108}
2109
2110UTimeZoneFormatTimeType
2111TimeZoneFormat::getTimeType(UTimeZoneNameType nameType) {
2112 switch (nameType) {
2113 case UTZNM_LONG_STANDARD:
2114 case UTZNM_SHORT_STANDARD:
2115 return UTZFMT_TIME_TYPE_STANDARD;
2116
2117 case UTZNM_LONG_DAYLIGHT:
2118 case UTZNM_SHORT_DAYLIGHT:
2119 return UTZFMT_TIME_TYPE_DAYLIGHT;
2120
2121 default:
2122 U_ASSERT(FALSE);
2123 }
2124 return UTZFMT_TIME_TYPE_UNKNOWN;
2125}
2126
2127UnicodeString&
2128TimeZoneFormat::getTimeZoneID(const TimeZoneNames::MatchInfoCollection* matches, int32_t idx, UnicodeString& tzID) const {
2129 if (!matches->getTimeZoneIDAt(idx, tzID)) {
2130 UnicodeString mzID;
2131 if (matches->getMetaZoneIDAt(idx, mzID)) {
2132 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, tzID);
2133 }
2134 }
2135 return tzID;
2136}
2137
2138U_NAMESPACE_END
2139
2140#endif