Line data Source code
1 : // © 2016 and later: Unicode, Inc. and others.
2 : // License & terms of use: http://www.unicode.org/copyright.html
3 : /*
4 : *******************************************************************************
5 : * Copyright (C) 2011-2015, International Business Machines Corporation and
6 : * others. All Rights Reserved.
7 : *******************************************************************************
8 : */
9 :
10 : #include "unicode/utypes.h"
11 :
12 : #if !UCONFIG_NO_FORMATTING
13 :
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 "tzgnames.h"
22 : #include "cmemory.h"
23 : #include "cstring.h"
24 : #include "putilimp.h"
25 : #include "uassert.h"
26 : #include "ucln_in.h"
27 : #include "umutex.h"
28 : #include "uresimp.h"
29 : #include "ureslocs.h"
30 : #include "uvector.h"
31 : #include "zonemeta.h"
32 : #include "tznames_impl.h" // TextTrieMap
33 :
34 : U_NAMESPACE_BEGIN
35 :
36 : // Bit flags used by the parse method.
37 : // The order must match UTimeZoneFormatStyle enum.
38 : #define ISO_Z_STYLE_FLAG 0x0080
39 : #define ISO_LOCAL_STYLE_FLAG 0x0100
40 : static const int16_t STYLE_PARSE_FLAGS[] = {
41 : 0x0001, // UTZFMT_STYLE_GENERIC_LOCATION,
42 : 0x0002, // UTZFMT_STYLE_GENERIC_LONG,
43 : 0x0004, // UTZFMT_STYLE_GENERIC_SHORT,
44 : 0x0008, // UTZFMT_STYLE_SPECIFIC_LONG,
45 : 0x0010, // UTZFMT_STYLE_SPECIFIC_SHORT,
46 : 0x0020, // UTZFMT_STYLE_LOCALIZED_GMT,
47 : 0x0040, // UTZFMT_STYLE_LOCALIZED_GMT_SHORT,
48 : ISO_Z_STYLE_FLAG, // UTZFMT_STYLE_ISO_BASIC_SHORT,
49 : ISO_LOCAL_STYLE_FLAG, // UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT,
50 : ISO_Z_STYLE_FLAG, // UTZFMT_STYLE_ISO_BASIC_FIXED,
51 : ISO_LOCAL_STYLE_FLAG, // UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED,
52 : ISO_Z_STYLE_FLAG, // UTZFMT_STYLE_ISO_BASIC_FULL,
53 : ISO_LOCAL_STYLE_FLAG, // UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
54 : ISO_Z_STYLE_FLAG, // UTZFMT_STYLE_ISO_EXTENDED_FIXED,
55 : ISO_LOCAL_STYLE_FLAG, // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED,
56 : ISO_Z_STYLE_FLAG, // UTZFMT_STYLE_ISO_EXTENDED_FULL,
57 : ISO_LOCAL_STYLE_FLAG, // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL,
58 : 0x0200, // UTZFMT_STYLE_ZONE_ID,
59 : 0x0400, // UTZFMT_STYLE_ZONE_ID_SHORT,
60 : 0x0800 // UTZFMT_STYLE_EXEMPLAR_LOCATION
61 : };
62 :
63 : static const char gZoneStringsTag[] = "zoneStrings";
64 : static const char gGmtFormatTag[]= "gmtFormat";
65 : static const char gGmtZeroFormatTag[] = "gmtZeroFormat";
66 : static const char gHourFormatTag[]= "hourFormat";
67 :
68 : static const UChar TZID_GMT[] = {0x0045, 0x0074, 0x0063, 0x002F, 0x0047, 0x004D, 0x0054, 0}; // Etc/GMT
69 : static const UChar UNKNOWN_ZONE_ID[] = {
70 : 0x0045, 0x0074, 0x0063, 0x002F, 0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0}; // Etc/Unknown
71 : static const UChar UNKNOWN_SHORT_ZONE_ID[] = {0x0075, 0x006E, 0x006B, 0}; // unk
72 : static const UChar UNKNOWN_LOCATION[] = {0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0}; // Unknown
73 :
74 : static const UChar DEFAULT_GMT_PATTERN[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0}; // GMT{0}
75 : //static const UChar DEFAULT_GMT_ZERO[] = {0x0047, 0x004D, 0x0054, 0}; // GMT
76 : static const UChar DEFAULT_GMT_POSITIVE_HM[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // +H:mm
77 : static const UChar DEFAULT_GMT_POSITIVE_HMS[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // +H:mm:ss
78 : static const UChar DEFAULT_GMT_NEGATIVE_HM[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // -H:mm
79 : static const UChar DEFAULT_GMT_NEGATIVE_HMS[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // -H:mm:ss
80 : static const UChar DEFAULT_GMT_POSITIVE_H[] = {0x002B, 0x0048, 0}; // +H
81 : static const UChar DEFAULT_GMT_NEGATIVE_H[] = {0x002D, 0x0048, 0}; // -H
82 :
83 : static const UChar32 DEFAULT_GMT_DIGITS[] = {
84 : 0x0030, 0x0031, 0x0032, 0x0033, 0x0034,
85 : 0x0035, 0x0036, 0x0037, 0x0038, 0x0039
86 : };
87 :
88 : static const UChar DEFAULT_GMT_OFFSET_SEP = 0x003A; // ':'
89 :
90 : static const UChar ARG0[] = {0x007B, 0x0030, 0x007D}; // "{0}"
91 : static const int32_t ARG0_LEN = 3;
92 :
93 : static const UChar DEFAULT_GMT_OFFSET_MINUTE_PATTERN[] = {0x006D, 0x006D, 0}; // "mm"
94 : static const UChar DEFAULT_GMT_OFFSET_SECOND_PATTERN[] = {0x0073, 0x0073, 0}; // "ss"
95 :
96 : static const UChar ALT_GMT_STRINGS[][4] = {
97 : {0x0047, 0x004D, 0x0054, 0}, // GMT
98 : {0x0055, 0x0054, 0x0043, 0}, // UTC
99 : {0x0055, 0x0054, 0, 0}, // UT
100 : {0, 0, 0, 0}
101 : };
102 :
103 : // Order of GMT offset pattern parsing, *_HMS must be evaluated first
104 : // because *_HM is most likely a substring of *_HMS
105 : static const int32_t PARSE_GMT_OFFSET_TYPES[] = {
106 : UTZFMT_PAT_POSITIVE_HMS,
107 : UTZFMT_PAT_NEGATIVE_HMS,
108 : UTZFMT_PAT_POSITIVE_HM,
109 : UTZFMT_PAT_NEGATIVE_HM,
110 : UTZFMT_PAT_POSITIVE_H,
111 : UTZFMT_PAT_NEGATIVE_H,
112 : -1
113 : };
114 :
115 : static const UChar SINGLEQUOTE = 0x0027;
116 : static const UChar PLUS = 0x002B;
117 : static const UChar MINUS = 0x002D;
118 : static const UChar ISO8601_UTC = 0x005A; // 'Z'
119 : static const UChar ISO8601_SEP = 0x003A; // ':'
120 :
121 : static const int32_t MILLIS_PER_HOUR = 60 * 60 * 1000;
122 : static const int32_t MILLIS_PER_MINUTE = 60 * 1000;
123 : static const int32_t MILLIS_PER_SECOND = 1000;
124 :
125 : // Maximum offset (exclusive) in millisecond supported by offset formats
126 : static int32_t MAX_OFFSET = 24 * MILLIS_PER_HOUR;
127 :
128 : // Maximum values for GMT offset fields
129 : static const int32_t MAX_OFFSET_HOUR = 23;
130 : static const int32_t MAX_OFFSET_MINUTE = 59;
131 : static const int32_t MAX_OFFSET_SECOND = 59;
132 :
133 : static const int32_t UNKNOWN_OFFSET = 0x7FFFFFFF;
134 :
135 : static const int32_t ALL_SIMPLE_NAME_TYPES = UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT | UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT | UTZNM_EXEMPLAR_LOCATION;
136 : static const int32_t ALL_GENERIC_NAME_TYPES = UTZGNM_LOCATION | UTZGNM_LONG | UTZGNM_SHORT;
137 :
138 : #define DIGIT_VAL(c) (0x0030 <= (c) && (c) <= 0x0039 ? (c) - 0x0030 : -1)
139 : #define MAX_OFFSET_DIGITS 6
140 :
141 : // Time Zone ID/Short ID trie
142 : static TextTrieMap *gZoneIdTrie = NULL;
143 : static icu::UInitOnce gZoneIdTrieInitOnce = U_INITONCE_INITIALIZER;
144 :
145 : static TextTrieMap *gShortZoneIdTrie = NULL;
146 : static icu::UInitOnce gShortZoneIdTrieInitOnce = U_INITONCE_INITIALIZER;
147 :
148 : static UMutex gLock = U_MUTEX_INITIALIZER;
149 :
150 : U_CDECL_BEGIN
151 : /**
152 : * Cleanup callback func
153 : */
154 0 : static UBool U_CALLCONV tzfmt_cleanup(void)
155 : {
156 0 : if (gZoneIdTrie != NULL) {
157 0 : delete gZoneIdTrie;
158 : }
159 0 : gZoneIdTrie = NULL;
160 0 : gZoneIdTrieInitOnce.reset();
161 :
162 0 : if (gShortZoneIdTrie != NULL) {
163 0 : delete gShortZoneIdTrie;
164 : }
165 0 : gShortZoneIdTrie = NULL;
166 0 : gShortZoneIdTrieInitOnce.reset();
167 :
168 0 : return TRUE;
169 : }
170 : U_CDECL_END
171 :
172 : // ------------------------------------------------------------------
173 : // GMTOffsetField
174 : //
175 : // This class represents a localized GMT offset pattern
176 : // item and used by TimeZoneFormat
177 : // ------------------------------------------------------------------
178 : class GMTOffsetField : public UMemory {
179 : public:
180 : enum FieldType {
181 : TEXT = 0,
182 : HOUR = 1,
183 : MINUTE = 2,
184 : SECOND = 4
185 : };
186 :
187 : virtual ~GMTOffsetField();
188 :
189 : static GMTOffsetField* createText(const UnicodeString& text, UErrorCode& status);
190 : static GMTOffsetField* createTimeField(FieldType type, uint8_t width, UErrorCode& status);
191 : static UBool isValid(FieldType type, int32_t width);
192 : static FieldType getTypeByLetter(UChar ch);
193 :
194 : FieldType getType() const;
195 : uint8_t getWidth() const;
196 : const UChar* getPatternText(void) const;
197 :
198 : private:
199 : UChar* fText;
200 : FieldType fType;
201 : uint8_t fWidth;
202 :
203 : GMTOffsetField();
204 : };
205 :
206 0 : GMTOffsetField::GMTOffsetField()
207 0 : : fText(NULL), fType(TEXT), fWidth(0) {
208 0 : }
209 :
210 0 : GMTOffsetField::~GMTOffsetField() {
211 0 : if (fText) {
212 0 : uprv_free(fText);
213 : }
214 0 : }
215 :
216 : GMTOffsetField*
217 0 : GMTOffsetField::createText(const UnicodeString& text, UErrorCode& status) {
218 0 : if (U_FAILURE(status)) {
219 0 : return NULL;
220 : }
221 0 : GMTOffsetField* result = new GMTOffsetField();
222 0 : if (result == NULL) {
223 0 : status = U_MEMORY_ALLOCATION_ERROR;
224 0 : return NULL;
225 : }
226 :
227 0 : int32_t len = text.length();
228 0 : result->fText = (UChar*)uprv_malloc((len + 1) * sizeof(UChar));
229 0 : if (result->fText == NULL) {
230 0 : status = U_MEMORY_ALLOCATION_ERROR;
231 0 : delete result;
232 0 : return NULL;
233 : }
234 0 : u_strncpy(result->fText, text.getBuffer(), len);
235 0 : result->fText[len] = 0;
236 0 : result->fType = TEXT;
237 :
238 0 : return result;
239 : }
240 :
241 : GMTOffsetField*
242 0 : GMTOffsetField::createTimeField(FieldType type, uint8_t width, UErrorCode& status) {
243 0 : U_ASSERT(type != TEXT);
244 0 : if (U_FAILURE(status)) {
245 0 : return NULL;
246 : }
247 0 : GMTOffsetField* result = new GMTOffsetField();
248 0 : if (result == NULL) {
249 0 : status = U_MEMORY_ALLOCATION_ERROR;
250 0 : return NULL;
251 : }
252 :
253 0 : result->fType = type;
254 0 : result->fWidth = width;
255 :
256 0 : return result;
257 : }
258 :
259 : UBool
260 0 : GMTOffsetField::isValid(FieldType type, int32_t width) {
261 0 : switch (type) {
262 : case HOUR:
263 0 : return (width == 1 || width == 2);
264 : case MINUTE:
265 : case SECOND:
266 0 : return (width == 2);
267 : default:
268 0 : U_ASSERT(FALSE);
269 : }
270 : return (width > 0);
271 : }
272 :
273 : GMTOffsetField::FieldType
274 0 : GMTOffsetField::getTypeByLetter(UChar ch) {
275 0 : if (ch == 0x0048 /* H */) {
276 0 : return HOUR;
277 0 : } else if (ch == 0x006D /* m */) {
278 0 : return MINUTE;
279 0 : } else if (ch == 0x0073 /* s */) {
280 0 : return SECOND;
281 : }
282 0 : return TEXT;
283 : }
284 :
285 : inline GMTOffsetField::FieldType
286 0 : GMTOffsetField::getType() const {
287 0 : return fType;
288 : }
289 :
290 : inline uint8_t
291 : GMTOffsetField::getWidth() const {
292 : return fWidth;
293 : }
294 :
295 : inline const UChar*
296 0 : GMTOffsetField::getPatternText(void) const {
297 0 : return fText;
298 : }
299 :
300 :
301 : U_CDECL_BEGIN
302 : static void U_CALLCONV
303 0 : deleteGMTOffsetField(void *obj) {
304 0 : delete static_cast<GMTOffsetField *>(obj);
305 0 : }
306 : U_CDECL_END
307 :
308 :
309 : // ------------------------------------------------------------------
310 : // TimeZoneFormat
311 : // ------------------------------------------------------------------
312 0 : UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneFormat)
313 :
314 0 : TimeZoneFormat::TimeZoneFormat(const Locale& locale, UErrorCode& status)
315 : : fLocale(locale), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL),
316 0 : fDefParseOptionFlags(0), fTZDBTimeZoneNames(NULL) {
317 :
318 0 : for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
319 0 : fGMTOffsetPatternItems[i] = NULL;
320 : }
321 :
322 0 : const char* region = fLocale.getCountry();
323 0 : int32_t regionLen = uprv_strlen(region);
324 0 : if (regionLen == 0) {
325 : char loc[ULOC_FULLNAME_CAPACITY];
326 0 : uloc_addLikelySubtags(fLocale.getName(), loc, sizeof(loc), &status);
327 :
328 0 : regionLen = uloc_getCountry(loc, fTargetRegion, sizeof(fTargetRegion), &status);
329 0 : if (U_SUCCESS(status)) {
330 0 : fTargetRegion[regionLen] = 0;
331 : } else {
332 0 : return;
333 : }
334 0 : } else if (regionLen < (int32_t)sizeof(fTargetRegion)) {
335 0 : uprv_strcpy(fTargetRegion, region);
336 : } else {
337 0 : fTargetRegion[0] = 0;
338 : }
339 :
340 0 : fTimeZoneNames = TimeZoneNames::createInstance(locale, status);
341 : // fTimeZoneGenericNames is lazily instantiated
342 0 : if (U_FAILURE(status)) {
343 0 : return;
344 : }
345 :
346 0 : const UChar* gmtPattern = NULL;
347 0 : const UChar* hourFormats = NULL;
348 :
349 0 : UResourceBundle *zoneBundle = ures_open(U_ICUDATA_ZONE, locale.getName(), &status);
350 0 : UResourceBundle *zoneStringsArray = ures_getByKeyWithFallback(zoneBundle, gZoneStringsTag, NULL, &status);
351 0 : if (U_SUCCESS(status)) {
352 : const UChar* resStr;
353 : int32_t len;
354 0 : resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtFormatTag, &len, &status);
355 0 : if (len > 0) {
356 0 : gmtPattern = resStr;
357 : }
358 0 : resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtZeroFormatTag, &len, &status);
359 0 : if (len > 0) {
360 0 : fGMTZeroFormat.setTo(TRUE, resStr, len);
361 : }
362 0 : resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gHourFormatTag, &len, &status);
363 0 : if (len > 0) {
364 0 : hourFormats = resStr;
365 : }
366 0 : ures_close(zoneStringsArray);
367 0 : ures_close(zoneBundle);
368 : }
369 :
370 0 : if (gmtPattern == NULL) {
371 0 : gmtPattern = DEFAULT_GMT_PATTERN;
372 : }
373 0 : initGMTPattern(UnicodeString(TRUE, gmtPattern, -1), status);
374 :
375 0 : UBool useDefaultOffsetPatterns = TRUE;
376 0 : if (hourFormats) {
377 0 : UChar *sep = u_strchr(hourFormats, (UChar)0x003B /* ';' */);
378 0 : if (sep != NULL) {
379 0 : UErrorCode tmpStatus = U_ZERO_ERROR;
380 0 : fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM].setTo(FALSE, hourFormats, (int32_t)(sep - hourFormats));
381 0 : fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM].setTo(TRUE, sep + 1, -1);
382 0 : expandOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HMS], tmpStatus);
383 0 : expandOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HMS], tmpStatus);
384 0 : truncateOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_H], tmpStatus);
385 0 : truncateOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_H], tmpStatus);
386 0 : if (U_SUCCESS(tmpStatus)) {
387 0 : useDefaultOffsetPatterns = FALSE;
388 : }
389 : }
390 : }
391 0 : if (useDefaultOffsetPatterns) {
392 0 : fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_H].setTo(TRUE, DEFAULT_GMT_POSITIVE_H, -1);
393 0 : fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM].setTo(TRUE, DEFAULT_GMT_POSITIVE_HM, -1);
394 0 : fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HMS].setTo(TRUE, DEFAULT_GMT_POSITIVE_HMS, -1);
395 0 : fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_H].setTo(TRUE, DEFAULT_GMT_NEGATIVE_H, -1);
396 0 : fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM].setTo(TRUE, DEFAULT_GMT_NEGATIVE_HM, -1);
397 0 : fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HMS].setTo(TRUE, DEFAULT_GMT_NEGATIVE_HMS, -1);
398 : }
399 0 : initGMTOffsetPatterns(status);
400 :
401 0 : NumberingSystem* ns = NumberingSystem::createInstance(locale, status);
402 0 : UBool useDefDigits = TRUE;
403 0 : if (ns && !ns->isAlgorithmic()) {
404 0 : UnicodeString digits = ns->getDescription();
405 0 : useDefDigits = !toCodePoints(digits, fGMTOffsetDigits, 10);
406 : }
407 0 : if (useDefDigits) {
408 0 : uprv_memcpy(fGMTOffsetDigits, DEFAULT_GMT_DIGITS, sizeof(UChar32) * 10);
409 : }
410 0 : delete ns;
411 : }
412 :
413 0 : TimeZoneFormat::TimeZoneFormat(const TimeZoneFormat& other)
414 : : Format(other), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL),
415 0 : fTZDBTimeZoneNames(NULL) {
416 :
417 0 : for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
418 0 : fGMTOffsetPatternItems[i] = NULL;
419 : }
420 0 : *this = other;
421 0 : }
422 :
423 :
424 0 : TimeZoneFormat::~TimeZoneFormat() {
425 0 : delete fTimeZoneNames;
426 0 : delete fTimeZoneGenericNames;
427 0 : delete fTZDBTimeZoneNames;
428 0 : for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
429 0 : delete fGMTOffsetPatternItems[i];
430 : }
431 0 : }
432 :
433 : TimeZoneFormat&
434 0 : TimeZoneFormat::operator=(const TimeZoneFormat& other) {
435 0 : if (this == &other) {
436 0 : return *this;
437 : }
438 :
439 0 : delete fTimeZoneNames;
440 0 : delete fTimeZoneGenericNames;
441 0 : fTimeZoneGenericNames = NULL;
442 0 : delete fTZDBTimeZoneNames;
443 0 : fTZDBTimeZoneNames = NULL;
444 :
445 0 : fLocale = other.fLocale;
446 0 : uprv_memcpy(fTargetRegion, other.fTargetRegion, sizeof(fTargetRegion));
447 :
448 0 : fTimeZoneNames = other.fTimeZoneNames->clone();
449 0 : if (other.fTimeZoneGenericNames) {
450 : // TODO: this test has dubious thread safety.
451 0 : fTimeZoneGenericNames = other.fTimeZoneGenericNames->clone();
452 : }
453 :
454 0 : fGMTPattern = other.fGMTPattern;
455 0 : fGMTPatternPrefix = other.fGMTPatternPrefix;
456 0 : fGMTPatternSuffix = other.fGMTPatternSuffix;
457 :
458 0 : UErrorCode status = U_ZERO_ERROR;
459 0 : for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
460 0 : fGMTOffsetPatterns[i] = other.fGMTOffsetPatterns[i];
461 0 : delete fGMTOffsetPatternItems[i];
462 0 : fGMTOffsetPatternItems[i] = NULL;
463 : }
464 0 : initGMTOffsetPatterns(status);
465 0 : U_ASSERT(U_SUCCESS(status));
466 :
467 0 : fGMTZeroFormat = other.fGMTZeroFormat;
468 :
469 0 : uprv_memcpy(fGMTOffsetDigits, other.fGMTOffsetDigits, sizeof(fGMTOffsetDigits));
470 :
471 0 : fDefParseOptionFlags = other.fDefParseOptionFlags;
472 :
473 0 : return *this;
474 : }
475 :
476 :
477 : UBool
478 0 : TimeZoneFormat::operator==(const Format& other) const {
479 0 : TimeZoneFormat* tzfmt = (TimeZoneFormat*)&other;
480 :
481 : UBool isEqual =
482 0 : fLocale == tzfmt->fLocale
483 0 : && fGMTPattern == tzfmt->fGMTPattern
484 0 : && fGMTZeroFormat == tzfmt->fGMTZeroFormat
485 0 : && *fTimeZoneNames == *tzfmt->fTimeZoneNames;
486 :
487 0 : for (int32_t i = 0; i < UTZFMT_PAT_COUNT && isEqual; i++) {
488 0 : isEqual = fGMTOffsetPatterns[i] == tzfmt->fGMTOffsetPatterns[i];
489 : }
490 0 : for (int32_t i = 0; i < 10 && isEqual; i++) {
491 0 : isEqual = fGMTOffsetDigits[i] == tzfmt->fGMTOffsetDigits[i];
492 : }
493 : // TODO
494 : // Check fTimeZoneGenericNames. For now,
495 : // if fTimeZoneNames is same, fTimeZoneGenericNames should
496 : // be also equivalent.
497 0 : return isEqual;
498 : }
499 :
500 : Format*
501 0 : TimeZoneFormat::clone() const {
502 0 : return new TimeZoneFormat(*this);
503 : }
504 :
505 : TimeZoneFormat* U_EXPORT2
506 0 : TimeZoneFormat::createInstance(const Locale& locale, UErrorCode& status) {
507 0 : TimeZoneFormat* tzfmt = new TimeZoneFormat(locale, status);
508 0 : if (U_SUCCESS(status)) {
509 0 : return tzfmt;
510 : }
511 0 : delete tzfmt;
512 0 : return NULL;
513 : }
514 :
515 : // ------------------------------------------------------------------
516 : // Setter and Getter
517 :
518 : const TimeZoneNames*
519 0 : TimeZoneFormat::getTimeZoneNames() const {
520 0 : return (const TimeZoneNames*)fTimeZoneNames;
521 : }
522 :
523 : void
524 0 : TimeZoneFormat::adoptTimeZoneNames(TimeZoneNames *tznames) {
525 0 : delete fTimeZoneNames;
526 0 : fTimeZoneNames = tznames;
527 :
528 : // TODO - We should also update fTimeZoneGenericNames
529 0 : }
530 :
531 : void
532 0 : TimeZoneFormat::setTimeZoneNames(const TimeZoneNames &tznames) {
533 0 : delete fTimeZoneNames;
534 0 : fTimeZoneNames = tznames.clone();
535 :
536 : // TODO - We should also update fTimeZoneGenericNames
537 0 : }
538 :
539 : void
540 0 : TimeZoneFormat::setDefaultParseOptions(uint32_t flags) {
541 0 : fDefParseOptionFlags = flags;
542 0 : }
543 :
544 : uint32_t
545 0 : TimeZoneFormat::getDefaultParseOptions(void) const {
546 0 : return fDefParseOptionFlags;
547 : }
548 :
549 :
550 : UnicodeString&
551 0 : TimeZoneFormat::getGMTPattern(UnicodeString& pattern) const {
552 0 : return pattern.setTo(fGMTPattern);
553 : }
554 :
555 : void
556 0 : TimeZoneFormat::setGMTPattern(const UnicodeString& pattern, UErrorCode& status) {
557 0 : initGMTPattern(pattern, status);
558 0 : }
559 :
560 : UnicodeString&
561 0 : TimeZoneFormat::getGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, UnicodeString& pattern) const {
562 0 : return pattern.setTo(fGMTOffsetPatterns[type]);
563 : }
564 :
565 : void
566 0 : TimeZoneFormat::setGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, const UnicodeString& pattern, UErrorCode& status) {
567 0 : if (U_FAILURE(status)) {
568 0 : return;
569 : }
570 0 : if (pattern == fGMTOffsetPatterns[type]) {
571 : // No need to reset
572 0 : return;
573 : }
574 :
575 0 : OffsetFields required = FIELDS_HM;
576 0 : switch (type) {
577 : case UTZFMT_PAT_POSITIVE_H:
578 : case UTZFMT_PAT_NEGATIVE_H:
579 0 : required = FIELDS_H;
580 0 : break;
581 : case UTZFMT_PAT_POSITIVE_HM:
582 : case UTZFMT_PAT_NEGATIVE_HM:
583 0 : required = FIELDS_HM;
584 0 : break;
585 : case UTZFMT_PAT_POSITIVE_HMS:
586 : case UTZFMT_PAT_NEGATIVE_HMS:
587 0 : required = FIELDS_HMS;
588 0 : break;
589 : default:
590 0 : U_ASSERT(FALSE);
591 : break;
592 : }
593 :
594 0 : UVector* patternItems = parseOffsetPattern(pattern, required, status);
595 0 : if (patternItems == NULL) {
596 0 : return;
597 : }
598 :
599 0 : fGMTOffsetPatterns[type].setTo(pattern);
600 0 : delete fGMTOffsetPatternItems[type];
601 0 : fGMTOffsetPatternItems[type] = patternItems;
602 0 : checkAbuttingHoursAndMinutes();
603 : }
604 :
605 : UnicodeString&
606 0 : TimeZoneFormat::getGMTOffsetDigits(UnicodeString& digits) const {
607 0 : digits.remove();
608 0 : for (int32_t i = 0; i < 10; i++) {
609 0 : digits.append(fGMTOffsetDigits[i]);
610 : }
611 0 : return digits;
612 : }
613 :
614 : void
615 0 : TimeZoneFormat::setGMTOffsetDigits(const UnicodeString& digits, UErrorCode& status) {
616 0 : if (U_FAILURE(status)) {
617 0 : return;
618 : }
619 : UChar32 digitArray[10];
620 0 : if (!toCodePoints(digits, digitArray, 10)) {
621 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
622 0 : return;
623 : }
624 0 : uprv_memcpy(fGMTOffsetDigits, digitArray, sizeof(UChar32)*10);
625 : }
626 :
627 : UnicodeString&
628 0 : TimeZoneFormat::getGMTZeroFormat(UnicodeString& gmtZeroFormat) const {
629 0 : return gmtZeroFormat.setTo(fGMTZeroFormat);
630 : }
631 :
632 : void
633 0 : TimeZoneFormat::setGMTZeroFormat(const UnicodeString& gmtZeroFormat, UErrorCode& status) {
634 0 : if (U_SUCCESS(status)) {
635 0 : if (gmtZeroFormat.isEmpty()) {
636 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
637 0 : } else if (gmtZeroFormat != fGMTZeroFormat) {
638 0 : fGMTZeroFormat.setTo(gmtZeroFormat);
639 : }
640 : }
641 0 : }
642 :
643 : // ------------------------------------------------------------------
644 : // Format and Parse
645 :
646 : UnicodeString&
647 0 : TimeZoneFormat::format(UTimeZoneFormatStyle style, const TimeZone& tz, UDate date,
648 : UnicodeString& name, UTimeZoneFormatTimeType* timeType /* = NULL */) const {
649 0 : if (timeType) {
650 0 : *timeType = UTZFMT_TIME_TYPE_UNKNOWN;
651 : }
652 :
653 0 : UBool noOffsetFormatFallback = FALSE;
654 :
655 0 : switch (style) {
656 : case UTZFMT_STYLE_GENERIC_LOCATION:
657 0 : formatGeneric(tz, UTZGNM_LOCATION, date, name);
658 0 : break;
659 : case UTZFMT_STYLE_GENERIC_LONG:
660 0 : formatGeneric(tz, UTZGNM_LONG, date, name);
661 0 : break;
662 : case UTZFMT_STYLE_GENERIC_SHORT:
663 0 : formatGeneric(tz, UTZGNM_SHORT, date, name);
664 0 : break;
665 : case UTZFMT_STYLE_SPECIFIC_LONG:
666 0 : formatSpecific(tz, UTZNM_LONG_STANDARD, UTZNM_LONG_DAYLIGHT, date, name, timeType);
667 0 : break;
668 : case UTZFMT_STYLE_SPECIFIC_SHORT:
669 0 : formatSpecific(tz, UTZNM_SHORT_STANDARD, UTZNM_SHORT_DAYLIGHT, date, name, timeType);
670 0 : break;
671 :
672 : case UTZFMT_STYLE_ZONE_ID:
673 0 : tz.getID(name);
674 0 : noOffsetFormatFallback = TRUE;
675 0 : break;
676 : case UTZFMT_STYLE_ZONE_ID_SHORT:
677 : {
678 0 : const UChar* shortID = ZoneMeta::getShortID(tz);
679 0 : if (shortID == NULL) {
680 0 : shortID = UNKNOWN_SHORT_ZONE_ID;
681 : }
682 0 : name.setTo(shortID, -1);
683 : }
684 0 : noOffsetFormatFallback = TRUE;
685 0 : break;
686 :
687 : case UTZFMT_STYLE_EXEMPLAR_LOCATION:
688 0 : formatExemplarLocation(tz, name);
689 0 : noOffsetFormatFallback = TRUE;
690 0 : break;
691 :
692 : default:
693 : // will be handled below
694 0 : break;
695 : }
696 :
697 0 : if (name.isEmpty() && !noOffsetFormatFallback) {
698 0 : UErrorCode status = U_ZERO_ERROR;
699 : int32_t rawOffset, dstOffset;
700 0 : tz.getOffset(date, FALSE, rawOffset, dstOffset, status);
701 0 : int32_t offset = rawOffset + dstOffset;
702 0 : if (U_SUCCESS(status)) {
703 0 : switch (style) {
704 : case UTZFMT_STYLE_GENERIC_LOCATION:
705 : case UTZFMT_STYLE_GENERIC_LONG:
706 : case UTZFMT_STYLE_SPECIFIC_LONG:
707 : case UTZFMT_STYLE_LOCALIZED_GMT:
708 0 : formatOffsetLocalizedGMT(offset, name, status);
709 0 : break;
710 :
711 : case UTZFMT_STYLE_GENERIC_SHORT:
712 : case UTZFMT_STYLE_SPECIFIC_SHORT:
713 : case UTZFMT_STYLE_LOCALIZED_GMT_SHORT:
714 0 : formatOffsetShortLocalizedGMT(offset, name, status);
715 0 : break;
716 :
717 : case UTZFMT_STYLE_ISO_BASIC_SHORT:
718 0 : formatOffsetISO8601Basic(offset, TRUE, TRUE, TRUE, name, status);
719 0 : break;
720 :
721 : case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT:
722 0 : formatOffsetISO8601Basic(offset, FALSE, TRUE, TRUE, name, status);
723 0 : break;
724 :
725 : case UTZFMT_STYLE_ISO_BASIC_FIXED:
726 0 : formatOffsetISO8601Basic(offset, TRUE, FALSE, TRUE, name, status);
727 0 : break;
728 :
729 : case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED:
730 0 : formatOffsetISO8601Basic(offset, FALSE, FALSE, TRUE, name, status);
731 0 : break;
732 :
733 : case UTZFMT_STYLE_ISO_EXTENDED_FIXED:
734 0 : formatOffsetISO8601Extended(offset, TRUE, FALSE, TRUE, name, status);
735 0 : break;
736 :
737 : case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED:
738 0 : formatOffsetISO8601Extended(offset, FALSE, FALSE, TRUE, name, status);
739 0 : break;
740 :
741 : case UTZFMT_STYLE_ISO_BASIC_FULL:
742 0 : formatOffsetISO8601Basic(offset, TRUE, FALSE, FALSE, name, status);
743 0 : break;
744 :
745 : case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL:
746 0 : formatOffsetISO8601Basic(offset, FALSE, FALSE, FALSE, name, status);
747 0 : break;
748 :
749 : case UTZFMT_STYLE_ISO_EXTENDED_FULL:
750 0 : formatOffsetISO8601Extended(offset, TRUE, FALSE, FALSE, name, status);
751 0 : break;
752 :
753 : case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL:
754 0 : formatOffsetISO8601Extended(offset, FALSE, FALSE, FALSE, name, status);
755 0 : break;
756 :
757 : default:
758 : // UTZFMT_STYLE_ZONE_ID, UTZFMT_STYLE_ZONE_ID_SHORT, UTZFMT_STYLE_EXEMPLAR_LOCATION
759 0 : break;
760 : }
761 :
762 0 : if (timeType) {
763 0 : *timeType = (dstOffset != 0) ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD;
764 : }
765 : }
766 : }
767 :
768 0 : return name;
769 : }
770 :
771 : UnicodeString&
772 0 : TimeZoneFormat::format(const Formattable& obj, UnicodeString& appendTo,
773 : FieldPosition& pos, UErrorCode& status) const {
774 0 : if (U_FAILURE(status)) {
775 0 : return appendTo;
776 : }
777 0 : UDate date = Calendar::getNow();
778 0 : if (obj.getType() == Formattable::kObject) {
779 0 : const UObject* formatObj = obj.getObject();
780 0 : const TimeZone* tz = dynamic_cast<const TimeZone*>(formatObj);
781 0 : if (tz == NULL) {
782 0 : const Calendar* cal = dynamic_cast<const Calendar*>(formatObj);
783 0 : if (cal != NULL) {
784 0 : tz = &cal->getTimeZone();
785 0 : date = cal->getTime(status);
786 : }
787 : }
788 0 : if (tz != NULL) {
789 : int32_t rawOffset, dstOffset;
790 0 : tz->getOffset(date, FALSE, rawOffset, dstOffset, status);
791 : UChar buf[32];
792 0 : UnicodeString result(buf, 0, UPRV_LENGTHOF(buf));
793 0 : formatOffsetLocalizedGMT(rawOffset + dstOffset, result, status);
794 0 : if (U_SUCCESS(status)) {
795 0 : appendTo.append(result);
796 0 : if (pos.getField() == UDAT_TIMEZONE_FIELD) {
797 0 : pos.setBeginIndex(0);
798 0 : pos.setEndIndex(result.length());
799 : }
800 : }
801 : }
802 : }
803 0 : return appendTo;
804 : }
805 :
806 : TimeZone*
807 0 : TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos,
808 : UTimeZoneFormatTimeType* timeType /*= NULL*/) const {
809 0 : return parse(style, text, pos, getDefaultParseOptions(), timeType);
810 : }
811 :
812 : TimeZone*
813 0 : TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos,
814 : int32_t parseOptions, UTimeZoneFormatTimeType* timeType /* = NULL */) const {
815 0 : if (timeType) {
816 0 : *timeType = UTZFMT_TIME_TYPE_UNKNOWN;
817 : }
818 :
819 0 : int32_t startIdx = pos.getIndex();
820 0 : int32_t maxPos = text.length();
821 : int32_t offset;
822 :
823 : // Styles using localized GMT format as fallback
824 : UBool fallbackLocalizedGMT =
825 0 : (style == UTZFMT_STYLE_SPECIFIC_LONG || style == UTZFMT_STYLE_GENERIC_LONG || style == UTZFMT_STYLE_GENERIC_LOCATION);
826 : UBool fallbackShortLocalizedGMT =
827 0 : (style == UTZFMT_STYLE_SPECIFIC_SHORT || style == UTZFMT_STYLE_GENERIC_SHORT);
828 :
829 0 : int32_t evaluated = 0; // bit flags representing already evaluated styles
830 0 : ParsePosition tmpPos(startIdx);
831 :
832 0 : int32_t parsedOffset = UNKNOWN_OFFSET; // stores successfully parsed offset for later use
833 0 : int32_t parsedPos = -1; // stores successfully parsed offset position for later use
834 :
835 : // Try localized GMT format first if necessary
836 0 : if (fallbackLocalizedGMT || fallbackShortLocalizedGMT) {
837 0 : UBool hasDigitOffset = FALSE;
838 0 : offset = parseOffsetLocalizedGMT(text, tmpPos, fallbackShortLocalizedGMT, &hasDigitOffset);
839 0 : if (tmpPos.getErrorIndex() == -1) {
840 : // Even when the input text was successfully parsed as a localized GMT format text,
841 : // we may still need to evaluate the specified style if -
842 : // 1) GMT zero format was used, and
843 : // 2) The input text was not completely processed
844 0 : if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
845 0 : pos.setIndex(tmpPos.getIndex());
846 0 : return createTimeZoneForOffset(offset);
847 : }
848 0 : parsedOffset = offset;
849 0 : parsedPos = tmpPos.getIndex();
850 : }
851 : // Note: For now, no distinction between long/short localized GMT format in the parser.
852 : // This might be changed in future.
853 : // evaluated |= (fallbackLocalizedGMT ? STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] : STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]);
854 0 : evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] | STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT];
855 : }
856 :
857 0 : UErrorCode status = U_ZERO_ERROR;
858 : UChar tzIDBuf[32];
859 0 : UnicodeString tzID(tzIDBuf, 0, UPRV_LENGTHOF(tzIDBuf));
860 :
861 0 : UBool parseTZDBAbbrev = ((parseOptions & UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS) != 0);
862 :
863 : // Try the specified style
864 0 : switch (style) {
865 : case UTZFMT_STYLE_LOCALIZED_GMT:
866 : {
867 0 : tmpPos.setIndex(startIdx);
868 0 : tmpPos.setErrorIndex(-1);
869 :
870 0 : offset = parseOffsetLocalizedGMT(text, tmpPos);
871 0 : if (tmpPos.getErrorIndex() == -1) {
872 0 : pos.setIndex(tmpPos.getIndex());
873 0 : return createTimeZoneForOffset(offset);
874 : }
875 :
876 : // Note: For now, no distinction between long/short localized GMT format in the parser.
877 : // This might be changed in future.
878 0 : evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT];
879 :
880 0 : break;
881 : }
882 : case UTZFMT_STYLE_LOCALIZED_GMT_SHORT:
883 : {
884 0 : tmpPos.setIndex(startIdx);
885 0 : tmpPos.setErrorIndex(-1);
886 :
887 0 : offset = parseOffsetShortLocalizedGMT(text, tmpPos);
888 0 : if (tmpPos.getErrorIndex() == -1) {
889 0 : pos.setIndex(tmpPos.getIndex());
890 0 : return createTimeZoneForOffset(offset);
891 : }
892 :
893 : // Note: For now, no distinction between long/short localized GMT format in the parser.
894 : // This might be changed in future.
895 0 : evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT];
896 :
897 0 : break;
898 : }
899 : case UTZFMT_STYLE_ISO_BASIC_SHORT:
900 : case UTZFMT_STYLE_ISO_BASIC_FIXED:
901 : case UTZFMT_STYLE_ISO_BASIC_FULL:
902 : case UTZFMT_STYLE_ISO_EXTENDED_FIXED:
903 : case UTZFMT_STYLE_ISO_EXTENDED_FULL:
904 : {
905 0 : tmpPos.setIndex(startIdx);
906 0 : tmpPos.setErrorIndex(-1);
907 :
908 0 : offset = parseOffsetISO8601(text, tmpPos);
909 0 : if (tmpPos.getErrorIndex() == -1) {
910 0 : pos.setIndex(tmpPos.getIndex());
911 0 : return createTimeZoneForOffset(offset);
912 : }
913 :
914 0 : break;
915 : }
916 :
917 : case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT:
918 : case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED:
919 : case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL:
920 : case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED:
921 : case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL:
922 : {
923 0 : tmpPos.setIndex(startIdx);
924 0 : tmpPos.setErrorIndex(-1);
925 :
926 : // Exclude the case of UTC Indicator "Z" here
927 0 : UBool hasDigitOffset = FALSE;
928 0 : offset = parseOffsetISO8601(text, tmpPos, FALSE, &hasDigitOffset);
929 0 : if (tmpPos.getErrorIndex() == -1 && hasDigitOffset) {
930 0 : pos.setIndex(tmpPos.getIndex());
931 0 : return createTimeZoneForOffset(offset);
932 : }
933 :
934 0 : break;
935 : }
936 :
937 : case UTZFMT_STYLE_SPECIFIC_LONG:
938 : case UTZFMT_STYLE_SPECIFIC_SHORT:
939 : {
940 : // Specific styles
941 0 : int32_t nameTypes = 0;
942 0 : if (style == UTZFMT_STYLE_SPECIFIC_LONG) {
943 0 : nameTypes = (UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT);
944 : } else {
945 0 : U_ASSERT(style == UTZFMT_STYLE_SPECIFIC_SHORT);
946 0 : nameTypes = (UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT);
947 : }
948 0 : LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, nameTypes, status));
949 0 : if (U_FAILURE(status)) {
950 0 : pos.setErrorIndex(startIdx);
951 0 : return NULL;
952 : }
953 0 : if (!specificMatches.isNull()) {
954 0 : int32_t matchIdx = -1;
955 0 : int32_t matchPos = -1;
956 0 : for (int32_t i = 0; i < specificMatches->size(); i++) {
957 0 : matchPos = startIdx + specificMatches->getMatchLengthAt(i);
958 0 : if (matchPos > parsedPos) {
959 0 : matchIdx = i;
960 0 : parsedPos = matchPos;
961 : }
962 : }
963 0 : if (matchIdx >= 0) {
964 0 : if (timeType) {
965 0 : *timeType = getTimeType(specificMatches->getNameTypeAt(matchIdx));
966 : }
967 0 : pos.setIndex(matchPos);
968 0 : getTimeZoneID(specificMatches.getAlias(), matchIdx, tzID);
969 0 : U_ASSERT(!tzID.isEmpty());
970 0 : return TimeZone::createTimeZone(tzID);
971 : }
972 : }
973 :
974 0 : if (parseTZDBAbbrev && style == UTZFMT_STYLE_SPECIFIC_SHORT) {
975 0 : U_ASSERT((nameTypes & UTZNM_SHORT_STANDARD) != 0);
976 0 : U_ASSERT((nameTypes & UTZNM_SHORT_DAYLIGHT) != 0);
977 :
978 0 : const TZDBTimeZoneNames *tzdbTimeZoneNames = getTZDBTimeZoneNames(status);
979 0 : if (U_SUCCESS(status)) {
980 : LocalPointer<TimeZoneNames::MatchInfoCollection> tzdbNameMatches(
981 0 : tzdbTimeZoneNames->find(text, startIdx, nameTypes, status));
982 0 : if (U_FAILURE(status)) {
983 0 : pos.setErrorIndex(startIdx);
984 0 : return NULL;
985 : }
986 0 : if (!tzdbNameMatches.isNull()) {
987 0 : int32_t matchIdx = -1;
988 0 : int32_t matchPos = -1;
989 0 : for (int32_t i = 0; i < tzdbNameMatches->size(); i++) {
990 0 : matchPos = startIdx + tzdbNameMatches->getMatchLengthAt(i);
991 0 : if (matchPos > parsedPos) {
992 0 : matchIdx = i;
993 0 : parsedPos = matchPos;
994 : }
995 : }
996 0 : if (matchIdx >= 0) {
997 0 : if (timeType) {
998 0 : *timeType = getTimeType(tzdbNameMatches->getNameTypeAt(matchIdx));
999 : }
1000 0 : pos.setIndex(matchPos);
1001 0 : getTimeZoneID(tzdbNameMatches.getAlias(), matchIdx, tzID);
1002 0 : U_ASSERT(!tzID.isEmpty());
1003 0 : return TimeZone::createTimeZone(tzID);
1004 : }
1005 : }
1006 : }
1007 : }
1008 0 : break;
1009 : }
1010 : case UTZFMT_STYLE_GENERIC_LONG:
1011 : case UTZFMT_STYLE_GENERIC_SHORT:
1012 : case UTZFMT_STYLE_GENERIC_LOCATION:
1013 : {
1014 0 : int32_t genericNameTypes = 0;
1015 0 : switch (style) {
1016 : case UTZFMT_STYLE_GENERIC_LOCATION:
1017 0 : genericNameTypes = UTZGNM_LOCATION;
1018 0 : break;
1019 :
1020 : case UTZFMT_STYLE_GENERIC_LONG:
1021 0 : genericNameTypes = UTZGNM_LONG | UTZGNM_LOCATION;
1022 0 : break;
1023 :
1024 : case UTZFMT_STYLE_GENERIC_SHORT:
1025 0 : genericNameTypes = UTZGNM_SHORT | UTZGNM_LOCATION;
1026 0 : break;
1027 :
1028 : default:
1029 0 : U_ASSERT(FALSE);
1030 : }
1031 :
1032 0 : int32_t len = 0;
1033 0 : UTimeZoneFormatTimeType tt = UTZFMT_TIME_TYPE_UNKNOWN;
1034 0 : const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status);
1035 0 : if (U_SUCCESS(status)) {
1036 0 : len = gnames->findBestMatch(text, startIdx, genericNameTypes, tzID, tt, status);
1037 : }
1038 0 : if (U_FAILURE(status)) {
1039 0 : pos.setErrorIndex(startIdx);
1040 0 : return NULL;
1041 : }
1042 0 : if (len > 0) {
1043 : // Found a match
1044 0 : if (timeType) {
1045 0 : *timeType = tt;
1046 : }
1047 0 : pos.setIndex(startIdx + len);
1048 0 : U_ASSERT(!tzID.isEmpty());
1049 0 : return TimeZone::createTimeZone(tzID);
1050 : }
1051 :
1052 0 : break;
1053 : }
1054 : case UTZFMT_STYLE_ZONE_ID:
1055 : {
1056 0 : tmpPos.setIndex(startIdx);
1057 0 : tmpPos.setErrorIndex(-1);
1058 :
1059 0 : parseZoneID(text, tmpPos, tzID);
1060 0 : if (tmpPos.getErrorIndex() == -1) {
1061 0 : pos.setIndex(tmpPos.getIndex());
1062 0 : return TimeZone::createTimeZone(tzID);
1063 : }
1064 0 : break;
1065 : }
1066 : case UTZFMT_STYLE_ZONE_ID_SHORT:
1067 : {
1068 0 : tmpPos.setIndex(startIdx);
1069 0 : tmpPos.setErrorIndex(-1);
1070 :
1071 0 : parseShortZoneID(text, tmpPos, tzID);
1072 0 : if (tmpPos.getErrorIndex() == -1) {
1073 0 : pos.setIndex(tmpPos.getIndex());
1074 0 : return TimeZone::createTimeZone(tzID);
1075 : }
1076 0 : break;
1077 : }
1078 : case UTZFMT_STYLE_EXEMPLAR_LOCATION:
1079 : {
1080 0 : tmpPos.setIndex(startIdx);
1081 0 : tmpPos.setErrorIndex(-1);
1082 :
1083 0 : parseExemplarLocation(text, tmpPos, tzID);
1084 0 : if (tmpPos.getErrorIndex() == -1) {
1085 0 : pos.setIndex(tmpPos.getIndex());
1086 0 : return TimeZone::createTimeZone(tzID);
1087 : }
1088 0 : break;
1089 : }
1090 : }
1091 0 : evaluated |= STYLE_PARSE_FLAGS[style];
1092 :
1093 :
1094 0 : if (parsedPos > startIdx) {
1095 : // When the specified style is one of SPECIFIC_XXX or GENERIC_XXX, we tried to parse the input
1096 : // as localized GMT format earlier. If parsedOffset is positive, it means it was successfully
1097 : // parsed as localized GMT format, but offset digits were not detected (more specifically, GMT
1098 : // zero format). Then, it tried to find a match within the set of display names, but could not
1099 : // find a match. At this point, we can safely assume the input text contains the localized
1100 : // GMT format.
1101 0 : U_ASSERT(parsedOffset != UNKNOWN_OFFSET);
1102 0 : pos.setIndex(parsedPos);
1103 0 : return createTimeZoneForOffset(parsedOffset);
1104 : }
1105 :
1106 : // Failed to parse the input text as the time zone format in the specified style.
1107 : // Check the longest match among other styles below.
1108 : UChar parsedIDBuf[32];
1109 0 : UnicodeString parsedID(parsedIDBuf, 0, UPRV_LENGTHOF(parsedIDBuf));
1110 0 : UTimeZoneFormatTimeType parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
1111 :
1112 0 : U_ASSERT(parsedPos < 0);
1113 0 : U_ASSERT(parsedOffset == UNKNOWN_OFFSET);
1114 :
1115 : // ISO 8601
1116 0 : if (parsedPos < maxPos &&
1117 0 : ((evaluated & ISO_Z_STYLE_FLAG) == 0 || (evaluated & ISO_LOCAL_STYLE_FLAG) == 0)) {
1118 0 : tmpPos.setIndex(startIdx);
1119 0 : tmpPos.setErrorIndex(-1);
1120 :
1121 0 : UBool hasDigitOffset = FALSE;
1122 0 : offset = parseOffsetISO8601(text, tmpPos, FALSE, &hasDigitOffset);
1123 0 : if (tmpPos.getErrorIndex() == -1) {
1124 0 : if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
1125 0 : pos.setIndex(tmpPos.getIndex());
1126 0 : return createTimeZoneForOffset(offset);
1127 : }
1128 : // Note: When ISO 8601 format contains offset digits, it should not
1129 : // collide with other formats. However, ISO 8601 UTC format "Z" (single letter)
1130 : // may collide with other names. In this case, we need to evaluate other names.
1131 0 : if (parsedPos < tmpPos.getIndex()) {
1132 0 : parsedOffset = offset;
1133 0 : parsedID.setToBogus();
1134 0 : parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
1135 0 : parsedPos = tmpPos.getIndex();
1136 0 : U_ASSERT(parsedPos == startIdx + 1); // only when "Z" is used
1137 : }
1138 : }
1139 : }
1140 :
1141 : // Localized GMT format
1142 0 : if (parsedPos < maxPos &&
1143 0 : (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT]) == 0) {
1144 0 : tmpPos.setIndex(startIdx);
1145 0 : tmpPos.setErrorIndex(-1);
1146 :
1147 0 : UBool hasDigitOffset = FALSE;
1148 0 : offset = parseOffsetLocalizedGMT(text, tmpPos, FALSE, &hasDigitOffset);
1149 0 : if (tmpPos.getErrorIndex() == -1) {
1150 0 : if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
1151 0 : pos.setIndex(tmpPos.getIndex());
1152 0 : return createTimeZoneForOffset(offset);
1153 : }
1154 : // Evaluate other names - see the comment earlier in this method.
1155 0 : if (parsedPos < tmpPos.getIndex()) {
1156 0 : parsedOffset = offset;
1157 0 : parsedID.setToBogus();
1158 0 : parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
1159 0 : parsedPos = tmpPos.getIndex();
1160 : }
1161 : }
1162 : }
1163 :
1164 0 : if (parsedPos < maxPos &&
1165 0 : (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]) == 0) {
1166 0 : tmpPos.setIndex(startIdx);
1167 0 : tmpPos.setErrorIndex(-1);
1168 :
1169 0 : UBool hasDigitOffset = FALSE;
1170 0 : offset = parseOffsetLocalizedGMT(text, tmpPos, TRUE, &hasDigitOffset);
1171 0 : if (tmpPos.getErrorIndex() == -1) {
1172 0 : if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
1173 0 : pos.setIndex(tmpPos.getIndex());
1174 0 : return createTimeZoneForOffset(offset);
1175 : }
1176 : // Evaluate other names - see the comment earlier in this method.
1177 0 : if (parsedPos < tmpPos.getIndex()) {
1178 0 : parsedOffset = offset;
1179 0 : parsedID.setToBogus();
1180 0 : parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
1181 0 : parsedPos = tmpPos.getIndex();
1182 : }
1183 : }
1184 : }
1185 :
1186 : // When ParseOption.ALL_STYLES is available, we also try to look all possible display names and IDs.
1187 : // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never
1188 : // used for America/New_York. With parseAllStyles true, this code parses "EST"
1189 : // as America/New_York.
1190 :
1191 : // Note: Adding all possible names into the trie used by the implementation is quite heavy operation,
1192 : // which we want to avoid normally (note that we cache the trie, so this is applicable to the
1193 : // first time only as long as the cache does not expire).
1194 :
1195 0 : if (parseOptions & UTZFMT_PARSE_OPTION_ALL_STYLES) {
1196 : // Try all specific names and exemplar location names
1197 0 : if (parsedPos < maxPos) {
1198 0 : LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, ALL_SIMPLE_NAME_TYPES, status));
1199 0 : if (U_FAILURE(status)) {
1200 0 : pos.setErrorIndex(startIdx);
1201 0 : return NULL;
1202 : }
1203 0 : int32_t specificMatchIdx = -1;
1204 0 : int32_t matchPos = -1;
1205 0 : if (!specificMatches.isNull()) {
1206 0 : for (int32_t i = 0; i < specificMatches->size(); i++) {
1207 0 : if (startIdx + specificMatches->getMatchLengthAt(i) > matchPos) {
1208 0 : specificMatchIdx = i;
1209 0 : matchPos = startIdx + specificMatches->getMatchLengthAt(i);
1210 : }
1211 : }
1212 : }
1213 0 : if (parsedPos < matchPos) {
1214 0 : U_ASSERT(specificMatchIdx >= 0);
1215 0 : parsedPos = matchPos;
1216 0 : getTimeZoneID(specificMatches.getAlias(), specificMatchIdx, parsedID);
1217 0 : parsedTimeType = getTimeType(specificMatches->getNameTypeAt(specificMatchIdx));
1218 0 : parsedOffset = UNKNOWN_OFFSET;
1219 : }
1220 : }
1221 0 : if (parseTZDBAbbrev && parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_SPECIFIC_SHORT]) == 0) {
1222 0 : const TZDBTimeZoneNames *tzdbTimeZoneNames = getTZDBTimeZoneNames(status);
1223 0 : if (U_SUCCESS(status)) {
1224 : LocalPointer<TimeZoneNames::MatchInfoCollection> tzdbNameMatches(
1225 0 : tzdbTimeZoneNames->find(text, startIdx, ALL_SIMPLE_NAME_TYPES, status));
1226 0 : if (U_FAILURE(status)) {
1227 0 : pos.setErrorIndex(startIdx);
1228 0 : return NULL;
1229 : }
1230 0 : int32_t tzdbNameMatchIdx = -1;
1231 0 : int32_t matchPos = -1;
1232 0 : if (!tzdbNameMatches.isNull()) {
1233 0 : for (int32_t i = 0; i < tzdbNameMatches->size(); i++) {
1234 0 : if (startIdx + tzdbNameMatches->getMatchLengthAt(i) > matchPos) {
1235 0 : tzdbNameMatchIdx = i;
1236 0 : matchPos = startIdx + tzdbNameMatches->getMatchLengthAt(i);
1237 : }
1238 : }
1239 : }
1240 0 : if (parsedPos < matchPos) {
1241 0 : U_ASSERT(tzdbNameMatchIdx >= 0);
1242 0 : parsedPos = matchPos;
1243 0 : getTimeZoneID(tzdbNameMatches.getAlias(), tzdbNameMatchIdx, parsedID);
1244 0 : parsedTimeType = getTimeType(tzdbNameMatches->getNameTypeAt(tzdbNameMatchIdx));
1245 0 : parsedOffset = UNKNOWN_OFFSET;
1246 : }
1247 : }
1248 : }
1249 : // Try generic names
1250 0 : if (parsedPos < maxPos) {
1251 0 : int32_t genMatchLen = -1;
1252 0 : UTimeZoneFormatTimeType tt = UTZFMT_TIME_TYPE_UNKNOWN;
1253 :
1254 0 : const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status);
1255 0 : if (U_SUCCESS(status)) {
1256 0 : genMatchLen = gnames->findBestMatch(text, startIdx, ALL_GENERIC_NAME_TYPES, tzID, tt, status);
1257 : }
1258 0 : if (U_FAILURE(status)) {
1259 0 : pos.setErrorIndex(startIdx);
1260 0 : return NULL;
1261 : }
1262 :
1263 0 : if (genMatchLen > 0 && parsedPos < startIdx + genMatchLen) {
1264 0 : parsedPos = startIdx + genMatchLen;
1265 0 : parsedID.setTo(tzID);
1266 0 : parsedTimeType = tt;
1267 0 : parsedOffset = UNKNOWN_OFFSET;
1268 : }
1269 : }
1270 :
1271 : // Try time zone ID
1272 0 : if (parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_ZONE_ID]) == 0) {
1273 0 : tmpPos.setIndex(startIdx);
1274 0 : tmpPos.setErrorIndex(-1);
1275 :
1276 0 : parseZoneID(text, tmpPos, tzID);
1277 0 : if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
1278 0 : parsedPos = tmpPos.getIndex();
1279 0 : parsedID.setTo(tzID);
1280 0 : parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
1281 0 : parsedOffset = UNKNOWN_OFFSET;
1282 : }
1283 : }
1284 : // Try short time zone ID
1285 0 : if (parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_ZONE_ID]) == 0) {
1286 0 : tmpPos.setIndex(startIdx);
1287 0 : tmpPos.setErrorIndex(-1);
1288 :
1289 0 : parseShortZoneID(text, tmpPos, tzID);
1290 0 : if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
1291 0 : parsedPos = tmpPos.getIndex();
1292 0 : parsedID.setTo(tzID);
1293 0 : parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
1294 0 : parsedOffset = UNKNOWN_OFFSET;
1295 : }
1296 : }
1297 : }
1298 :
1299 0 : if (parsedPos > startIdx) {
1300 : // Parsed successfully
1301 : TimeZone* parsedTZ;
1302 0 : if (parsedID.length() > 0) {
1303 0 : parsedTZ = TimeZone::createTimeZone(parsedID);
1304 : } else {
1305 0 : U_ASSERT(parsedOffset != UNKNOWN_OFFSET);
1306 0 : parsedTZ = createTimeZoneForOffset(parsedOffset);
1307 : }
1308 0 : if (timeType) {
1309 0 : *timeType = parsedTimeType;
1310 : }
1311 0 : pos.setIndex(parsedPos);
1312 0 : return parsedTZ;
1313 : }
1314 :
1315 0 : pos.setErrorIndex(startIdx);
1316 0 : return NULL;
1317 : }
1318 :
1319 : void
1320 0 : TimeZoneFormat::parseObject(const UnicodeString& source, Formattable& result,
1321 : ParsePosition& parse_pos) const {
1322 0 : result.adoptObject(parse(UTZFMT_STYLE_GENERIC_LOCATION, source, parse_pos, UTZFMT_PARSE_OPTION_ALL_STYLES));
1323 0 : }
1324 :
1325 :
1326 : // ------------------------------------------------------------------
1327 : // Private zone name format/parse implementation
1328 :
1329 : UnicodeString&
1330 0 : TimeZoneFormat::formatGeneric(const TimeZone& tz, int32_t genType, UDate date, UnicodeString& name) const {
1331 0 : UErrorCode status = U_ZERO_ERROR;
1332 0 : const TimeZoneGenericNames* gnames = getTimeZoneGenericNames(status);
1333 0 : if (U_FAILURE(status)) {
1334 0 : name.setToBogus();
1335 0 : return name;
1336 : }
1337 :
1338 0 : if (genType == UTZGNM_LOCATION) {
1339 0 : const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
1340 0 : if (canonicalID == NULL) {
1341 0 : name.setToBogus();
1342 0 : return name;
1343 : }
1344 0 : return gnames->getGenericLocationName(UnicodeString(TRUE, canonicalID, -1), name);
1345 : }
1346 0 : return gnames->getDisplayName(tz, (UTimeZoneGenericNameType)genType, date, name);
1347 : }
1348 :
1349 : UnicodeString&
1350 0 : TimeZoneFormat::formatSpecific(const TimeZone& tz, UTimeZoneNameType stdType, UTimeZoneNameType dstType,
1351 : UDate date, UnicodeString& name, UTimeZoneFormatTimeType *timeType) const {
1352 0 : if (fTimeZoneNames == NULL) {
1353 0 : name.setToBogus();
1354 0 : return name;
1355 : }
1356 :
1357 0 : UErrorCode status = U_ZERO_ERROR;
1358 0 : UBool isDaylight = tz.inDaylightTime(date, status);
1359 0 : const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
1360 :
1361 0 : if (U_FAILURE(status) || canonicalID == NULL) {
1362 0 : name.setToBogus();
1363 0 : return name;
1364 : }
1365 :
1366 0 : if (isDaylight) {
1367 0 : fTimeZoneNames->getDisplayName(UnicodeString(TRUE, canonicalID, -1), dstType, date, name);
1368 : } else {
1369 0 : fTimeZoneNames->getDisplayName(UnicodeString(TRUE, canonicalID, -1), stdType, date, name);
1370 : }
1371 :
1372 0 : if (timeType && !name.isEmpty()) {
1373 0 : *timeType = isDaylight ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD;
1374 : }
1375 0 : return name;
1376 : }
1377 :
1378 : const TimeZoneGenericNames*
1379 0 : TimeZoneFormat::getTimeZoneGenericNames(UErrorCode& status) const {
1380 0 : if (U_FAILURE(status)) {
1381 0 : return NULL;
1382 : }
1383 :
1384 0 : umtx_lock(&gLock);
1385 0 : if (fTimeZoneGenericNames == NULL) {
1386 0 : TimeZoneFormat *nonConstThis = const_cast<TimeZoneFormat *>(this);
1387 0 : nonConstThis->fTimeZoneGenericNames = TimeZoneGenericNames::createInstance(fLocale, status);
1388 : }
1389 0 : umtx_unlock(&gLock);
1390 :
1391 0 : return fTimeZoneGenericNames;
1392 : }
1393 :
1394 : const TZDBTimeZoneNames*
1395 0 : TimeZoneFormat::getTZDBTimeZoneNames(UErrorCode& status) const {
1396 0 : if (U_FAILURE(status)) {
1397 0 : return NULL;
1398 : }
1399 :
1400 0 : umtx_lock(&gLock);
1401 0 : if (fTZDBTimeZoneNames == NULL) {
1402 0 : TZDBTimeZoneNames *tzdbNames = new TZDBTimeZoneNames(fLocale);
1403 0 : if (tzdbNames == NULL) {
1404 0 : status = U_MEMORY_ALLOCATION_ERROR;
1405 : } else {
1406 0 : TimeZoneFormat *nonConstThis = const_cast<TimeZoneFormat *>(this);
1407 0 : nonConstThis->fTZDBTimeZoneNames = tzdbNames;
1408 : }
1409 : }
1410 0 : umtx_unlock(&gLock);
1411 :
1412 0 : return fTZDBTimeZoneNames;
1413 : }
1414 :
1415 : UnicodeString&
1416 0 : TimeZoneFormat::formatExemplarLocation(const TimeZone& tz, UnicodeString& name) const {
1417 : UChar locationBuf[64];
1418 0 : UnicodeString location(locationBuf, 0, UPRV_LENGTHOF(locationBuf));
1419 0 : const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
1420 :
1421 0 : if (canonicalID) {
1422 0 : fTimeZoneNames->getExemplarLocationName(UnicodeString(TRUE, canonicalID, -1), location);
1423 : }
1424 0 : if (location.length() > 0) {
1425 0 : name.setTo(location);
1426 : } else {
1427 : // Use "unknown" location
1428 0 : fTimeZoneNames->getExemplarLocationName(UnicodeString(TRUE, UNKNOWN_ZONE_ID, -1), location);
1429 0 : if (location.length() > 0) {
1430 0 : name.setTo(location);
1431 : } else {
1432 : // last resort
1433 0 : name.setTo(UNKNOWN_LOCATION, -1);
1434 : }
1435 : }
1436 0 : return name;
1437 : }
1438 :
1439 :
1440 : // ------------------------------------------------------------------
1441 : // Zone offset format and parse
1442 :
1443 : UnicodeString&
1444 0 : TimeZoneFormat::formatOffsetISO8601Basic(int32_t offset, UBool useUtcIndicator, UBool isShort, UBool ignoreSeconds,
1445 : UnicodeString& result, UErrorCode& status) const {
1446 0 : return formatOffsetISO8601(offset, TRUE, useUtcIndicator, isShort, ignoreSeconds, result, status);
1447 : }
1448 :
1449 : UnicodeString&
1450 0 : TimeZoneFormat::formatOffsetISO8601Extended(int32_t offset, UBool useUtcIndicator, UBool isShort, UBool ignoreSeconds,
1451 : UnicodeString& result, UErrorCode& status) const {
1452 0 : return formatOffsetISO8601(offset, FALSE, useUtcIndicator, isShort, ignoreSeconds, result, status);
1453 : }
1454 :
1455 : UnicodeString&
1456 0 : TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset, UnicodeString& result, UErrorCode& status) const {
1457 0 : return formatOffsetLocalizedGMT(offset, FALSE, result, status);
1458 : }
1459 :
1460 : UnicodeString&
1461 0 : TimeZoneFormat::formatOffsetShortLocalizedGMT(int32_t offset, UnicodeString& result, UErrorCode& status) const {
1462 0 : return formatOffsetLocalizedGMT(offset, TRUE, result, status);
1463 : }
1464 :
1465 : int32_t
1466 0 : TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& pos) const {
1467 0 : return parseOffsetISO8601(text, pos, FALSE);
1468 : }
1469 :
1470 : int32_t
1471 0 : TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos) const {
1472 0 : return parseOffsetLocalizedGMT(text, pos, FALSE, NULL);
1473 : }
1474 :
1475 : int32_t
1476 0 : TimeZoneFormat::parseOffsetShortLocalizedGMT(const UnicodeString& text, ParsePosition& pos) const {
1477 0 : return parseOffsetLocalizedGMT(text, pos, TRUE, NULL);
1478 : }
1479 :
1480 : // ------------------------------------------------------------------
1481 : // Private zone offset format/parse implementation
1482 :
1483 : UnicodeString&
1484 0 : TimeZoneFormat::formatOffsetISO8601(int32_t offset, UBool isBasic, UBool useUtcIndicator,
1485 : UBool isShort, UBool ignoreSeconds, UnicodeString& result, UErrorCode& status) const {
1486 0 : if (U_FAILURE(status)) {
1487 0 : result.setToBogus();
1488 0 : return result;
1489 : }
1490 0 : int32_t absOffset = offset < 0 ? -offset : offset;
1491 0 : if (useUtcIndicator && (absOffset < MILLIS_PER_SECOND || (ignoreSeconds && absOffset < MILLIS_PER_MINUTE))) {
1492 0 : result.setTo(ISO8601_UTC);
1493 0 : return result;
1494 : }
1495 :
1496 0 : OffsetFields minFields = isShort ? FIELDS_H : FIELDS_HM;
1497 0 : OffsetFields maxFields = ignoreSeconds ? FIELDS_HM : FIELDS_HMS;
1498 0 : UChar sep = isBasic ? 0 : ISO8601_SEP;
1499 :
1500 : // Note: FIELDS_HMS as maxFields is a CLDR/ICU extension. ISO 8601 specification does
1501 : // not support seconds field.
1502 :
1503 0 : if (absOffset >= MAX_OFFSET) {
1504 0 : result.setToBogus();
1505 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
1506 0 : return result;
1507 : }
1508 :
1509 : int fields[3];
1510 0 : fields[0] = absOffset / MILLIS_PER_HOUR;
1511 0 : absOffset = absOffset % MILLIS_PER_HOUR;
1512 0 : fields[1] = absOffset / MILLIS_PER_MINUTE;
1513 0 : absOffset = absOffset % MILLIS_PER_MINUTE;
1514 0 : fields[2] = absOffset / MILLIS_PER_SECOND;
1515 :
1516 0 : U_ASSERT(fields[0] >= 0 && fields[0] <= MAX_OFFSET_HOUR);
1517 0 : U_ASSERT(fields[1] >= 0 && fields[1] <= MAX_OFFSET_MINUTE);
1518 0 : U_ASSERT(fields[2] >= 0 && fields[2] <= MAX_OFFSET_SECOND);
1519 :
1520 0 : int32_t lastIdx = maxFields;
1521 0 : while (lastIdx > minFields) {
1522 0 : if (fields[lastIdx] != 0) {
1523 0 : break;
1524 : }
1525 0 : lastIdx--;
1526 : }
1527 :
1528 0 : UChar sign = PLUS;
1529 0 : if (offset < 0) {
1530 : // if all output fields are 0s, do not use negative sign
1531 0 : for (int32_t idx = 0; idx <= lastIdx; idx++) {
1532 0 : if (fields[idx] != 0) {
1533 0 : sign = MINUS;
1534 0 : break;
1535 : }
1536 : }
1537 : }
1538 0 : result.setTo(sign);
1539 :
1540 0 : for (int32_t idx = 0; idx <= lastIdx; idx++) {
1541 0 : if (sep && idx != 0) {
1542 0 : result.append(sep);
1543 : }
1544 0 : result.append((UChar)(0x0030 + fields[idx]/10));
1545 0 : result.append((UChar)(0x0030 + fields[idx]%10));
1546 : }
1547 :
1548 0 : return result;
1549 : }
1550 :
1551 : UnicodeString&
1552 0 : TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset, UBool isShort, UnicodeString& result, UErrorCode& status) const {
1553 0 : if (U_FAILURE(status)) {
1554 0 : result.setToBogus();
1555 0 : return result;
1556 : }
1557 0 : if (offset <= -MAX_OFFSET || offset >= MAX_OFFSET) {
1558 0 : result.setToBogus();
1559 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
1560 0 : return result;
1561 : }
1562 :
1563 0 : if (offset == 0) {
1564 0 : result.setTo(fGMTZeroFormat);
1565 0 : return result;
1566 : }
1567 :
1568 0 : UBool positive = TRUE;
1569 0 : if (offset < 0) {
1570 0 : offset = -offset;
1571 0 : positive = FALSE;
1572 : }
1573 :
1574 0 : int32_t offsetH = offset / MILLIS_PER_HOUR;
1575 0 : offset = offset % MILLIS_PER_HOUR;
1576 0 : int32_t offsetM = offset / MILLIS_PER_MINUTE;
1577 0 : offset = offset % MILLIS_PER_MINUTE;
1578 0 : int32_t offsetS = offset / MILLIS_PER_SECOND;
1579 :
1580 0 : U_ASSERT(offsetH <= MAX_OFFSET_HOUR && offsetM <= MAX_OFFSET_MINUTE && offsetS <= MAX_OFFSET_SECOND);
1581 :
1582 0 : const UVector* offsetPatternItems = NULL;
1583 0 : if (positive) {
1584 0 : if (offsetS != 0) {
1585 0 : offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HMS];
1586 0 : } else if (offsetM != 0 || !isShort) {
1587 0 : offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HM];
1588 : } else {
1589 0 : offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_H];
1590 : }
1591 : } else {
1592 0 : if (offsetS != 0) {
1593 0 : offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HMS];
1594 0 : } else if (offsetM != 0 || !isShort) {
1595 0 : offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HM];
1596 : } else {
1597 0 : offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_H];
1598 : }
1599 : }
1600 :
1601 0 : U_ASSERT(offsetPatternItems != NULL);
1602 :
1603 : // Building the GMT format string
1604 0 : result.setTo(fGMTPatternPrefix);
1605 :
1606 0 : for (int32_t i = 0; i < offsetPatternItems->size(); i++) {
1607 0 : const GMTOffsetField* item = (GMTOffsetField*)offsetPatternItems->elementAt(i);
1608 0 : GMTOffsetField::FieldType type = item->getType();
1609 :
1610 0 : switch (type) {
1611 : case GMTOffsetField::TEXT:
1612 0 : result.append(item->getPatternText(), -1);
1613 0 : break;
1614 :
1615 : case GMTOffsetField::HOUR:
1616 0 : appendOffsetDigits(result, offsetH, (isShort ? 1 : 2));
1617 0 : break;
1618 :
1619 : case GMTOffsetField::MINUTE:
1620 0 : appendOffsetDigits(result, offsetM, 2);
1621 0 : break;
1622 :
1623 : case GMTOffsetField::SECOND:
1624 0 : appendOffsetDigits(result, offsetS, 2);
1625 0 : break;
1626 : }
1627 : }
1628 :
1629 0 : result.append(fGMTPatternSuffix);
1630 0 : return result;
1631 : }
1632 :
1633 : int32_t
1634 0 : TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& pos, UBool extendedOnly, UBool* hasDigitOffset /* = NULL */) const {
1635 0 : if (hasDigitOffset) {
1636 0 : *hasDigitOffset = FALSE;
1637 : }
1638 0 : int32_t start = pos.getIndex();
1639 0 : if (start >= text.length()) {
1640 0 : pos.setErrorIndex(start);
1641 0 : return 0;
1642 : }
1643 :
1644 0 : UChar firstChar = text.charAt(start);
1645 0 : if (firstChar == ISO8601_UTC || firstChar == (UChar)(ISO8601_UTC + 0x20)) {
1646 : // "Z" (or "z") - indicates UTC
1647 0 : pos.setIndex(start + 1);
1648 0 : return 0;
1649 : }
1650 :
1651 0 : int32_t sign = 1;
1652 0 : if (firstChar == PLUS) {
1653 0 : sign = 1;
1654 0 : } else if (firstChar == MINUS) {
1655 0 : sign = -1;
1656 : } else {
1657 : // Not an ISO 8601 offset string
1658 0 : pos.setErrorIndex(start);
1659 0 : return 0;
1660 : }
1661 0 : ParsePosition posOffset(start + 1);
1662 0 : int32_t offset = parseAsciiOffsetFields(text, posOffset, ISO8601_SEP, FIELDS_H, FIELDS_HMS);
1663 0 : if (posOffset.getErrorIndex() == -1 && !extendedOnly && (posOffset.getIndex() - start <= 3)) {
1664 : // If the text is successfully parsed as extended format with the options above, it can be also parsed
1665 : // as basic format. For example, "0230" can be parsed as offset 2:00 (only first digits are valid for
1666 : // extended format), but it can be parsed as offset 2:30 with basic format. We use longer result.
1667 0 : ParsePosition posBasic(start + 1);
1668 0 : int32_t tmpOffset = parseAbuttingAsciiOffsetFields(text, posBasic, FIELDS_H, FIELDS_HMS, FALSE);
1669 0 : if (posBasic.getErrorIndex() == -1 && posBasic.getIndex() > posOffset.getIndex()) {
1670 0 : offset = tmpOffset;
1671 0 : posOffset.setIndex(posBasic.getIndex());
1672 : }
1673 : }
1674 :
1675 0 : if (posOffset.getErrorIndex() != -1) {
1676 0 : pos.setErrorIndex(start);
1677 0 : return 0;
1678 : }
1679 :
1680 0 : pos.setIndex(posOffset.getIndex());
1681 0 : if (hasDigitOffset) {
1682 0 : *hasDigitOffset = TRUE;
1683 : }
1684 0 : return sign * offset;
1685 : }
1686 :
1687 : int32_t
1688 0 : TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos, UBool isShort, UBool* hasDigitOffset) const {
1689 0 : int32_t start = pos.getIndex();
1690 0 : int32_t offset = 0;
1691 0 : int32_t parsedLength = 0;
1692 :
1693 0 : if (hasDigitOffset) {
1694 0 : *hasDigitOffset = FALSE;
1695 : }
1696 :
1697 0 : offset = parseOffsetLocalizedGMTPattern(text, start, isShort, parsedLength);
1698 :
1699 : // For now, parseOffsetLocalizedGMTPattern handles both long and short
1700 : // formats, no matter isShort is true or false. This might be changed in future
1701 : // when strict parsing is necessary, or different set of patterns are used for
1702 : // short/long formats.
1703 : #if 0
1704 : if (parsedLength == 0) {
1705 : offset = parseOffsetLocalizedGMTPattern(text, start, !isShort, parsedLength);
1706 : }
1707 : #endif
1708 :
1709 0 : if (parsedLength > 0) {
1710 0 : if (hasDigitOffset) {
1711 0 : *hasDigitOffset = TRUE;
1712 : }
1713 0 : pos.setIndex(start + parsedLength);
1714 0 : return offset;
1715 : }
1716 :
1717 : // Try the default patterns
1718 0 : offset = parseOffsetDefaultLocalizedGMT(text, start, parsedLength);
1719 0 : if (parsedLength > 0) {
1720 0 : if (hasDigitOffset) {
1721 0 : *hasDigitOffset = TRUE;
1722 : }
1723 0 : pos.setIndex(start + parsedLength);
1724 0 : return offset;
1725 : }
1726 :
1727 : // Check if this is a GMT zero format
1728 0 : if (text.caseCompare(start, fGMTZeroFormat.length(), fGMTZeroFormat, 0) == 0) {
1729 0 : pos.setIndex(start + fGMTZeroFormat.length());
1730 0 : return 0;
1731 : }
1732 :
1733 : // Check if this is a default GMT zero format
1734 0 : for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) {
1735 0 : const UChar* defGMTZero = ALT_GMT_STRINGS[i];
1736 0 : int32_t defGMTZeroLen = u_strlen(defGMTZero);
1737 0 : if (text.caseCompare(start, defGMTZeroLen, defGMTZero, 0) == 0) {
1738 0 : pos.setIndex(start + defGMTZeroLen);
1739 0 : return 0;
1740 : }
1741 : }
1742 :
1743 : // Nothing matched
1744 0 : pos.setErrorIndex(start);
1745 0 : return 0;
1746 : }
1747 :
1748 : int32_t
1749 0 : TimeZoneFormat::parseOffsetLocalizedGMTPattern(const UnicodeString& text, int32_t start, UBool /*isShort*/, int32_t& parsedLen) const {
1750 0 : int32_t idx = start;
1751 0 : int32_t offset = 0;
1752 0 : UBool parsed = FALSE;
1753 :
1754 : do {
1755 : // Prefix part
1756 0 : int32_t len = fGMTPatternPrefix.length();
1757 0 : if (len > 0 && text.caseCompare(idx, len, fGMTPatternPrefix, 0) != 0) {
1758 : // prefix match failed
1759 0 : break;
1760 : }
1761 0 : idx += len;
1762 :
1763 : // Offset part
1764 0 : offset = parseOffsetFields(text, idx, FALSE, len);
1765 0 : if (len == 0) {
1766 : // offset field match failed
1767 0 : break;
1768 : }
1769 0 : idx += len;
1770 :
1771 0 : len = fGMTPatternSuffix.length();
1772 0 : if (len > 0 && text.caseCompare(idx, len, fGMTPatternSuffix, 0) != 0) {
1773 : // no suffix match
1774 0 : break;
1775 : }
1776 0 : idx += len;
1777 0 : parsed = TRUE;
1778 : } while (FALSE);
1779 :
1780 0 : parsedLen = parsed ? idx - start : 0;
1781 0 : return offset;
1782 : }
1783 :
1784 : int32_t
1785 0 : TimeZoneFormat::parseOffsetFields(const UnicodeString& text, int32_t start, UBool /*isShort*/, int32_t& parsedLen) const {
1786 0 : int32_t outLen = 0;
1787 0 : int32_t offset = 0;
1788 0 : int32_t sign = 1;
1789 :
1790 0 : parsedLen = 0;
1791 :
1792 : int32_t offsetH, offsetM, offsetS;
1793 0 : offsetH = offsetM = offsetS = 0;
1794 :
1795 0 : for (int32_t patidx = 0; PARSE_GMT_OFFSET_TYPES[patidx] >= 0; patidx++) {
1796 0 : int32_t gmtPatType = PARSE_GMT_OFFSET_TYPES[patidx];
1797 0 : UVector* items = fGMTOffsetPatternItems[gmtPatType];
1798 0 : U_ASSERT(items != NULL);
1799 :
1800 0 : outLen = parseOffsetFieldsWithPattern(text, start, items, FALSE, offsetH, offsetM, offsetS);
1801 0 : if (outLen > 0) {
1802 0 : sign = (gmtPatType == UTZFMT_PAT_POSITIVE_H || gmtPatType == UTZFMT_PAT_POSITIVE_HM || gmtPatType == UTZFMT_PAT_POSITIVE_HMS) ?
1803 : 1 : -1;
1804 0 : break;
1805 : }
1806 : }
1807 :
1808 0 : if (outLen > 0 && fAbuttingOffsetHoursAndMinutes) {
1809 : // When hours field is sabutting minutes field,
1810 : // the parse result above may not be appropriate.
1811 : // For example, "01020" is parsed as 01:02: above,
1812 : // but it should be parsed as 00:10:20.
1813 0 : int32_t tmpLen = 0;
1814 0 : int32_t tmpSign = 1;
1815 : int32_t tmpH, tmpM, tmpS;
1816 :
1817 0 : for (int32_t patidx = 0; PARSE_GMT_OFFSET_TYPES[patidx] >= 0; patidx++) {
1818 0 : int32_t gmtPatType = PARSE_GMT_OFFSET_TYPES[patidx];
1819 0 : UVector* items = fGMTOffsetPatternItems[gmtPatType];
1820 0 : U_ASSERT(items != NULL);
1821 :
1822 : // forcing parse to use single hour digit
1823 0 : tmpLen = parseOffsetFieldsWithPattern(text, start, items, TRUE, tmpH, tmpM, tmpS);
1824 0 : if (tmpLen > 0) {
1825 0 : tmpSign = (gmtPatType == UTZFMT_PAT_POSITIVE_H || gmtPatType == UTZFMT_PAT_POSITIVE_HM || gmtPatType == UTZFMT_PAT_POSITIVE_HMS) ?
1826 : 1 : -1;
1827 0 : break;
1828 : }
1829 : }
1830 0 : if (tmpLen > outLen) {
1831 : // Better parse result with single hour digit
1832 0 : outLen = tmpLen;
1833 0 : sign = tmpSign;
1834 0 : offsetH = tmpH;
1835 0 : offsetM = tmpM;
1836 0 : offsetS = tmpS;
1837 : }
1838 : }
1839 :
1840 0 : if (outLen > 0) {
1841 0 : offset = ((((offsetH * 60) + offsetM) * 60) + offsetS) * 1000 * sign;
1842 0 : parsedLen = outLen;
1843 : }
1844 :
1845 0 : return offset;
1846 : }
1847 :
1848 : int32_t
1849 0 : TimeZoneFormat::parseOffsetFieldsWithPattern(const UnicodeString& text, int32_t start,
1850 : UVector* patternItems, UBool forceSingleHourDigit, int32_t& hour, int32_t& min, int32_t& sec) const {
1851 0 : UBool failed = FALSE;
1852 : int32_t offsetH, offsetM, offsetS;
1853 0 : offsetH = offsetM = offsetS = 0;
1854 0 : int32_t idx = start;
1855 :
1856 0 : for (int32_t i = 0; i < patternItems->size(); i++) {
1857 0 : int32_t len = 0;
1858 0 : const GMTOffsetField* field = (const GMTOffsetField*)patternItems->elementAt(i);
1859 0 : GMTOffsetField::FieldType fieldType = field->getType();
1860 0 : if (fieldType == GMTOffsetField::TEXT) {
1861 0 : const UChar* patStr = field->getPatternText();
1862 0 : len = u_strlen(patStr);
1863 0 : if (text.caseCompare(idx, len, patStr, 0) != 0) {
1864 0 : failed = TRUE;
1865 0 : break;
1866 : }
1867 0 : idx += len;
1868 : } else {
1869 0 : if (fieldType == GMTOffsetField::HOUR) {
1870 0 : uint8_t maxDigits = forceSingleHourDigit ? 1 : 2;
1871 0 : offsetH = parseOffsetFieldWithLocalizedDigits(text, idx, 1, maxDigits, 0, MAX_OFFSET_HOUR, len);
1872 0 : } else if (fieldType == GMTOffsetField::MINUTE) {
1873 0 : offsetM = parseOffsetFieldWithLocalizedDigits(text, idx, 2, 2, 0, MAX_OFFSET_MINUTE, len);
1874 0 : } else if (fieldType == GMTOffsetField::SECOND) {
1875 0 : offsetS = parseOffsetFieldWithLocalizedDigits(text, idx, 2, 2, 0, MAX_OFFSET_SECOND, len);
1876 : }
1877 :
1878 0 : if (len == 0) {
1879 0 : failed = TRUE;
1880 0 : break;
1881 : }
1882 0 : idx += len;
1883 : }
1884 : }
1885 :
1886 0 : if (failed) {
1887 0 : hour = min = sec = 0;
1888 0 : return 0;
1889 : }
1890 :
1891 0 : hour = offsetH;
1892 0 : min = offsetM;
1893 0 : sec = offsetS;
1894 :
1895 0 : return idx - start;
1896 : }
1897 :
1898 : int32_t
1899 0 : TimeZoneFormat::parseAbuttingOffsetFields(const UnicodeString& text, int32_t start, int32_t& parsedLen) const {
1900 : int32_t digits[MAX_OFFSET_DIGITS];
1901 : int32_t parsed[MAX_OFFSET_DIGITS]; // accumulative offsets
1902 :
1903 : // Parse digits into int[]
1904 0 : int32_t idx = start;
1905 0 : int32_t len = 0;
1906 0 : int32_t numDigits = 0;
1907 0 : for (int32_t i = 0; i < MAX_OFFSET_DIGITS; i++) {
1908 0 : digits[i] = parseSingleLocalizedDigit(text, idx, len);
1909 0 : if (digits[i] < 0) {
1910 0 : break;
1911 : }
1912 0 : idx += len;
1913 0 : parsed[i] = idx - start;
1914 0 : numDigits++;
1915 : }
1916 :
1917 0 : if (numDigits == 0) {
1918 0 : parsedLen = 0;
1919 0 : return 0;
1920 : }
1921 :
1922 0 : int32_t offset = 0;
1923 0 : while (numDigits > 0) {
1924 0 : int32_t hour = 0;
1925 0 : int32_t min = 0;
1926 0 : int32_t sec = 0;
1927 :
1928 0 : U_ASSERT(numDigits > 0 && numDigits <= MAX_OFFSET_DIGITS);
1929 0 : switch (numDigits) {
1930 : case 1: // H
1931 0 : hour = digits[0];
1932 0 : break;
1933 : case 2: // HH
1934 0 : hour = digits[0] * 10 + digits[1];
1935 0 : break;
1936 : case 3: // Hmm
1937 0 : hour = digits[0];
1938 0 : min = digits[1] * 10 + digits[2];
1939 0 : break;
1940 : case 4: // HHmm
1941 0 : hour = digits[0] * 10 + digits[1];
1942 0 : min = digits[2] * 10 + digits[3];
1943 0 : break;
1944 : case 5: // Hmmss
1945 0 : hour = digits[0];
1946 0 : min = digits[1] * 10 + digits[2];
1947 0 : sec = digits[3] * 10 + digits[4];
1948 0 : break;
1949 : case 6: // HHmmss
1950 0 : hour = digits[0] * 10 + digits[1];
1951 0 : min = digits[2] * 10 + digits[3];
1952 0 : sec = digits[4] * 10 + digits[5];
1953 0 : break;
1954 : }
1955 0 : if (hour <= MAX_OFFSET_HOUR && min <= MAX_OFFSET_MINUTE && sec <= MAX_OFFSET_SECOND) {
1956 : // found a valid combination
1957 0 : offset = hour * MILLIS_PER_HOUR + min * MILLIS_PER_MINUTE + sec * MILLIS_PER_SECOND;
1958 0 : parsedLen = parsed[numDigits - 1];
1959 0 : break;
1960 : }
1961 0 : numDigits--;
1962 : }
1963 0 : return offset;
1964 : }
1965 :
1966 : int32_t
1967 0 : TimeZoneFormat::parseOffsetDefaultLocalizedGMT(const UnicodeString& text, int start, int32_t& parsedLen) const {
1968 0 : int32_t idx = start;
1969 0 : int32_t offset = 0;
1970 0 : int32_t parsed = 0;
1971 :
1972 : do {
1973 : // check global default GMT alternatives
1974 0 : int32_t gmtLen = 0;
1975 :
1976 0 : for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) {
1977 0 : const UChar* gmt = ALT_GMT_STRINGS[i];
1978 0 : int32_t len = u_strlen(gmt);
1979 0 : if (text.caseCompare(start, len, gmt, 0) == 0) {
1980 0 : gmtLen = len;
1981 0 : break;
1982 : }
1983 : }
1984 0 : if (gmtLen == 0) {
1985 0 : break;
1986 : }
1987 0 : idx += gmtLen;
1988 :
1989 : // offset needs a sign char and a digit at minimum
1990 0 : if (idx + 1 >= text.length()) {
1991 0 : break;
1992 : }
1993 :
1994 : // parse sign
1995 0 : int32_t sign = 1;
1996 0 : UChar c = text.charAt(idx);
1997 0 : if (c == PLUS) {
1998 0 : sign = 1;
1999 0 : } else if (c == MINUS) {
2000 0 : sign = -1;
2001 : } else {
2002 0 : break;
2003 : }
2004 0 : idx++;
2005 :
2006 : // offset part
2007 : // try the default pattern with the separator first
2008 0 : int32_t lenWithSep = 0;
2009 0 : int32_t offsetWithSep = parseDefaultOffsetFields(text, idx, DEFAULT_GMT_OFFSET_SEP, lenWithSep);
2010 0 : if (lenWithSep == text.length() - idx) {
2011 : // maximum match
2012 0 : offset = offsetWithSep * sign;
2013 0 : idx += lenWithSep;
2014 : } else {
2015 : // try abutting field pattern
2016 0 : int32_t lenAbut = 0;
2017 0 : int32_t offsetAbut = parseAbuttingOffsetFields(text, idx, lenAbut);
2018 :
2019 0 : if (lenWithSep > lenAbut) {
2020 0 : offset = offsetWithSep * sign;
2021 0 : idx += lenWithSep;
2022 : } else {
2023 0 : offset = offsetAbut * sign;
2024 0 : idx += lenAbut;
2025 : }
2026 : }
2027 0 : parsed = idx - start;
2028 : } while (false);
2029 :
2030 0 : parsedLen = parsed;
2031 0 : return offset;
2032 : }
2033 :
2034 : int32_t
2035 0 : TimeZoneFormat::parseDefaultOffsetFields(const UnicodeString& text, int32_t start, UChar separator, int32_t& parsedLen) const {
2036 0 : int32_t max = text.length();
2037 0 : int32_t idx = start;
2038 0 : int32_t len = 0;
2039 0 : int32_t hour = 0, min = 0, sec = 0;
2040 :
2041 0 : parsedLen = 0;
2042 :
2043 : do {
2044 0 : hour = parseOffsetFieldWithLocalizedDigits(text, idx, 1, 2, 0, MAX_OFFSET_HOUR, len);
2045 0 : if (len == 0) {
2046 0 : break;
2047 : }
2048 0 : idx += len;
2049 :
2050 0 : if (idx + 1 < max && text.charAt(idx) == separator) {
2051 0 : min = parseOffsetFieldWithLocalizedDigits(text, idx + 1, 2, 2, 0, MAX_OFFSET_MINUTE, len);
2052 0 : if (len == 0) {
2053 0 : break;
2054 : }
2055 0 : idx += (1 + len);
2056 :
2057 0 : if (idx + 1 < max && text.charAt(idx) == separator) {
2058 0 : sec = parseOffsetFieldWithLocalizedDigits(text, idx + 1, 2, 2, 0, MAX_OFFSET_SECOND, len);
2059 0 : if (len == 0) {
2060 0 : break;
2061 : }
2062 0 : idx += (1 + len);
2063 : }
2064 : }
2065 : } while (FALSE);
2066 :
2067 0 : if (idx == start) {
2068 0 : return 0;
2069 : }
2070 :
2071 0 : parsedLen = idx - start;
2072 0 : return hour * MILLIS_PER_HOUR + min * MILLIS_PER_MINUTE + sec * MILLIS_PER_SECOND;
2073 : }
2074 :
2075 : int32_t
2076 0 : TimeZoneFormat::parseOffsetFieldWithLocalizedDigits(const UnicodeString& text, int32_t start, uint8_t minDigits, uint8_t maxDigits, uint16_t minVal, uint16_t maxVal, int32_t& parsedLen) const {
2077 0 : parsedLen = 0;
2078 :
2079 0 : int32_t decVal = 0;
2080 0 : int32_t numDigits = 0;
2081 0 : int32_t idx = start;
2082 0 : int32_t digitLen = 0;
2083 :
2084 0 : while (idx < text.length() && numDigits < maxDigits) {
2085 0 : int32_t digit = parseSingleLocalizedDigit(text, idx, digitLen);
2086 0 : if (digit < 0) {
2087 0 : break;
2088 : }
2089 0 : int32_t tmpVal = decVal * 10 + digit;
2090 0 : if (tmpVal > maxVal) {
2091 0 : break;
2092 : }
2093 0 : decVal = tmpVal;
2094 0 : numDigits++;
2095 0 : idx += digitLen;
2096 : }
2097 :
2098 : // Note: maxVal is checked in the while loop
2099 0 : if (numDigits < minDigits || decVal < minVal) {
2100 0 : decVal = -1;
2101 0 : numDigits = 0;
2102 : } else {
2103 0 : parsedLen = idx - start;
2104 : }
2105 :
2106 0 : return decVal;
2107 : }
2108 :
2109 : int32_t
2110 0 : TimeZoneFormat::parseSingleLocalizedDigit(const UnicodeString& text, int32_t start, int32_t& len) const {
2111 0 : int32_t digit = -1;
2112 0 : len = 0;
2113 0 : if (start < text.length()) {
2114 0 : UChar32 cp = text.char32At(start);
2115 :
2116 : // First, try digits configured for this instance
2117 0 : for (int32_t i = 0; i < 10; i++) {
2118 0 : if (cp == fGMTOffsetDigits[i]) {
2119 0 : digit = i;
2120 0 : break;
2121 : }
2122 : }
2123 : // If failed, check if this is a Unicode digit
2124 0 : if (digit < 0) {
2125 0 : int32_t tmp = u_charDigitValue(cp);
2126 0 : digit = (tmp >= 0 && tmp <= 9) ? tmp : -1;
2127 : }
2128 :
2129 0 : if (digit >= 0) {
2130 0 : int32_t next = text.moveIndex32(start, 1);
2131 0 : len = next - start;
2132 : }
2133 : }
2134 0 : return digit;
2135 : }
2136 :
2137 : UnicodeString&
2138 0 : TimeZoneFormat::formatOffsetWithAsciiDigits(int32_t offset, UChar sep, OffsetFields minFields, OffsetFields maxFields, UnicodeString& result) {
2139 0 : U_ASSERT(maxFields >= minFields);
2140 0 : U_ASSERT(offset > -MAX_OFFSET && offset < MAX_OFFSET);
2141 :
2142 0 : UChar sign = PLUS;
2143 0 : if (offset < 0) {
2144 0 : sign = MINUS;
2145 0 : offset = -offset;
2146 : }
2147 0 : result.setTo(sign);
2148 :
2149 : int fields[3];
2150 0 : fields[0] = offset / MILLIS_PER_HOUR;
2151 0 : offset = offset % MILLIS_PER_HOUR;
2152 0 : fields[1] = offset / MILLIS_PER_MINUTE;
2153 0 : offset = offset % MILLIS_PER_MINUTE;
2154 0 : fields[2] = offset / MILLIS_PER_SECOND;
2155 :
2156 0 : U_ASSERT(fields[0] >= 0 && fields[0] <= MAX_OFFSET_HOUR);
2157 0 : U_ASSERT(fields[1] >= 0 && fields[1] <= MAX_OFFSET_MINUTE);
2158 0 : U_ASSERT(fields[2] >= 0 && fields[2] <= MAX_OFFSET_SECOND);
2159 :
2160 0 : int32_t lastIdx = maxFields;
2161 0 : while (lastIdx > minFields) {
2162 0 : if (fields[lastIdx] != 0) {
2163 0 : break;
2164 : }
2165 0 : lastIdx--;
2166 : }
2167 :
2168 0 : for (int32_t idx = 0; idx <= lastIdx; idx++) {
2169 0 : if (sep && idx != 0) {
2170 0 : result.append(sep);
2171 : }
2172 0 : result.append((UChar)(0x0030 + fields[idx]/10));
2173 0 : result.append((UChar)(0x0030 + fields[idx]%10));
2174 : }
2175 :
2176 0 : return result;
2177 : }
2178 :
2179 : int32_t
2180 0 : TimeZoneFormat::parseAbuttingAsciiOffsetFields(const UnicodeString& text, ParsePosition& pos, OffsetFields minFields, OffsetFields maxFields, UBool fixedHourWidth) {
2181 0 : int32_t start = pos.getIndex();
2182 :
2183 0 : int32_t minDigits = 2 * (minFields + 1) - (fixedHourWidth ? 0 : 1);
2184 0 : int32_t maxDigits = 2 * (maxFields + 1);
2185 :
2186 0 : U_ASSERT(maxDigits <= MAX_OFFSET_DIGITS);
2187 :
2188 0 : int32_t digits[MAX_OFFSET_DIGITS] = {};
2189 0 : int32_t numDigits = 0;
2190 0 : int32_t idx = start;
2191 0 : while (numDigits < maxDigits && idx < text.length()) {
2192 0 : UChar uch = text.charAt(idx);
2193 0 : int32_t digit = DIGIT_VAL(uch);
2194 0 : if (digit < 0) {
2195 0 : break;
2196 : }
2197 0 : digits[numDigits] = digit;
2198 0 : numDigits++;
2199 0 : idx++;
2200 : }
2201 :
2202 0 : if (fixedHourWidth && (numDigits & 1)) {
2203 : // Fixed digits, so the number of digits must be even number. Truncating.
2204 0 : numDigits--;
2205 : }
2206 :
2207 0 : if (numDigits < minDigits) {
2208 0 : pos.setErrorIndex(start);
2209 0 : return 0;
2210 : }
2211 :
2212 0 : int32_t hour = 0, min = 0, sec = 0;
2213 0 : UBool bParsed = FALSE;
2214 0 : while (numDigits >= minDigits) {
2215 0 : switch (numDigits) {
2216 : case 1: //H
2217 0 : hour = digits[0];
2218 0 : break;
2219 : case 2: //HH
2220 0 : hour = digits[0] * 10 + digits[1];
2221 0 : break;
2222 : case 3: //Hmm
2223 0 : hour = digits[0];
2224 0 : min = digits[1] * 10 + digits[2];
2225 0 : break;
2226 : case 4: //HHmm
2227 0 : hour = digits[0] * 10 + digits[1];
2228 0 : min = digits[2] * 10 + digits[3];
2229 0 : break;
2230 : case 5: //Hmmss
2231 0 : hour = digits[0];
2232 0 : min = digits[1] * 10 + digits[2];
2233 0 : sec = digits[3] * 10 + digits[4];
2234 0 : break;
2235 : case 6: //HHmmss
2236 0 : hour = digits[0] * 10 + digits[1];
2237 0 : min = digits[2] * 10 + digits[3];
2238 0 : sec = digits[4] * 10 + digits[5];
2239 0 : break;
2240 : }
2241 :
2242 0 : if (hour <= MAX_OFFSET_HOUR && min <= MAX_OFFSET_MINUTE && sec <= MAX_OFFSET_SECOND) {
2243 : // Successfully parsed
2244 0 : bParsed = true;
2245 0 : break;
2246 : }
2247 :
2248 : // Truncating
2249 0 : numDigits -= (fixedHourWidth ? 2 : 1);
2250 0 : hour = min = sec = 0;
2251 : }
2252 :
2253 0 : if (!bParsed) {
2254 0 : pos.setErrorIndex(start);
2255 0 : return 0;
2256 : }
2257 0 : pos.setIndex(start + numDigits);
2258 0 : return ((((hour * 60) + min) * 60) + sec) * 1000;
2259 : }
2260 :
2261 : int32_t
2262 0 : TimeZoneFormat::parseAsciiOffsetFields(const UnicodeString& text, ParsePosition& pos, UChar sep, OffsetFields minFields, OffsetFields maxFields) {
2263 0 : int32_t start = pos.getIndex();
2264 0 : int32_t fieldVal[] = {0, 0, 0};
2265 0 : int32_t fieldLen[] = {0, -1, -1};
2266 0 : for (int32_t idx = start, fieldIdx = 0; idx < text.length() && fieldIdx <= maxFields; idx++) {
2267 0 : UChar c = text.charAt(idx);
2268 0 : if (c == sep) {
2269 0 : if (fieldIdx == 0) {
2270 0 : if (fieldLen[0] == 0) {
2271 : // no hours field
2272 0 : break;
2273 : }
2274 : // 1 digit hour, move to next field
2275 : } else {
2276 0 : if (fieldLen[fieldIdx] != -1) {
2277 : // premature minute or seconds field
2278 0 : break;
2279 : }
2280 0 : fieldLen[fieldIdx] = 0;
2281 : }
2282 0 : continue;
2283 0 : } else if (fieldLen[fieldIdx] == -1) {
2284 : // no separator after 2 digit field
2285 0 : break;
2286 : }
2287 0 : int32_t digit = DIGIT_VAL(c);
2288 0 : if (digit < 0) {
2289 : // not a digit
2290 0 : break;
2291 : }
2292 0 : fieldVal[fieldIdx] = fieldVal[fieldIdx] * 10 + digit;
2293 0 : fieldLen[fieldIdx]++;
2294 0 : if (fieldLen[fieldIdx] >= 2) {
2295 : // parsed 2 digits, move to next field
2296 0 : fieldIdx++;
2297 : }
2298 : }
2299 :
2300 0 : int32_t offset = 0;
2301 0 : int32_t parsedLen = 0;
2302 0 : int32_t parsedFields = -1;
2303 : do {
2304 : // hour
2305 0 : if (fieldLen[0] == 0) {
2306 0 : break;
2307 : }
2308 0 : if (fieldVal[0] > MAX_OFFSET_HOUR) {
2309 0 : offset = (fieldVal[0] / 10) * MILLIS_PER_HOUR;
2310 0 : parsedFields = FIELDS_H;
2311 0 : parsedLen = 1;
2312 0 : break;
2313 : }
2314 0 : offset = fieldVal[0] * MILLIS_PER_HOUR;
2315 0 : parsedLen = fieldLen[0];
2316 0 : parsedFields = FIELDS_H;
2317 :
2318 : // minute
2319 0 : if (fieldLen[1] != 2 || fieldVal[1] > MAX_OFFSET_MINUTE) {
2320 : break;
2321 : }
2322 0 : offset += fieldVal[1] * MILLIS_PER_MINUTE;
2323 0 : parsedLen += (1 + fieldLen[1]);
2324 0 : parsedFields = FIELDS_HM;
2325 :
2326 : // second
2327 0 : if (fieldLen[2] != 2 || fieldVal[2] > MAX_OFFSET_SECOND) {
2328 : break;
2329 : }
2330 0 : offset += fieldVal[2] * MILLIS_PER_SECOND;
2331 0 : parsedLen += (1 + fieldLen[2]);
2332 0 : parsedFields = FIELDS_HMS;
2333 : } while (false);
2334 :
2335 0 : if (parsedFields < minFields) {
2336 0 : pos.setErrorIndex(start);
2337 0 : return 0;
2338 : }
2339 :
2340 0 : pos.setIndex(start + parsedLen);
2341 0 : return offset;
2342 : }
2343 :
2344 : void
2345 0 : TimeZoneFormat::appendOffsetDigits(UnicodeString& buf, int32_t n, uint8_t minDigits) const {
2346 0 : U_ASSERT(n >= 0 && n < 60);
2347 0 : int32_t numDigits = n >= 10 ? 2 : 1;
2348 0 : for (int32_t i = 0; i < minDigits - numDigits; i++) {
2349 0 : buf.append(fGMTOffsetDigits[0]);
2350 : }
2351 0 : if (numDigits == 2) {
2352 0 : buf.append(fGMTOffsetDigits[n / 10]);
2353 : }
2354 0 : buf.append(fGMTOffsetDigits[n % 10]);
2355 0 : }
2356 :
2357 : // ------------------------------------------------------------------
2358 : // Private misc
2359 : void
2360 0 : TimeZoneFormat::initGMTPattern(const UnicodeString& gmtPattern, UErrorCode& status) {
2361 0 : if (U_FAILURE(status)) {
2362 0 : return;
2363 : }
2364 : // This implementation not perfect, but sufficient practically.
2365 0 : int32_t idx = gmtPattern.indexOf(ARG0, ARG0_LEN, 0);
2366 0 : if (idx < 0) {
2367 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
2368 0 : return;
2369 : }
2370 0 : fGMTPattern.setTo(gmtPattern);
2371 0 : unquote(gmtPattern.tempSubString(0, idx), fGMTPatternPrefix);
2372 0 : unquote(gmtPattern.tempSubString(idx + ARG0_LEN), fGMTPatternSuffix);
2373 : }
2374 :
2375 : UnicodeString&
2376 0 : TimeZoneFormat::unquote(const UnicodeString& pattern, UnicodeString& result) {
2377 0 : if (pattern.indexOf(SINGLEQUOTE) < 0) {
2378 0 : result.setTo(pattern);
2379 0 : return result;
2380 : }
2381 0 : result.remove();
2382 0 : UBool isPrevQuote = FALSE;
2383 0 : UBool inQuote = FALSE;
2384 0 : for (int32_t i = 0; i < pattern.length(); i++) {
2385 0 : UChar c = pattern.charAt(i);
2386 0 : if (c == SINGLEQUOTE) {
2387 0 : if (isPrevQuote) {
2388 0 : result.append(c);
2389 0 : isPrevQuote = FALSE;
2390 : } else {
2391 0 : isPrevQuote = TRUE;
2392 : }
2393 0 : inQuote = !inQuote;
2394 : } else {
2395 0 : isPrevQuote = FALSE;
2396 0 : result.append(c);
2397 : }
2398 : }
2399 0 : return result;
2400 : }
2401 :
2402 : UVector*
2403 0 : TimeZoneFormat::parseOffsetPattern(const UnicodeString& pattern, OffsetFields required, UErrorCode& status) {
2404 0 : if (U_FAILURE(status)) {
2405 0 : return NULL;
2406 : }
2407 0 : UVector* result = new UVector(deleteGMTOffsetField, NULL, status);
2408 0 : if (result == NULL) {
2409 0 : status = U_MEMORY_ALLOCATION_ERROR;
2410 0 : return NULL;
2411 : }
2412 :
2413 0 : int32_t checkBits = 0;
2414 0 : UBool isPrevQuote = FALSE;
2415 0 : UBool inQuote = FALSE;
2416 : UChar textBuf[32];
2417 0 : UnicodeString text(textBuf, 0, UPRV_LENGTHOF(textBuf));
2418 0 : GMTOffsetField::FieldType itemType = GMTOffsetField::TEXT;
2419 0 : int32_t itemLength = 1;
2420 :
2421 0 : for (int32_t i = 0; i < pattern.length(); i++) {
2422 0 : UChar ch = pattern.charAt(i);
2423 0 : if (ch == SINGLEQUOTE) {
2424 0 : if (isPrevQuote) {
2425 0 : text.append(SINGLEQUOTE);
2426 0 : isPrevQuote = FALSE;
2427 : } else {
2428 0 : isPrevQuote = TRUE;
2429 0 : if (itemType != GMTOffsetField::TEXT) {
2430 0 : if (GMTOffsetField::isValid(itemType, itemLength)) {
2431 0 : GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, (uint8_t)itemLength, status);
2432 0 : result->addElement(fld, status);
2433 0 : if (U_FAILURE(status)) {
2434 0 : break;
2435 : }
2436 : } else {
2437 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
2438 0 : break;
2439 : }
2440 0 : itemType = GMTOffsetField::TEXT;
2441 : }
2442 : }
2443 0 : inQuote = !inQuote;
2444 : } else {
2445 0 : isPrevQuote = FALSE;
2446 0 : if (inQuote) {
2447 0 : text.append(ch);
2448 : } else {
2449 0 : GMTOffsetField::FieldType tmpType = GMTOffsetField::getTypeByLetter(ch);
2450 0 : if (tmpType != GMTOffsetField::TEXT) {
2451 : // an offset time pattern character
2452 0 : if (tmpType == itemType) {
2453 0 : itemLength++;
2454 : } else {
2455 0 : if (itemType == GMTOffsetField::TEXT) {
2456 0 : if (text.length() > 0) {
2457 0 : GMTOffsetField* textfld = GMTOffsetField::createText(text, status);
2458 0 : result->addElement(textfld, status);
2459 0 : if (U_FAILURE(status)) {
2460 0 : break;
2461 : }
2462 0 : text.remove();
2463 : }
2464 : } else {
2465 0 : if (GMTOffsetField::isValid(itemType, itemLength)) {
2466 0 : GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
2467 0 : result->addElement(fld, status);
2468 0 : if (U_FAILURE(status)) {
2469 0 : break;
2470 : }
2471 : } else {
2472 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
2473 0 : break;
2474 : }
2475 : }
2476 0 : itemType = tmpType;
2477 0 : itemLength = 1;
2478 0 : checkBits |= tmpType;
2479 : }
2480 : } else {
2481 : // a string literal
2482 0 : if (itemType != GMTOffsetField::TEXT) {
2483 0 : if (GMTOffsetField::isValid(itemType, itemLength)) {
2484 0 : GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
2485 0 : result->addElement(fld, status);
2486 0 : if (U_FAILURE(status)) {
2487 0 : break;
2488 : }
2489 : } else {
2490 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
2491 0 : break;
2492 : }
2493 0 : itemType = GMTOffsetField::TEXT;
2494 : }
2495 0 : text.append(ch);
2496 : }
2497 : }
2498 : }
2499 : }
2500 : // handle last item
2501 0 : if (U_SUCCESS(status)) {
2502 0 : if (itemType == GMTOffsetField::TEXT) {
2503 0 : if (text.length() > 0) {
2504 0 : GMTOffsetField* tfld = GMTOffsetField::createText(text, status);
2505 0 : result->addElement(tfld, status);
2506 : }
2507 : } else {
2508 0 : if (GMTOffsetField::isValid(itemType, itemLength)) {
2509 0 : GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
2510 0 : result->addElement(fld, status);
2511 : } else {
2512 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
2513 : }
2514 : }
2515 :
2516 : // Check all required fields are set
2517 0 : if (U_SUCCESS(status)) {
2518 0 : int32_t reqBits = 0;
2519 0 : switch (required) {
2520 : case FIELDS_H:
2521 0 : reqBits = GMTOffsetField::HOUR;
2522 0 : break;
2523 : case FIELDS_HM:
2524 0 : reqBits = GMTOffsetField::HOUR | GMTOffsetField::MINUTE;
2525 0 : break;
2526 : case FIELDS_HMS:
2527 0 : reqBits = GMTOffsetField::HOUR | GMTOffsetField::MINUTE | GMTOffsetField::SECOND;
2528 0 : break;
2529 : }
2530 0 : if (checkBits == reqBits) {
2531 : // all required fields are set, no extra fields
2532 0 : return result;
2533 : }
2534 : }
2535 : }
2536 :
2537 : // error
2538 0 : delete result;
2539 0 : return NULL;
2540 : }
2541 :
2542 : UnicodeString&
2543 0 : TimeZoneFormat::expandOffsetPattern(const UnicodeString& offsetHM, UnicodeString& result, UErrorCode& status) {
2544 0 : result.setToBogus();
2545 0 : if (U_FAILURE(status)) {
2546 0 : return result;
2547 : }
2548 0 : U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN) == 2);
2549 :
2550 0 : int32_t idx_mm = offsetHM.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN, 2, 0);
2551 0 : if (idx_mm < 0) {
2552 : // Bad time zone hour pattern data
2553 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
2554 0 : return result;
2555 : }
2556 :
2557 0 : UnicodeString sep;
2558 0 : int32_t idx_H = offsetHM.tempSubString(0, idx_mm).lastIndexOf((UChar)0x0048 /* H */);
2559 0 : if (idx_H >= 0) {
2560 0 : sep = offsetHM.tempSubString(idx_H + 1, idx_mm - (idx_H + 1));
2561 : }
2562 0 : result.setTo(offsetHM.tempSubString(0, idx_mm + 2));
2563 0 : result.append(sep);
2564 0 : result.append(DEFAULT_GMT_OFFSET_SECOND_PATTERN, -1);
2565 0 : result.append(offsetHM.tempSubString(idx_mm + 2));
2566 0 : return result;
2567 : }
2568 :
2569 : UnicodeString&
2570 0 : TimeZoneFormat::truncateOffsetPattern(const UnicodeString& offsetHM, UnicodeString& result, UErrorCode& status) {
2571 0 : result.setToBogus();
2572 0 : if (U_FAILURE(status)) {
2573 0 : return result;
2574 : }
2575 0 : U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN) == 2);
2576 :
2577 0 : int32_t idx_mm = offsetHM.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN, 2, 0);
2578 0 : if (idx_mm < 0) {
2579 : // Bad time zone hour pattern data
2580 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
2581 0 : return result;
2582 : }
2583 0 : UChar HH[] = {0x0048, 0x0048};
2584 0 : int32_t idx_HH = offsetHM.tempSubString(0, idx_mm).lastIndexOf(HH, 2, 0);
2585 0 : if (idx_HH >= 0) {
2586 0 : return result.setTo(offsetHM.tempSubString(0, idx_HH + 2));
2587 : }
2588 0 : int32_t idx_H = offsetHM.tempSubString(0, idx_mm).lastIndexOf((UChar)0x0048, 0);
2589 0 : if (idx_H >= 0) {
2590 0 : return result.setTo(offsetHM.tempSubString(0, idx_H + 1));
2591 : }
2592 : // Bad time zone hour pattern data
2593 0 : status = U_ILLEGAL_ARGUMENT_ERROR;
2594 0 : return result;
2595 : }
2596 :
2597 : void
2598 0 : TimeZoneFormat::initGMTOffsetPatterns(UErrorCode& status) {
2599 0 : for (int32_t type = 0; type < UTZFMT_PAT_COUNT; type++) {
2600 0 : switch (type) {
2601 : case UTZFMT_PAT_POSITIVE_H:
2602 : case UTZFMT_PAT_NEGATIVE_H:
2603 0 : fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_H, status);
2604 0 : break;
2605 : case UTZFMT_PAT_POSITIVE_HM:
2606 : case UTZFMT_PAT_NEGATIVE_HM:
2607 0 : fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_HM, status);
2608 0 : break;
2609 : case UTZFMT_PAT_POSITIVE_HMS:
2610 : case UTZFMT_PAT_NEGATIVE_HMS:
2611 0 : fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_HMS, status);
2612 0 : break;
2613 : }
2614 : }
2615 0 : checkAbuttingHoursAndMinutes();
2616 0 : }
2617 :
2618 : void
2619 0 : TimeZoneFormat::checkAbuttingHoursAndMinutes() {
2620 0 : fAbuttingOffsetHoursAndMinutes= FALSE;
2621 0 : for (int32_t type = 0; type < UTZFMT_PAT_COUNT; type++) {
2622 0 : UBool afterH = FALSE;
2623 0 : UVector *items = fGMTOffsetPatternItems[type];
2624 0 : for (int32_t i = 0; i < items->size(); i++) {
2625 0 : const GMTOffsetField* item = (GMTOffsetField*)items->elementAt(i);
2626 0 : GMTOffsetField::FieldType type = item->getType();
2627 0 : if (type != GMTOffsetField::TEXT) {
2628 0 : if (afterH) {
2629 0 : fAbuttingOffsetHoursAndMinutes = TRUE;
2630 0 : break;
2631 0 : } else if (type == GMTOffsetField::HOUR) {
2632 0 : afterH = TRUE;
2633 : }
2634 0 : } else if (afterH) {
2635 0 : break;
2636 : }
2637 : }
2638 0 : if (fAbuttingOffsetHoursAndMinutes) {
2639 0 : break;
2640 : }
2641 : }
2642 0 : }
2643 :
2644 : UBool
2645 0 : TimeZoneFormat::toCodePoints(const UnicodeString& str, UChar32* codeArray, int32_t size) {
2646 0 : int32_t count = str.countChar32();
2647 0 : if (count != size) {
2648 0 : return FALSE;
2649 : }
2650 :
2651 0 : for (int32_t idx = 0, start = 0; idx < size; idx++) {
2652 0 : codeArray[idx] = str.char32At(start);
2653 0 : start = str.moveIndex32(start, 1);
2654 : }
2655 :
2656 0 : return TRUE;
2657 : }
2658 :
2659 : TimeZone*
2660 0 : TimeZoneFormat::createTimeZoneForOffset(int32_t offset) const {
2661 0 : if (offset == 0) {
2662 : // when offset is 0, we should use "Etc/GMT"
2663 0 : return TimeZone::createTimeZone(UnicodeString(TRUE, TZID_GMT, -1));
2664 : }
2665 0 : return ZoneMeta::createCustomTimeZone(offset);
2666 : }
2667 :
2668 : UTimeZoneFormatTimeType
2669 0 : TimeZoneFormat::getTimeType(UTimeZoneNameType nameType) {
2670 0 : switch (nameType) {
2671 : case UTZNM_LONG_STANDARD:
2672 : case UTZNM_SHORT_STANDARD:
2673 0 : return UTZFMT_TIME_TYPE_STANDARD;
2674 :
2675 : case UTZNM_LONG_DAYLIGHT:
2676 : case UTZNM_SHORT_DAYLIGHT:
2677 0 : return UTZFMT_TIME_TYPE_DAYLIGHT;
2678 :
2679 : default:
2680 0 : return UTZFMT_TIME_TYPE_UNKNOWN;
2681 : }
2682 : }
2683 :
2684 : UnicodeString&
2685 0 : TimeZoneFormat::getTimeZoneID(const TimeZoneNames::MatchInfoCollection* matches, int32_t idx, UnicodeString& tzID) const {
2686 0 : if (!matches->getTimeZoneIDAt(idx, tzID)) {
2687 : UChar mzIDBuf[32];
2688 0 : UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
2689 0 : if (matches->getMetaZoneIDAt(idx, mzID)) {
2690 0 : fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, tzID);
2691 : }
2692 : }
2693 0 : return tzID;
2694 : }
2695 :
2696 :
2697 : class ZoneIdMatchHandler : public TextTrieMapSearchResultHandler {
2698 : public:
2699 : ZoneIdMatchHandler();
2700 : virtual ~ZoneIdMatchHandler();
2701 :
2702 : UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
2703 : const UChar* getID();
2704 : int32_t getMatchLen();
2705 : private:
2706 : int32_t fLen;
2707 : const UChar* fID;
2708 : };
2709 :
2710 0 : ZoneIdMatchHandler::ZoneIdMatchHandler()
2711 0 : : fLen(0), fID(NULL) {
2712 0 : }
2713 :
2714 0 : ZoneIdMatchHandler::~ZoneIdMatchHandler() {
2715 0 : }
2716 :
2717 : UBool
2718 0 : ZoneIdMatchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
2719 0 : if (U_FAILURE(status)) {
2720 0 : return FALSE;
2721 : }
2722 0 : if (node->hasValues()) {
2723 0 : const UChar* id = (const UChar*)node->getValue(0);
2724 0 : if (id != NULL) {
2725 0 : if (fLen < matchLength) {
2726 0 : fID = id;
2727 0 : fLen = matchLength;
2728 : }
2729 : }
2730 : }
2731 0 : return TRUE;
2732 : }
2733 :
2734 : const UChar*
2735 0 : ZoneIdMatchHandler::getID() {
2736 0 : return fID;
2737 : }
2738 :
2739 : int32_t
2740 0 : ZoneIdMatchHandler::getMatchLen() {
2741 0 : return fLen;
2742 : }
2743 :
2744 :
2745 0 : static void U_CALLCONV initZoneIdTrie(UErrorCode &status) {
2746 0 : U_ASSERT(gZoneIdTrie == NULL);
2747 0 : ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT, tzfmt_cleanup);
2748 0 : gZoneIdTrie = new TextTrieMap(TRUE, NULL); // No deleter, because values are pooled by ZoneMeta
2749 0 : if (gZoneIdTrie == NULL) {
2750 0 : status = U_MEMORY_ALLOCATION_ERROR;
2751 0 : return;
2752 : }
2753 0 : StringEnumeration *tzenum = TimeZone::createEnumeration();
2754 : const UnicodeString *id;
2755 0 : while ((id = tzenum->snext(status))) {
2756 0 : const UChar* uid = ZoneMeta::findTimeZoneID(*id);
2757 0 : if (uid) {
2758 0 : gZoneIdTrie->put(uid, const_cast<UChar *>(uid), status);
2759 : }
2760 : }
2761 0 : delete tzenum;
2762 : }
2763 :
2764 :
2765 : UnicodeString&
2766 0 : TimeZoneFormat::parseZoneID(const UnicodeString& text, ParsePosition& pos, UnicodeString& tzID) const {
2767 0 : UErrorCode status = U_ZERO_ERROR;
2768 0 : umtx_initOnce(gZoneIdTrieInitOnce, &initZoneIdTrie, status);
2769 :
2770 0 : int32_t start = pos.getIndex();
2771 0 : int32_t len = 0;
2772 0 : tzID.setToBogus();
2773 :
2774 0 : if (U_SUCCESS(status)) {
2775 0 : LocalPointer<ZoneIdMatchHandler> handler(new ZoneIdMatchHandler());
2776 0 : gZoneIdTrie->search(text, start, handler.getAlias(), status);
2777 0 : len = handler->getMatchLen();
2778 0 : if (len > 0) {
2779 0 : tzID.setTo(handler->getID(), -1);
2780 : }
2781 : }
2782 :
2783 0 : if (len > 0) {
2784 0 : pos.setIndex(start + len);
2785 : } else {
2786 0 : pos.setErrorIndex(start);
2787 : }
2788 :
2789 0 : return tzID;
2790 : }
2791 :
2792 0 : static void U_CALLCONV initShortZoneIdTrie(UErrorCode &status) {
2793 0 : U_ASSERT(gShortZoneIdTrie == NULL);
2794 0 : ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT, tzfmt_cleanup);
2795 0 : StringEnumeration *tzenum = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
2796 0 : if (U_SUCCESS(status)) {
2797 0 : gShortZoneIdTrie = new TextTrieMap(TRUE, NULL); // No deleter, because values are pooled by ZoneMeta
2798 0 : if (gShortZoneIdTrie == NULL) {
2799 0 : status = U_MEMORY_ALLOCATION_ERROR;
2800 : } else {
2801 : const UnicodeString *id;
2802 0 : while ((id = tzenum->snext(status))) {
2803 0 : const UChar* uID = ZoneMeta::findTimeZoneID(*id);
2804 0 : const UChar* shortID = ZoneMeta::getShortID(*id);
2805 0 : if (shortID && uID) {
2806 0 : gShortZoneIdTrie->put(shortID, const_cast<UChar *>(uID), status);
2807 : }
2808 : }
2809 : }
2810 : }
2811 0 : delete tzenum;
2812 0 : }
2813 :
2814 :
2815 : UnicodeString&
2816 0 : TimeZoneFormat::parseShortZoneID(const UnicodeString& text, ParsePosition& pos, UnicodeString& tzID) const {
2817 0 : UErrorCode status = U_ZERO_ERROR;
2818 0 : umtx_initOnce(gShortZoneIdTrieInitOnce, &initShortZoneIdTrie, status);
2819 :
2820 0 : int32_t start = pos.getIndex();
2821 0 : int32_t len = 0;
2822 0 : tzID.setToBogus();
2823 :
2824 0 : if (U_SUCCESS(status)) {
2825 0 : LocalPointer<ZoneIdMatchHandler> handler(new ZoneIdMatchHandler());
2826 0 : gShortZoneIdTrie->search(text, start, handler.getAlias(), status);
2827 0 : len = handler->getMatchLen();
2828 0 : if (len > 0) {
2829 0 : tzID.setTo(handler->getID(), -1);
2830 : }
2831 : }
2832 :
2833 0 : if (len > 0) {
2834 0 : pos.setIndex(start + len);
2835 : } else {
2836 0 : pos.setErrorIndex(start);
2837 : }
2838 :
2839 0 : return tzID;
2840 : }
2841 :
2842 :
2843 : UnicodeString&
2844 0 : TimeZoneFormat::parseExemplarLocation(const UnicodeString& text, ParsePosition& pos, UnicodeString& tzID) const {
2845 0 : int32_t startIdx = pos.getIndex();
2846 0 : int32_t parsedPos = -1;
2847 0 : tzID.setToBogus();
2848 :
2849 0 : UErrorCode status = U_ZERO_ERROR;
2850 0 : LocalPointer<TimeZoneNames::MatchInfoCollection> exemplarMatches(fTimeZoneNames->find(text, startIdx, UTZNM_EXEMPLAR_LOCATION, status));
2851 0 : if (U_FAILURE(status)) {
2852 0 : pos.setErrorIndex(startIdx);
2853 0 : return tzID;
2854 : }
2855 0 : int32_t matchIdx = -1;
2856 0 : if (!exemplarMatches.isNull()) {
2857 0 : for (int32_t i = 0; i < exemplarMatches->size(); i++) {
2858 0 : if (startIdx + exemplarMatches->getMatchLengthAt(i) > parsedPos) {
2859 0 : matchIdx = i;
2860 0 : parsedPos = startIdx + exemplarMatches->getMatchLengthAt(i);
2861 : }
2862 : }
2863 0 : if (parsedPos > 0) {
2864 0 : pos.setIndex(parsedPos);
2865 0 : getTimeZoneID(exemplarMatches.getAlias(), matchIdx, tzID);
2866 : }
2867 : }
2868 :
2869 0 : if (tzID.length() == 0) {
2870 0 : pos.setErrorIndex(startIdx);
2871 : }
2872 :
2873 0 : return tzID;
2874 : }
2875 :
2876 : U_NAMESPACE_END
2877 :
2878 : #endif
|