2 *******************************************************************************
3 * Copyright (C) 2011-2015, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
8 #include "unicode/utypes.h"
10 #if !UCONFIG_NO_FORMATTING
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"
28 #include "tznames_impl.h" // TextTrieMap
32 // Bit flags used by the parse method.
33 // The order must match UTimeZoneFormatStyle enum.
34 #define ISO_Z_STYLE_FLAG 0x0080
35 #define ISO_LOCAL_STYLE_FLAG 0x0100
36 static const int16_t STYLE_PARSE_FLAGS
[] = {
37 0x0001, // UTZFMT_STYLE_GENERIC_LOCATION,
38 0x0002, // UTZFMT_STYLE_GENERIC_LONG,
39 0x0004, // UTZFMT_STYLE_GENERIC_SHORT,
40 0x0008, // UTZFMT_STYLE_SPECIFIC_LONG,
41 0x0010, // UTZFMT_STYLE_SPECIFIC_SHORT,
42 0x0020, // UTZFMT_STYLE_LOCALIZED_GMT,
43 0x0040, // UTZFMT_STYLE_LOCALIZED_GMT_SHORT,
44 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_SHORT,
45 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT,
46 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_FIXED,
47 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED,
48 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_FULL,
49 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
50 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_FIXED,
51 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED,
52 ISO_Z_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_FULL,
53 ISO_LOCAL_STYLE_FLAG
, // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL,
54 0x0200, // UTZFMT_STYLE_ZONE_ID,
55 0x0400, // UTZFMT_STYLE_ZONE_ID_SHORT,
56 0x0800 // UTZFMT_STYLE_EXEMPLAR_LOCATION
59 static const char gZoneStringsTag
[] = "zoneStrings";
60 static const char gGmtFormatTag
[]= "gmtFormat";
61 static const char gGmtZeroFormatTag
[] = "gmtZeroFormat";
62 static const char gHourFormatTag
[]= "hourFormat";
64 static const UChar TZID_GMT
[] = {0x0045, 0x0074, 0x0063, 0x002F, 0x0047, 0x004D, 0x0054, 0}; // Etc/GMT
65 static const UChar UNKNOWN_ZONE_ID
[] = {
66 0x0045, 0x0074, 0x0063, 0x002F, 0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0}; // Etc/Unknown
67 static const UChar UNKNOWN_SHORT_ZONE_ID
[] = {0x0075, 0x006E, 0x006B, 0}; // unk
68 static const UChar UNKNOWN_LOCATION
[] = {0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0}; // Unknown
70 static const UChar DEFAULT_GMT_PATTERN
[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0}; // GMT{0}
71 //static const UChar DEFAULT_GMT_ZERO[] = {0x0047, 0x004D, 0x0054, 0}; // GMT
72 static const UChar DEFAULT_GMT_POSITIVE_HM
[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // +H:mm
73 static const UChar DEFAULT_GMT_POSITIVE_HMS
[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // +H:mm:ss
74 static const UChar DEFAULT_GMT_NEGATIVE_HM
[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // -H:mm
75 static const UChar DEFAULT_GMT_NEGATIVE_HMS
[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // -H:mm:ss
76 static const UChar DEFAULT_GMT_POSITIVE_H
[] = {0x002B, 0x0048, 0}; // +H
77 static const UChar DEFAULT_GMT_NEGATIVE_H
[] = {0x002D, 0x0048, 0}; // -H
79 static const UChar32 DEFAULT_GMT_DIGITS
[] = {
80 0x0030, 0x0031, 0x0032, 0x0033, 0x0034,
81 0x0035, 0x0036, 0x0037, 0x0038, 0x0039
84 static const UChar DEFAULT_GMT_OFFSET_SEP
= 0x003A; // ':'
86 static const UChar ARG0
[] = {0x007B, 0x0030, 0x007D}; // "{0}"
87 static const int32_t ARG0_LEN
= 3;
89 static const UChar DEFAULT_GMT_OFFSET_MINUTE_PATTERN
[] = {0x006D, 0x006D, 0}; // "mm"
90 static const UChar DEFAULT_GMT_OFFSET_SECOND_PATTERN
[] = {0x0073, 0x0073, 0}; // "ss"
92 static const UChar ALT_GMT_STRINGS
[][4] = {
93 {0x0047, 0x004D, 0x0054, 0}, // GMT
94 {0x0055, 0x0054, 0x0043, 0}, // UTC
95 {0x0055, 0x0054, 0, 0}, // UT
99 // Order of GMT offset pattern parsing, *_HMS must be evaluated first
100 // because *_HM is most likely a substring of *_HMS
101 static const int32_t PARSE_GMT_OFFSET_TYPES
[] = {
102 UTZFMT_PAT_POSITIVE_HMS
,
103 UTZFMT_PAT_NEGATIVE_HMS
,
104 UTZFMT_PAT_POSITIVE_HM
,
105 UTZFMT_PAT_NEGATIVE_HM
,
106 UTZFMT_PAT_POSITIVE_H
,
107 UTZFMT_PAT_NEGATIVE_H
,
111 static const UChar SINGLEQUOTE
= 0x0027;
112 static const UChar PLUS
= 0x002B;
113 static const UChar MINUS
= 0x002D;
114 static const UChar ISO8601_UTC
= 0x005A; // 'Z'
115 static const UChar ISO8601_SEP
= 0x003A; // ':'
117 static const int32_t MILLIS_PER_HOUR
= 60 * 60 * 1000;
118 static const int32_t MILLIS_PER_MINUTE
= 60 * 1000;
119 static const int32_t MILLIS_PER_SECOND
= 1000;
121 // Maximum offset (exclusive) in millisecond supported by offset formats
122 static int32_t MAX_OFFSET
= 24 * MILLIS_PER_HOUR
;
124 // Maximum values for GMT offset fields
125 static const int32_t MAX_OFFSET_HOUR
= 23;
126 static const int32_t MAX_OFFSET_MINUTE
= 59;
127 static const int32_t MAX_OFFSET_SECOND
= 59;
129 static const int32_t UNKNOWN_OFFSET
= 0x7FFFFFFF;
131 static const int32_t ALL_SIMPLE_NAME_TYPES
= UTZNM_LONG_STANDARD
| UTZNM_LONG_DAYLIGHT
| UTZNM_SHORT_STANDARD
| UTZNM_SHORT_DAYLIGHT
| UTZNM_EXEMPLAR_LOCATION
;
132 static const int32_t ALL_GENERIC_NAME_TYPES
= UTZGNM_LOCATION
| UTZGNM_LONG
| UTZGNM_SHORT
;
134 #define DIGIT_VAL(c) (0x0030 <= (c) && (c) <= 0x0039 ? (c) - 0x0030 : -1)
135 #define MAX_OFFSET_DIGITS 6
137 // Time Zone ID/Short ID trie
138 static TextTrieMap
*gZoneIdTrie
= NULL
;
139 static icu::UInitOnce gZoneIdTrieInitOnce
= U_INITONCE_INITIALIZER
;
141 static TextTrieMap
*gShortZoneIdTrie
= NULL
;
142 static icu::UInitOnce gShortZoneIdTrieInitOnce
= U_INITONCE_INITIALIZER
;
144 static UMutex gLock
= U_MUTEX_INITIALIZER
;
148 * Cleanup callback func
150 static UBool U_CALLCONV
tzfmt_cleanup(void)
152 if (gZoneIdTrie
!= NULL
) {
156 gZoneIdTrieInitOnce
.reset();
158 if (gShortZoneIdTrie
!= NULL
) {
159 delete gShortZoneIdTrie
;
161 gShortZoneIdTrie
= NULL
;
162 gShortZoneIdTrieInitOnce
.reset();
168 // ------------------------------------------------------------------
171 // This class represents a localized GMT offset pattern
172 // item and used by TimeZoneFormat
173 // ------------------------------------------------------------------
174 class GMTOffsetField
: public UMemory
{
183 virtual ~GMTOffsetField();
185 static GMTOffsetField
* createText(const UnicodeString
& text
, UErrorCode
& status
);
186 static GMTOffsetField
* createTimeField(FieldType type
, uint8_t width
, UErrorCode
& status
);
187 static UBool
isValid(FieldType type
, int32_t width
);
188 static FieldType
getTypeByLetter(UChar ch
);
190 FieldType
getType() const;
191 uint8_t getWidth() const;
192 const UChar
* getPatternText(void) const;
202 GMTOffsetField::GMTOffsetField()
203 : fText(NULL
), fType(TEXT
), fWidth(0) {
206 GMTOffsetField::~GMTOffsetField() {
213 GMTOffsetField::createText(const UnicodeString
& text
, UErrorCode
& status
) {
214 if (U_FAILURE(status
)) {
217 GMTOffsetField
* result
= new GMTOffsetField();
218 if (result
== NULL
) {
219 status
= U_MEMORY_ALLOCATION_ERROR
;
223 int32_t len
= text
.length();
224 result
->fText
= (UChar
*)uprv_malloc((len
+ 1) * sizeof(UChar
));
225 if (result
->fText
== NULL
) {
226 status
= U_MEMORY_ALLOCATION_ERROR
;
230 u_strncpy(result
->fText
, text
.getBuffer(), len
);
231 result
->fText
[len
] = 0;
232 result
->fType
= TEXT
;
238 GMTOffsetField::createTimeField(FieldType type
, uint8_t width
, UErrorCode
& status
) {
239 U_ASSERT(type
!= TEXT
);
240 if (U_FAILURE(status
)) {
243 GMTOffsetField
* result
= new GMTOffsetField();
244 if (result
== NULL
) {
245 status
= U_MEMORY_ALLOCATION_ERROR
;
249 result
->fType
= type
;
250 result
->fWidth
= width
;
256 GMTOffsetField::isValid(FieldType type
, int32_t width
) {
259 return (width
== 1 || width
== 2);
269 GMTOffsetField::FieldType
270 GMTOffsetField::getTypeByLetter(UChar ch
) {
271 if (ch
== 0x0048 /* H */) {
273 } else if (ch
== 0x006D /* m */) {
275 } else if (ch
== 0x0073 /* s */) {
281 inline GMTOffsetField::FieldType
282 GMTOffsetField::getType() const {
287 GMTOffsetField::getWidth() const {
292 GMTOffsetField::getPatternText(void) const {
298 static void U_CALLCONV
299 deleteGMTOffsetField(void *obj
) {
300 delete static_cast<GMTOffsetField
*>(obj
);
305 // ------------------------------------------------------------------
307 // ------------------------------------------------------------------
308 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneFormat
)
310 TimeZoneFormat::TimeZoneFormat(const Locale
& locale
, UErrorCode
& status
)
311 : fLocale(locale
), fTimeZoneNames(NULL
), fTimeZoneGenericNames(NULL
),
312 fDefParseOptionFlags(0), fTZDBTimeZoneNames(NULL
) {
314 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
315 fGMTOffsetPatternItems
[i
] = NULL
;
318 const char* region
= fLocale
.getCountry();
319 int32_t regionLen
= uprv_strlen(region
);
320 if (regionLen
== 0) {
321 char loc
[ULOC_FULLNAME_CAPACITY
];
322 uloc_addLikelySubtags(fLocale
.getName(), loc
, sizeof(loc
), &status
);
324 regionLen
= uloc_getCountry(loc
, fTargetRegion
, sizeof(fTargetRegion
), &status
);
325 if (U_SUCCESS(status
)) {
326 fTargetRegion
[regionLen
] = 0;
330 } else if (regionLen
< (int32_t)sizeof(fTargetRegion
)) {
331 uprv_strcpy(fTargetRegion
, region
);
333 fTargetRegion
[0] = 0;
336 fTimeZoneNames
= TimeZoneNames::createInstance(locale
, status
);
337 // fTimeZoneGenericNames is lazily instantiated
338 if (U_FAILURE(status
)) {
342 const UChar
* gmtPattern
= NULL
;
343 const UChar
* hourFormats
= NULL
;
345 UResourceBundle
*zoneBundle
= ures_open(U_ICUDATA_ZONE
, locale
.getName(), &status
);
346 UResourceBundle
*zoneStringsArray
= ures_getByKeyWithFallback(zoneBundle
, gZoneStringsTag
, NULL
, &status
);
347 if (U_SUCCESS(status
)) {
350 resStr
= ures_getStringByKeyWithFallback(zoneStringsArray
, gGmtFormatTag
, &len
, &status
);
354 resStr
= ures_getStringByKeyWithFallback(zoneStringsArray
, gGmtZeroFormatTag
, &len
, &status
);
356 fGMTZeroFormat
.setTo(TRUE
, resStr
, len
);
358 resStr
= ures_getStringByKeyWithFallback(zoneStringsArray
, gHourFormatTag
, &len
, &status
);
360 hourFormats
= resStr
;
362 ures_close(zoneStringsArray
);
363 ures_close(zoneBundle
);
366 if (gmtPattern
== NULL
) {
367 gmtPattern
= DEFAULT_GMT_PATTERN
;
369 initGMTPattern(UnicodeString(TRUE
, gmtPattern
, -1), status
);
371 UBool useDefaultOffsetPatterns
= TRUE
;
373 UChar
*sep
= u_strchr(hourFormats
, (UChar
)0x003B /* ';' */);
375 UErrorCode tmpStatus
= U_ZERO_ERROR
;
376 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
].setTo(FALSE
, hourFormats
, (int32_t)(sep
- hourFormats
));
377 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
].setTo(TRUE
, sep
+ 1, -1);
378 expandOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HMS
], tmpStatus
);
379 expandOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HMS
], tmpStatus
);
380 truncateOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_H
], tmpStatus
);
381 truncateOffsetPattern(fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
], fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_H
], tmpStatus
);
382 if (U_SUCCESS(tmpStatus
)) {
383 useDefaultOffsetPatterns
= FALSE
;
387 if (useDefaultOffsetPatterns
) {
388 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_H
].setTo(TRUE
, DEFAULT_GMT_POSITIVE_H
, -1);
389 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HM
].setTo(TRUE
, DEFAULT_GMT_POSITIVE_HM
, -1);
390 fGMTOffsetPatterns
[UTZFMT_PAT_POSITIVE_HMS
].setTo(TRUE
, DEFAULT_GMT_POSITIVE_HMS
, -1);
391 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_H
].setTo(TRUE
, DEFAULT_GMT_NEGATIVE_H
, -1);
392 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HM
].setTo(TRUE
, DEFAULT_GMT_NEGATIVE_HM
, -1);
393 fGMTOffsetPatterns
[UTZFMT_PAT_NEGATIVE_HMS
].setTo(TRUE
, DEFAULT_GMT_NEGATIVE_HMS
, -1);
395 initGMTOffsetPatterns(status
);
397 NumberingSystem
* ns
= NumberingSystem::createInstance(locale
, status
);
398 UBool useDefDigits
= TRUE
;
399 if (ns
&& !ns
->isAlgorithmic()) {
400 UnicodeString digits
= ns
->getDescription();
401 useDefDigits
= !toCodePoints(digits
, fGMTOffsetDigits
, 10);
404 uprv_memcpy(fGMTOffsetDigits
, DEFAULT_GMT_DIGITS
, sizeof(UChar32
) * 10);
409 TimeZoneFormat::TimeZoneFormat(const TimeZoneFormat
& other
)
410 : Format(other
), fTimeZoneNames(NULL
), fTimeZoneGenericNames(NULL
),
411 fTZDBTimeZoneNames(NULL
) {
413 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
414 fGMTOffsetPatternItems
[i
] = NULL
;
420 TimeZoneFormat::~TimeZoneFormat() {
421 delete fTimeZoneNames
;
422 delete fTimeZoneGenericNames
;
423 delete fTZDBTimeZoneNames
;
424 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
425 delete fGMTOffsetPatternItems
[i
];
430 TimeZoneFormat::operator=(const TimeZoneFormat
& other
) {
431 if (this == &other
) {
435 delete fTimeZoneNames
;
436 delete fTimeZoneGenericNames
;
437 fTimeZoneGenericNames
= NULL
;
438 delete fTZDBTimeZoneNames
;
439 fTZDBTimeZoneNames
= NULL
;
441 fLocale
= other
.fLocale
;
442 uprv_memcpy(fTargetRegion
, other
.fTargetRegion
, sizeof(fTargetRegion
));
444 fTimeZoneNames
= other
.fTimeZoneNames
->clone();
445 if (other
.fTimeZoneGenericNames
) {
446 // TODO: this test has dubious thread safety.
447 fTimeZoneGenericNames
= other
.fTimeZoneGenericNames
->clone();
450 fGMTPattern
= other
.fGMTPattern
;
451 fGMTPatternPrefix
= other
.fGMTPatternPrefix
;
452 fGMTPatternSuffix
= other
.fGMTPatternSuffix
;
454 UErrorCode status
= U_ZERO_ERROR
;
455 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
; i
++) {
456 fGMTOffsetPatterns
[i
] = other
.fGMTOffsetPatterns
[i
];
457 delete fGMTOffsetPatternItems
[i
];
458 fGMTOffsetPatternItems
[i
] = NULL
;
460 initGMTOffsetPatterns(status
);
461 U_ASSERT(U_SUCCESS(status
));
463 fGMTZeroFormat
= other
.fGMTZeroFormat
;
465 uprv_memcpy(fGMTOffsetDigits
, other
.fGMTOffsetDigits
, sizeof(fGMTOffsetDigits
));
467 fDefParseOptionFlags
= other
.fDefParseOptionFlags
;
474 TimeZoneFormat::operator==(const Format
& other
) const {
475 TimeZoneFormat
* tzfmt
= (TimeZoneFormat
*)&other
;
478 fLocale
== tzfmt
->fLocale
479 && fGMTPattern
== tzfmt
->fGMTPattern
480 && fGMTZeroFormat
== tzfmt
->fGMTZeroFormat
481 && *fTimeZoneNames
== *tzfmt
->fTimeZoneNames
;
483 for (int32_t i
= 0; i
< UTZFMT_PAT_COUNT
&& isEqual
; i
++) {
484 isEqual
= fGMTOffsetPatterns
[i
] == tzfmt
->fGMTOffsetPatterns
[i
];
486 for (int32_t i
= 0; i
< 10 && isEqual
; i
++) {
487 isEqual
= fGMTOffsetDigits
[i
] == tzfmt
->fGMTOffsetDigits
[i
];
490 // Check fTimeZoneGenericNames. For now,
491 // if fTimeZoneNames is same, fTimeZoneGenericNames should
492 // be also equivalent.
497 TimeZoneFormat::clone() const {
498 return new TimeZoneFormat(*this);
501 TimeZoneFormat
* U_EXPORT2
502 TimeZoneFormat::createInstance(const Locale
& locale
, UErrorCode
& status
) {
503 TimeZoneFormat
* tzfmt
= new TimeZoneFormat(locale
, status
);
504 if (U_SUCCESS(status
)) {
511 // ------------------------------------------------------------------
515 TimeZoneFormat::getTimeZoneNames() const {
516 return (const TimeZoneNames
*)fTimeZoneNames
;
520 TimeZoneFormat::adoptTimeZoneNames(TimeZoneNames
*tznames
) {
521 delete fTimeZoneNames
;
522 fTimeZoneNames
= tznames
;
524 // TODO - We should also update fTimeZoneGenericNames
528 TimeZoneFormat::setTimeZoneNames(const TimeZoneNames
&tznames
) {
529 delete fTimeZoneNames
;
530 fTimeZoneNames
= tznames
.clone();
532 // TODO - We should also update fTimeZoneGenericNames
536 TimeZoneFormat::setDefaultParseOptions(uint32_t flags
) {
537 fDefParseOptionFlags
= flags
;
541 TimeZoneFormat::getDefaultParseOptions(void) const {
542 return fDefParseOptionFlags
;
547 TimeZoneFormat::getGMTPattern(UnicodeString
& pattern
) const {
548 return pattern
.setTo(fGMTPattern
);
552 TimeZoneFormat::setGMTPattern(const UnicodeString
& pattern
, UErrorCode
& status
) {
553 initGMTPattern(pattern
, status
);
557 TimeZoneFormat::getGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type
, UnicodeString
& pattern
) const {
558 return pattern
.setTo(fGMTOffsetPatterns
[type
]);
562 TimeZoneFormat::setGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type
, const UnicodeString
& pattern
, UErrorCode
& status
) {
563 if (U_FAILURE(status
)) {
566 if (pattern
== fGMTOffsetPatterns
[type
]) {
571 OffsetFields required
= FIELDS_HM
;
573 case UTZFMT_PAT_POSITIVE_H
:
574 case UTZFMT_PAT_NEGATIVE_H
:
577 case UTZFMT_PAT_POSITIVE_HM
:
578 case UTZFMT_PAT_NEGATIVE_HM
:
579 required
= FIELDS_HM
;
581 case UTZFMT_PAT_POSITIVE_HMS
:
582 case UTZFMT_PAT_NEGATIVE_HMS
:
583 required
= FIELDS_HMS
;
590 UVector
* patternItems
= parseOffsetPattern(pattern
, required
, status
);
591 if (patternItems
== NULL
) {
595 fGMTOffsetPatterns
[type
].setTo(pattern
);
596 delete fGMTOffsetPatternItems
[type
];
597 fGMTOffsetPatternItems
[type
] = patternItems
;
598 checkAbuttingHoursAndMinutes();
602 TimeZoneFormat::getGMTOffsetDigits(UnicodeString
& digits
) const {
604 for (int32_t i
= 0; i
< 10; i
++) {
605 digits
.append(fGMTOffsetDigits
[i
]);
611 TimeZoneFormat::setGMTOffsetDigits(const UnicodeString
& digits
, UErrorCode
& status
) {
612 if (U_FAILURE(status
)) {
615 UChar32 digitArray
[10];
616 if (!toCodePoints(digits
, digitArray
, 10)) {
617 status
= U_ILLEGAL_ARGUMENT_ERROR
;
620 uprv_memcpy(fGMTOffsetDigits
, digitArray
, sizeof(UChar32
)*10);
624 TimeZoneFormat::getGMTZeroFormat(UnicodeString
& gmtZeroFormat
) const {
625 return gmtZeroFormat
.setTo(fGMTZeroFormat
);
629 TimeZoneFormat::setGMTZeroFormat(const UnicodeString
& gmtZeroFormat
, UErrorCode
& status
) {
630 if (U_SUCCESS(status
)) {
631 if (gmtZeroFormat
.isEmpty()) {
632 status
= U_ILLEGAL_ARGUMENT_ERROR
;
633 } else if (gmtZeroFormat
!= fGMTZeroFormat
) {
634 fGMTZeroFormat
.setTo(gmtZeroFormat
);
639 // ------------------------------------------------------------------
643 TimeZoneFormat::format(UTimeZoneFormatStyle style
, const TimeZone
& tz
, UDate date
,
644 UnicodeString
& name
, UTimeZoneFormatTimeType
* timeType
/* = NULL */) const {
646 *timeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
649 UBool noOffsetFormatFallback
= FALSE
;
652 case UTZFMT_STYLE_GENERIC_LOCATION
:
653 formatGeneric(tz
, UTZGNM_LOCATION
, date
, name
);
655 case UTZFMT_STYLE_GENERIC_LONG
:
656 formatGeneric(tz
, UTZGNM_LONG
, date
, name
);
658 case UTZFMT_STYLE_GENERIC_SHORT
:
659 formatGeneric(tz
, UTZGNM_SHORT
, date
, name
);
661 case UTZFMT_STYLE_SPECIFIC_LONG
:
662 formatSpecific(tz
, UTZNM_LONG_STANDARD
, UTZNM_LONG_DAYLIGHT
, date
, name
, timeType
);
664 case UTZFMT_STYLE_SPECIFIC_SHORT
:
665 formatSpecific(tz
, UTZNM_SHORT_STANDARD
, UTZNM_SHORT_DAYLIGHT
, date
, name
, timeType
);
668 case UTZFMT_STYLE_ZONE_ID
:
670 noOffsetFormatFallback
= TRUE
;
672 case UTZFMT_STYLE_ZONE_ID_SHORT
:
674 const UChar
* shortID
= ZoneMeta::getShortID(tz
);
675 if (shortID
== NULL
) {
676 shortID
= UNKNOWN_SHORT_ZONE_ID
;
678 name
.setTo(shortID
, -1);
680 noOffsetFormatFallback
= TRUE
;
683 case UTZFMT_STYLE_EXEMPLAR_LOCATION
:
684 formatExemplarLocation(tz
, name
);
685 noOffsetFormatFallback
= TRUE
;
689 // will be handled below
693 if (name
.isEmpty() && !noOffsetFormatFallback
) {
694 UErrorCode status
= U_ZERO_ERROR
;
695 int32_t rawOffset
, dstOffset
;
696 tz
.getOffset(date
, FALSE
, rawOffset
, dstOffset
, status
);
697 int32_t offset
= rawOffset
+ dstOffset
;
698 if (U_SUCCESS(status
)) {
700 case UTZFMT_STYLE_GENERIC_LOCATION
:
701 case UTZFMT_STYLE_GENERIC_LONG
:
702 case UTZFMT_STYLE_SPECIFIC_LONG
:
703 case UTZFMT_STYLE_LOCALIZED_GMT
:
704 formatOffsetLocalizedGMT(offset
, name
, status
);
707 case UTZFMT_STYLE_GENERIC_SHORT
:
708 case UTZFMT_STYLE_SPECIFIC_SHORT
:
709 case UTZFMT_STYLE_LOCALIZED_GMT_SHORT
:
710 formatOffsetShortLocalizedGMT(offset
, name
, status
);
713 case UTZFMT_STYLE_ISO_BASIC_SHORT
:
714 formatOffsetISO8601Basic(offset
, TRUE
, TRUE
, TRUE
, name
, status
);
717 case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
:
718 formatOffsetISO8601Basic(offset
, FALSE
, TRUE
, TRUE
, name
, status
);
721 case UTZFMT_STYLE_ISO_BASIC_FIXED
:
722 formatOffsetISO8601Basic(offset
, TRUE
, FALSE
, TRUE
, name
, status
);
725 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
:
726 formatOffsetISO8601Basic(offset
, FALSE
, FALSE
, TRUE
, name
, status
);
729 case UTZFMT_STYLE_ISO_EXTENDED_FIXED
:
730 formatOffsetISO8601Extended(offset
, TRUE
, FALSE
, TRUE
, name
, status
);
733 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
:
734 formatOffsetISO8601Extended(offset
, FALSE
, FALSE
, TRUE
, name
, status
);
737 case UTZFMT_STYLE_ISO_BASIC_FULL
:
738 formatOffsetISO8601Basic(offset
, TRUE
, FALSE
, FALSE
, name
, status
);
741 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
:
742 formatOffsetISO8601Basic(offset
, FALSE
, FALSE
, FALSE
, name
, status
);
745 case UTZFMT_STYLE_ISO_EXTENDED_FULL
:
746 formatOffsetISO8601Extended(offset
, TRUE
, FALSE
, FALSE
, name
, status
);
749 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
:
750 formatOffsetISO8601Extended(offset
, FALSE
, FALSE
, FALSE
, name
, status
);
754 // UTZFMT_STYLE_ZONE_ID, UTZFMT_STYLE_ZONE_ID_SHORT, UTZFMT_STYLE_EXEMPLAR_LOCATION
759 *timeType
= (dstOffset
!= 0) ? UTZFMT_TIME_TYPE_DAYLIGHT
: UTZFMT_TIME_TYPE_STANDARD
;
768 TimeZoneFormat::format(const Formattable
& obj
, UnicodeString
& appendTo
,
769 FieldPosition
& pos
, UErrorCode
& status
) const {
770 if (U_FAILURE(status
)) {
773 UDate date
= Calendar::getNow();
774 if (obj
.getType() == Formattable::kObject
) {
775 const UObject
* formatObj
= obj
.getObject();
776 const TimeZone
* tz
= dynamic_cast<const TimeZone
*>(formatObj
);
778 const Calendar
* cal
= dynamic_cast<const Calendar
*>(formatObj
);
780 tz
= &cal
->getTimeZone();
781 date
= cal
->getTime(status
);
785 int32_t rawOffset
, dstOffset
;
786 tz
->getOffset(date
, FALSE
, rawOffset
, dstOffset
, status
);
788 UnicodeString
result(buf
, 0, UPRV_LENGTHOF(buf
));
789 formatOffsetLocalizedGMT(rawOffset
+ dstOffset
, result
, status
);
790 if (U_SUCCESS(status
)) {
791 appendTo
.append(result
);
792 if (pos
.getField() == UDAT_TIMEZONE_FIELD
) {
793 pos
.setBeginIndex(0);
794 pos
.setEndIndex(result
.length());
803 TimeZoneFormat::parse(UTimeZoneFormatStyle style
, const UnicodeString
& text
, ParsePosition
& pos
,
804 UTimeZoneFormatTimeType
* timeType
/*= NULL*/) const {
805 return parse(style
, text
, pos
, getDefaultParseOptions(), timeType
);
809 TimeZoneFormat::parse(UTimeZoneFormatStyle style
, const UnicodeString
& text
, ParsePosition
& pos
,
810 int32_t parseOptions
, UTimeZoneFormatTimeType
* timeType
/* = NULL */) const {
812 *timeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
815 int32_t startIdx
= pos
.getIndex();
816 int32_t maxPos
= text
.length();
819 // Styles using localized GMT format as fallback
820 UBool fallbackLocalizedGMT
=
821 (style
== UTZFMT_STYLE_SPECIFIC_LONG
|| style
== UTZFMT_STYLE_GENERIC_LONG
|| style
== UTZFMT_STYLE_GENERIC_LOCATION
);
822 UBool fallbackShortLocalizedGMT
=
823 (style
== UTZFMT_STYLE_SPECIFIC_SHORT
|| style
== UTZFMT_STYLE_GENERIC_SHORT
);
825 int32_t evaluated
= 0; // bit flags representing already evaluated styles
826 ParsePosition
tmpPos(startIdx
);
828 int32_t parsedOffset
= UNKNOWN_OFFSET
; // stores successfully parsed offset for later use
829 int32_t parsedPos
= -1; // stores successfully parsed offset position for later use
831 // Try localized GMT format first if necessary
832 if (fallbackLocalizedGMT
|| fallbackShortLocalizedGMT
) {
833 UBool hasDigitOffset
= FALSE
;
834 offset
= parseOffsetLocalizedGMT(text
, tmpPos
, fallbackShortLocalizedGMT
, &hasDigitOffset
);
835 if (tmpPos
.getErrorIndex() == -1) {
836 // Even when the input text was successfully parsed as a localized GMT format text,
837 // we may still need to evaluate the specified style if -
838 // 1) GMT zero format was used, and
839 // 2) The input text was not completely processed
840 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
841 pos
.setIndex(tmpPos
.getIndex());
842 return createTimeZoneForOffset(offset
);
844 parsedOffset
= offset
;
845 parsedPos
= tmpPos
.getIndex();
847 // Note: For now, no distinction between long/short localized GMT format in the parser.
848 // This might be changed in future.
849 // evaluated |= (fallbackLocalizedGMT ? STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] : STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]);
850 evaluated
|= STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT
] | STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT_SHORT
];
853 UErrorCode status
= U_ZERO_ERROR
;
855 UnicodeString
tzID(tzIDBuf
, 0, UPRV_LENGTHOF(tzIDBuf
));
857 UBool parseTZDBAbbrev
= ((parseOptions
& UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS
) != 0);
859 // Try the specified style
861 case UTZFMT_STYLE_LOCALIZED_GMT
:
863 tmpPos
.setIndex(startIdx
);
864 tmpPos
.setErrorIndex(-1);
866 offset
= parseOffsetLocalizedGMT(text
, tmpPos
);
867 if (tmpPos
.getErrorIndex() == -1) {
868 pos
.setIndex(tmpPos
.getIndex());
869 return createTimeZoneForOffset(offset
);
872 // Note: For now, no distinction between long/short localized GMT format in the parser.
873 // This might be changed in future.
874 evaluated
|= STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT_SHORT
];
878 case UTZFMT_STYLE_LOCALIZED_GMT_SHORT
:
880 tmpPos
.setIndex(startIdx
);
881 tmpPos
.setErrorIndex(-1);
883 offset
= parseOffsetShortLocalizedGMT(text
, tmpPos
);
884 if (tmpPos
.getErrorIndex() == -1) {
885 pos
.setIndex(tmpPos
.getIndex());
886 return createTimeZoneForOffset(offset
);
889 // Note: For now, no distinction between long/short localized GMT format in the parser.
890 // This might be changed in future.
891 evaluated
|= STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT
];
895 case UTZFMT_STYLE_ISO_BASIC_SHORT
:
896 case UTZFMT_STYLE_ISO_BASIC_FIXED
:
897 case UTZFMT_STYLE_ISO_BASIC_FULL
:
898 case UTZFMT_STYLE_ISO_EXTENDED_FIXED
:
899 case UTZFMT_STYLE_ISO_EXTENDED_FULL
:
901 tmpPos
.setIndex(startIdx
);
902 tmpPos
.setErrorIndex(-1);
904 offset
= parseOffsetISO8601(text
, tmpPos
);
905 if (tmpPos
.getErrorIndex() == -1) {
906 pos
.setIndex(tmpPos
.getIndex());
907 return createTimeZoneForOffset(offset
);
913 case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT
:
914 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED
:
915 case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL
:
916 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED
:
917 case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL
:
919 tmpPos
.setIndex(startIdx
);
920 tmpPos
.setErrorIndex(-1);
922 // Exclude the case of UTC Indicator "Z" here
923 UBool hasDigitOffset
= FALSE
;
924 offset
= parseOffsetISO8601(text
, tmpPos
, FALSE
, &hasDigitOffset
);
925 if (tmpPos
.getErrorIndex() == -1 && hasDigitOffset
) {
926 pos
.setIndex(tmpPos
.getIndex());
927 return createTimeZoneForOffset(offset
);
933 case UTZFMT_STYLE_SPECIFIC_LONG
:
934 case UTZFMT_STYLE_SPECIFIC_SHORT
:
937 int32_t nameTypes
= 0;
938 if (style
== UTZFMT_STYLE_SPECIFIC_LONG
) {
939 nameTypes
= (UTZNM_LONG_STANDARD
| UTZNM_LONG_DAYLIGHT
);
941 U_ASSERT(style
== UTZFMT_STYLE_SPECIFIC_SHORT
);
942 nameTypes
= (UTZNM_SHORT_STANDARD
| UTZNM_SHORT_DAYLIGHT
);
944 LocalPointer
<TimeZoneNames::MatchInfoCollection
> specificMatches(fTimeZoneNames
->find(text
, startIdx
, nameTypes
, status
));
945 if (U_FAILURE(status
)) {
946 pos
.setErrorIndex(startIdx
);
949 if (!specificMatches
.isNull()) {
950 int32_t matchIdx
= -1;
951 int32_t matchPos
= -1;
952 for (int32_t i
= 0; i
< specificMatches
->size(); i
++) {
953 matchPos
= startIdx
+ specificMatches
->getMatchLengthAt(i
);
954 if (matchPos
> parsedPos
) {
956 parsedPos
= matchPos
;
961 *timeType
= getTimeType(specificMatches
->getNameTypeAt(matchIdx
));
963 pos
.setIndex(matchPos
);
964 getTimeZoneID(specificMatches
.getAlias(), matchIdx
, tzID
);
965 U_ASSERT(!tzID
.isEmpty());
966 return TimeZone::createTimeZone(tzID
);
970 if (parseTZDBAbbrev
&& style
== UTZFMT_STYLE_SPECIFIC_SHORT
) {
971 U_ASSERT((nameTypes
& UTZNM_SHORT_STANDARD
) != 0);
972 U_ASSERT((nameTypes
& UTZNM_SHORT_DAYLIGHT
) != 0);
974 const TZDBTimeZoneNames
*tzdbTimeZoneNames
= getTZDBTimeZoneNames(status
);
975 if (U_SUCCESS(status
)) {
976 LocalPointer
<TimeZoneNames::MatchInfoCollection
> tzdbNameMatches(
977 tzdbTimeZoneNames
->find(text
, startIdx
, nameTypes
, status
));
978 if (U_FAILURE(status
)) {
979 pos
.setErrorIndex(startIdx
);
982 if (!tzdbNameMatches
.isNull()) {
983 int32_t matchIdx
= -1;
984 int32_t matchPos
= -1;
985 for (int32_t i
= 0; i
< tzdbNameMatches
->size(); i
++) {
986 matchPos
= startIdx
+ tzdbNameMatches
->getMatchLengthAt(i
);
987 if (matchPos
> parsedPos
) {
989 parsedPos
= matchPos
;
994 *timeType
= getTimeType(tzdbNameMatches
->getNameTypeAt(matchIdx
));
996 pos
.setIndex(matchPos
);
997 getTimeZoneID(tzdbNameMatches
.getAlias(), matchIdx
, tzID
);
998 U_ASSERT(!tzID
.isEmpty());
999 return TimeZone::createTimeZone(tzID
);
1006 case UTZFMT_STYLE_GENERIC_LONG
:
1007 case UTZFMT_STYLE_GENERIC_SHORT
:
1008 case UTZFMT_STYLE_GENERIC_LOCATION
:
1010 int32_t genericNameTypes
= 0;
1012 case UTZFMT_STYLE_GENERIC_LOCATION
:
1013 genericNameTypes
= UTZGNM_LOCATION
;
1016 case UTZFMT_STYLE_GENERIC_LONG
:
1017 genericNameTypes
= UTZGNM_LONG
| UTZGNM_LOCATION
;
1020 case UTZFMT_STYLE_GENERIC_SHORT
:
1021 genericNameTypes
= UTZGNM_SHORT
| UTZGNM_LOCATION
;
1029 UTimeZoneFormatTimeType tt
= UTZFMT_TIME_TYPE_UNKNOWN
;
1030 const TimeZoneGenericNames
*gnames
= getTimeZoneGenericNames(status
);
1031 if (U_SUCCESS(status
)) {
1032 len
= gnames
->findBestMatch(text
, startIdx
, genericNameTypes
, tzID
, tt
, status
);
1034 if (U_FAILURE(status
)) {
1035 pos
.setErrorIndex(startIdx
);
1043 pos
.setIndex(startIdx
+ len
);
1044 U_ASSERT(!tzID
.isEmpty());
1045 return TimeZone::createTimeZone(tzID
);
1050 case UTZFMT_STYLE_ZONE_ID
:
1052 tmpPos
.setIndex(startIdx
);
1053 tmpPos
.setErrorIndex(-1);
1055 parseZoneID(text
, tmpPos
, tzID
);
1056 if (tmpPos
.getErrorIndex() == -1) {
1057 pos
.setIndex(tmpPos
.getIndex());
1058 return TimeZone::createTimeZone(tzID
);
1062 case UTZFMT_STYLE_ZONE_ID_SHORT
:
1064 tmpPos
.setIndex(startIdx
);
1065 tmpPos
.setErrorIndex(-1);
1067 parseShortZoneID(text
, tmpPos
, tzID
);
1068 if (tmpPos
.getErrorIndex() == -1) {
1069 pos
.setIndex(tmpPos
.getIndex());
1070 return TimeZone::createTimeZone(tzID
);
1074 case UTZFMT_STYLE_EXEMPLAR_LOCATION
:
1076 tmpPos
.setIndex(startIdx
);
1077 tmpPos
.setErrorIndex(-1);
1079 parseExemplarLocation(text
, tmpPos
, tzID
);
1080 if (tmpPos
.getErrorIndex() == -1) {
1081 pos
.setIndex(tmpPos
.getIndex());
1082 return TimeZone::createTimeZone(tzID
);
1087 evaluated
|= STYLE_PARSE_FLAGS
[style
];
1090 if (parsedPos
> startIdx
) {
1091 // When the specified style is one of SPECIFIC_XXX or GENERIC_XXX, we tried to parse the input
1092 // as localized GMT format earlier. If parsedOffset is positive, it means it was successfully
1093 // parsed as localized GMT format, but offset digits were not detected (more specifically, GMT
1094 // zero format). Then, it tried to find a match within the set of display names, but could not
1095 // find a match. At this point, we can safely assume the input text contains the localized
1097 U_ASSERT(parsedOffset
!= UNKNOWN_OFFSET
);
1098 pos
.setIndex(parsedPos
);
1099 return createTimeZoneForOffset(parsedOffset
);
1102 // Failed to parse the input text as the time zone format in the specified style.
1103 // Check the longest match among other styles below.
1104 UChar parsedIDBuf
[32];
1105 UnicodeString
parsedID(parsedIDBuf
, 0, UPRV_LENGTHOF(parsedIDBuf
));
1106 UTimeZoneFormatTimeType parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1108 U_ASSERT(parsedPos
< 0);
1109 U_ASSERT(parsedOffset
== UNKNOWN_OFFSET
);
1112 if (parsedPos
< maxPos
&&
1113 ((evaluated
& ISO_Z_STYLE_FLAG
) == 0 || (evaluated
& ISO_LOCAL_STYLE_FLAG
) == 0)) {
1114 tmpPos
.setIndex(startIdx
);
1115 tmpPos
.setErrorIndex(-1);
1117 UBool hasDigitOffset
= FALSE
;
1118 offset
= parseOffsetISO8601(text
, tmpPos
, FALSE
, &hasDigitOffset
);
1119 if (tmpPos
.getErrorIndex() == -1) {
1120 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
1121 pos
.setIndex(tmpPos
.getIndex());
1122 return createTimeZoneForOffset(offset
);
1124 // Note: When ISO 8601 format contains offset digits, it should not
1125 // collide with other formats. However, ISO 8601 UTC format "Z" (single letter)
1126 // may collide with other names. In this case, we need to evaluate other names.
1127 if (parsedPos
< tmpPos
.getIndex()) {
1128 parsedOffset
= offset
;
1129 parsedID
.setToBogus();
1130 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1131 parsedPos
= tmpPos
.getIndex();
1132 U_ASSERT(parsedPos
== startIdx
+ 1); // only when "Z" is used
1137 // Localized GMT format
1138 if (parsedPos
< maxPos
&&
1139 (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT
]) == 0) {
1140 tmpPos
.setIndex(startIdx
);
1141 tmpPos
.setErrorIndex(-1);
1143 UBool hasDigitOffset
= FALSE
;
1144 offset
= parseOffsetLocalizedGMT(text
, tmpPos
, FALSE
, &hasDigitOffset
);
1145 if (tmpPos
.getErrorIndex() == -1) {
1146 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
1147 pos
.setIndex(tmpPos
.getIndex());
1148 return createTimeZoneForOffset(offset
);
1150 // Evaluate other names - see the comment earlier in this method.
1151 if (parsedPos
< tmpPos
.getIndex()) {
1152 parsedOffset
= offset
;
1153 parsedID
.setToBogus();
1154 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1155 parsedPos
= tmpPos
.getIndex();
1160 if (parsedPos
< maxPos
&&
1161 (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_LOCALIZED_GMT_SHORT
]) == 0) {
1162 tmpPos
.setIndex(startIdx
);
1163 tmpPos
.setErrorIndex(-1);
1165 UBool hasDigitOffset
= FALSE
;
1166 offset
= parseOffsetLocalizedGMT(text
, tmpPos
, TRUE
, &hasDigitOffset
);
1167 if (tmpPos
.getErrorIndex() == -1) {
1168 if (tmpPos
.getIndex() == maxPos
|| hasDigitOffset
) {
1169 pos
.setIndex(tmpPos
.getIndex());
1170 return createTimeZoneForOffset(offset
);
1172 // Evaluate other names - see the comment earlier in this method.
1173 if (parsedPos
< tmpPos
.getIndex()) {
1174 parsedOffset
= offset
;
1175 parsedID
.setToBogus();
1176 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1177 parsedPos
= tmpPos
.getIndex();
1182 // When ParseOption.ALL_STYLES is available, we also try to look all possible display names and IDs.
1183 // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never
1184 // used for America/New_York. With parseAllStyles true, this code parses "EST"
1185 // as America/New_York.
1187 // Note: Adding all possible names into the trie used by the implementation is quite heavy operation,
1188 // which we want to avoid normally (note that we cache the trie, so this is applicable to the
1189 // first time only as long as the cache does not expire).
1191 if (parseOptions
& UTZFMT_PARSE_OPTION_ALL_STYLES
) {
1192 // Try all specific names and exemplar location names
1193 if (parsedPos
< maxPos
) {
1194 LocalPointer
<TimeZoneNames::MatchInfoCollection
> specificMatches(fTimeZoneNames
->find(text
, startIdx
, ALL_SIMPLE_NAME_TYPES
, status
));
1195 if (U_FAILURE(status
)) {
1196 pos
.setErrorIndex(startIdx
);
1199 int32_t specificMatchIdx
= -1;
1200 int32_t matchPos
= -1;
1201 if (!specificMatches
.isNull()) {
1202 for (int32_t i
= 0; i
< specificMatches
->size(); i
++) {
1203 if (startIdx
+ specificMatches
->getMatchLengthAt(i
) > matchPos
) {
1204 specificMatchIdx
= i
;
1205 matchPos
= startIdx
+ specificMatches
->getMatchLengthAt(i
);
1209 if (parsedPos
< matchPos
) {
1210 U_ASSERT(specificMatchIdx
>= 0);
1211 parsedPos
= matchPos
;
1212 getTimeZoneID(specificMatches
.getAlias(), specificMatchIdx
, parsedID
);
1213 parsedTimeType
= getTimeType(specificMatches
->getNameTypeAt(specificMatchIdx
));
1214 parsedOffset
= UNKNOWN_OFFSET
;
1217 if (parseTZDBAbbrev
&& parsedPos
< maxPos
&& (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_SPECIFIC_SHORT
]) == 0) {
1218 const TZDBTimeZoneNames
*tzdbTimeZoneNames
= getTZDBTimeZoneNames(status
);
1219 if (U_SUCCESS(status
)) {
1220 LocalPointer
<TimeZoneNames::MatchInfoCollection
> tzdbNameMatches(
1221 tzdbTimeZoneNames
->find(text
, startIdx
, ALL_SIMPLE_NAME_TYPES
, status
));
1222 if (U_FAILURE(status
)) {
1223 pos
.setErrorIndex(startIdx
);
1226 int32_t tzdbNameMatchIdx
= -1;
1227 int32_t matchPos
= -1;
1228 if (!tzdbNameMatches
.isNull()) {
1229 for (int32_t i
= 0; i
< tzdbNameMatches
->size(); i
++) {
1230 if (startIdx
+ tzdbNameMatches
->getMatchLengthAt(i
) > matchPos
) {
1231 tzdbNameMatchIdx
= i
;
1232 matchPos
= startIdx
+ tzdbNameMatches
->getMatchLengthAt(i
);
1236 if (parsedPos
< matchPos
) {
1237 U_ASSERT(tzdbNameMatchIdx
>= 0);
1238 parsedPos
= matchPos
;
1239 getTimeZoneID(tzdbNameMatches
.getAlias(), tzdbNameMatchIdx
, parsedID
);
1240 parsedTimeType
= getTimeType(tzdbNameMatches
->getNameTypeAt(tzdbNameMatchIdx
));
1241 parsedOffset
= UNKNOWN_OFFSET
;
1245 // Try generic names
1246 if (parsedPos
< maxPos
) {
1247 int32_t genMatchLen
= -1;
1248 UTimeZoneFormatTimeType tt
= UTZFMT_TIME_TYPE_UNKNOWN
;
1250 const TimeZoneGenericNames
*gnames
= getTimeZoneGenericNames(status
);
1251 if (U_SUCCESS(status
)) {
1252 genMatchLen
= gnames
->findBestMatch(text
, startIdx
, ALL_GENERIC_NAME_TYPES
, tzID
, tt
, status
);
1254 if (U_FAILURE(status
)) {
1255 pos
.setErrorIndex(startIdx
);
1259 if (genMatchLen
> 0 && parsedPos
< startIdx
+ genMatchLen
) {
1260 parsedPos
= startIdx
+ genMatchLen
;
1261 parsedID
.setTo(tzID
);
1262 parsedTimeType
= tt
;
1263 parsedOffset
= UNKNOWN_OFFSET
;
1268 if (parsedPos
< maxPos
&& (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_ZONE_ID
]) == 0) {
1269 tmpPos
.setIndex(startIdx
);
1270 tmpPos
.setErrorIndex(-1);
1272 parseZoneID(text
, tmpPos
, tzID
);
1273 if (tmpPos
.getErrorIndex() == -1 && parsedPos
< tmpPos
.getIndex()) {
1274 parsedPos
= tmpPos
.getIndex();
1275 parsedID
.setTo(tzID
);
1276 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1277 parsedOffset
= UNKNOWN_OFFSET
;
1280 // Try short time zone ID
1281 if (parsedPos
< maxPos
&& (evaluated
& STYLE_PARSE_FLAGS
[UTZFMT_STYLE_ZONE_ID
]) == 0) {
1282 tmpPos
.setIndex(startIdx
);
1283 tmpPos
.setErrorIndex(-1);
1285 parseShortZoneID(text
, tmpPos
, tzID
);
1286 if (tmpPos
.getErrorIndex() == -1 && parsedPos
< tmpPos
.getIndex()) {
1287 parsedPos
= tmpPos
.getIndex();
1288 parsedID
.setTo(tzID
);
1289 parsedTimeType
= UTZFMT_TIME_TYPE_UNKNOWN
;
1290 parsedOffset
= UNKNOWN_OFFSET
;
1295 if (parsedPos
> startIdx
) {
1296 // Parsed successfully
1298 if (parsedID
.length() > 0) {
1299 parsedTZ
= TimeZone::createTimeZone(parsedID
);
1301 U_ASSERT(parsedOffset
!= UNKNOWN_OFFSET
);
1302 parsedTZ
= createTimeZoneForOffset(parsedOffset
);
1305 *timeType
= parsedTimeType
;
1307 pos
.setIndex(parsedPos
);
1311 pos
.setErrorIndex(startIdx
);
1316 TimeZoneFormat::parseObject(const UnicodeString
& source
, Formattable
& result
,
1317 ParsePosition
& parse_pos
) const {
1318 result
.adoptObject(parse(UTZFMT_STYLE_GENERIC_LOCATION
, source
, parse_pos
, UTZFMT_PARSE_OPTION_ALL_STYLES
));
1322 // ------------------------------------------------------------------
1323 // Private zone name format/parse implementation
1326 TimeZoneFormat::formatGeneric(const TimeZone
& tz
, int32_t genType
, UDate date
, UnicodeString
& name
) const {
1327 UErrorCode status
= U_ZERO_ERROR
;
1328 const TimeZoneGenericNames
* gnames
= getTimeZoneGenericNames(status
);
1329 if (U_FAILURE(status
)) {
1334 if (genType
== UTZGNM_LOCATION
) {
1335 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(tz
);
1336 if (canonicalID
== NULL
) {
1340 return gnames
->getGenericLocationName(UnicodeString(TRUE
, canonicalID
, -1), name
);
1342 return gnames
->getDisplayName(tz
, (UTimeZoneGenericNameType
)genType
, date
, name
);
1346 TimeZoneFormat::formatSpecific(const TimeZone
& tz
, UTimeZoneNameType stdType
, UTimeZoneNameType dstType
,
1347 UDate date
, UnicodeString
& name
, UTimeZoneFormatTimeType
*timeType
) const {
1348 if (fTimeZoneNames
== NULL
) {
1353 UErrorCode status
= U_ZERO_ERROR
;
1354 UBool isDaylight
= tz
.inDaylightTime(date
, status
);
1355 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(tz
);
1357 if (U_FAILURE(status
) || canonicalID
== NULL
) {
1363 fTimeZoneNames
->getDisplayName(UnicodeString(TRUE
, canonicalID
, -1), dstType
, date
, name
);
1365 fTimeZoneNames
->getDisplayName(UnicodeString(TRUE
, canonicalID
, -1), stdType
, date
, name
);
1368 if (timeType
&& !name
.isEmpty()) {
1369 *timeType
= isDaylight
? UTZFMT_TIME_TYPE_DAYLIGHT
: UTZFMT_TIME_TYPE_STANDARD
;
1374 const TimeZoneGenericNames
*
1375 TimeZoneFormat::getTimeZoneGenericNames(UErrorCode
& status
) const {
1376 if (U_FAILURE(status
)) {
1381 if (fTimeZoneGenericNames
== NULL
) {
1382 TimeZoneFormat
*nonConstThis
= const_cast<TimeZoneFormat
*>(this);
1383 nonConstThis
->fTimeZoneGenericNames
= TimeZoneGenericNames::createInstance(fLocale
, status
);
1385 umtx_unlock(&gLock
);
1387 return fTimeZoneGenericNames
;
1390 const TZDBTimeZoneNames
*
1391 TimeZoneFormat::getTZDBTimeZoneNames(UErrorCode
& status
) const {
1392 if (U_FAILURE(status
)) {
1397 if (fTZDBTimeZoneNames
== NULL
) {
1398 TZDBTimeZoneNames
*tzdbNames
= new TZDBTimeZoneNames(fLocale
);
1399 if (tzdbNames
== NULL
) {
1400 status
= U_MEMORY_ALLOCATION_ERROR
;
1402 TimeZoneFormat
*nonConstThis
= const_cast<TimeZoneFormat
*>(this);
1403 nonConstThis
->fTZDBTimeZoneNames
= tzdbNames
;
1406 umtx_unlock(&gLock
);
1408 return fTZDBTimeZoneNames
;
1412 TimeZoneFormat::formatExemplarLocation(const TimeZone
& tz
, UnicodeString
& name
) const {
1413 UChar locationBuf
[64];
1414 UnicodeString
location(locationBuf
, 0, UPRV_LENGTHOF(locationBuf
));
1415 const UChar
* canonicalID
= ZoneMeta::getCanonicalCLDRID(tz
);
1418 fTimeZoneNames
->getExemplarLocationName(UnicodeString(TRUE
, canonicalID
, -1), location
);
1420 if (location
.length() > 0) {
1421 name
.setTo(location
);
1423 // Use "unknown" location
1424 fTimeZoneNames
->getExemplarLocationName(UnicodeString(TRUE
, UNKNOWN_ZONE_ID
, -1), location
);
1425 if (location
.length() > 0) {
1426 name
.setTo(location
);
1429 name
.setTo(UNKNOWN_LOCATION
, -1);
1436 // ------------------------------------------------------------------
1437 // Zone offset format and parse
1440 TimeZoneFormat::formatOffsetISO8601Basic(int32_t offset
, UBool useUtcIndicator
, UBool isShort
, UBool ignoreSeconds
,
1441 UnicodeString
& result
, UErrorCode
& status
) const {
1442 return formatOffsetISO8601(offset
, TRUE
, useUtcIndicator
, isShort
, ignoreSeconds
, result
, status
);
1446 TimeZoneFormat::formatOffsetISO8601Extended(int32_t offset
, UBool useUtcIndicator
, UBool isShort
, UBool ignoreSeconds
,
1447 UnicodeString
& result
, UErrorCode
& status
) const {
1448 return formatOffsetISO8601(offset
, FALSE
, useUtcIndicator
, isShort
, ignoreSeconds
, result
, status
);
1452 TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset
, UnicodeString
& result
, UErrorCode
& status
) const {
1453 return formatOffsetLocalizedGMT(offset
, FALSE
, result
, status
);
1457 TimeZoneFormat::formatOffsetShortLocalizedGMT(int32_t offset
, UnicodeString
& result
, UErrorCode
& status
) const {
1458 return formatOffsetLocalizedGMT(offset
, TRUE
, result
, status
);
1462 TimeZoneFormat::parseOffsetISO8601(const UnicodeString
& text
, ParsePosition
& pos
) const {
1463 return parseOffsetISO8601(text
, pos
, FALSE
);
1467 TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString
& text
, ParsePosition
& pos
) const {
1468 return parseOffsetLocalizedGMT(text
, pos
, FALSE
, NULL
);
1472 TimeZoneFormat::parseOffsetShortLocalizedGMT(const UnicodeString
& text
, ParsePosition
& pos
) const {
1473 return parseOffsetLocalizedGMT(text
, pos
, TRUE
, NULL
);
1476 // ------------------------------------------------------------------
1477 // Private zone offset format/parse implementation
1480 TimeZoneFormat::formatOffsetISO8601(int32_t offset
, UBool isBasic
, UBool useUtcIndicator
,
1481 UBool isShort
, UBool ignoreSeconds
, UnicodeString
& result
, UErrorCode
& status
) const {
1482 if (U_FAILURE(status
)) {
1483 result
.setToBogus();
1486 int32_t absOffset
= offset
< 0 ? -offset
: offset
;
1487 if (useUtcIndicator
&& (absOffset
< MILLIS_PER_SECOND
|| (ignoreSeconds
&& absOffset
< MILLIS_PER_MINUTE
))) {
1488 result
.setTo(ISO8601_UTC
);
1492 OffsetFields minFields
= isShort
? FIELDS_H
: FIELDS_HM
;
1493 OffsetFields maxFields
= ignoreSeconds
? FIELDS_HM
: FIELDS_HMS
;
1494 UChar sep
= isBasic
? 0 : ISO8601_SEP
;
1496 // Note: FIELDS_HMS as maxFields is a CLDR/ICU extension. ISO 8601 specification does
1497 // not support seconds field.
1499 if (absOffset
>= MAX_OFFSET
) {
1500 result
.setToBogus();
1501 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1506 fields
[0] = absOffset
/ MILLIS_PER_HOUR
;
1507 absOffset
= absOffset
% MILLIS_PER_HOUR
;
1508 fields
[1] = absOffset
/ MILLIS_PER_MINUTE
;
1509 absOffset
= absOffset
% MILLIS_PER_MINUTE
;
1510 fields
[2] = absOffset
/ MILLIS_PER_SECOND
;
1512 U_ASSERT(fields
[0] >= 0 && fields
[0] <= MAX_OFFSET_HOUR
);
1513 U_ASSERT(fields
[1] >= 0 && fields
[1] <= MAX_OFFSET_MINUTE
);
1514 U_ASSERT(fields
[2] >= 0 && fields
[2] <= MAX_OFFSET_SECOND
);
1516 int32_t lastIdx
= maxFields
;
1517 while (lastIdx
> minFields
) {
1518 if (fields
[lastIdx
] != 0) {
1526 // if all output fields are 0s, do not use negative sign
1527 for (int32_t idx
= 0; idx
<= lastIdx
; idx
++) {
1528 if (fields
[idx
] != 0) {
1536 for (int32_t idx
= 0; idx
<= lastIdx
; idx
++) {
1537 if (sep
&& idx
!= 0) {
1540 result
.append((UChar
)(0x0030 + fields
[idx
]/10));
1541 result
.append((UChar
)(0x0030 + fields
[idx
]%10
));
1548 TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset
, UBool isShort
, UnicodeString
& result
, UErrorCode
& status
) const {
1549 if (U_FAILURE(status
)) {
1550 result
.setToBogus();
1553 if (offset
<= -MAX_OFFSET
|| offset
>= MAX_OFFSET
) {
1554 result
.setToBogus();
1555 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1560 result
.setTo(fGMTZeroFormat
);
1564 UBool positive
= TRUE
;
1570 int32_t offsetH
= offset
/ MILLIS_PER_HOUR
;
1571 offset
= offset
% MILLIS_PER_HOUR
;
1572 int32_t offsetM
= offset
/ MILLIS_PER_MINUTE
;
1573 offset
= offset
% MILLIS_PER_MINUTE
;
1574 int32_t offsetS
= offset
/ MILLIS_PER_SECOND
;
1576 U_ASSERT(offsetH
<= MAX_OFFSET_HOUR
&& offsetM
<= MAX_OFFSET_MINUTE
&& offsetS
<= MAX_OFFSET_SECOND
);
1578 const UVector
* offsetPatternItems
= NULL
;
1581 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_POSITIVE_HMS
];
1582 } else if (offsetM
!= 0 || !isShort
) {
1583 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_POSITIVE_HM
];
1585 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_POSITIVE_H
];
1589 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_NEGATIVE_HMS
];
1590 } else if (offsetM
!= 0 || !isShort
) {
1591 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_NEGATIVE_HM
];
1593 offsetPatternItems
= fGMTOffsetPatternItems
[UTZFMT_PAT_NEGATIVE_H
];
1597 U_ASSERT(offsetPatternItems
!= NULL
);
1599 // Building the GMT format string
1600 result
.setTo(fGMTPatternPrefix
);
1602 for (int32_t i
= 0; i
< offsetPatternItems
->size(); i
++) {
1603 const GMTOffsetField
* item
= (GMTOffsetField
*)offsetPatternItems
->elementAt(i
);
1604 GMTOffsetField::FieldType type
= item
->getType();
1607 case GMTOffsetField::TEXT
:
1608 result
.append(item
->getPatternText(), -1);
1611 case GMTOffsetField::HOUR
:
1612 appendOffsetDigits(result
, offsetH
, (isShort
? 1 : 2));
1615 case GMTOffsetField::MINUTE
:
1616 appendOffsetDigits(result
, offsetM
, 2);
1619 case GMTOffsetField::SECOND
:
1620 appendOffsetDigits(result
, offsetS
, 2);
1625 result
.append(fGMTPatternSuffix
);
1630 TimeZoneFormat::parseOffsetISO8601(const UnicodeString
& text
, ParsePosition
& pos
, UBool extendedOnly
, UBool
* hasDigitOffset
/* = NULL */) const {
1631 if (hasDigitOffset
) {
1632 *hasDigitOffset
= FALSE
;
1634 int32_t start
= pos
.getIndex();
1635 if (start
>= text
.length()) {
1636 pos
.setErrorIndex(start
);
1640 UChar firstChar
= text
.charAt(start
);
1641 if (firstChar
== ISO8601_UTC
|| firstChar
== (UChar
)(ISO8601_UTC
+ 0x20)) {
1642 // "Z" (or "z") - indicates UTC
1643 pos
.setIndex(start
+ 1);
1648 if (firstChar
== PLUS
) {
1650 } else if (firstChar
== MINUS
) {
1653 // Not an ISO 8601 offset string
1654 pos
.setErrorIndex(start
);
1657 ParsePosition
posOffset(start
+ 1);
1658 int32_t offset
= parseAsciiOffsetFields(text
, posOffset
, ISO8601_SEP
, FIELDS_H
, FIELDS_HMS
);
1659 if (posOffset
.getErrorIndex() == -1 && !extendedOnly
&& (posOffset
.getIndex() - start
<= 3)) {
1660 // If the text is successfully parsed as extended format with the options above, it can be also parsed
1661 // as basic format. For example, "0230" can be parsed as offset 2:00 (only first digits are valid for
1662 // extended format), but it can be parsed as offset 2:30 with basic format. We use longer result.
1663 ParsePosition
posBasic(start
+ 1);
1664 int32_t tmpOffset
= parseAbuttingAsciiOffsetFields(text
, posBasic
, FIELDS_H
, FIELDS_HMS
, FALSE
);
1665 if (posBasic
.getErrorIndex() == -1 && posBasic
.getIndex() > posOffset
.getIndex()) {
1667 posOffset
.setIndex(posBasic
.getIndex());
1671 if (posOffset
.getErrorIndex() != -1) {
1672 pos
.setErrorIndex(start
);
1676 pos
.setIndex(posOffset
.getIndex());
1677 if (hasDigitOffset
) {
1678 *hasDigitOffset
= TRUE
;
1680 return sign
* offset
;
1684 TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString
& text
, ParsePosition
& pos
, UBool isShort
, UBool
* hasDigitOffset
) const {
1685 int32_t start
= pos
.getIndex();
1687 int32_t parsedLength
= 0;
1689 if (hasDigitOffset
) {
1690 *hasDigitOffset
= FALSE
;
1693 offset
= parseOffsetLocalizedGMTPattern(text
, start
, isShort
, parsedLength
);
1695 // For now, parseOffsetLocalizedGMTPattern handles both long and short
1696 // formats, no matter isShort is true or false. This might be changed in future
1697 // when strict parsing is necessary, or different set of patterns are used for
1698 // short/long formats.
1700 if (parsedLength
== 0) {
1701 offset
= parseOffsetLocalizedGMTPattern(text
, start
, !isShort
, parsedLength
);
1705 if (parsedLength
> 0) {
1706 if (hasDigitOffset
) {
1707 *hasDigitOffset
= TRUE
;
1709 pos
.setIndex(start
+ parsedLength
);
1713 // Try the default patterns
1714 offset
= parseOffsetDefaultLocalizedGMT(text
, start
, parsedLength
);
1715 if (parsedLength
> 0) {
1716 if (hasDigitOffset
) {
1717 *hasDigitOffset
= TRUE
;
1719 pos
.setIndex(start
+ parsedLength
);
1723 // Check if this is a GMT zero format
1724 if (text
.caseCompare(start
, fGMTZeroFormat
.length(), fGMTZeroFormat
, 0) == 0) {
1725 pos
.setIndex(start
+ fGMTZeroFormat
.length());
1729 // Check if this is a default GMT zero format
1730 for (int32_t i
= 0; ALT_GMT_STRINGS
[i
][0] != 0; i
++) {
1731 const UChar
* defGMTZero
= ALT_GMT_STRINGS
[i
];
1732 int32_t defGMTZeroLen
= u_strlen(defGMTZero
);
1733 if (text
.caseCompare(start
, defGMTZeroLen
, defGMTZero
, 0) == 0) {
1734 pos
.setIndex(start
+ defGMTZeroLen
);
1740 pos
.setErrorIndex(start
);
1745 TimeZoneFormat::parseOffsetLocalizedGMTPattern(const UnicodeString
& text
, int32_t start
, UBool
/*isShort*/, int32_t& parsedLen
) const {
1746 int32_t idx
= start
;
1748 UBool parsed
= FALSE
;
1752 int32_t len
= fGMTPatternPrefix
.length();
1753 if (len
> 0 && text
.caseCompare(idx
, len
, fGMTPatternPrefix
, 0) != 0) {
1754 // prefix match failed
1760 offset
= parseOffsetFields(text
, idx
, FALSE
, len
);
1762 // offset field match failed
1767 len
= fGMTPatternSuffix
.length();
1768 if (len
> 0 && text
.caseCompare(idx
, len
, fGMTPatternSuffix
, 0) != 0) {
1776 parsedLen
= parsed
? idx
- start
: 0;
1781 TimeZoneFormat::parseOffsetFields(const UnicodeString
& text
, int32_t start
, UBool
/*isShort*/, int32_t& parsedLen
) const {
1788 int32_t offsetH
, offsetM
, offsetS
;
1789 offsetH
= offsetM
= offsetS
= 0;
1791 for (int32_t patidx
= 0; PARSE_GMT_OFFSET_TYPES
[patidx
] >= 0; patidx
++) {
1792 int32_t gmtPatType
= PARSE_GMT_OFFSET_TYPES
[patidx
];
1793 UVector
* items
= fGMTOffsetPatternItems
[gmtPatType
];
1794 U_ASSERT(items
!= NULL
);
1796 outLen
= parseOffsetFieldsWithPattern(text
, start
, items
, FALSE
, offsetH
, offsetM
, offsetS
);
1798 sign
= (gmtPatType
== UTZFMT_PAT_POSITIVE_H
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HM
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HMS
) ?
1804 if (outLen
> 0 && fAbuttingOffsetHoursAndMinutes
) {
1805 // When hours field is sabutting minutes field,
1806 // the parse result above may not be appropriate.
1807 // For example, "01020" is parsed as 01:02: above,
1808 // but it should be parsed as 00:10:20.
1810 int32_t tmpSign
= 1;
1811 int32_t tmpH
, tmpM
, tmpS
;
1813 for (int32_t patidx
= 0; PARSE_GMT_OFFSET_TYPES
[patidx
] >= 0; patidx
++) {
1814 int32_t gmtPatType
= PARSE_GMT_OFFSET_TYPES
[patidx
];
1815 UVector
* items
= fGMTOffsetPatternItems
[gmtPatType
];
1816 U_ASSERT(items
!= NULL
);
1818 // forcing parse to use single hour digit
1819 tmpLen
= parseOffsetFieldsWithPattern(text
, start
, items
, TRUE
, tmpH
, tmpM
, tmpS
);
1821 tmpSign
= (gmtPatType
== UTZFMT_PAT_POSITIVE_H
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HM
|| gmtPatType
== UTZFMT_PAT_POSITIVE_HMS
) ?
1826 if (tmpLen
> outLen
) {
1827 // Better parse result with single hour digit
1837 offset
= ((((offsetH
* 60) + offsetM
) * 60) + offsetS
) * 1000 * sign
;
1845 TimeZoneFormat::parseOffsetFieldsWithPattern(const UnicodeString
& text
, int32_t start
,
1846 UVector
* patternItems
, UBool forceSingleHourDigit
, int32_t& hour
, int32_t& min
, int32_t& sec
) const {
1847 UBool failed
= FALSE
;
1848 int32_t offsetH
, offsetM
, offsetS
;
1849 offsetH
= offsetM
= offsetS
= 0;
1850 int32_t idx
= start
;
1852 for (int32_t i
= 0; i
< patternItems
->size(); i
++) {
1854 const GMTOffsetField
* field
= (const GMTOffsetField
*)patternItems
->elementAt(i
);
1855 GMTOffsetField::FieldType fieldType
= field
->getType();
1856 if (fieldType
== GMTOffsetField::TEXT
) {
1857 const UChar
* patStr
= field
->getPatternText();
1858 len
= u_strlen(patStr
);
1859 if (text
.caseCompare(idx
, len
, patStr
, 0) != 0) {
1865 if (fieldType
== GMTOffsetField::HOUR
) {
1866 uint8_t maxDigits
= forceSingleHourDigit
? 1 : 2;
1867 offsetH
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 1, maxDigits
, 0, MAX_OFFSET_HOUR
, len
);
1868 } else if (fieldType
== GMTOffsetField::MINUTE
) {
1869 offsetM
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 2, 2, 0, MAX_OFFSET_MINUTE
, len
);
1870 } else if (fieldType
== GMTOffsetField::SECOND
) {
1871 offsetS
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 2, 2, 0, MAX_OFFSET_SECOND
, len
);
1883 hour
= min
= sec
= 0;
1895 TimeZoneFormat::parseAbuttingOffsetFields(const UnicodeString
& text
, int32_t start
, int32_t& parsedLen
) const {
1896 int32_t digits
[MAX_OFFSET_DIGITS
];
1897 int32_t parsed
[MAX_OFFSET_DIGITS
]; // accumulative offsets
1899 // Parse digits into int[]
1900 int32_t idx
= start
;
1902 int32_t numDigits
= 0;
1903 for (int32_t i
= 0; i
< MAX_OFFSET_DIGITS
; i
++) {
1904 digits
[i
] = parseSingleLocalizedDigit(text
, idx
, len
);
1905 if (digits
[i
] < 0) {
1909 parsed
[i
] = idx
- start
;
1913 if (numDigits
== 0) {
1919 while (numDigits
> 0) {
1924 U_ASSERT(numDigits
> 0 && numDigits
<= MAX_OFFSET_DIGITS
);
1925 switch (numDigits
) {
1930 hour
= digits
[0] * 10 + digits
[1];
1934 min
= digits
[1] * 10 + digits
[2];
1937 hour
= digits
[0] * 10 + digits
[1];
1938 min
= digits
[2] * 10 + digits
[3];
1942 min
= digits
[1] * 10 + digits
[2];
1943 sec
= digits
[3] * 10 + digits
[4];
1946 hour
= digits
[0] * 10 + digits
[1];
1947 min
= digits
[2] * 10 + digits
[3];
1948 sec
= digits
[4] * 10 + digits
[5];
1951 if (hour
<= MAX_OFFSET_HOUR
&& min
<= MAX_OFFSET_MINUTE
&& sec
<= MAX_OFFSET_SECOND
) {
1952 // found a valid combination
1953 offset
= hour
* MILLIS_PER_HOUR
+ min
* MILLIS_PER_MINUTE
+ sec
* MILLIS_PER_SECOND
;
1954 parsedLen
= parsed
[numDigits
- 1];
1963 TimeZoneFormat::parseOffsetDefaultLocalizedGMT(const UnicodeString
& text
, int start
, int32_t& parsedLen
) const {
1964 int32_t idx
= start
;
1969 // check global default GMT alternatives
1972 for (int32_t i
= 0; ALT_GMT_STRINGS
[i
][0] != 0; i
++) {
1973 const UChar
* gmt
= ALT_GMT_STRINGS
[i
];
1974 int32_t len
= u_strlen(gmt
);
1975 if (text
.caseCompare(start
, len
, gmt
, 0) == 0) {
1985 // offset needs a sign char and a digit at minimum
1986 if (idx
+ 1 >= text
.length()) {
1992 UChar c
= text
.charAt(idx
);
1995 } else if (c
== MINUS
) {
2003 // try the default pattern with the separator first
2004 int32_t lenWithSep
= 0;
2005 int32_t offsetWithSep
= parseDefaultOffsetFields(text
, idx
, DEFAULT_GMT_OFFSET_SEP
, lenWithSep
);
2006 if (lenWithSep
== text
.length() - idx
) {
2008 offset
= offsetWithSep
* sign
;
2011 // try abutting field pattern
2012 int32_t lenAbut
= 0;
2013 int32_t offsetAbut
= parseAbuttingOffsetFields(text
, idx
, lenAbut
);
2015 if (lenWithSep
> lenAbut
) {
2016 offset
= offsetWithSep
* sign
;
2019 offset
= offsetAbut
* sign
;
2023 parsed
= idx
- start
;
2031 TimeZoneFormat::parseDefaultOffsetFields(const UnicodeString
& text
, int32_t start
, UChar separator
, int32_t& parsedLen
) const {
2032 int32_t max
= text
.length();
2033 int32_t idx
= start
;
2035 int32_t hour
= 0, min
= 0, sec
= 0;
2040 hour
= parseOffsetFieldWithLocalizedDigits(text
, idx
, 1, 2, 0, MAX_OFFSET_HOUR
, len
);
2046 if (idx
+ 1 < max
&& text
.charAt(idx
) == separator
) {
2047 min
= parseOffsetFieldWithLocalizedDigits(text
, idx
+ 1, 2, 2, 0, MAX_OFFSET_MINUTE
, len
);
2053 if (idx
+ 1 < max
&& text
.charAt(idx
) == separator
) {
2054 sec
= parseOffsetFieldWithLocalizedDigits(text
, idx
+ 1, 2, 2, 0, MAX_OFFSET_SECOND
, len
);
2067 parsedLen
= idx
- start
;
2068 return hour
* MILLIS_PER_HOUR
+ min
* MILLIS_PER_MINUTE
+ sec
* MILLIS_PER_SECOND
;
2072 TimeZoneFormat::parseOffsetFieldWithLocalizedDigits(const UnicodeString
& text
, int32_t start
, uint8_t minDigits
, uint8_t maxDigits
, uint16_t minVal
, uint16_t maxVal
, int32_t& parsedLen
) const {
2076 int32_t numDigits
= 0;
2077 int32_t idx
= start
;
2078 int32_t digitLen
= 0;
2080 while (idx
< text
.length() && numDigits
< maxDigits
) {
2081 int32_t digit
= parseSingleLocalizedDigit(text
, idx
, digitLen
);
2085 int32_t tmpVal
= decVal
* 10 + digit
;
2086 if (tmpVal
> maxVal
) {
2094 // Note: maxVal is checked in the while loop
2095 if (numDigits
< minDigits
|| decVal
< minVal
) {
2099 parsedLen
= idx
- start
;
2106 TimeZoneFormat::parseSingleLocalizedDigit(const UnicodeString
& text
, int32_t start
, int32_t& len
) const {
2109 if (start
< text
.length()) {
2110 UChar32 cp
= text
.char32At(start
);
2112 // First, try digits configured for this instance
2113 for (int32_t i
= 0; i
< 10; i
++) {
2114 if (cp
== fGMTOffsetDigits
[i
]) {
2119 // If failed, check if this is a Unicode digit
2121 int32_t tmp
= u_charDigitValue(cp
);
2122 digit
= (tmp
>= 0 && tmp
<= 9) ? tmp
: -1;
2126 int32_t next
= text
.moveIndex32(start
, 1);
2134 TimeZoneFormat::formatOffsetWithAsciiDigits(int32_t offset
, UChar sep
, OffsetFields minFields
, OffsetFields maxFields
, UnicodeString
& result
) {
2135 U_ASSERT(maxFields
>= minFields
);
2136 U_ASSERT(offset
> -MAX_OFFSET
&& offset
< MAX_OFFSET
);
2146 fields
[0] = offset
/ MILLIS_PER_HOUR
;
2147 offset
= offset
% MILLIS_PER_HOUR
;
2148 fields
[1] = offset
/ MILLIS_PER_MINUTE
;
2149 offset
= offset
% MILLIS_PER_MINUTE
;
2150 fields
[2] = offset
/ MILLIS_PER_SECOND
;
2152 U_ASSERT(fields
[0] >= 0 && fields
[0] <= MAX_OFFSET_HOUR
);
2153 U_ASSERT(fields
[1] >= 0 && fields
[1] <= MAX_OFFSET_MINUTE
);
2154 U_ASSERT(fields
[2] >= 0 && fields
[2] <= MAX_OFFSET_SECOND
);
2156 int32_t lastIdx
= maxFields
;
2157 while (lastIdx
> minFields
) {
2158 if (fields
[lastIdx
] != 0) {
2164 for (int32_t idx
= 0; idx
<= lastIdx
; idx
++) {
2165 if (sep
&& idx
!= 0) {
2168 result
.append((UChar
)(0x0030 + fields
[idx
]/10));
2169 result
.append((UChar
)(0x0030 + fields
[idx
]%10
));
2176 TimeZoneFormat::parseAbuttingAsciiOffsetFields(const UnicodeString
& text
, ParsePosition
& pos
, OffsetFields minFields
, OffsetFields maxFields
, UBool fixedHourWidth
) {
2177 int32_t start
= pos
.getIndex();
2179 int32_t minDigits
= 2 * (minFields
+ 1) - (fixedHourWidth
? 0 : 1);
2180 int32_t maxDigits
= 2 * (maxFields
+ 1);
2182 U_ASSERT(maxDigits
<= MAX_OFFSET_DIGITS
);
2184 int32_t digits
[MAX_OFFSET_DIGITS
] = {};
2185 int32_t numDigits
= 0;
2186 int32_t idx
= start
;
2187 while (numDigits
< maxDigits
&& idx
< text
.length()) {
2188 UChar uch
= text
.charAt(idx
);
2189 int32_t digit
= DIGIT_VAL(uch
);
2193 digits
[numDigits
] = digit
;
2198 if (fixedHourWidth
&& (numDigits
& 1)) {
2199 // Fixed digits, so the number of digits must be even number. Truncating.
2203 if (numDigits
< minDigits
) {
2204 pos
.setErrorIndex(start
);
2208 int32_t hour
= 0, min
= 0, sec
= 0;
2209 UBool bParsed
= FALSE
;
2210 while (numDigits
>= minDigits
) {
2211 switch (numDigits
) {
2216 hour
= digits
[0] * 10 + digits
[1];
2220 min
= digits
[1] * 10 + digits
[2];
2223 hour
= digits
[0] * 10 + digits
[1];
2224 min
= digits
[2] * 10 + digits
[3];
2228 min
= digits
[1] * 10 + digits
[2];
2229 sec
= digits
[3] * 10 + digits
[4];
2232 hour
= digits
[0] * 10 + digits
[1];
2233 min
= digits
[2] * 10 + digits
[3];
2234 sec
= digits
[4] * 10 + digits
[5];
2238 if (hour
<= MAX_OFFSET_HOUR
&& min
<= MAX_OFFSET_MINUTE
&& sec
<= MAX_OFFSET_SECOND
) {
2239 // Successfully parsed
2245 numDigits
-= (fixedHourWidth
? 2 : 1);
2246 hour
= min
= sec
= 0;
2250 pos
.setErrorIndex(start
);
2253 pos
.setIndex(start
+ numDigits
);
2254 return ((((hour
* 60) + min
) * 60) + sec
) * 1000;
2258 TimeZoneFormat::parseAsciiOffsetFields(const UnicodeString
& text
, ParsePosition
& pos
, UChar sep
, OffsetFields minFields
, OffsetFields maxFields
) {
2259 int32_t start
= pos
.getIndex();
2260 int32_t fieldVal
[] = {0, 0, 0};
2261 int32_t fieldLen
[] = {0, -1, -1};
2262 for (int32_t idx
= start
, fieldIdx
= 0; idx
< text
.length() && fieldIdx
<= maxFields
; idx
++) {
2263 UChar c
= text
.charAt(idx
);
2265 if (fieldIdx
== 0) {
2266 if (fieldLen
[0] == 0) {
2270 // 1 digit hour, move to next field
2272 if (fieldLen
[fieldIdx
] != -1) {
2273 // premature minute or seconds field
2276 fieldLen
[fieldIdx
] = 0;
2279 } else if (fieldLen
[fieldIdx
] == -1) {
2280 // no separator after 2 digit field
2283 int32_t digit
= DIGIT_VAL(c
);
2288 fieldVal
[fieldIdx
] = fieldVal
[fieldIdx
] * 10 + digit
;
2289 fieldLen
[fieldIdx
]++;
2290 if (fieldLen
[fieldIdx
] >= 2) {
2291 // parsed 2 digits, move to next field
2297 int32_t parsedLen
= 0;
2298 int32_t parsedFields
= -1;
2301 if (fieldLen
[0] == 0) {
2304 if (fieldVal
[0] > MAX_OFFSET_HOUR
) {
2305 offset
= (fieldVal
[0] / 10) * MILLIS_PER_HOUR
;
2306 parsedFields
= FIELDS_H
;
2310 offset
= fieldVal
[0] * MILLIS_PER_HOUR
;
2311 parsedLen
= fieldLen
[0];
2312 parsedFields
= FIELDS_H
;
2315 if (fieldLen
[1] != 2 || fieldVal
[1] > MAX_OFFSET_MINUTE
) {
2318 offset
+= fieldVal
[1] * MILLIS_PER_MINUTE
;
2319 parsedLen
+= (1 + fieldLen
[1]);
2320 parsedFields
= FIELDS_HM
;
2323 if (fieldLen
[2] != 2 || fieldVal
[2] > MAX_OFFSET_SECOND
) {
2326 offset
+= fieldVal
[2] * MILLIS_PER_SECOND
;
2327 parsedLen
+= (1 + fieldLen
[2]);
2328 parsedFields
= FIELDS_HMS
;
2331 if (parsedFields
< minFields
) {
2332 pos
.setErrorIndex(start
);
2336 pos
.setIndex(start
+ parsedLen
);
2341 TimeZoneFormat::appendOffsetDigits(UnicodeString
& buf
, int32_t n
, uint8_t minDigits
) const {
2342 U_ASSERT(n
>= 0 && n
< 60);
2343 int32_t numDigits
= n
>= 10 ? 2 : 1;
2344 for (int32_t i
= 0; i
< minDigits
- numDigits
; i
++) {
2345 buf
.append(fGMTOffsetDigits
[0]);
2347 if (numDigits
== 2) {
2348 buf
.append(fGMTOffsetDigits
[n
/ 10]);
2350 buf
.append(fGMTOffsetDigits
[n
% 10]);
2353 // ------------------------------------------------------------------
2356 TimeZoneFormat::initGMTPattern(const UnicodeString
& gmtPattern
, UErrorCode
& status
) {
2357 if (U_FAILURE(status
)) {
2360 // This implementation not perfect, but sufficient practically.
2361 int32_t idx
= gmtPattern
.indexOf(ARG0
, ARG0_LEN
, 0);
2363 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2366 fGMTPattern
.setTo(gmtPattern
);
2367 unquote(gmtPattern
.tempSubString(0, idx
), fGMTPatternPrefix
);
2368 unquote(gmtPattern
.tempSubString(idx
+ ARG0_LEN
), fGMTPatternSuffix
);
2372 TimeZoneFormat::unquote(const UnicodeString
& pattern
, UnicodeString
& result
) {
2373 if (pattern
.indexOf(SINGLEQUOTE
) < 0) {
2374 result
.setTo(pattern
);
2378 UBool isPrevQuote
= FALSE
;
2379 UBool inQuote
= FALSE
;
2380 for (int32_t i
= 0; i
< pattern
.length(); i
++) {
2381 UChar c
= pattern
.charAt(i
);
2382 if (c
== SINGLEQUOTE
) {
2385 isPrevQuote
= FALSE
;
2391 isPrevQuote
= FALSE
;
2399 TimeZoneFormat::parseOffsetPattern(const UnicodeString
& pattern
, OffsetFields required
, UErrorCode
& status
) {
2400 if (U_FAILURE(status
)) {
2403 UVector
* result
= new UVector(deleteGMTOffsetField
, NULL
, status
);
2404 if (result
== NULL
) {
2405 status
= U_MEMORY_ALLOCATION_ERROR
;
2409 int32_t checkBits
= 0;
2410 UBool isPrevQuote
= FALSE
;
2411 UBool inQuote
= FALSE
;
2413 UnicodeString
text(textBuf
, 0, UPRV_LENGTHOF(textBuf
));
2414 GMTOffsetField::FieldType itemType
= GMTOffsetField::TEXT
;
2415 int32_t itemLength
= 1;
2417 for (int32_t i
= 0; i
< pattern
.length(); i
++) {
2418 UChar ch
= pattern
.charAt(i
);
2419 if (ch
== SINGLEQUOTE
) {
2421 text
.append(SINGLEQUOTE
);
2422 isPrevQuote
= FALSE
;
2425 if (itemType
!= GMTOffsetField::TEXT
) {
2426 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2427 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, (uint8_t)itemLength
, status
);
2428 result
->addElement(fld
, status
);
2429 if (U_FAILURE(status
)) {
2433 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2436 itemType
= GMTOffsetField::TEXT
;
2441 isPrevQuote
= FALSE
;
2445 GMTOffsetField::FieldType tmpType
= GMTOffsetField::getTypeByLetter(ch
);
2446 if (tmpType
!= GMTOffsetField::TEXT
) {
2447 // an offset time pattern character
2448 if (tmpType
== itemType
) {
2451 if (itemType
== GMTOffsetField::TEXT
) {
2452 if (text
.length() > 0) {
2453 GMTOffsetField
* textfld
= GMTOffsetField::createText(text
, status
);
2454 result
->addElement(textfld
, status
);
2455 if (U_FAILURE(status
)) {
2461 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2462 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, itemLength
, status
);
2463 result
->addElement(fld
, status
);
2464 if (U_FAILURE(status
)) {
2468 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2474 checkBits
|= tmpType
;
2478 if (itemType
!= GMTOffsetField::TEXT
) {
2479 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2480 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, itemLength
, status
);
2481 result
->addElement(fld
, status
);
2482 if (U_FAILURE(status
)) {
2486 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2489 itemType
= GMTOffsetField::TEXT
;
2497 if (U_SUCCESS(status
)) {
2498 if (itemType
== GMTOffsetField::TEXT
) {
2499 if (text
.length() > 0) {
2500 GMTOffsetField
* tfld
= GMTOffsetField::createText(text
, status
);
2501 result
->addElement(tfld
, status
);
2504 if (GMTOffsetField::isValid(itemType
, itemLength
)) {
2505 GMTOffsetField
* fld
= GMTOffsetField::createTimeField(itemType
, itemLength
, status
);
2506 result
->addElement(fld
, status
);
2508 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2512 // Check all required fields are set
2513 if (U_SUCCESS(status
)) {
2514 int32_t reqBits
= 0;
2517 reqBits
= GMTOffsetField::HOUR
;
2520 reqBits
= GMTOffsetField::HOUR
| GMTOffsetField::MINUTE
;
2523 reqBits
= GMTOffsetField::HOUR
| GMTOffsetField::MINUTE
| GMTOffsetField::SECOND
;
2526 if (checkBits
== reqBits
) {
2527 // all required fields are set, no extra fields
2539 TimeZoneFormat::expandOffsetPattern(const UnicodeString
& offsetHM
, UnicodeString
& result
, UErrorCode
& status
) {
2540 result
.setToBogus();
2541 if (U_FAILURE(status
)) {
2544 U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
) == 2);
2546 int32_t idx_mm
= offsetHM
.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
, 2, 0);
2548 // Bad time zone hour pattern data
2549 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2554 int32_t idx_H
= offsetHM
.tempSubString(0, idx_mm
).lastIndexOf((UChar
)0x0048 /* H */);
2556 sep
= offsetHM
.tempSubString(idx_H
+ 1, idx_mm
- (idx_H
+ 1));
2558 result
.setTo(offsetHM
.tempSubString(0, idx_mm
+ 2));
2560 result
.append(DEFAULT_GMT_OFFSET_SECOND_PATTERN
, -1);
2561 result
.append(offsetHM
.tempSubString(idx_mm
+ 2));
2566 TimeZoneFormat::truncateOffsetPattern(const UnicodeString
& offsetHM
, UnicodeString
& result
, UErrorCode
& status
) {
2567 result
.setToBogus();
2568 if (U_FAILURE(status
)) {
2571 U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
) == 2);
2573 int32_t idx_mm
= offsetHM
.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN
, 2, 0);
2575 // Bad time zone hour pattern data
2576 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2579 UChar HH
[] = {0x0048, 0x0048};
2580 int32_t idx_HH
= offsetHM
.tempSubString(0, idx_mm
).lastIndexOf(HH
, 2, 0);
2582 return result
.setTo(offsetHM
.tempSubString(0, idx_HH
+ 2));
2584 int32_t idx_H
= offsetHM
.tempSubString(0, idx_mm
).lastIndexOf((UChar
)0x0048, 0);
2586 return result
.setTo(offsetHM
.tempSubString(0, idx_H
+ 1));
2588 // Bad time zone hour pattern data
2589 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2594 TimeZoneFormat::initGMTOffsetPatterns(UErrorCode
& status
) {
2595 for (int32_t type
= 0; type
< UTZFMT_PAT_COUNT
; type
++) {
2597 case UTZFMT_PAT_POSITIVE_H
:
2598 case UTZFMT_PAT_NEGATIVE_H
:
2599 fGMTOffsetPatternItems
[type
] = parseOffsetPattern(fGMTOffsetPatterns
[type
], FIELDS_H
, status
);
2601 case UTZFMT_PAT_POSITIVE_HM
:
2602 case UTZFMT_PAT_NEGATIVE_HM
:
2603 fGMTOffsetPatternItems
[type
] = parseOffsetPattern(fGMTOffsetPatterns
[type
], FIELDS_HM
, status
);
2605 case UTZFMT_PAT_POSITIVE_HMS
:
2606 case UTZFMT_PAT_NEGATIVE_HMS
:
2607 fGMTOffsetPatternItems
[type
] = parseOffsetPattern(fGMTOffsetPatterns
[type
], FIELDS_HMS
, status
);
2611 checkAbuttingHoursAndMinutes();
2615 TimeZoneFormat::checkAbuttingHoursAndMinutes() {
2616 fAbuttingOffsetHoursAndMinutes
= FALSE
;
2617 for (int32_t type
= 0; type
< UTZFMT_PAT_COUNT
; type
++) {
2618 UBool afterH
= FALSE
;
2619 UVector
*items
= fGMTOffsetPatternItems
[type
];
2620 for (int32_t i
= 0; i
< items
->size(); i
++) {
2621 const GMTOffsetField
* item
= (GMTOffsetField
*)items
->elementAt(i
);
2622 GMTOffsetField::FieldType type
= item
->getType();
2623 if (type
!= GMTOffsetField::TEXT
) {
2625 fAbuttingOffsetHoursAndMinutes
= TRUE
;
2627 } else if (type
== GMTOffsetField::HOUR
) {
2630 } else if (afterH
) {
2634 if (fAbuttingOffsetHoursAndMinutes
) {
2641 TimeZoneFormat::toCodePoints(const UnicodeString
& str
, UChar32
* codeArray
, int32_t size
) {
2642 int32_t count
= str
.countChar32();
2643 if (count
!= size
) {
2647 for (int32_t idx
= 0, start
= 0; idx
< size
; idx
++) {
2648 codeArray
[idx
] = str
.char32At(start
);
2649 start
= str
.moveIndex32(start
, 1);
2656 TimeZoneFormat::createTimeZoneForOffset(int32_t offset
) const {
2658 // when offset is 0, we should use "Etc/GMT"
2659 return TimeZone::createTimeZone(UnicodeString(TRUE
, TZID_GMT
, -1));
2661 return ZoneMeta::createCustomTimeZone(offset
);
2664 UTimeZoneFormatTimeType
2665 TimeZoneFormat::getTimeType(UTimeZoneNameType nameType
) {
2667 case UTZNM_LONG_STANDARD
:
2668 case UTZNM_SHORT_STANDARD
:
2669 return UTZFMT_TIME_TYPE_STANDARD
;
2671 case UTZNM_LONG_DAYLIGHT
:
2672 case UTZNM_SHORT_DAYLIGHT
:
2673 return UTZFMT_TIME_TYPE_DAYLIGHT
;
2676 return UTZFMT_TIME_TYPE_UNKNOWN
;
2681 TimeZoneFormat::getTimeZoneID(const TimeZoneNames::MatchInfoCollection
* matches
, int32_t idx
, UnicodeString
& tzID
) const {
2682 if (!matches
->getTimeZoneIDAt(idx
, tzID
)) {
2684 UnicodeString
mzID(mzIDBuf
, 0, UPRV_LENGTHOF(mzIDBuf
));
2685 if (matches
->getMetaZoneIDAt(idx
, mzID
)) {
2686 fTimeZoneNames
->getReferenceZoneID(mzID
, fTargetRegion
, tzID
);
2693 class ZoneIdMatchHandler
: public TextTrieMapSearchResultHandler
{
2695 ZoneIdMatchHandler();
2696 virtual ~ZoneIdMatchHandler();
2698 UBool
handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
);
2699 const UChar
* getID();
2700 int32_t getMatchLen();
2706 ZoneIdMatchHandler::ZoneIdMatchHandler()
2707 : fLen(0), fID(NULL
) {
2710 ZoneIdMatchHandler::~ZoneIdMatchHandler() {
2714 ZoneIdMatchHandler::handleMatch(int32_t matchLength
, const CharacterNode
*node
, UErrorCode
&status
) {
2715 if (U_FAILURE(status
)) {
2718 if (node
->hasValues()) {
2719 const UChar
* id
= (const UChar
*)node
->getValue(0);
2721 if (fLen
< matchLength
) {
2731 ZoneIdMatchHandler::getID() {
2736 ZoneIdMatchHandler::getMatchLen() {
2741 static void U_CALLCONV
initZoneIdTrie(UErrorCode
&status
) {
2742 U_ASSERT(gZoneIdTrie
== NULL
);
2743 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT
, tzfmt_cleanup
);
2744 gZoneIdTrie
= new TextTrieMap(TRUE
, NULL
); // No deleter, because values are pooled by ZoneMeta
2745 if (gZoneIdTrie
== NULL
) {
2746 status
= U_MEMORY_ALLOCATION_ERROR
;
2749 StringEnumeration
*tzenum
= TimeZone::createEnumeration();
2750 const UnicodeString
*id
;
2751 while ((id
= tzenum
->snext(status
))) {
2752 const UChar
* uid
= ZoneMeta::findTimeZoneID(*id
);
2754 gZoneIdTrie
->put(uid
, const_cast<UChar
*>(uid
), status
);
2762 TimeZoneFormat::parseZoneID(const UnicodeString
& text
, ParsePosition
& pos
, UnicodeString
& tzID
) const {
2763 UErrorCode status
= U_ZERO_ERROR
;
2764 umtx_initOnce(gZoneIdTrieInitOnce
, &initZoneIdTrie
, status
);
2766 int32_t start
= pos
.getIndex();
2770 if (U_SUCCESS(status
)) {
2771 LocalPointer
<ZoneIdMatchHandler
> handler(new ZoneIdMatchHandler());
2772 gZoneIdTrie
->search(text
, start
, handler
.getAlias(), status
);
2773 len
= handler
->getMatchLen();
2775 tzID
.setTo(handler
->getID(), -1);
2780 pos
.setIndex(start
+ len
);
2782 pos
.setErrorIndex(start
);
2788 static void U_CALLCONV
initShortZoneIdTrie(UErrorCode
&status
) {
2789 U_ASSERT(gShortZoneIdTrie
== NULL
);
2790 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT
, tzfmt_cleanup
);
2791 StringEnumeration
*tzenum
= TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL
, NULL
, NULL
, status
);
2792 if (U_SUCCESS(status
)) {
2793 gShortZoneIdTrie
= new TextTrieMap(TRUE
, NULL
); // No deleter, because values are pooled by ZoneMeta
2794 if (gShortZoneIdTrie
== NULL
) {
2795 status
= U_MEMORY_ALLOCATION_ERROR
;
2797 const UnicodeString
*id
;
2798 while ((id
= tzenum
->snext(status
))) {
2799 const UChar
* uID
= ZoneMeta::findTimeZoneID(*id
);
2800 const UChar
* shortID
= ZoneMeta::getShortID(*id
);
2801 if (shortID
&& uID
) {
2802 gShortZoneIdTrie
->put(shortID
, const_cast<UChar
*>(uID
), status
);
2812 TimeZoneFormat::parseShortZoneID(const UnicodeString
& text
, ParsePosition
& pos
, UnicodeString
& tzID
) const {
2813 UErrorCode status
= U_ZERO_ERROR
;
2814 umtx_initOnce(gShortZoneIdTrieInitOnce
, &initShortZoneIdTrie
, status
);
2816 int32_t start
= pos
.getIndex();
2820 if (U_SUCCESS(status
)) {
2821 LocalPointer
<ZoneIdMatchHandler
> handler(new ZoneIdMatchHandler());
2822 gShortZoneIdTrie
->search(text
, start
, handler
.getAlias(), status
);
2823 len
= handler
->getMatchLen();
2825 tzID
.setTo(handler
->getID(), -1);
2830 pos
.setIndex(start
+ len
);
2832 pos
.setErrorIndex(start
);
2840 TimeZoneFormat::parseExemplarLocation(const UnicodeString
& text
, ParsePosition
& pos
, UnicodeString
& tzID
) const {
2841 int32_t startIdx
= pos
.getIndex();
2842 int32_t parsedPos
= -1;
2845 UErrorCode status
= U_ZERO_ERROR
;
2846 LocalPointer
<TimeZoneNames::MatchInfoCollection
> exemplarMatches(fTimeZoneNames
->find(text
, startIdx
, UTZNM_EXEMPLAR_LOCATION
, status
));
2847 if (U_FAILURE(status
)) {
2848 pos
.setErrorIndex(startIdx
);
2851 int32_t matchIdx
= -1;
2852 if (!exemplarMatches
.isNull()) {
2853 for (int32_t i
= 0; i
< exemplarMatches
->size(); i
++) {
2854 if (startIdx
+ exemplarMatches
->getMatchLengthAt(i
) > parsedPos
) {
2856 parsedPos
= startIdx
+ exemplarMatches
->getMatchLengthAt(i
);
2859 if (parsedPos
> 0) {
2860 pos
.setIndex(parsedPos
);
2861 getTimeZoneID(exemplarMatches
.getAlias(), matchIdx
, tzID
);
2865 if (tzID
.length() == 0) {
2866 pos
.setErrorIndex(startIdx
);