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