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