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