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