1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2011-2015, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
10 #include "unicode/utypes.h"
12 #if !UCONFIG_NO_FORMATTING
14 #include "unicode/calendar.h"
15 #include "unicode/tzfmt.h"
16 #include "unicode/numsys.h"
17 #include "unicode/strenum.h"
18 #include "unicode/uchar.h"
19 #include "unicode/udat.h"
20 #include "unicode/ustring.h"
21 #include "unicode/utf16.h"
33 #include "tznames_impl.h" // TextTrieMap
34 #include "patternprops.h"
38 // Bit flags used by the parse method.
39 // The order must match UTimeZoneFormatStyle enum.
40 #define ISO_Z_STYLE_FLAG 0x0080
41 #define ISO_LOCAL_STYLE_FLAG 0x0100
42 static const int16_t STYLE_PARSE_FLAGS
[] = {
43 0x0001, // UTZFMT_STYLE_GENERIC_LOCATION,
44 0x0002, // UTZFMT_STYLE_GENERIC_LONG,
45 0x0004, // UTZFMT_STYLE_GENERIC_SHORT,
46 0x0008, // UTZFMT_STYLE_SPECIFIC_LONG,
47 0x0010, // UTZFMT_STYLE_SPECIFIC_SHORT,
48 0x0020, // UTZFMT_STYLE_LOCALIZED_GMT,
49 0x0040, // UTZFMT_STYLE_LOCALIZED_GMT_SHORT,
50 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_SHORT,
51 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT,
52 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_FIXED,
53 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED,
54 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_FULL,
55 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
56 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_FIXED,
57 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED,
58 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_FULL,
59 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL,
60 0x0200, // UTZFMT_STYLE_ZONE_ID,
61 0x0400, // UTZFMT_STYLE_ZONE_ID_SHORT,
62 0x0800 // UTZFMT_STYLE_EXEMPLAR_LOCATION
65 static const char gZoneStringsTag
[] = "zoneStrings";
66 static const char gGmtFormatTag
[]= "gmtFormat";
67 static const char gGmtZeroFormatTag
[] = "gmtZeroFormat";
68 static const char gHourFormatTag
[]= "hourFormat";
70 static const UChar TZID_GMT
[] = {0x0045, 0x0074, 0x0063, 0x002F, 0x0047, 0x004D, 0x0054, 0}; // Etc/GMT
71 static const UChar UNKNOWN_ZONE_ID
[] = {
72 0x0045, 0x0074, 0x0063, 0x002F, 0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0}; // Etc/Unknown
73 static const UChar UNKNOWN_SHORT_ZONE_ID
[] = {0x0075, 0x006E, 0x006B, 0}; // unk
74 static const UChar UNKNOWN_LOCATION
[] = {0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0}; // Unknown
76 static const UChar DEFAULT_GMT_PATTERN
[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0}; // GMT{0}
77 //static const UChar DEFAULT_GMT_ZERO[] = {0x0047, 0x004D, 0x0054, 0}; // GMT
78 static const UChar DEFAULT_GMT_POSITIVE_HM
[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // +H:mm
79 static const UChar DEFAULT_GMT_POSITIVE_HMS
[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // +H:mm:ss
80 static const UChar DEFAULT_GMT_NEGATIVE_HM
[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // -H:mm
81 static const UChar DEFAULT_GMT_NEGATIVE_HMS
[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // -H:mm:ss
82 static const UChar DEFAULT_GMT_POSITIVE_H
[] = {0x002B, 0x0048, 0}; // +H
83 static const UChar DEFAULT_GMT_NEGATIVE_H
[] = {0x002D, 0x0048, 0}; // -H
85 static const UChar32 DEFAULT_GMT_DIGITS
[] = {
86 0x0030, 0x0031, 0x0032, 0x0033, 0x0034,
87 0x0035, 0x0036, 0x0037, 0x0038, 0x0039
90 static const UChar DEFAULT_GMT_OFFSET_SEP
= 0x003A; // ':'
92 static const UChar ARG0
[] = {0x007B, 0x0030, 0x007D}; // "{0}"
93 static const int32_t ARG0_LEN
= 3;
95 static const UChar DEFAULT_GMT_OFFSET_MINUTE_PATTERN
[] = {0x006D, 0x006D, 0}; // "mm"
96 static const UChar DEFAULT_GMT_OFFSET_SECOND_PATTERN
[] = {0x0073, 0x0073, 0}; // "ss"
98 static const UChar ALT_GMT_STRINGS
[][4] = {
99 {0x0047, 0x004D, 0x0054, 0}, // GMT
100 {0x0055, 0x0054, 0x0043, 0}, // UTC
101 {0x0055, 0x0054, 0, 0}, // UT
105 // Order of GMT offset pattern parsing, *_HMS must be evaluated first
106 // because *_HM is most likely a substring of *_HMS
107 static const int32_t PARSE_GMT_OFFSET_TYPES
[] = {
108 UTZFMT_PAT_POSITIVE_HMS
,
109 UTZFMT_PAT_NEGATIVE_HMS
,
110 UTZFMT_PAT_POSITIVE_HM
,
111 UTZFMT_PAT_NEGATIVE_HM
,
112 UTZFMT_PAT_POSITIVE_H
,
113 UTZFMT_PAT_NEGATIVE_H
,
117 static const UChar SINGLEQUOTE
= 0x0027;
118 static const UChar PLUS
= 0x002B;
119 static const UChar MINUS
= 0x002D;
120 static const UChar ISO8601_UTC
= 0x005A; // 'Z'
121 static const UChar ISO8601_SEP
= 0x003A; // ':'
123 static const int32_t MILLIS_PER_HOUR
= 60 * 60 * 1000;
124 static const int32_t MILLIS_PER_MINUTE
= 60 * 1000;
125 static const int32_t MILLIS_PER_SECOND
= 1000;
127 // Maximum offset (exclusive) in millisecond supported by offset formats
128 static int32_t MAX_OFFSET
= 24 * MILLIS_PER_HOUR
;
130 // Maximum values for GMT offset fields
131 static const int32_t MAX_OFFSET_HOUR
= 23;
132 static const int32_t MAX_OFFSET_MINUTE
= 59;
133 static const int32_t MAX_OFFSET_SECOND
= 59;
135 static const int32_t UNKNOWN_OFFSET
= 0x7FFFFFFF;
137 static const int32_t ALL_SIMPLE_NAME_TYPES
= UTZNM_LONG_STANDARD
| UTZNM_LONG_DAYLIGHT
| UTZNM_SHORT_STANDARD
| UTZNM_SHORT_DAYLIGHT
| UTZNM_EXEMPLAR_LOCATION
;
138 static const int32_t ALL_GENERIC_NAME_TYPES
= UTZGNM_LOCATION
| UTZGNM_LONG
| UTZGNM_SHORT
;
140 #define DIGIT_VAL(c) (0x0030 <= (c) && (c) <= 0x0039 ? (c) - 0x0030 : -1)
141 #define MAX_OFFSET_DIGITS 6
143 // Time Zone ID/Short ID trie
144 static TextTrieMap
*gZoneIdTrie
= NULL
;
145 static icu::UInitOnce gZoneIdTrieInitOnce
= U_INITONCE_INITIALIZER
;
147 static TextTrieMap
*gShortZoneIdTrie
= NULL
;
148 static icu::UInitOnce gShortZoneIdTrieInitOnce
= U_INITONCE_INITIALIZER
;
150 static UMutex gLock
= U_MUTEX_INITIALIZER
;
154 * Cleanup callback func
156 static UBool U_CALLCONV
tzfmt_cleanup(void)
158 if (gZoneIdTrie
!= NULL
) {
162 gZoneIdTrieInitOnce
.reset();
164 if (gShortZoneIdTrie
!= NULL
) {
165 delete gShortZoneIdTrie
;
167 gShortZoneIdTrie
= NULL
;
168 gShortZoneIdTrieInitOnce
.reset();
174 // ------------------------------------------------------------------
177 // This class represents a localized GMT offset pattern
178 // item and used by TimeZoneFormat
179 // ------------------------------------------------------------------
180 class GMTOffsetField
: public UMemory
{
189 virtual ~GMTOffsetField();
191 static GMTOffsetField
* createText(const UnicodeString
& text
, UErrorCode
& status
);
192 static GMTOffsetField
* createTimeField(FieldType type
, uint8_t width
, UErrorCode
& status
);
193 static UBool
isValid(FieldType type
, int32_t width
);
194 static FieldType
getTypeByLetter(UChar ch
);
196 FieldType
getType() const;
197 uint8_t getWidth() const;
198 const UChar
* getPatternText(void) const;
208 GMTOffsetField::GMTOffsetField()
209 : fText(NULL
), fType(TEXT
), fWidth(0) {
212 GMTOffsetField::~GMTOffsetField() {
219 GMTOffsetField::createText(const UnicodeString
& text
, UErrorCode
& status
) {
220 if (U_FAILURE(status
)) {
223 GMTOffsetField
* result
= new GMTOffsetField();
224 if (result
== NULL
) {
225 status
= U_MEMORY_ALLOCATION_ERROR
;
229 int32_t len
= text
.length();
230 result
->fText
= (UChar
*)uprv_malloc((len
+ 1) * sizeof(UChar
));
231 if (result
->fText
== NULL
) {
232 status
= U_MEMORY_ALLOCATION_ERROR
;
236 u_strncpy(result
->fText
, text
.getBuffer(), len
);
237 result
->fText
[len
] = 0;
238 result
->fType
= TEXT
;
244 GMTOffsetField::createTimeField(FieldType type
, uint8_t width
, UErrorCode
& status
) {
245 U_ASSERT(type
!= TEXT
);
246 if (U_FAILURE(status
)) {
249 GMTOffsetField
* result
= new GMTOffsetField();
250 if (result
== NULL
) {
251 status
= U_MEMORY_ALLOCATION_ERROR
;
255 result
->fType
= type
;
256 result
->fWidth
= width
;
262 GMTOffsetField::isValid(FieldType type
, int32_t width
) {
265 return (width
== 1 || width
== 2);
275 GMTOffsetField::FieldType
276 GMTOffsetField::getTypeByLetter(UChar ch
) {
277 if (ch
== 0x0048 /* H */) {
279 } else if (ch
== 0x006D /* m */) {
281 } else if (ch
== 0x0073 /* s */) {
287 inline GMTOffsetField::FieldType
288 GMTOffsetField::getType() const {
293 GMTOffsetField::getWidth() const {
298 GMTOffsetField::getPatternText(void) const {
304 static void U_CALLCONV
305 deleteGMTOffsetField(void *obj
) {
306 delete static_cast<GMTOffsetField
*>(obj
);
311 // ------------------------------------------------------------------
313 // ------------------------------------------------------------------
314 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneFormat
)
316 TimeZoneFormat::TimeZoneFormat(const Locale
& locale
, UErrorCode
& status
)
317 : fLocale(locale
), fTimeZoneNames(NULL
), fTimeZoneGenericNames(NULL
),
318 fDefParseOptionFlags(0), fTZDBTimeZoneNames(NULL
) {
320 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
321 fGMTOffsetPatternItems
[i
] = NULL
;
324 const char* region
= fLocale
.getCountry();
325 int32_t regionLen
= static_cast<int32_t>(uprv_strlen(region
));
326 if (regionLen
== 0) {
327 char loc
[ULOC_FULLNAME_CAPACITY
];
328 uloc_addLikelySubtags(fLocale
.getName(), loc
, sizeof(loc
), &status
);
330 regionLen
= uloc_getCountry(loc
, fTargetRegion
, sizeof(fTargetRegion
), &status
);
331 if (U_SUCCESS(status
)) {
332 fTargetRegion
[regionLen
] = 0;
336 } else if (regionLen
< (int32_t)sizeof(fTargetRegion
)) {
337 uprv_strcpy(fTargetRegion
, region
);
339 fTargetRegion
[0] = 0;
342 fTimeZoneNames
= TimeZoneNames::createInstance(locale
, status
);
343 // fTimeZoneGenericNames is lazily instantiated
344 if (U_FAILURE(status
)) {
348 const UChar
* gmtPattern
= NULL
;
349 const UChar
* hourFormats
= NULL
;
351 UResourceBundle
*zoneBundle
= ures_open(U_ICUDATA_ZONE
, locale
.getName(), &status
);
352 UResourceBundle
*zoneStringsArray
= ures_getByKeyWithFallback(zoneBundle
, gZoneStringsTag
, NULL
, &status
);
353 if (U_SUCCESS(status
)) {
356 resStr
= ures_getStringByKeyWithFallback(zoneStringsArray
, gGmtFormatTag
, &len
, &status
);
360 resStr
= ures_getStringByKeyWithFallback(zoneStringsArray
, gGmtZeroFormatTag
, &len
, &status
);
362 fGMTZeroFormat
.setTo(TRUE
, resStr
, len
);
364 resStr
= ures_getStringByKeyWithFallback(zoneStringsArray
, gHourFormatTag
, &len
, &status
);
366 hourFormats
= resStr
;
368 ures_close(zoneStringsArray
);
369 ures_close(zoneBundle
);
372 if (gmtPattern
== NULL
) {
373 gmtPattern
= DEFAULT_GMT_PATTERN
;
375 initGMTPattern(UnicodeString(TRUE
, gmtPattern
, -1), status
);
377 UBool useDefaultOffsetPatterns
= TRUE
;
379 UChar
*sep
= u_strchr(hourFormats
, (UChar
)0x003B /* ';' */);
381 UErrorCode tmpStatus
= U_ZERO_ERROR
;
382 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
].setTo(FALSE
, hourFormats
, (int32_t)(sep
- hourFormats
));
383 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
].setTo(TRUE
, sep
+ 1, -1);
384 expandOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HMS
], tmpStatus
);
385 expandOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HMS
], tmpStatus
);
386 truncateOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_H
], tmpStatus
);
387 truncateOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_H
], tmpStatus
);
388 if (U_SUCCESS(tmpStatus
)) {
389 useDefaultOffsetPatterns
= FALSE
;
393 if (useDefaultOffsetPatterns
) {
394 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_H
].setTo(TRUE
, DEFAULT_GMT_POSITIVE_H
, -1);
395 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
].setTo(TRUE
, DEFAULT_GMT_POSITIVE_HM
, -1);
396 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HMS
].setTo(TRUE
, DEFAULT_GMT_POSITIVE_HMS
, -1);
397 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_H
].setTo(TRUE
, DEFAULT_GMT_NEGATIVE_H
, -1);
398 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
].setTo(TRUE
, DEFAULT_GMT_NEGATIVE_HM
, -1);
399 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HMS
].setTo(TRUE
, DEFAULT_GMT_NEGATIVE_HMS
, -1);
401 initGMTOffsetPatterns(status
);
403 NumberingSystem
* ns
= NumberingSystem::createInstance(locale
, status
);
404 UBool useDefDigits
= TRUE
;
405 if (ns
&& !ns
->isAlgorithmic()) {
406 UnicodeString digits
= ns
->getDescription();
407 useDefDigits
= !toCodePoints(digits
, fGMTOffsetDigits
, 10);
410 uprv_memcpy(fGMTOffsetDigits
, DEFAULT_GMT_DIGITS
, sizeof(UChar32
) * 10);
415 TimeZoneFormat::TimeZoneFormat(const TimeZoneFormat
& other
)
416 : Format(other
), fTimeZoneNames(NULL
), fTimeZoneGenericNames(NULL
),
417 fTZDBTimeZoneNames(NULL
) {
419 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
420 fGMTOffsetPatternItems
[i
] = NULL
;
426 TimeZoneFormat::~TimeZoneFormat() {
427 delete fTimeZoneNames
;
428 delete fTimeZoneGenericNames
;
429 delete fTZDBTimeZoneNames
;
430 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
431 delete fGMTOffsetPatternItems
[i
];
436 TimeZoneFormat::operator=(const TimeZoneFormat
& other
) {
437 if (this == &other
) {
441 delete fTimeZoneNames
;
442 delete fTimeZoneGenericNames
;
443 fTimeZoneGenericNames
= NULL
;
444 delete fTZDBTimeZoneNames
;
445 fTZDBTimeZoneNames
= NULL
;
447 fLocale
= other
.fLocale
;
448 uprv_memcpy(fTargetRegion
, other
.fTargetRegion
, sizeof(fTargetRegion
));
450 fTimeZoneNames
= other
.fTimeZoneNames
->clone();
451 if (other
.fTimeZoneGenericNames
) {
452 // TODO: this test has dubious thread safety.
453 fTimeZoneGenericNames
= other
.fTimeZoneGenericNames
->clone();
456 fGMTPattern
= other
.fGMTPattern
;
457 fGMTPatternPrefix
= other
.fGMTPatternPrefix
;
458 fGMTPatternSuffix
= other
.fGMTPatternSuffix
;
460 UErrorCode status
= U_ZERO_ERROR
;
461 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
462 fGMTOffsetPatterns
[i
] = other
.fGMTOffsetPatterns
[i
];
463 delete fGMTOffsetPatternItems
[i
];
464 fGMTOffsetPatternItems
[i
] = NULL
;
466 initGMTOffsetPatterns(status
);
467 U_ASSERT(U_SUCCESS(status
));
469 fGMTZeroFormat
= other
.fGMTZeroFormat
;
471 uprv_memcpy(fGMTOffsetDigits
, other
.fGMTOffsetDigits
, sizeof(fGMTOffsetDigits
));
473 fDefParseOptionFlags
= other
.fDefParseOptionFlags
;
480 TimeZoneFormat::operator==(const Format
& other
) const {
481 TimeZoneFormat
* tzfmt
= (TimeZoneFormat
*)&other
;
484 fLocale
== tzfmt
->fLocale
485 && fGMTPattern
== tzfmt
->fGMTPattern
486 && fGMTZeroFormat
== tzfmt
->fGMTZeroFormat
487 && *fTimeZoneNames
== *tzfmt
->fTimeZoneNames
;
489 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
&& isEqual
; i
++) {
490 isEqual
= fGMTOffsetPatterns
[i
] == tzfmt
->fGMTOffsetPatterns
[i
];
492 for (int32_t i
= 0; i
< 10 && isEqual
; i
++) {
493 isEqual
= fGMTOffsetDigits
[i
] == tzfmt
->fGMTOffsetDigits
[i
];
496 // Check fTimeZoneGenericNames. For now,
497 // if fTimeZoneNames is same, fTimeZoneGenericNames should
498 // be also equivalent.
503 TimeZoneFormat::clone() const {
504 return new TimeZoneFormat(*this);
507 TimeZoneFormat
* U_EXPORT2
508 TimeZoneFormat::createInstance(const Locale
& locale
, UErrorCode
& status
) {
509 TimeZoneFormat
* tzfmt
= new TimeZoneFormat(locale
, status
);
510 if (U_SUCCESS(status
)) {
517 // ------------------------------------------------------------------
521 TimeZoneFormat::getTimeZoneNames() const {
522 return (const TimeZoneNames
*)fTimeZoneNames
;
526 TimeZoneFormat::adoptTimeZoneNames(TimeZoneNames
*tznames
) {
527 delete fTimeZoneNames
;
528 fTimeZoneNames
= tznames
;
530 // TODO - We should also update fTimeZoneGenericNames
534 TimeZoneFormat::setTimeZoneNames(const TimeZoneNames
&tznames
) {
535 delete fTimeZoneNames
;
536 fTimeZoneNames
= tznames
.clone();
538 // TODO - We should also update fTimeZoneGenericNames
542 TimeZoneFormat::setDefaultParseOptions(uint32_t flags
) {
543 fDefParseOptionFlags
= flags
;
547 TimeZoneFormat::getDefaultParseOptions(void) const {
548 return fDefParseOptionFlags
;
553 TimeZoneFormat::getGMTPattern(UnicodeString
& pattern
) const {
554 return pattern
.setTo(fGMTPattern
);
558 TimeZoneFormat::setGMTPattern(const UnicodeString
& pattern
, UErrorCode
& status
) {
559 initGMTPattern(pattern
, status
);
563 TimeZoneFormat::getGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type
, UnicodeString
& pattern
) const {
564 return pattern
.setTo(fGMTOffsetPatterns
[type
]);
568 TimeZoneFormat::setGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type
, const UnicodeString
& pattern
, UErrorCode
& status
) {
569 if (U_FAILURE(status
)) {
572 if (pattern
== fGMTOffsetPatterns
[type
]) {
577 OffsetFields required
= FIELDS_HM
;
579 case UTZFMT_PAT_POSITIVE_H
:
580 case UTZFMT_PAT_NEGATIVE_H
:
583 case UTZFMT_PAT_POSITIVE_HM
:
584 case UTZFMT_PAT_NEGATIVE_HM
:
585 required
= FIELDS_HM
;
587 case UTZFMT_PAT_POSITIVE_HMS
:
588 case UTZFMT_PAT_NEGATIVE_HMS
:
589 required
= FIELDS_HMS
;
596 UVector
* patternItems
= parseOffsetPattern(pattern
, required
, status
);
597 if (patternItems
== NULL
) {
601 fGMTOffsetPatterns
[type
].setTo(pattern
);
602 delete fGMTOffsetPatternItems
[type
];
603 fGMTOffsetPatternItems
[type
] = patternItems
;
604 checkAbuttingHoursAndMinutes();
608 TimeZoneFormat::getGMTOffsetDigits(UnicodeString
& digits
) const {
610 for (int32_t i
= 0; i
< 10; i
++) {
611 digits
.append(fGMTOffsetDigits
[i
]);
617 TimeZoneFormat::setGMTOffsetDigits(const UnicodeString
& digits
, UErrorCode
& status
) {
618 if (U_FAILURE(status
)) {
621 UChar32 digitArray
[10];
622 if (!toCodePoints(digits
, digitArray
, 10)) {
623 status
= U_ILLEGAL_ARGUMENT_ERROR
;
626 uprv_memcpy(fGMTOffsetDigits
, digitArray
, sizeof(UChar32
)*10);
630 TimeZoneFormat::getGMTZeroFormat(UnicodeString
& gmtZeroFormat
) const {
631 return gmtZeroFormat
.setTo(fGMTZeroFormat
);
635 TimeZoneFormat::setGMTZeroFormat(const UnicodeString
& gmtZeroFormat
, UErrorCode
& status
) {
636 if (U_SUCCESS(status
)) {
637 if (gmtZeroFormat
.isEmpty()) {
638 status
= U_ILLEGAL_ARGUMENT_ERROR
;
639 } else if (gmtZeroFormat
!= fGMTZeroFormat
) {
640 fGMTZeroFormat
.setTo(gmtZeroFormat
);
645 // ------------------------------------------------------------------
649 TimeZoneFormat::format(UTimeZoneFormatStyle style
, const TimeZone
& tz
, UDate date
,
650 UnicodeString
& name
, UTimeZoneFormatTimeType
* timeType
/* = NULL */) const {
652 *timeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
655 UBool noOffsetFormatFallback
= FALSE
;
658 case UTZFMT_STYLE_GENERIC_LOCATION
:
659 formatGeneric(tz
, UTZGNM_LOCATION
, date
, name
);
661 case UTZFMT_STYLE_GENERIC_LONG
:
662 formatGeneric(tz
, UTZGNM_LONG
, date
, name
);
664 case UTZFMT_STYLE_GENERIC_SHORT
:
665 formatGeneric(tz
, UTZGNM_SHORT
, date
, name
);
667 case UTZFMT_STYLE_SPECIFIC_LONG
:
668 formatSpecific(tz
, UTZNM_LONG_STANDARD
, UTZNM_LONG_DAYLIGHT
, date
, name
, timeType
);
670 case UTZFMT_STYLE_SPECIFIC_SHORT
:
671 formatSpecific(tz
, UTZNM_SHORT_STANDARD
, UTZNM_SHORT_DAYLIGHT
, date
, name
, timeType
);
674 case UTZFMT_STYLE_ZONE_ID
:
676 noOffsetFormatFallback
= TRUE
;
678 case UTZFMT_STYLE_ZONE_ID_SHORT
:
680 const UChar
* shortID
= ZoneMeta::getShortID(tz
);
681 if (shortID
== NULL
) {
682 shortID
= UNKNOWN_SHORT_ZONE_ID
;
684 name
.setTo(shortID
, -1);
686 noOffsetFormatFallback
= TRUE
;
689 case UTZFMT_STYLE_EXEMPLAR_LOCATION
:
690 formatExemplarLocation(tz
, name
);
691 noOffsetFormatFallback
= TRUE
;
695 // will be handled below
699 if (name
.isEmpty() && !noOffsetFormatFallback
) {
700 UErrorCode status
= U_ZERO_ERROR
;
701 int32_t rawOffset
, dstOffset
;
702 tz
.getOffset(date
, FALSE
, rawOffset
, dstOffset
, status
);
703 int32_t offset
= rawOffset
+ dstOffset
;
704 if (U_SUCCESS(status
)) {
706 case UTZFMT_STYLE_GENERIC_LOCATION
:
707 case UTZFMT_STYLE_GENERIC_LONG
:
708 case UTZFMT_STYLE_SPECIFIC_LONG
:
709 case UTZFMT_STYLE_LOCALIZED_GMT
:
710 formatOffsetLocalizedGMT(offset
, name
, status
);
713 case UTZFMT_STYLE_GENERIC_SHORT
:
714 case UTZFMT_STYLE_SPECIFIC_SHORT
:
715 case UTZFMT_STYLE_LOCALIZED_GMT_SHORT
:
716 formatOffsetShortLocalizedGMT(offset
, name
, status
);
719 case UTZFMT_STYLE_ISO_BASIC_SHORT
:
720 formatOffsetISO8601Basic(offset
, TRUE
, TRUE
, TRUE
, name
, status
);
723 case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
:
724 formatOffsetISO8601Basic(offset
, FALSE
, TRUE
, TRUE
, name
, status
);
727 case UTZFMT_STYLE_ISO_BASIC_FIXED
:
728 formatOffsetISO8601Basic(offset
, TRUE
, FALSE
, TRUE
, name
, status
);
731 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
:
732 formatOffsetISO8601Basic(offset
, FALSE
, FALSE
, TRUE
, name
, status
);
735 case UTZFMT_STYLE_ISO_EXTENDED_FIXED
:
736 formatOffsetISO8601Extended(offset
, TRUE
, FALSE
, TRUE
, name
, status
);
739 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
:
740 formatOffsetISO8601Extended(offset
, FALSE
, FALSE
, TRUE
, name
, status
);
743 case UTZFMT_STYLE_ISO_BASIC_FULL
:
744 formatOffsetISO8601Basic(offset
, TRUE
, FALSE
, FALSE
, name
, status
);
747 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
:
748 formatOffsetISO8601Basic(offset
, FALSE
, FALSE
, FALSE
, name
, status
);
751 case UTZFMT_STYLE_ISO_EXTENDED_FULL
:
752 formatOffsetISO8601Extended(offset
, TRUE
, FALSE
, FALSE
, name
, status
);
755 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
:
756 formatOffsetISO8601Extended(offset
, FALSE
, FALSE
, FALSE
, name
, status
);
760 // UTZFMT_STYLE_ZONE_ID, UTZFMT_STYLE_ZONE_ID_SHORT, UTZFMT_STYLE_EXEMPLAR_LOCATION
765 *timeType
= (dstOffset
!= 0) ? UTZFMT_TIME_TYPE_DAYLIGHT
: UTZFMT_TIME_TYPE_STANDARD
;
774 TimeZoneFormat::format(const Formattable
& obj
, UnicodeString
& appendTo
,
775 FieldPosition
& pos
, UErrorCode
& status
) const {
776 if (U_FAILURE(status
)) {
779 UDate date
= Calendar::getNow();
780 if (obj
.getType() == Formattable::kObject
) {
781 const UObject
* formatObj
= obj
.getObject();
782 const TimeZone
* tz
= dynamic_cast<const TimeZone
*>(formatObj
);
784 const Calendar
* cal
= dynamic_cast<const Calendar
*>(formatObj
);
786 tz
= &cal
->getTimeZone();
787 date
= cal
->getTime(status
);
791 int32_t rawOffset
, dstOffset
;
792 tz
->getOffset(date
, FALSE
, rawOffset
, dstOffset
, status
);
793 UChar buf
[ZONE_NAME_U16_MAX
];
794 UnicodeString
result(buf
, 0, UPRV_LENGTHOF(buf
));
795 formatOffsetLocalizedGMT(rawOffset
+ dstOffset
, result
, status
);
796 if (U_SUCCESS(status
)) {
797 appendTo
.append(result
);
798 if (pos
.getField() == UDAT_TIMEZONE_FIELD
) {
799 pos
.setBeginIndex(0);
800 pos
.setEndIndex(result
.length());
809 TimeZoneFormat::parse(UTimeZoneFormatStyle style
, const UnicodeString
& text
, ParsePosition
& pos
,
810 UTimeZoneFormatTimeType
* timeType
/*= NULL*/) const {
811 return parse(style
, text
, pos
, getDefaultParseOptions(), timeType
);
815 TimeZoneFormat::parse(UTimeZoneFormatStyle style
, const UnicodeString
& text
, ParsePosition
& pos
,
816 int32_t parseOptions
, UTimeZoneFormatTimeType
* timeType
/* = NULL */) const {
818 *timeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
821 int32_t startIdx
= pos
.getIndex();
822 int32_t maxPos
= text
.length();
825 // Styles using localized GMT format as fallback
826 UBool fallbackLocalizedGMT
=
827 (style
== UTZFMT_STYLE_SPECIFIC_LONG
|| style
== UTZFMT_STYLE_GENERIC_LONG
|| style
== UTZFMT_STYLE_GENERIC_LOCATION
);
828 UBool fallbackShortLocalizedGMT
=
829 (style
== UTZFMT_STYLE_SPECIFIC_SHORT
|| style
== UTZFMT_STYLE_GENERIC_SHORT
);
831 int32_t evaluated
= 0; // bit flags representing already evaluated styles
832 ParsePosition
tmpPos(startIdx
);
834 int32_t parsedOffset
= UNKNOWN_OFFSET
; // stores successfully parsed offset for later use
835 int32_t parsedPos
= -1; // stores successfully parsed offset position for later use
837 // Try localized GMT format first if necessary
838 if (fallbackLocalizedGMT
|| fallbackShortLocalizedGMT
) {
839 UBool hasDigitOffset
= FALSE
;
840 offset
= parseOffsetLocalizedGMT(text
, tmpPos
, fallbackShortLocalizedGMT
, &hasDigitOffset
);
841 if (tmpPos
.getErrorIndex() == -1) {
842 // Even when the input text was successfully parsed as a localized GMT format text,
843 // we may still need to evaluate the specified style if -
844 // 1) GMT zero format was used, and
845 // 2) The input text was not completely processed
846 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
847 pos
.setIndex(tmpPos
.getIndex());
848 return createTimeZoneForOffset(offset
);
850 parsedOffset
= offset
;
851 parsedPos
= tmpPos
.getIndex();
853 // Note: For now, no distinction between long/short localized GMT format in the parser.
854 // This might be changed in future.
855 // evaluated |= (fallbackLocalizedGMT ? STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] : STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]);
856 evaluated
|= STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT
] | STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT_SHORT
];
859 UErrorCode status
= U_ZERO_ERROR
;
861 UnicodeString
tzID(tzIDBuf
, 0, UPRV_LENGTHOF(tzIDBuf
));
863 UBool parseTZDBAbbrev
= ((parseOptions
& UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
) != 0);
865 // Try the specified style
867 case UTZFMT_STYLE_LOCALIZED_GMT
:
869 tmpPos
.setIndex(startIdx
);
870 tmpPos
.setErrorIndex(-1);
872 offset
= parseOffsetLocalizedGMT(text
, tmpPos
);
873 if (tmpPos
.getErrorIndex() == -1) {
874 pos
.setIndex(tmpPos
.getIndex());
875 return createTimeZoneForOffset(offset
);
878 // Note: For now, no distinction between long/short localized GMT format in the parser.
879 // This might be changed in future.
880 evaluated
|= STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT_SHORT
];
884 case UTZFMT_STYLE_LOCALIZED_GMT_SHORT
:
886 tmpPos
.setIndex(startIdx
);
887 tmpPos
.setErrorIndex(-1);
889 offset
= parseOffsetShortLocalizedGMT(text
, tmpPos
);
890 if (tmpPos
.getErrorIndex() == -1) {
891 pos
.setIndex(tmpPos
.getIndex());
892 return createTimeZoneForOffset(offset
);
895 // Note: For now, no distinction between long/short localized GMT format in the parser.
896 // This might be changed in future.
897 evaluated
|= STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT
];
901 case UTZFMT_STYLE_ISO_BASIC_SHORT
:
902 case UTZFMT_STYLE_ISO_BASIC_FIXED
:
903 case UTZFMT_STYLE_ISO_BASIC_FULL
:
904 case UTZFMT_STYLE_ISO_EXTENDED_FIXED
:
905 case UTZFMT_STYLE_ISO_EXTENDED_FULL
:
907 tmpPos
.setIndex(startIdx
);
908 tmpPos
.setErrorIndex(-1);
910 offset
= parseOffsetISO8601(text
, tmpPos
);
911 if (tmpPos
.getErrorIndex() == -1) {
912 pos
.setIndex(tmpPos
.getIndex());
913 return createTimeZoneForOffset(offset
);
919 case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
:
920 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
:
921 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
:
922 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
:
923 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
:
925 tmpPos
.setIndex(startIdx
);
926 tmpPos
.setErrorIndex(-1);
928 // Exclude the case of UTC Indicator "Z" here
929 UBool hasDigitOffset
= FALSE
;
930 offset
= parseOffsetISO8601(text
, tmpPos
, FALSE
, &hasDigitOffset
);
931 if (tmpPos
.getErrorIndex() == -1 && hasDigitOffset
) {
932 pos
.setIndex(tmpPos
.getIndex());
933 return createTimeZoneForOffset(offset
);
939 case UTZFMT_STYLE_SPECIFIC_LONG
:
940 case UTZFMT_STYLE_SPECIFIC_SHORT
:
943 int32_t nameTypes
= 0;
944 if (style
== UTZFMT_STYLE_SPECIFIC_LONG
) {
945 nameTypes
= (UTZNM_LONG_STANDARD
| UTZNM_LONG_DAYLIGHT
);
947 U_ASSERT(style
== UTZFMT_STYLE_SPECIFIC_SHORT
);
948 nameTypes
= (UTZNM_SHORT_STANDARD
| UTZNM_SHORT_DAYLIGHT
);
950 LocalPointer
<TimeZoneNames::MatchInfoCollection
> specificMatches(fTimeZoneNames
->find(text
, startIdx
, nameTypes
, status
));
951 if (U_FAILURE(status
)) {
952 pos
.setErrorIndex(startIdx
);
955 if (!specificMatches
.isNull()) {
956 int32_t matchIdx
= -1;
957 int32_t matchPos
= -1;
958 for (int32_t i
= 0; i
< specificMatches
->size(); i
++) {
959 matchPos
= startIdx
+ specificMatches
->getMatchLengthAt(i
);
960 if (matchPos
> parsedPos
) {
962 parsedPos
= matchPos
;
967 *timeType
= getTimeType(specificMatches
->getNameTypeAt(matchIdx
));
969 pos
.setIndex(matchPos
);
970 getTimeZoneID(specificMatches
.getAlias(), matchIdx
, tzID
);
971 U_ASSERT(!tzID
.isEmpty());
972 return TimeZone::createTimeZone(tzID
);
976 if (parseTZDBAbbrev
&& style
== UTZFMT_STYLE_SPECIFIC_SHORT
) {
977 U_ASSERT((nameTypes
& UTZNM_SHORT_STANDARD
) != 0);
978 U_ASSERT((nameTypes
& UTZNM_SHORT_DAYLIGHT
) != 0);
980 const TZDBTimeZoneNames
*tzdbTimeZoneNames
= getTZDBTimeZoneNames(status
);
981 if (U_SUCCESS(status
)) {
982 LocalPointer
<TimeZoneNames::MatchInfoCollection
> tzdbNameMatches(
983 tzdbTimeZoneNames
->find(text
, startIdx
, nameTypes
, status
));
984 if (U_FAILURE(status
)) {
985 pos
.setErrorIndex(startIdx
);
988 if (!tzdbNameMatches
.isNull()) {
989 int32_t matchIdx
= -1;
990 int32_t matchPos
= -1;
991 for (int32_t i
= 0; i
< tzdbNameMatches
->size(); i
++) {
992 matchPos
= startIdx
+ tzdbNameMatches
->getMatchLengthAt(i
);
993 if (matchPos
> parsedPos
) {
995 parsedPos
= matchPos
;
1000 *timeType
= getTimeType(tzdbNameMatches
->getNameTypeAt(matchIdx
));
1002 pos
.setIndex(matchPos
);
1003 getTimeZoneID(tzdbNameMatches
.getAlias(), matchIdx
, tzID
);
1004 U_ASSERT(!tzID
.isEmpty());
1005 return TimeZone::createTimeZone(tzID
);
1012 case UTZFMT_STYLE_GENERIC_LONG
:
1013 case UTZFMT_STYLE_GENERIC_SHORT
:
1014 case UTZFMT_STYLE_GENERIC_LOCATION
:
1016 int32_t genericNameTypes
= 0;
1018 case UTZFMT_STYLE_GENERIC_LOCATION
:
1019 genericNameTypes
= UTZGNM_LOCATION
;
1022 case UTZFMT_STYLE_GENERIC_LONG
:
1023 genericNameTypes
= UTZGNM_LONG
| UTZGNM_LOCATION
;
1026 case UTZFMT_STYLE_GENERIC_SHORT
:
1027 genericNameTypes
= UTZGNM_SHORT
| UTZGNM_LOCATION
;
1035 UTimeZoneFormatTimeType tt
= UTZFMT_TIME_TYPE_UNKNOWN
;
1036 const TimeZoneGenericNames
*gnames
= getTimeZoneGenericNames(status
);
1037 if (U_SUCCESS(status
)) {
1038 len
= gnames
->findBestMatch(text
, startIdx
, genericNameTypes
, tzID
, tt
, status
);
1040 if (U_FAILURE(status
)) {
1041 pos
.setErrorIndex(startIdx
);
1049 pos
.setIndex(startIdx
+ len
);
1050 U_ASSERT(!tzID
.isEmpty());
1051 return TimeZone::createTimeZone(tzID
);
1056 case UTZFMT_STYLE_ZONE_ID
:
1058 tmpPos
.setIndex(startIdx
);
1059 tmpPos
.setErrorIndex(-1);
1061 parseZoneID(text
, tmpPos
, tzID
);
1062 if (tmpPos
.getErrorIndex() == -1) {
1063 pos
.setIndex(tmpPos
.getIndex());
1064 return TimeZone::createTimeZone(tzID
);
1068 case UTZFMT_STYLE_ZONE_ID_SHORT
:
1070 tmpPos
.setIndex(startIdx
);
1071 tmpPos
.setErrorIndex(-1);
1073 parseShortZoneID(text
, tmpPos
, tzID
);
1074 if (tmpPos
.getErrorIndex() == -1) {
1075 pos
.setIndex(tmpPos
.getIndex());
1076 return TimeZone::createTimeZone(tzID
);
1080 case UTZFMT_STYLE_EXEMPLAR_LOCATION
:
1082 tmpPos
.setIndex(startIdx
);
1083 tmpPos
.setErrorIndex(-1);
1085 parseExemplarLocation(text
, tmpPos
, tzID
);
1086 if (tmpPos
.getErrorIndex() == -1) {
1087 pos
.setIndex(tmpPos
.getIndex());
1088 return TimeZone::createTimeZone(tzID
);
1093 evaluated
|= STYLE_PARSE_FLAGS
[style
];
1096 if (parsedPos
> startIdx
) {
1097 // When the specified style is one of SPECIFIC_XXX or GENERIC_XXX, we tried to parse the input
1098 // as localized GMT format earlier. If parsedOffset is positive, it means it was successfully
1099 // parsed as localized GMT format, but offset digits were not detected (more specifically, GMT
1100 // zero format). Then, it tried to find a match within the set of display names, but could not
1101 // find a match. At this point, we can safely assume the input text contains the localized
1103 U_ASSERT(parsedOffset
!= UNKNOWN_OFFSET
);
1104 pos
.setIndex(parsedPos
);
1105 return createTimeZoneForOffset(parsedOffset
);
1108 // Failed to parse the input text as the time zone format in the specified style.
1109 // Check the longest match among other styles below.
1110 UChar parsedIDBuf
[32];
1111 UnicodeString
parsedID(parsedIDBuf
, 0, UPRV_LENGTHOF(parsedIDBuf
));
1112 UTimeZoneFormatTimeType parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1114 U_ASSERT(parsedPos
< 0);
1115 U_ASSERT(parsedOffset
== UNKNOWN_OFFSET
);
1118 if (parsedPos
< maxPos
&&
1119 ((evaluated
& ISO_Z_STYLE_FLAG
) == 0 || (evaluated
& ISO_LOCAL_STYLE_FLAG
) == 0)) {
1120 tmpPos
.setIndex(startIdx
);
1121 tmpPos
.setErrorIndex(-1);
1123 UBool hasDigitOffset
= FALSE
;
1124 offset
= parseOffsetISO8601(text
, tmpPos
, FALSE
, &hasDigitOffset
);
1125 if (tmpPos
.getErrorIndex() == -1) {
1126 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
1127 pos
.setIndex(tmpPos
.getIndex());
1128 return createTimeZoneForOffset(offset
);
1130 // Note: When ISO 8601 format contains offset digits, it should not
1131 // collide with other formats. However, ISO 8601 UTC format "Z" (single letter)
1132 // may collide with other names. In this case, we need to evaluate other names.
1133 if (parsedPos
< tmpPos
.getIndex()) {
1134 parsedOffset
= offset
;
1135 parsedID
.setToBogus();
1136 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1137 parsedPos
= tmpPos
.getIndex();
1138 U_ASSERT(parsedPos
== startIdx
+ 1); // only when "Z" is used
1143 // Localized GMT format
1144 if (parsedPos
< maxPos
&&
1145 (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT
]) == 0) {
1146 tmpPos
.setIndex(startIdx
);
1147 tmpPos
.setErrorIndex(-1);
1149 UBool hasDigitOffset
= FALSE
;
1150 offset
= parseOffsetLocalizedGMT(text
, tmpPos
, FALSE
, &hasDigitOffset
);
1151 if (tmpPos
.getErrorIndex() == -1) {
1152 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
1153 pos
.setIndex(tmpPos
.getIndex());
1154 return createTimeZoneForOffset(offset
);
1156 // Evaluate other names - see the comment earlier in this method.
1157 if (parsedPos
< tmpPos
.getIndex()) {
1158 parsedOffset
= offset
;
1159 parsedID
.setToBogus();
1160 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1161 parsedPos
= tmpPos
.getIndex();
1166 if (parsedPos
< maxPos
&&
1167 (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT_SHORT
]) == 0) {
1168 tmpPos
.setIndex(startIdx
);
1169 tmpPos
.setErrorIndex(-1);
1171 UBool hasDigitOffset
= FALSE
;
1172 offset
= parseOffsetLocalizedGMT(text
, tmpPos
, TRUE
, &hasDigitOffset
);
1173 if (tmpPos
.getErrorIndex() == -1) {
1174 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
1175 pos
.setIndex(tmpPos
.getIndex());
1176 return createTimeZoneForOffset(offset
);
1178 // Evaluate other names - see the comment earlier in this method.
1179 if (parsedPos
< tmpPos
.getIndex()) {
1180 parsedOffset
= offset
;
1181 parsedID
.setToBogus();
1182 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1183 parsedPos
= tmpPos
.getIndex();
1188 // When ParseOption.ALL_STYLES is available, we also try to look all possible display names and IDs.
1189 // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never
1190 // used for America/New_York. With parseAllStyles true, this code parses "EST"
1191 // as America/New_York.
1193 // Note: Adding all possible names into the trie used by the implementation is quite heavy operation,
1194 // which we want to avoid normally (note that we cache the trie, so this is applicable to the
1195 // first time only as long as the cache does not expire).
1197 if (parseOptions
& UTZFMT_PARSE_OPTION_ALL_STYLES
) {
1198 // Try all specific names and exemplar location names
1199 if (parsedPos
< maxPos
) {
1200 LocalPointer
<TimeZoneNames::MatchInfoCollection
> specificMatches(fTimeZoneNames
->find(text
, startIdx
, ALL_SIMPLE_NAME_TYPES
, status
));
1201 if (U_FAILURE(status
)) {
1202 pos
.setErrorIndex(startIdx
);
1205 int32_t specificMatchIdx
= -1;
1206 int32_t matchPos
= -1;
1207 if (!specificMatches
.isNull()) {
1208 for (int32_t i
= 0; i
< specificMatches
->size(); i
++) {
1209 if (startIdx
+ specificMatches
->getMatchLengthAt(i
) > matchPos
) {
1210 specificMatchIdx
= i
;
1211 matchPos
= startIdx
+ specificMatches
->getMatchLengthAt(i
);
1215 if (parsedPos
< matchPos
) {
1216 U_ASSERT(specificMatchIdx
>= 0);
1217 parsedPos
= matchPos
;
1218 getTimeZoneID(specificMatches
.getAlias(), specificMatchIdx
, parsedID
);
1219 parsedTimeType
= getTimeType(specificMatches
->getNameTypeAt(specificMatchIdx
));
1220 parsedOffset
= UNKNOWN_OFFSET
;
1223 if (parseTZDBAbbrev
&& parsedPos
< maxPos
&& (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_SPECIFIC_SHORT
]) == 0) {
1224 const TZDBTimeZoneNames
*tzdbTimeZoneNames
= getTZDBTimeZoneNames(status
);
1225 if (U_SUCCESS(status
)) {
1226 LocalPointer
<TimeZoneNames::MatchInfoCollection
> tzdbNameMatches(
1227 tzdbTimeZoneNames
->find(text
, startIdx
, ALL_SIMPLE_NAME_TYPES
, status
));
1228 if (U_FAILURE(status
)) {
1229 pos
.setErrorIndex(startIdx
);
1232 int32_t tzdbNameMatchIdx
= -1;
1233 int32_t matchPos
= -1;
1234 if (!tzdbNameMatches
.isNull()) {
1235 for (int32_t i
= 0; i
< tzdbNameMatches
->size(); i
++) {
1236 if (startIdx
+ tzdbNameMatches
->getMatchLengthAt(i
) > matchPos
) {
1237 tzdbNameMatchIdx
= i
;
1238 matchPos
= startIdx
+ tzdbNameMatches
->getMatchLengthAt(i
);
1242 if (parsedPos
< matchPos
) {
1243 U_ASSERT(tzdbNameMatchIdx
>= 0);
1244 parsedPos
= matchPos
;
1245 getTimeZoneID(tzdbNameMatches
.getAlias(), tzdbNameMatchIdx
, parsedID
);
1246 parsedTimeType
= getTimeType(tzdbNameMatches
->getNameTypeAt(tzdbNameMatchIdx
));
1247 parsedOffset
= UNKNOWN_OFFSET
;
1251 // Try generic names
1252 if (parsedPos
< maxPos
) {
1253 int32_t genMatchLen
= -1;
1254 UTimeZoneFormatTimeType tt
= UTZFMT_TIME_TYPE_UNKNOWN
;
1256 const TimeZoneGenericNames
*gnames
= getTimeZoneGenericNames(status
);
1257 if (U_SUCCESS(status
)) {
1258 genMatchLen
= gnames
->findBestMatch(text
, startIdx
, ALL_GENERIC_NAME_TYPES
, tzID
, tt
, status
);
1260 if (U_FAILURE(status
)) {
1261 pos
.setErrorIndex(startIdx
);
1265 if (genMatchLen
> 0 && parsedPos
< startIdx
+ genMatchLen
) {
1266 parsedPos
= startIdx
+ genMatchLen
;
1267 parsedID
.setTo(tzID
);
1268 parsedTimeType
= tt
;
1269 parsedOffset
= UNKNOWN_OFFSET
;
1274 if (parsedPos
< maxPos
&& (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_ZONE_ID
]) == 0) {
1275 tmpPos
.setIndex(startIdx
);
1276 tmpPos
.setErrorIndex(-1);
1278 parseZoneID(text
, tmpPos
, tzID
);
1279 if (tmpPos
.getErrorIndex() == -1 && parsedPos
< tmpPos
.getIndex()) {
1280 parsedPos
= tmpPos
.getIndex();
1281 parsedID
.setTo(tzID
);
1282 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1283 parsedOffset
= UNKNOWN_OFFSET
;
1286 // Try short time zone ID
1287 if (parsedPos
< maxPos
&& (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_ZONE_ID
]) == 0) {
1288 tmpPos
.setIndex(startIdx
);
1289 tmpPos
.setErrorIndex(-1);
1291 parseShortZoneID(text
, tmpPos
, tzID
);
1292 if (tmpPos
.getErrorIndex() == -1 && parsedPos
< tmpPos
.getIndex()) {
1293 parsedPos
= tmpPos
.getIndex();
1294 parsedID
.setTo(tzID
);
1295 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1296 parsedOffset
= UNKNOWN_OFFSET
;
1301 if (parsedPos
> startIdx
) {
1302 // Parsed successfully
1304 if (parsedID
.length() > 0) {
1305 parsedTZ
= TimeZone::createTimeZone(parsedID
);
1307 U_ASSERT(parsedOffset
!= UNKNOWN_OFFSET
);
1308 parsedTZ
= createTimeZoneForOffset(parsedOffset
);
1311 *timeType
= parsedTimeType
;
1313 pos
.setIndex(parsedPos
);
1317 pos
.setErrorIndex(startIdx
);
1322 TimeZoneFormat::parseObject(const UnicodeString
& source
, Formattable
& result
,
1323 ParsePosition
& parse_pos
) const {
1324 result
.adoptObject(parse(UTZFMT_STYLE_GENERIC_LOCATION
, source
, parse_pos
, UTZFMT_PARSE_OPTION_ALL_STYLES
));
1328 // ------------------------------------------------------------------
1329 // Private zone name format/parse implementation
1332 TimeZoneFormat::formatGeneric(const TimeZone
& tz
, int32_t genType
, UDate date
, UnicodeString
& name
) const {
1333 UErrorCode status
= U_ZERO_ERROR
;
1334 const TimeZoneGenericNames
* gnames
= getTimeZoneGenericNames(status
);
1335 if (U_FAILURE(status
)) {
1340 if (genType
== UTZGNM_LOCATION
) {
1341 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(tz
);
1342 if (canonicalID
== NULL
) {
1346 return gnames
->getGenericLocationName(UnicodeString(TRUE
, canonicalID
, -1), name
);
1348 return gnames
->getDisplayName(tz
, (UTimeZoneGenericNameType
)genType
, date
, name
);
1352 TimeZoneFormat::formatSpecific(const TimeZone
& tz
, UTimeZoneNameType stdType
, UTimeZoneNameType dstType
,
1353 UDate date
, UnicodeString
& name
, UTimeZoneFormatTimeType
*timeType
) const {
1354 if (fTimeZoneNames
== NULL
) {
1359 UErrorCode status
= U_ZERO_ERROR
;
1360 UBool isDaylight
= tz
.inDaylightTime(date
, status
);
1361 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(tz
);
1363 if (U_FAILURE(status
) || canonicalID
== NULL
) {
1369 fTimeZoneNames
->getDisplayName(UnicodeString(TRUE
, canonicalID
, -1), dstType
, date
, name
);
1371 fTimeZoneNames
->getDisplayName(UnicodeString(TRUE
, canonicalID
, -1), stdType
, date
, name
);
1374 if (timeType
&& !name
.isEmpty()) {
1375 *timeType
= isDaylight
? UTZFMT_TIME_TYPE_DAYLIGHT
: UTZFMT_TIME_TYPE_STANDARD
;
1380 const TimeZoneGenericNames
*
1381 TimeZoneFormat::getTimeZoneGenericNames(UErrorCode
& status
) const {
1382 if (U_FAILURE(status
)) {
1387 if (fTimeZoneGenericNames
== NULL
) {
1388 TimeZoneFormat
*nonConstThis
= const_cast<TimeZoneFormat
*>(this);
1389 nonConstThis
->fTimeZoneGenericNames
= TimeZoneGenericNames::createInstance(fLocale
, status
);
1391 umtx_unlock(&gLock
);
1393 return fTimeZoneGenericNames
;
1396 const TZDBTimeZoneNames
*
1397 TimeZoneFormat::getTZDBTimeZoneNames(UErrorCode
& status
) const {
1398 if (U_FAILURE(status
)) {
1403 if (fTZDBTimeZoneNames
== NULL
) {
1404 TZDBTimeZoneNames
*tzdbNames
= new TZDBTimeZoneNames(fLocale
);
1405 if (tzdbNames
== NULL
) {
1406 status
= U_MEMORY_ALLOCATION_ERROR
;
1408 TimeZoneFormat
*nonConstThis
= const_cast<TimeZoneFormat
*>(this);
1409 nonConstThis
->fTZDBTimeZoneNames
= tzdbNames
;
1412 umtx_unlock(&gLock
);
1414 return fTZDBTimeZoneNames
;
1418 TimeZoneFormat::formatExemplarLocation(const TimeZone
& tz
, UnicodeString
& name
) const {
1419 UChar locationBuf
[ZONE_NAME_U16_MAX
];
1420 UnicodeString
location(locationBuf
, 0, UPRV_LENGTHOF(locationBuf
));
1421 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(tz
);
1424 fTimeZoneNames
->getExemplarLocationName(UnicodeString(TRUE
, canonicalID
, -1), location
);
1426 if (location
.length() > 0) {
1427 name
.setTo(location
);
1429 // Use "unknown" location
1430 fTimeZoneNames
->getExemplarLocationName(UnicodeString(TRUE
, UNKNOWN_ZONE_ID
, -1), location
);
1431 if (location
.length() > 0) {
1432 name
.setTo(location
);
1435 name
.setTo(UNKNOWN_LOCATION
, -1);
1442 // ------------------------------------------------------------------
1443 // Zone offset format and parse
1446 TimeZoneFormat::formatOffsetISO8601Basic(int32_t offset
, UBool useUtcIndicator
, UBool isShort
, UBool ignoreSeconds
,
1447 UnicodeString
& result
, UErrorCode
& status
) const {
1448 return formatOffsetISO8601(offset
, TRUE
, useUtcIndicator
, isShort
, ignoreSeconds
, result
, status
);
1452 TimeZoneFormat::formatOffsetISO8601Extended(int32_t offset
, UBool useUtcIndicator
, UBool isShort
, UBool ignoreSeconds
,
1453 UnicodeString
& result
, UErrorCode
& status
) const {
1454 return formatOffsetISO8601(offset
, FALSE
, useUtcIndicator
, isShort
, ignoreSeconds
, result
, status
);
1458 TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset
, UnicodeString
& result
, UErrorCode
& status
) const {
1459 return formatOffsetLocalizedGMT(offset
, FALSE
, result
, status
);
1463 TimeZoneFormat::formatOffsetShortLocalizedGMT(int32_t offset
, UnicodeString
& result
, UErrorCode
& status
) const {
1464 return formatOffsetLocalizedGMT(offset
, TRUE
, result
, status
);
1468 TimeZoneFormat::parseOffsetISO8601(const UnicodeString
& text
, ParsePosition
& pos
) const {
1469 return parseOffsetISO8601(text
, pos
, FALSE
);
1473 TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString
& text
, ParsePosition
& pos
) const {
1474 return parseOffsetLocalizedGMT(text
, pos
, FALSE
, NULL
);
1478 TimeZoneFormat::parseOffsetShortLocalizedGMT(const UnicodeString
& text
, ParsePosition
& pos
) const {
1479 return parseOffsetLocalizedGMT(text
, pos
, TRUE
, NULL
);
1482 // ------------------------------------------------------------------
1483 // Private zone offset format/parse implementation
1486 TimeZoneFormat::formatOffsetISO8601(int32_t offset
, UBool isBasic
, UBool useUtcIndicator
,
1487 UBool isShort
, UBool ignoreSeconds
, UnicodeString
& result
, UErrorCode
& status
) const {
1488 if (U_FAILURE(status
)) {
1489 result
.setToBogus();
1492 int32_t absOffset
= offset
< 0 ? -offset
: offset
;
1493 if (useUtcIndicator
&& (absOffset
< MILLIS_PER_SECOND
|| (ignoreSeconds
&& absOffset
< MILLIS_PER_MINUTE
))) {
1494 result
.setTo(ISO8601_UTC
);
1498 OffsetFields minFields
= isShort
? FIELDS_H
: FIELDS_HM
;
1499 OffsetFields maxFields
= ignoreSeconds
? FIELDS_HM
: FIELDS_HMS
;
1500 UChar sep
= isBasic
? 0 : ISO8601_SEP
;
1502 // Note: FIELDS_HMS as maxFields is a CLDR/ICU extension. ISO 8601 specification does
1503 // not support seconds field.
1505 if (absOffset
>= MAX_OFFSET
) {
1506 result
.setToBogus();
1507 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1512 fields
[0] = absOffset
/ MILLIS_PER_HOUR
;
1513 absOffset
= absOffset
% MILLIS_PER_HOUR
;
1514 fields
[1] = absOffset
/ MILLIS_PER_MINUTE
;
1515 absOffset
= absOffset
% MILLIS_PER_MINUTE
;
1516 fields
[2] = absOffset
/ MILLIS_PER_SECOND
;
1518 U_ASSERT(fields
[0] >= 0 && fields
[0] <= MAX_OFFSET_HOUR
);
1519 U_ASSERT(fields
[1] >= 0 && fields
[1] <= MAX_OFFSET_MINUTE
);
1520 U_ASSERT(fields
[2] >= 0 && fields
[2] <= MAX_OFFSET_SECOND
);
1522 int32_t lastIdx
= maxFields
;
1523 while (lastIdx
> minFields
) {
1524 if (fields
[lastIdx
] != 0) {
1532 // if all output fields are 0s, do not use negative sign
1533 for (int32_t idx
= 0; idx
<= lastIdx
; idx
++) {
1534 if (fields
[idx
] != 0) {
1542 for (int32_t idx
= 0; idx
<= lastIdx
; idx
++) {
1543 if (sep
&& idx
!= 0) {
1546 result
.append((UChar
)(0x0030 + fields
[idx
]/10));
1547 result
.append((UChar
)(0x0030 + fields
[idx
]%10
));
1554 TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset
, UBool isShort
, UnicodeString
& result
, UErrorCode
& status
) const {
1555 if (U_FAILURE(status
)) {
1556 result
.setToBogus();
1559 if (offset
<= -MAX_OFFSET
|| offset
>= MAX_OFFSET
) {
1560 result
.setToBogus();
1561 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1566 result
.setTo(fGMTZeroFormat
);
1570 UBool positive
= TRUE
;
1576 int32_t offsetH
= offset
/ MILLIS_PER_HOUR
;
1577 offset
= offset
% MILLIS_PER_HOUR
;
1578 int32_t offsetM
= offset
/ MILLIS_PER_MINUTE
;
1579 offset
= offset
% MILLIS_PER_MINUTE
;
1580 int32_t offsetS
= offset
/ MILLIS_PER_SECOND
;
1582 U_ASSERT(offsetH
<= MAX_OFFSET_HOUR
&& offsetM
<= MAX_OFFSET_MINUTE
&& offsetS
<= MAX_OFFSET_SECOND
);
1584 const UVector
* offsetPatternItems
= NULL
;
1587 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_POSITIVE_HMS
];
1588 } else if (offsetM
!= 0 || !isShort
) {
1589 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_POSITIVE_HM
];
1591 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_POSITIVE_H
];
1595 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_NEGATIVE_HMS
];
1596 } else if (offsetM
!= 0 || !isShort
) {
1597 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_NEGATIVE_HM
];
1599 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_NEGATIVE_H
];
1603 U_ASSERT(offsetPatternItems
!= NULL
);
1605 // Building the GMT format string
1606 result
.setTo(fGMTPatternPrefix
);
1608 for (int32_t i
= 0; i
< offsetPatternItems
->size(); i
++) {
1609 const GMTOffsetField
* item
= (GMTOffsetField
*)offsetPatternItems
->elementAt(i
);
1610 GMTOffsetField::FieldType type
= item
->getType();
1613 case GMTOffsetField::TEXT
:
1614 result
.append(item
->getPatternText(), -1);
1617 case GMTOffsetField::HOUR
:
1618 appendOffsetDigits(result
, offsetH
, (isShort
? 1 : 2));
1621 case GMTOffsetField::MINUTE
:
1622 appendOffsetDigits(result
, offsetM
, 2);
1625 case GMTOffsetField::SECOND
:
1626 appendOffsetDigits(result
, offsetS
, 2);
1631 result
.append(fGMTPatternSuffix
);
1636 TimeZoneFormat::parseOffsetISO8601(const UnicodeString
& text
, ParsePosition
& pos
, UBool extendedOnly
, UBool
* hasDigitOffset
/* = NULL */) const {
1637 if (hasDigitOffset
) {
1638 *hasDigitOffset
= FALSE
;
1640 int32_t start
= pos
.getIndex();
1641 if (start
>= text
.length()) {
1642 pos
.setErrorIndex(start
);
1646 UChar firstChar
= text
.charAt(start
);
1647 if (firstChar
== ISO8601_UTC
|| firstChar
== (UChar
)(ISO8601_UTC
+ 0x20)) {
1648 // "Z" (or "z") - indicates UTC
1649 pos
.setIndex(start
+ 1);
1654 if (firstChar
== PLUS
) {
1656 } else if (firstChar
== MINUS
) {
1659 // Not an ISO 8601 offset string
1660 pos
.setErrorIndex(start
);
1663 ParsePosition
posOffset(start
+ 1);
1664 int32_t offset
= parseAsciiOffsetFields(text
, posOffset
, ISO8601_SEP
, FIELDS_H
, FIELDS_HMS
);
1665 if (posOffset
.getErrorIndex() == -1 && !extendedOnly
&& (posOffset
.getIndex() - start
<= 3)) {
1666 // If the text is successfully parsed as extended format with the options above, it can be also parsed
1667 // as basic format. For example, "0230" can be parsed as offset 2:00 (only first digits are valid for
1668 // extended format), but it can be parsed as offset 2:30 with basic format. We use longer result.
1669 ParsePosition
posBasic(start
+ 1);
1670 int32_t tmpOffset
= parseAbuttingAsciiOffsetFields(text
, posBasic
, FIELDS_H
, FIELDS_HMS
, FALSE
);
1671 if (posBasic
.getErrorIndex() == -1 && posBasic
.getIndex() > posOffset
.getIndex()) {
1673 posOffset
.setIndex(posBasic
.getIndex());
1677 if (posOffset
.getErrorIndex() != -1) {
1678 pos
.setErrorIndex(start
);
1682 pos
.setIndex(posOffset
.getIndex());
1683 if (hasDigitOffset
) {
1684 *hasDigitOffset
= TRUE
;
1686 return sign
* offset
;
1690 TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString
& text
, ParsePosition
& pos
, UBool isShort
, UBool
* hasDigitOffset
) const {
1691 int32_t start
= pos
.getIndex();
1693 int32_t parsedLength
= 0;
1695 if (hasDigitOffset
) {
1696 *hasDigitOffset
= FALSE
;
1699 offset
= parseOffsetLocalizedGMTPattern(text
, start
, isShort
, parsedLength
);
1701 // For now, parseOffsetLocalizedGMTPattern handles both long and short
1702 // formats, no matter isShort is true or false. This might be changed in future
1703 // when strict parsing is necessary, or different set of patterns are used for
1704 // short/long formats.
1706 if (parsedLength
== 0) {
1707 offset
= parseOffsetLocalizedGMTPattern(text
, start
, !isShort
, parsedLength
);
1711 if (parsedLength
> 0) {
1712 if (hasDigitOffset
) {
1713 *hasDigitOffset
= TRUE
;
1715 pos
.setIndex(start
+ parsedLength
);
1719 // Try the default patterns
1720 offset
= parseOffsetDefaultLocalizedGMT(text
, start
, parsedLength
);
1721 if (parsedLength
> 0) {
1722 if (hasDigitOffset
) {
1723 *hasDigitOffset
= TRUE
;
1725 pos
.setIndex(start
+ parsedLength
);
1729 // Check if this is a GMT zero format
1730 if (text
.caseCompare(start
, fGMTZeroFormat
.length(), fGMTZeroFormat
, 0) == 0) {
1731 pos
.setIndex(start
+ fGMTZeroFormat
.length());
1735 // Check if this is a default GMT zero format
1736 for (int32_t i
= 0; ALT_GMT_STRINGS
[i
][0] != 0; i
++) {
1737 const UChar
* defGMTZero
= ALT_GMT_STRINGS
[i
];
1738 int32_t defGMTZeroLen
= u_strlen(defGMTZero
);
1739 if (text
.caseCompare(start
, defGMTZeroLen
, defGMTZero
, 0) == 0) {
1740 pos
.setIndex(start
+ defGMTZeroLen
);
1746 pos
.setErrorIndex(start
);
1751 TimeZoneFormat::parseOffsetLocalizedGMTPattern(const UnicodeString
& text
, int32_t start
, UBool
/*isShort*/, int32_t& parsedLen
) const {
1752 int32_t idx
= start
;
1754 UBool parsed
= FALSE
;
1758 int32_t len
= fGMTPatternPrefix
.length();
1759 if (len
> 0 && text
.caseCompare(idx
, len
, fGMTPatternPrefix
, 0) != 0) {
1760 // prefix match failed
1766 offset
= parseOffsetFields(text
, idx
, FALSE
, len
);
1768 // offset field match failed
1773 len
= fGMTPatternSuffix
.length();
1774 if (len
> 0 && text
.caseCompare(idx
, len
, fGMTPatternSuffix
, 0) != 0) {
1782 parsedLen
= parsed
? idx
- start
: 0;
1787 TimeZoneFormat::parseOffsetFields(const UnicodeString
& text
, int32_t start
, UBool
/*isShort*/, int32_t& parsedLen
) const {
1794 int32_t offsetH
, offsetM
, offsetS
;
1795 offsetH
= offsetM
= offsetS
= 0;
1797 for (int32_t patidx
= 0; PARSE_GMT_OFFSET_TYPES
[patidx
] >= 0; patidx
++) {
1798 int32_t gmtPatType
= PARSE_GMT_OFFSET_TYPES
[patidx
];
1799 UVector
* items
= fGMTOffsetPatternItems
[gmtPatType
];
1800 U_ASSERT(items
!= NULL
);
1802 outLen
= parseOffsetFieldsWithPattern(text
, start
, items
, FALSE
, offsetH
, offsetM
, offsetS
);
1804 sign
= (gmtPatType
== UTZFMT_PAT_POSITIVE_H
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HM
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HMS
) ?
1810 if (outLen
> 0 && fAbuttingOffsetHoursAndMinutes
) {
1811 // When hours field is sabutting minutes field,
1812 // the parse result above may not be appropriate.
1813 // For example, "01020" is parsed as 01:02: above,
1814 // but it should be parsed as 00:10:20.
1816 int32_t tmpSign
= 1;
1821 for (int32_t patidx
= 0; PARSE_GMT_OFFSET_TYPES
[patidx
] >= 0; patidx
++) {
1822 int32_t gmtPatType
= PARSE_GMT_OFFSET_TYPES
[patidx
];
1823 UVector
* items
= fGMTOffsetPatternItems
[gmtPatType
];
1824 U_ASSERT(items
!= NULL
);
1826 // forcing parse to use single hour digit
1827 tmpLen
= parseOffsetFieldsWithPattern(text
, start
, items
, TRUE
, tmpH
, tmpM
, tmpS
);
1829 tmpSign
= (gmtPatType
== UTZFMT_PAT_POSITIVE_H
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HM
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HMS
) ?
1834 if (tmpLen
> outLen
) {
1835 // Better parse result with single hour digit
1845 offset
= ((((offsetH
* 60) + offsetM
) * 60) + offsetS
) * 1000 * sign
;
1853 TimeZoneFormat::parseOffsetFieldsWithPattern(const UnicodeString
& text
, int32_t start
,
1854 UVector
* patternItems
, UBool forceSingleHourDigit
, int32_t& hour
, int32_t& min
, int32_t& sec
) const {
1855 UBool failed
= FALSE
;
1856 int32_t offsetH
, offsetM
, offsetS
;
1857 offsetH
= offsetM
= offsetS
= 0;
1858 int32_t idx
= start
;
1860 for (int32_t i
= 0; i
< patternItems
->size(); i
++) {
1862 const GMTOffsetField
* field
= (const GMTOffsetField
*)patternItems
->elementAt(i
);
1863 GMTOffsetField::FieldType fieldType
= field
->getType();
1864 if (fieldType
== GMTOffsetField::TEXT
) {
1865 const UChar
* patStr
= field
->getPatternText();
1866 len
= u_strlen(patStr
);
1868 // When TimeZoneFormat parse() is called from SimpleDateFormat,
1869 // leading space characters might be truncated. If the first pattern text
1870 // starts with such character (e.g. Bidi control), then we need to
1871 // skip the leading space charcters.
1872 if (idx
< text
.length() && !PatternProps::isWhiteSpace(text
.char32At(idx
))) {
1876 U16_GET(patStr
, 0, 0, len
, ch
)
1877 if (PatternProps::isWhiteSpace(ch
)) {
1878 chLen
= U16_LENGTH(ch
);
1888 if (text
.caseCompare(idx
, len
, patStr
, 0) != 0) {
1894 if (fieldType
== GMTOffsetField::HOUR
) {
1895 uint8_t maxDigits
= forceSingleHourDigit
? 1 : 2;
1896 offsetH
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 1, maxDigits
, 0, MAX_OFFSET_HOUR
, len
);
1897 } else if (fieldType
== GMTOffsetField::MINUTE
) {
1898 offsetM
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 2, 2, 0, MAX_OFFSET_MINUTE
, len
);
1899 } else if (fieldType
== GMTOffsetField::SECOND
) {
1900 offsetS
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 2, 2, 0, MAX_OFFSET_SECOND
, len
);
1912 hour
= min
= sec
= 0;
1924 TimeZoneFormat::parseAbuttingOffsetFields(const UnicodeString
& text
, int32_t start
, int32_t& parsedLen
) const {
1925 int32_t digits
[MAX_OFFSET_DIGITS
];
1926 int32_t parsed
[MAX_OFFSET_DIGITS
]; // accumulative offsets
1928 // Parse digits into int[]
1929 int32_t idx
= start
;
1931 int32_t numDigits
= 0;
1932 for (int32_t i
= 0; i
< MAX_OFFSET_DIGITS
; i
++) {
1933 digits
[i
] = parseSingleLocalizedDigit(text
, idx
, len
);
1934 if (digits
[i
] < 0) {
1938 parsed
[i
] = idx
- start
;
1942 if (numDigits
== 0) {
1948 while (numDigits
> 0) {
1953 U_ASSERT(numDigits
> 0 && numDigits
<= MAX_OFFSET_DIGITS
);
1954 switch (numDigits
) {
1959 hour
= digits
[0] * 10 + digits
[1];
1963 min
= digits
[1] * 10 + digits
[2];
1966 hour
= digits
[0] * 10 + digits
[1];
1967 min
= digits
[2] * 10 + digits
[3];
1971 min
= digits
[1] * 10 + digits
[2];
1972 sec
= digits
[3] * 10 + digits
[4];
1975 hour
= digits
[0] * 10 + digits
[1];
1976 min
= digits
[2] * 10 + digits
[3];
1977 sec
= digits
[4] * 10 + digits
[5];
1980 if (hour
<= MAX_OFFSET_HOUR
&& min
<= MAX_OFFSET_MINUTE
&& sec
<= MAX_OFFSET_SECOND
) {
1981 // found a valid combination
1982 offset
= hour
* MILLIS_PER_HOUR
+ min
* MILLIS_PER_MINUTE
+ sec
* MILLIS_PER_SECOND
;
1983 parsedLen
= parsed
[numDigits
- 1];
1992 TimeZoneFormat::parseOffsetDefaultLocalizedGMT(const UnicodeString
& text
, int start
, int32_t& parsedLen
) const {
1993 int32_t idx
= start
;
1998 // check global default GMT alternatives
2001 for (int32_t i
= 0; ALT_GMT_STRINGS
[i
][0] != 0; i
++) {
2002 const UChar
* gmt
= ALT_GMT_STRINGS
[i
];
2003 int32_t len
= u_strlen(gmt
);
2004 if (text
.caseCompare(start
, len
, gmt
, 0) == 0) {
2014 // offset needs a sign char and a digit at minimum
2015 if (idx
+ 1 >= text
.length()) {
2021 UChar c
= text
.charAt(idx
);
2024 } else if (c
== MINUS
) {
2032 // try the default pattern with the separator first
2033 int32_t lenWithSep
= 0;
2034 int32_t offsetWithSep
= parseDefaultOffsetFields(text
, idx
, DEFAULT_GMT_OFFSET_SEP
, lenWithSep
);
2035 if (lenWithSep
== text
.length() - idx
) {
2037 offset
= offsetWithSep
* sign
;
2040 // try abutting field pattern
2041 int32_t lenAbut
= 0;
2042 int32_t offsetAbut
= parseAbuttingOffsetFields(text
, idx
, lenAbut
);
2044 if (lenWithSep
> lenAbut
) {
2045 offset
= offsetWithSep
* sign
;
2048 offset
= offsetAbut
* sign
;
2052 parsed
= idx
- start
;
2060 TimeZoneFormat::parseDefaultOffsetFields(const UnicodeString
& text
, int32_t start
, UChar separator
, int32_t& parsedLen
) const {
2061 int32_t max
= text
.length();
2062 int32_t idx
= start
;
2064 int32_t hour
= 0, min
= 0, sec
= 0;
2069 hour
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 1, 2, 0, MAX_OFFSET_HOUR
, len
);
2075 if (idx
+ 1 < max
&& text
.charAt(idx
) == separator
) {
2076 min
= parseOffsetFieldWithLocalizedDigits(text
, idx
+ 1, 2, 2, 0, MAX_OFFSET_MINUTE
, len
);
2082 if (idx
+ 1 < max
&& text
.charAt(idx
) == separator
) {
2083 sec
= parseOffsetFieldWithLocalizedDigits(text
, idx
+ 1, 2, 2, 0, MAX_OFFSET_SECOND
, len
);
2096 parsedLen
= idx
- start
;
2097 return hour
* MILLIS_PER_HOUR
+ min
* MILLIS_PER_MINUTE
+ sec
* MILLIS_PER_SECOND
;
2101 TimeZoneFormat::parseOffsetFieldWithLocalizedDigits(const UnicodeString
& text
, int32_t start
, uint8_t minDigits
, uint8_t maxDigits
, uint16_t minVal
, uint16_t maxVal
, int32_t& parsedLen
) const {
2105 int32_t numDigits
= 0;
2106 int32_t idx
= start
;
2107 int32_t digitLen
= 0;
2109 while (idx
< text
.length() && numDigits
< maxDigits
) {
2110 int32_t digit
= parseSingleLocalizedDigit(text
, idx
, digitLen
);
2114 int32_t tmpVal
= decVal
* 10 + digit
;
2115 if (tmpVal
> maxVal
) {
2123 // Note: maxVal is checked in the while loop
2124 if (numDigits
< minDigits
|| decVal
< minVal
) {
2128 parsedLen
= idx
- start
;
2135 TimeZoneFormat::parseSingleLocalizedDigit(const UnicodeString
& text
, int32_t start
, int32_t& len
) const {
2138 if (start
< text
.length()) {
2139 UChar32 cp
= text
.char32At(start
);
2141 // First, try digits configured for this instance
2142 for (int32_t i
= 0; i
< 10; i
++) {
2143 if (cp
== fGMTOffsetDigits
[i
]) {
2148 // If failed, check if this is a Unicode digit
2150 int32_t tmp
= u_charDigitValue(cp
);
2151 digit
= (tmp
>= 0 && tmp
<= 9) ? tmp
: -1;
2155 int32_t next
= text
.moveIndex32(start
, 1);
2163 TimeZoneFormat::formatOffsetWithAsciiDigits(int32_t offset
, UChar sep
, OffsetFields minFields
, OffsetFields maxFields
, UnicodeString
& result
) {
2164 U_ASSERT(maxFields
>= minFields
);
2165 U_ASSERT(offset
> -MAX_OFFSET
&& offset
< MAX_OFFSET
);
2175 fields
[0] = offset
/ MILLIS_PER_HOUR
;
2176 offset
= offset
% MILLIS_PER_HOUR
;
2177 fields
[1] = offset
/ MILLIS_PER_MINUTE
;
2178 offset
= offset
% MILLIS_PER_MINUTE
;
2179 fields
[2] = offset
/ MILLIS_PER_SECOND
;
2181 U_ASSERT(fields
[0] >= 0 && fields
[0] <= MAX_OFFSET_HOUR
);
2182 U_ASSERT(fields
[1] >= 0 && fields
[1] <= MAX_OFFSET_MINUTE
);
2183 U_ASSERT(fields
[2] >= 0 && fields
[2] <= MAX_OFFSET_SECOND
);
2185 int32_t lastIdx
= maxFields
;
2186 while (lastIdx
> minFields
) {
2187 if (fields
[lastIdx
] != 0) {
2193 for (int32_t idx
= 0; idx
<= lastIdx
; idx
++) {
2194 if (sep
&& idx
!= 0) {
2197 result
.append((UChar
)(0x0030 + fields
[idx
]/10));
2198 result
.append((UChar
)(0x0030 + fields
[idx
]%10
));
2205 TimeZoneFormat::parseAbuttingAsciiOffsetFields(const UnicodeString
& text
, ParsePosition
& pos
, OffsetFields minFields
, OffsetFields maxFields
, UBool fixedHourWidth
) {
2206 int32_t start
= pos
.getIndex();
2208 int32_t minDigits
= 2 * (minFields
+ 1) - (fixedHourWidth
? 0 : 1);
2209 int32_t maxDigits
= 2 * (maxFields
+ 1);
2211 U_ASSERT(maxDigits
<= MAX_OFFSET_DIGITS
);
2213 int32_t digits
[MAX_OFFSET_DIGITS
] = {};
2214 int32_t numDigits
= 0;
2215 int32_t idx
= start
;
2216 while (numDigits
< maxDigits
&& idx
< text
.length()) {
2217 UChar uch
= text
.charAt(idx
);
2218 int32_t digit
= DIGIT_VAL(uch
);
2222 digits
[numDigits
] = digit
;
2227 if (fixedHourWidth
&& (numDigits
& 1)) {
2228 // Fixed digits, so the number of digits must be even number. Truncating.
2232 if (numDigits
< minDigits
) {
2233 pos
.setErrorIndex(start
);
2237 int32_t hour
= 0, min
= 0, sec
= 0;
2238 UBool bParsed
= FALSE
;
2239 while (numDigits
>= minDigits
) {
2240 switch (numDigits
) {
2245 hour
= digits
[0] * 10 + digits
[1];
2249 min
= digits
[1] * 10 + digits
[2];
2252 hour
= digits
[0] * 10 + digits
[1];
2253 min
= digits
[2] * 10 + digits
[3];
2257 min
= digits
[1] * 10 + digits
[2];
2258 sec
= digits
[3] * 10 + digits
[4];
2261 hour
= digits
[0] * 10 + digits
[1];
2262 min
= digits
[2] * 10 + digits
[3];
2263 sec
= digits
[4] * 10 + digits
[5];
2267 if (hour
<= MAX_OFFSET_HOUR
&& min
<= MAX_OFFSET_MINUTE
&& sec
<= MAX_OFFSET_SECOND
) {
2268 // Successfully parsed
2274 numDigits
-= (fixedHourWidth
? 2 : 1);
2275 hour
= min
= sec
= 0;
2279 pos
.setErrorIndex(start
);
2282 pos
.setIndex(start
+ numDigits
);
2283 return ((((hour
* 60) + min
) * 60) + sec
) * 1000;
2287 TimeZoneFormat::parseAsciiOffsetFields(const UnicodeString
& text
, ParsePosition
& pos
, UChar sep
, OffsetFields minFields
, OffsetFields maxFields
) {
2288 int32_t start
= pos
.getIndex();
2289 int32_t fieldVal
[] = {0, 0, 0};
2290 int32_t fieldLen
[] = {0, -1, -1};
2291 for (int32_t idx
= start
, fieldIdx
= 0; idx
< text
.length() && fieldIdx
<= maxFields
; idx
++) {
2292 UChar c
= text
.charAt(idx
);
2294 if (fieldIdx
== 0) {
2295 if (fieldLen
[0] == 0) {
2299 // 1 digit hour, move to next field
2301 if (fieldLen
[fieldIdx
] != -1) {
2302 // premature minute or seconds field
2305 fieldLen
[fieldIdx
] = 0;
2308 } else if (fieldLen
[fieldIdx
] == -1) {
2309 // no separator after 2 digit field
2312 int32_t digit
= DIGIT_VAL(c
);
2317 fieldVal
[fieldIdx
] = fieldVal
[fieldIdx
] * 10 + digit
;
2318 fieldLen
[fieldIdx
]++;
2319 if (fieldLen
[fieldIdx
] >= 2) {
2320 // parsed 2 digits, move to next field
2326 int32_t parsedLen
= 0;
2327 int32_t parsedFields
= -1;
2330 if (fieldLen
[0] == 0) {
2333 if (fieldVal
[0] > MAX_OFFSET_HOUR
) {
2334 offset
= (fieldVal
[0] / 10) * MILLIS_PER_HOUR
;
2335 parsedFields
= FIELDS_H
;
2339 offset
= fieldVal
[0] * MILLIS_PER_HOUR
;
2340 parsedLen
= fieldLen
[0];
2341 parsedFields
= FIELDS_H
;
2344 if (fieldLen
[1] != 2 || fieldVal
[1] > MAX_OFFSET_MINUTE
) {
2347 offset
+= fieldVal
[1] * MILLIS_PER_MINUTE
;
2348 parsedLen
+= (1 + fieldLen
[1]);
2349 parsedFields
= FIELDS_HM
;
2352 if (fieldLen
[2] != 2 || fieldVal
[2] > MAX_OFFSET_SECOND
) {
2355 offset
+= fieldVal
[2] * MILLIS_PER_SECOND
;
2356 parsedLen
+= (1 + fieldLen
[2]);
2357 parsedFields
= FIELDS_HMS
;
2360 if (parsedFields
< minFields
) {
2361 pos
.setErrorIndex(start
);
2365 pos
.setIndex(start
+ parsedLen
);
2370 TimeZoneFormat::appendOffsetDigits(UnicodeString
& buf
, int32_t n
, uint8_t minDigits
) const {
2371 U_ASSERT(n
>= 0 && n
< 60);
2372 int32_t numDigits
= n
>= 10 ? 2 : 1;
2373 for (int32_t i
= 0; i
< minDigits
- numDigits
; i
++) {
2374 buf
.append(fGMTOffsetDigits
[0]);
2376 if (numDigits
== 2) {
2377 buf
.append(fGMTOffsetDigits
[n
/ 10]);
2379 buf
.append(fGMTOffsetDigits
[n
% 10]);
2382 // ------------------------------------------------------------------
2385 TimeZoneFormat::initGMTPattern(const UnicodeString
& gmtPattern
, UErrorCode
& status
) {
2386 if (U_FAILURE(status
)) {
2389 // This implementation not perfect, but sufficient practically.
2390 int32_t idx
= gmtPattern
.indexOf(ARG0
, ARG0_LEN
, 0);
2392 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2395 fGMTPattern
.setTo(gmtPattern
);
2396 unquote(gmtPattern
.tempSubString(0, idx
), fGMTPatternPrefix
);
2397 unquote(gmtPattern
.tempSubString(idx
+ ARG0_LEN
), fGMTPatternSuffix
);
2401 TimeZoneFormat::unquote(const UnicodeString
& pattern
, UnicodeString
& result
) {
2402 if (pattern
.indexOf(SINGLEQUOTE
) < 0) {
2403 result
.setTo(pattern
);
2407 UBool isPrevQuote
= FALSE
;
2408 UBool inQuote
= FALSE
;
2409 for (int32_t i
= 0; i
< pattern
.length(); i
++) {
2410 UChar c
= pattern
.charAt(i
);
2411 if (c
== SINGLEQUOTE
) {
2414 isPrevQuote
= FALSE
;
2420 isPrevQuote
= FALSE
;
2428 TimeZoneFormat::parseOffsetPattern(const UnicodeString
& pattern
, OffsetFields required
, UErrorCode
& status
) {
2429 if (U_FAILURE(status
)) {
2432 UVector
* result
= new UVector(deleteGMTOffsetField
, NULL
, status
);
2433 if (result
== NULL
) {
2434 status
= U_MEMORY_ALLOCATION_ERROR
;
2438 int32_t checkBits
= 0;
2439 UBool isPrevQuote
= FALSE
;
2440 UBool inQuote
= FALSE
;
2442 UnicodeString
text(textBuf
, 0, UPRV_LENGTHOF(textBuf
));
2443 GMTOffsetField::FieldType itemType
= GMTOffsetField::TEXT
;
2444 int32_t itemLength
= 1;
2446 for (int32_t i
= 0; i
< pattern
.length(); i
++) {
2447 UChar ch
= pattern
.charAt(i
);
2448 if (ch
== SINGLEQUOTE
) {
2450 text
.append(SINGLEQUOTE
);
2451 isPrevQuote
= FALSE
;
2454 if (itemType
!= GMTOffsetField::TEXT
) {
2455 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2456 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, static_cast<uint8_t>(itemLength
), status
);
2457 result
->addElement(fld
, status
);
2458 if (U_FAILURE(status
)) {
2462 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2465 itemType
= GMTOffsetField::TEXT
;
2470 isPrevQuote
= FALSE
;
2474 GMTOffsetField::FieldType tmpType
= GMTOffsetField::getTypeByLetter(ch
);
2475 if (tmpType
!= GMTOffsetField::TEXT
) {
2476 // an offset time pattern character
2477 if (tmpType
== itemType
) {
2480 if (itemType
== GMTOffsetField::TEXT
) {
2481 if (text
.length() > 0) {
2482 GMTOffsetField
* textfld
= GMTOffsetField::createText(text
, status
);
2483 result
->addElement(textfld
, status
);
2484 if (U_FAILURE(status
)) {
2490 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2491 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, static_cast<uint8_t>(itemLength
), status
);
2492 result
->addElement(fld
, status
);
2493 if (U_FAILURE(status
)) {
2497 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2503 checkBits
|= tmpType
;
2507 if (itemType
!= GMTOffsetField::TEXT
) {
2508 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2509 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, static_cast<uint8_t>(itemLength
), status
);
2510 result
->addElement(fld
, status
);
2511 if (U_FAILURE(status
)) {
2515 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2518 itemType
= GMTOffsetField::TEXT
;
2526 if (U_SUCCESS(status
)) {
2527 if (itemType
== GMTOffsetField::TEXT
) {
2528 if (text
.length() > 0) {
2529 GMTOffsetField
* tfld
= GMTOffsetField::createText(text
, status
);
2530 result
->addElement(tfld
, status
);
2533 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2534 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, static_cast<uint8_t>(itemLength
), status
);
2535 result
->addElement(fld
, status
);
2537 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2541 // Check all required fields are set
2542 if (U_SUCCESS(status
)) {
2543 int32_t reqBits
= 0;
2546 reqBits
= GMTOffsetField::HOUR
;
2549 reqBits
= GMTOffsetField::HOUR
| GMTOffsetField::MINUTE
;
2552 reqBits
= GMTOffsetField::HOUR
| GMTOffsetField::MINUTE
| GMTOffsetField::SECOND
;
2555 if (checkBits
== reqBits
) {
2556 // all required fields are set, no extra fields
2568 TimeZoneFormat::expandOffsetPattern(const UnicodeString
& offsetHM
, UnicodeString
& result
, UErrorCode
& status
) {
2569 result
.setToBogus();
2570 if (U_FAILURE(status
)) {
2573 U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
) == 2);
2575 int32_t idx_mm
= offsetHM
.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
, 2, 0);
2577 // Bad time zone hour pattern data
2578 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2583 int32_t idx_H
= offsetHM
.tempSubString(0, idx_mm
).lastIndexOf((UChar
)0x0048 /* H */);
2585 sep
= offsetHM
.tempSubString(idx_H
+ 1, idx_mm
- (idx_H
+ 1));
2587 result
.setTo(offsetHM
.tempSubString(0, idx_mm
+ 2));
2589 result
.append(DEFAULT_GMT_OFFSET_SECOND_PATTERN
, -1);
2590 result
.append(offsetHM
.tempSubString(idx_mm
+ 2));
2595 TimeZoneFormat::truncateOffsetPattern(const UnicodeString
& offsetHM
, UnicodeString
& result
, UErrorCode
& status
) {
2596 result
.setToBogus();
2597 if (U_FAILURE(status
)) {
2600 U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
) == 2);
2602 int32_t idx_mm
= offsetHM
.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
, 2, 0);
2604 // Bad time zone hour pattern data
2605 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2608 UChar HH
[] = {0x0048, 0x0048};
2609 int32_t idx_HH
= offsetHM
.tempSubString(0, idx_mm
).lastIndexOf(HH
, 2, 0);
2611 return result
.setTo(offsetHM
.tempSubString(0, idx_HH
+ 2));
2613 int32_t idx_H
= offsetHM
.tempSubString(0, idx_mm
).lastIndexOf((UChar
)0x0048, 0);
2615 return result
.setTo(offsetHM
.tempSubString(0, idx_H
+ 1));
2617 // Bad time zone hour pattern data
2618 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2623 TimeZoneFormat::initGMTOffsetPatterns(UErrorCode
& status
) {
2624 for (int32_t type
= 0; type
< UTZFMT_PAT_COUNT
; type
++) {
2626 case UTZFMT_PAT_POSITIVE_H
:
2627 case UTZFMT_PAT_NEGATIVE_H
:
2628 fGMTOffsetPatternItems
[type
] = parseOffsetPattern(fGMTOffsetPatterns
[type
], FIELDS_H
, status
);
2630 case UTZFMT_PAT_POSITIVE_HM
:
2631 case UTZFMT_PAT_NEGATIVE_HM
:
2632 fGMTOffsetPatternItems
[type
] = parseOffsetPattern(fGMTOffsetPatterns
[type
], FIELDS_HM
, status
);
2634 case UTZFMT_PAT_POSITIVE_HMS
:
2635 case UTZFMT_PAT_NEGATIVE_HMS
:
2636 fGMTOffsetPatternItems
[type
] = parseOffsetPattern(fGMTOffsetPatterns
[type
], FIELDS_HMS
, status
);
2640 checkAbuttingHoursAndMinutes();
2644 TimeZoneFormat::checkAbuttingHoursAndMinutes() {
2645 fAbuttingOffsetHoursAndMinutes
= FALSE
;
2646 for (int32_t type
= 0; type
< UTZFMT_PAT_COUNT
; type
++) {
2647 UBool afterH
= FALSE
;
2648 UVector
*items
= fGMTOffsetPatternItems
[type
];
2649 for (int32_t i
= 0; i
< items
->size(); i
++) {
2650 const GMTOffsetField
* item
= (GMTOffsetField
*)items
->elementAt(i
);
2651 GMTOffsetField::FieldType type
= item
->getType();
2652 if (type
!= GMTOffsetField::TEXT
) {
2654 fAbuttingOffsetHoursAndMinutes
= TRUE
;
2656 } else if (type
== GMTOffsetField::HOUR
) {
2659 } else if (afterH
) {
2663 if (fAbuttingOffsetHoursAndMinutes
) {
2670 TimeZoneFormat::toCodePoints(const UnicodeString
& str
, UChar32
* codeArray
, int32_t size
) {
2671 int32_t count
= str
.countChar32();
2672 if (count
!= size
) {
2676 for (int32_t idx
= 0, start
= 0; idx
< size
; idx
++) {
2677 codeArray
[idx
] = str
.char32At(start
);
2678 start
= str
.moveIndex32(start
, 1);
2685 TimeZoneFormat::createTimeZoneForOffset(int32_t offset
) const {
2687 // when offset is 0, we should use "Etc/GMT"
2688 return TimeZone::createTimeZone(UnicodeString(TRUE
, TZID_GMT
, -1));
2690 return ZoneMeta::createCustomTimeZone(offset
);
2693 UTimeZoneFormatTimeType
2694 TimeZoneFormat::getTimeType(UTimeZoneNameType nameType
) {
2696 case UTZNM_LONG_STANDARD
:
2697 case UTZNM_SHORT_STANDARD
:
2698 return UTZFMT_TIME_TYPE_STANDARD
;
2700 case UTZNM_LONG_DAYLIGHT
:
2701 case UTZNM_SHORT_DAYLIGHT
:
2702 return UTZFMT_TIME_TYPE_DAYLIGHT
;
2705 return UTZFMT_TIME_TYPE_UNKNOWN
;
2710 TimeZoneFormat::getTimeZoneID(const TimeZoneNames::MatchInfoCollection
* matches
, int32_t idx
, UnicodeString
& tzID
) const {
2711 if (!matches
->getTimeZoneIDAt(idx
, tzID
)) {
2713 UnicodeString
mzID(mzIDBuf
, 0, UPRV_LENGTHOF(mzIDBuf
));
2714 if (matches
->getMetaZoneIDAt(idx
, mzID
)) {
2715 fTimeZoneNames
->getReferenceZoneID(mzID
, fTargetRegion
, tzID
);
2722 class ZoneIdMatchHandler
: public TextTrieMapSearchResultHandler
{
2724 ZoneIdMatchHandler();
2725 virtual ~ZoneIdMatchHandler();
2727 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
2728 const UChar
* getID();
2729 int32_t getMatchLen();
2735 ZoneIdMatchHandler::ZoneIdMatchHandler()
2736 : fLen(0), fID(NULL
) {
2739 ZoneIdMatchHandler::~ZoneIdMatchHandler() {
2743 ZoneIdMatchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
2744 if (U_FAILURE(status
)) {
2747 if (node
->hasValues()) {
2748 const UChar
* id
= (const UChar
*)node
->getValue(0);
2750 if (fLen
< matchLength
) {
2760 ZoneIdMatchHandler::getID() {
2765 ZoneIdMatchHandler::getMatchLen() {
2770 static void U_CALLCONV
initZoneIdTrie(UErrorCode
&status
) {
2771 U_ASSERT(gZoneIdTrie
== NULL
);
2772 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT
, tzfmt_cleanup
);
2773 gZoneIdTrie
= new TextTrieMap(TRUE
, NULL
); // No deleter, because values are pooled by ZoneMeta
2774 if (gZoneIdTrie
== NULL
) {
2775 status
= U_MEMORY_ALLOCATION_ERROR
;
2778 StringEnumeration
*tzenum
= TimeZone::createEnumeration();
2779 const UnicodeString
*id
;
2780 while ((id
= tzenum
->snext(status
)) != NULL
) {
2781 const UChar
* uid
= ZoneMeta::findTimeZoneID(*id
);
2783 gZoneIdTrie
->put(uid
, const_cast<UChar
*>(uid
), status
);
2791 TimeZoneFormat::parseZoneID(const UnicodeString
& text
, ParsePosition
& pos
, UnicodeString
& tzID
) const {
2792 UErrorCode status
= U_ZERO_ERROR
;
2793 umtx_initOnce(gZoneIdTrieInitOnce
, &initZoneIdTrie
, status
);
2795 int32_t start
= pos
.getIndex();
2799 if (U_SUCCESS(status
)) {
2800 LocalPointer
<ZoneIdMatchHandler
> handler(new ZoneIdMatchHandler());
2801 gZoneIdTrie
->search(text
, start
, handler
.getAlias(), status
);
2802 len
= handler
->getMatchLen();
2804 tzID
.setTo(handler
->getID(), -1);
2809 pos
.setIndex(start
+ len
);
2811 pos
.setErrorIndex(start
);
2817 static void U_CALLCONV
initShortZoneIdTrie(UErrorCode
&status
) {
2818 U_ASSERT(gShortZoneIdTrie
== NULL
);
2819 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT
, tzfmt_cleanup
);
2820 StringEnumeration
*tzenum
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL
, NULL
, NULL
, status
);
2821 if (U_SUCCESS(status
)) {
2822 gShortZoneIdTrie
= new TextTrieMap(TRUE
, NULL
); // No deleter, because values are pooled by ZoneMeta
2823 if (gShortZoneIdTrie
== NULL
) {
2824 status
= U_MEMORY_ALLOCATION_ERROR
;
2826 const UnicodeString
*id
;
2827 while ((id
= tzenum
->snext(status
)) != NULL
) {
2828 const UChar
* uID
= ZoneMeta::findTimeZoneID(*id
);
2829 const UChar
* shortID
= ZoneMeta::getShortID(*id
);
2830 if (shortID
&& uID
) {
2831 gShortZoneIdTrie
->put(shortID
, const_cast<UChar
*>(uID
), status
);
2841 TimeZoneFormat::parseShortZoneID(const UnicodeString
& text
, ParsePosition
& pos
, UnicodeString
& tzID
) const {
2842 UErrorCode status
= U_ZERO_ERROR
;
2843 umtx_initOnce(gShortZoneIdTrieInitOnce
, &initShortZoneIdTrie
, status
);
2845 int32_t start
= pos
.getIndex();
2849 if (U_SUCCESS(status
)) {
2850 LocalPointer
<ZoneIdMatchHandler
> handler(new ZoneIdMatchHandler());
2851 gShortZoneIdTrie
->search(text
, start
, handler
.getAlias(), status
);
2852 len
= handler
->getMatchLen();
2854 tzID
.setTo(handler
->getID(), -1);
2859 pos
.setIndex(start
+ len
);
2861 pos
.setErrorIndex(start
);
2869 TimeZoneFormat::parseExemplarLocation(const UnicodeString
& text
, ParsePosition
& pos
, UnicodeString
& tzID
) const {
2870 int32_t startIdx
= pos
.getIndex();
2871 int32_t parsedPos
= -1;
2874 UErrorCode status
= U_ZERO_ERROR
;
2875 LocalPointer
<TimeZoneNames::MatchInfoCollection
> exemplarMatches(fTimeZoneNames
->find(text
, startIdx
, UTZNM_EXEMPLAR_LOCATION
, status
));
2876 if (U_FAILURE(status
)) {
2877 pos
.setErrorIndex(startIdx
);
2880 int32_t matchIdx
= -1;
2881 if (!exemplarMatches
.isNull()) {
2882 for (int32_t i
= 0; i
< exemplarMatches
->size(); i
++) {
2883 if (startIdx
+ exemplarMatches
->getMatchLengthAt(i
) > parsedPos
) {
2885 parsedPos
= startIdx
+ exemplarMatches
->getMatchLengthAt(i
);
2888 if (parsedPos
> 0) {
2889 pos
.setIndex(parsedPos
);
2890 getTimeZoneID(exemplarMatches
.getAlias(), matchIdx
, tzID
);
2894 if (tzID
.length() == 0) {
2895 pos
.setErrorIndex(startIdx
);