intl/icu/source/i18n/tzfmt.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /*
     2 *******************************************************************************
     3 * Copyright (C) 2011-2013, International Business Machines Corporation and
     4 * others. All Rights Reserved.
     5 *******************************************************************************
     6 */
     8 #include "unicode/utypes.h"
    10 #if !UCONFIG_NO_FORMATTING
    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
    30 U_NAMESPACE_BEGIN
    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 };
    59 static const char gZoneStringsTag[] = "zoneStrings";
    60 static const char gGmtFormatTag[]= "gmtFormat";
    61 static const char gGmtZeroFormatTag[] = "gmtZeroFormat";
    62 static const char gHourFormatTag[]= "hourFormat";
    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
    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
    79 static const UChar32 DEFAULT_GMT_DIGITS[] = {
    80     0x0030, 0x0031, 0x0032, 0x0033, 0x0034,
    81     0x0035, 0x0036, 0x0037, 0x0038, 0x0039
    82 };
    84 static const UChar DEFAULT_GMT_OFFSET_SEP = 0x003A; // ':'
    86 static const UChar ARG0[] = {0x007B, 0x0030, 0x007D};   // "{0}"
    87 static const int32_t ARG0_LEN = 3;
    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"
    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 };
    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 };
   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;   // ':'
   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;
   121 // Maximum offset (exclusive) in millisecond supported by offset formats
   122 static int32_t MAX_OFFSET = 24 * MILLIS_PER_HOUR;
   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;
   129 static const int32_t UNKNOWN_OFFSET = 0x7FFFFFFF;
   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;
   134 #define DIGIT_VAL(c) (0x0030 <= (c) && (c) <= 0x0039 ? (c) - 0x0030 : -1)
   135 #define MAX_OFFSET_DIGITS 6
   137 // Time Zone ID/Short ID trie
   138 static TextTrieMap *gZoneIdTrie = NULL;
   139 static icu::UInitOnce gZoneIdTrieInitOnce = U_INITONCE_INITIALIZER;
   141 static TextTrieMap *gShortZoneIdTrie = NULL;
   142 static icu::UInitOnce gShortZoneIdTrieInitOnce = U_INITONCE_INITIALIZER;
   144 static UMutex gLock = U_MUTEX_INITIALIZER;
   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();
   158     if (gShortZoneIdTrie != NULL) {
   159         delete gShortZoneIdTrie;
   160     }
   161     gShortZoneIdTrie = NULL;
   162     gShortZoneIdTrieInitOnce.reset();
   164     return TRUE;
   165 }
   166 U_CDECL_END
   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     };
   183     virtual ~GMTOffsetField();
   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);
   190     FieldType getType() const;
   191     uint8_t getWidth() const;
   192     const UChar* getPatternText(void) const;
   194 private:
   195     UChar* fText;
   196     FieldType fType;
   197     uint8_t fWidth;
   199     GMTOffsetField();
   200 };
   202 GMTOffsetField::GMTOffsetField()
   203 : fText(NULL), fType(TEXT), fWidth(0) {
   204 }
   206 GMTOffsetField::~GMTOffsetField() {
   207     if (fText) {
   208         uprv_free(fText);
   209     }
   210 }
   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     }
   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;
   234     return result;
   235 }
   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     }
   249     result->fType = type;
   250     result->fWidth = width;
   252     return result;
   253 }
   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 }
   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 }
   281 inline GMTOffsetField::FieldType
   282 GMTOffsetField::getType() const {
   283      return fType;
   284  }
   286 inline uint8_t
   287 GMTOffsetField::getWidth() const {
   288     return fWidth;
   289 }
   291 inline const UChar*
   292 GMTOffsetField::getPatternText(void) const {
   293     return fText;
   294 }
   297 U_CDECL_BEGIN
   298 static void U_CALLCONV
   299 deleteGMTOffsetField(void *obj) {
   300     delete static_cast<GMTOffsetField *>(obj);
   301 }
   302 U_CDECL_END
   305 // ------------------------------------------------------------------
   306 // TimeZoneFormat
   307 // ------------------------------------------------------------------
   308 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneFormat)
   310 TimeZoneFormat::TimeZoneFormat(const Locale& locale, UErrorCode& status) 
   311 : fLocale(locale), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL), fDefParseOptionFlags(0) {
   313     for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
   314         fGMTOffsetPatternItems[i] = NULL;
   315     }
   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);
   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     }
   335     fTimeZoneNames = TimeZoneNames::createInstance(locale, status);
   336     // fTimeZoneGenericNames is lazily instantiated
   337     if (U_FAILURE(status)) {
   338         return;
   339     }
   341     const UChar* gmtPattern = NULL;
   342     const UChar* hourFormats = NULL;
   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     }
   365     if (gmtPattern == NULL) {
   366         gmtPattern = DEFAULT_GMT_PATTERN;
   367     }
   368     initGMTPattern(UnicodeString(gmtPattern, -1), status);
   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);
   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 }
   408 TimeZoneFormat::TimeZoneFormat(const TimeZoneFormat& other)
   409 : Format(other), fTimeZoneNames(NULL), fTimeZoneGenericNames(NULL) {
   411     for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
   412         fGMTOffsetPatternItems[i] = NULL;
   413     }
   414     *this = other;
   415 }
   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 }
   426 TimeZoneFormat&
   427 TimeZoneFormat::operator=(const TimeZoneFormat& other) {
   428     if (this == &other) {
   429         return *this;
   430     }
   432     delete fTimeZoneNames;
   433     delete fTimeZoneGenericNames;
   434     fTimeZoneGenericNames = NULL;
   436     fLocale = other.fLocale;
   437     uprv_memcpy(fTargetRegion, other.fTargetRegion, sizeof(fTargetRegion));
   439     fTimeZoneNames = other.fTimeZoneNames->clone();
   440     if (other.fTimeZoneGenericNames) {
   441         // TODO: this test has dubious thread safety.
   442         fTimeZoneGenericNames = other.fTimeZoneGenericNames->clone();
   443     }
   445     fGMTPattern = other.fGMTPattern;
   446     fGMTPatternPrefix = other.fGMTPatternPrefix;
   447     fGMTPatternSuffix = other.fGMTPatternSuffix;
   449     UErrorCode status = U_ZERO_ERROR;
   450     for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
   451         fGMTOffsetPatterns[i] = other.fGMTOffsetPatterns[i];
   452         delete fGMTOffsetPatternItems[i];
   453     }
   454     initGMTOffsetPatterns(status);
   455     U_ASSERT(U_SUCCESS(status));
   457     fGMTZeroFormat = other.fGMTZeroFormat;
   459     uprv_memcpy(fGMTOffsetDigits, other.fGMTOffsetDigits, sizeof(fGMTOffsetDigits));
   461     fDefParseOptionFlags = other.fDefParseOptionFlags;
   463     return *this;
   464 }
   467 UBool
   468 TimeZoneFormat::operator==(const Format& other) const {
   469     TimeZoneFormat* tzfmt = (TimeZoneFormat*)&other;
   471     UBool isEqual =
   472             fLocale == tzfmt->fLocale
   473             && fGMTPattern == tzfmt->fGMTPattern
   474             && fGMTZeroFormat == tzfmt->fGMTZeroFormat
   475             && *fTimeZoneNames == *tzfmt->fTimeZoneNames;
   477     for (int32_t i = 0; i < UTZFMT_PAT_COUNT && isEqual; i++) {
   478         isEqual = fGMTOffsetPatterns[i] == tzfmt->fGMTOffsetPatterns[i];
   479     }
   480     for (int32_t i = 0; i < 10 && isEqual; i++) {
   481         isEqual = fGMTOffsetDigits[i] == tzfmt->fGMTOffsetDigits[i];
   482     }
   483     // TODO
   484     // Check fTimeZoneGenericNames. For now,
   485     // if fTimeZoneNames is same, fTimeZoneGenericNames should
   486     // be also equivalent.
   487     return isEqual;
   488 }
   490 Format*
   491 TimeZoneFormat::clone() const {
   492     return new TimeZoneFormat(*this);
   493 }
   495 TimeZoneFormat* U_EXPORT2
   496 TimeZoneFormat::createInstance(const Locale& locale, UErrorCode& status) {
   497     TimeZoneFormat* tzfmt = new TimeZoneFormat(locale, status);
   498     if (U_SUCCESS(status)) {
   499         return tzfmt;
   500     }
   501     delete tzfmt;
   502     return NULL;
   503 }
   505 // ------------------------------------------------------------------
   506 // Setter and Getter
   508 const TimeZoneNames*
   509 TimeZoneFormat::getTimeZoneNames() const {
   510     return (const TimeZoneNames*)fTimeZoneNames;
   511 }
   513 void
   514 TimeZoneFormat::adoptTimeZoneNames(TimeZoneNames *tznames) {
   515     delete fTimeZoneNames;
   516     fTimeZoneNames = tznames;
   518     // TODO - We should also update fTimeZoneGenericNames
   519 }
   521 void
   522 TimeZoneFormat::setTimeZoneNames(const TimeZoneNames &tznames) {
   523     delete fTimeZoneNames;
   524     fTimeZoneNames = tznames.clone();
   526     // TODO - We should also update fTimeZoneGenericNames
   527 }
   529 void
   530 TimeZoneFormat::setDefaultParseOptions(uint32_t flags) {
   531     fDefParseOptionFlags = flags;
   532 }
   534 uint32_t
   535 TimeZoneFormat::getDefaultParseOptions(void) const {
   536     return fDefParseOptionFlags;
   537 }
   540 UnicodeString& 
   541 TimeZoneFormat::getGMTPattern(UnicodeString& pattern) const {
   542     return pattern.setTo(fGMTPattern);
   543 }
   545 void
   546 TimeZoneFormat::setGMTPattern(const UnicodeString& pattern, UErrorCode& status) {
   547     initGMTPattern(pattern, status);
   548 }
   550 UnicodeString&
   551 TimeZoneFormat::getGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, UnicodeString& pattern) const {
   552     return pattern.setTo(fGMTOffsetPatterns[type]);
   553 }
   555 void
   556 TimeZoneFormat::setGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, const UnicodeString& pattern, UErrorCode& status) {
   557     if (U_FAILURE(status)) {
   558         return;
   559     }
   560     if (pattern == fGMTOffsetPatterns[type]) {
   561         // No need to reset
   562         return;
   563     }
   565     OffsetFields required = FIELDS_HM;
   566     switch (type) {
   567     case UTZFMT_PAT_POSITIVE_H:
   568     case UTZFMT_PAT_NEGATIVE_H:
   569         required = FIELDS_H;
   570         break;
   571     case UTZFMT_PAT_POSITIVE_HM:
   572     case UTZFMT_PAT_NEGATIVE_HM:
   573         required = FIELDS_HM;
   574         break;
   575     case UTZFMT_PAT_POSITIVE_HMS:
   576     case UTZFMT_PAT_NEGATIVE_HMS:
   577         required = FIELDS_HMS;
   578         break;
   579     default:
   580         U_ASSERT(FALSE);
   581         break;
   582     }
   584     UVector* patternItems = parseOffsetPattern(pattern, required, status);
   585     if (patternItems == NULL) {
   586         return;
   587     }
   589     fGMTOffsetPatterns[type].setTo(pattern);
   590     delete fGMTOffsetPatternItems[type];
   591     fGMTOffsetPatternItems[type] = patternItems;
   592     checkAbuttingHoursAndMinutes();
   593 }
   595 UnicodeString&
   596 TimeZoneFormat::getGMTOffsetDigits(UnicodeString& digits) const {
   597     digits.remove();
   598     for (int32_t i = 0; i < 10; i++) {
   599         digits.append(fGMTOffsetDigits[i]);
   600     }
   601     return digits;
   602 }
   604 void
   605 TimeZoneFormat::setGMTOffsetDigits(const UnicodeString& digits, UErrorCode& status) {
   606     if (U_FAILURE(status)) {
   607         return;
   608     }
   609     UChar32 digitArray[10];
   610     if (!toCodePoints(digits, digitArray, 10)) {
   611         status = U_ILLEGAL_ARGUMENT_ERROR;
   612         return;
   613     }
   614     uprv_memcpy(fGMTOffsetDigits, digitArray, sizeof(UChar32)*10);
   615 }
   617 UnicodeString&
   618 TimeZoneFormat::getGMTZeroFormat(UnicodeString& gmtZeroFormat) const {
   619     return gmtZeroFormat.setTo(fGMTZeroFormat);
   620 }
   622 void
   623 TimeZoneFormat::setGMTZeroFormat(const UnicodeString& gmtZeroFormat, UErrorCode& status) {
   624     if (U_SUCCESS(status)) {
   625         if (gmtZeroFormat.isEmpty()) {
   626             status = U_ILLEGAL_ARGUMENT_ERROR;
   627         } else if (gmtZeroFormat != fGMTZeroFormat) {
   628             fGMTZeroFormat.setTo(gmtZeroFormat);
   629         }
   630     }
   631 }
   633 // ------------------------------------------------------------------
   634 // Format and Parse
   636 UnicodeString&
   637 TimeZoneFormat::format(UTimeZoneFormatStyle style, const TimeZone& tz, UDate date,
   638         UnicodeString& name, UTimeZoneFormatTimeType* timeType /* = NULL */) const {
   639     if (timeType) {
   640         *timeType = UTZFMT_TIME_TYPE_UNKNOWN;
   641     }
   643     UBool noOffsetFormatFallback = FALSE;
   645     switch (style) {
   646     case UTZFMT_STYLE_GENERIC_LOCATION:
   647         formatGeneric(tz, UTZGNM_LOCATION, date, name);
   648         break;
   649     case UTZFMT_STYLE_GENERIC_LONG:
   650         formatGeneric(tz, UTZGNM_LONG, date, name);
   651         break;
   652     case UTZFMT_STYLE_GENERIC_SHORT:
   653         formatGeneric(tz, UTZGNM_SHORT, date, name);
   654         break;
   655     case UTZFMT_STYLE_SPECIFIC_LONG:
   656         formatSpecific(tz, UTZNM_LONG_STANDARD, UTZNM_LONG_DAYLIGHT, date, name, timeType);
   657         break;
   658     case UTZFMT_STYLE_SPECIFIC_SHORT:
   659         formatSpecific(tz, UTZNM_SHORT_STANDARD, UTZNM_SHORT_DAYLIGHT, date, name, timeType);
   660         break;
   662     case UTZFMT_STYLE_ZONE_ID:
   663         tz.getID(name);
   664         noOffsetFormatFallback = TRUE;
   665         break;
   666     case UTZFMT_STYLE_ZONE_ID_SHORT:
   667         {
   668             const UChar* shortID = ZoneMeta::getShortID(tz);
   669             if (shortID == NULL) {
   670                 shortID = UNKNOWN_SHORT_ZONE_ID;
   671             }
   672             name.setTo(shortID, -1);
   673         }
   674         noOffsetFormatFallback = TRUE;
   675         break;
   677     case UTZFMT_STYLE_EXEMPLAR_LOCATION:
   678         formatExemplarLocation(tz, name);
   679         noOffsetFormatFallback = TRUE;
   680         break;
   682     default:
   683         // will be handled below
   684         break;
   685     }
   687     if (name.isEmpty() && !noOffsetFormatFallback) {
   688         UErrorCode status = U_ZERO_ERROR;
   689         int32_t rawOffset, dstOffset;
   690         tz.getOffset(date, FALSE, rawOffset, dstOffset, status);
   691         int32_t offset = rawOffset + dstOffset;
   692         if (U_SUCCESS(status)) {
   693             switch (style) {
   694             case UTZFMT_STYLE_GENERIC_LOCATION:
   695             case UTZFMT_STYLE_GENERIC_LONG:
   696             case UTZFMT_STYLE_SPECIFIC_LONG:
   697             case UTZFMT_STYLE_LOCALIZED_GMT:
   698                 formatOffsetLocalizedGMT(offset, name, status);
   699                 break;
   701             case UTZFMT_STYLE_GENERIC_SHORT:
   702             case UTZFMT_STYLE_SPECIFIC_SHORT:
   703             case UTZFMT_STYLE_LOCALIZED_GMT_SHORT:
   704                 formatOffsetShortLocalizedGMT(offset, name, status);
   705                 break;
   707             case UTZFMT_STYLE_ISO_BASIC_SHORT:
   708                 formatOffsetISO8601Basic(offset, TRUE, TRUE, TRUE, name, status);
   709                 break;
   711             case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT:
   712                 formatOffsetISO8601Basic(offset, FALSE, TRUE, TRUE, name, status);
   713                 break;
   715             case UTZFMT_STYLE_ISO_BASIC_FIXED:
   716                 formatOffsetISO8601Basic(offset, TRUE, FALSE, TRUE, name, status);
   717                 break;
   719             case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED:
   720                 formatOffsetISO8601Basic(offset, FALSE, FALSE, TRUE, name, status);
   721                 break;
   723             case UTZFMT_STYLE_ISO_EXTENDED_FIXED:
   724                 formatOffsetISO8601Extended(offset, TRUE, FALSE, TRUE, name, status);
   725                 break;
   727             case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED:
   728                 formatOffsetISO8601Extended(offset, FALSE, FALSE, TRUE, name, status);
   729                 break;
   731             case UTZFMT_STYLE_ISO_BASIC_FULL:
   732                 formatOffsetISO8601Basic(offset, TRUE, FALSE, FALSE, name, status);
   733                 break;
   735             case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL:
   736                 formatOffsetISO8601Basic(offset, FALSE, FALSE, FALSE, name, status);
   737                 break;
   739             case UTZFMT_STYLE_ISO_EXTENDED_FULL:
   740                 formatOffsetISO8601Extended(offset, TRUE, FALSE, FALSE, name, status);
   741                 break;
   743             case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL:
   744                 formatOffsetISO8601Extended(offset, FALSE, FALSE, FALSE, name, status);
   745                 break;
   747             default:
   748               // UTZFMT_STYLE_ZONE_ID, UTZFMT_STYLE_ZONE_ID_SHORT, UTZFMT_STYLE_EXEMPLAR_LOCATION
   749               break;
   750             }
   752             if (timeType) {
   753                 *timeType = (dstOffset != 0) ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD;
   754             }
   755         }
   756     }
   758     return name;
   759 }
   761 UnicodeString&
   762 TimeZoneFormat::format(const Formattable& obj, UnicodeString& appendTo,
   763         FieldPosition& pos, UErrorCode& status) const {
   764     if (U_FAILURE(status)) {
   765         return appendTo;
   766     }
   767     UDate date = Calendar::getNow();
   768     if (obj.getType() == Formattable::kObject) {
   769         const UObject* formatObj = obj.getObject();
   770         const TimeZone* tz = dynamic_cast<const TimeZone*>(formatObj);
   771         if (tz == NULL) {
   772             const Calendar* cal = dynamic_cast<const Calendar*>(formatObj);
   773             if (cal != NULL) {
   774                 tz = &cal->getTimeZone();
   775                 date = cal->getTime(status);
   776             }
   777         }
   778         if (tz != NULL) {
   779             int32_t rawOffset, dstOffset;
   780             tz->getOffset(date, FALSE, rawOffset, dstOffset, status);
   781             UnicodeString result;
   782             formatOffsetLocalizedGMT(rawOffset + dstOffset, result, status);
   783             if (U_SUCCESS(status)) {
   784                 appendTo.append(result);
   785                 if (pos.getField() == UDAT_TIMEZONE_FIELD) {
   786                     pos.setBeginIndex(0);
   787                     pos.setEndIndex(result.length());
   788                 }
   789             }
   790         }
   791     }
   792     return appendTo;
   793 }
   795 TimeZone*
   796 TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos,
   797         UTimeZoneFormatTimeType* timeType /*= NULL*/) const {
   798     return parse(style, text, pos, getDefaultParseOptions(), timeType);
   799 }
   801 TimeZone*
   802 TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos,
   803         int32_t parseOptions, UTimeZoneFormatTimeType* timeType /* = NULL */) const {
   804     if (timeType) {
   805         *timeType = UTZFMT_TIME_TYPE_UNKNOWN;
   806     }
   808     int32_t startIdx = pos.getIndex();
   809     int32_t maxPos = text.length();
   810     int32_t offset;
   812     // Styles using localized GMT format as fallback
   813     UBool fallbackLocalizedGMT = 
   814         (style == UTZFMT_STYLE_SPECIFIC_LONG || style == UTZFMT_STYLE_GENERIC_LONG || style == UTZFMT_STYLE_GENERIC_LOCATION);
   815     UBool fallbackShortLocalizedGMT =
   816         (style == UTZFMT_STYLE_SPECIFIC_SHORT || style == UTZFMT_STYLE_GENERIC_SHORT);
   818     int32_t evaluated = 0;  // bit flags representing already evaluated styles
   819     ParsePosition tmpPos(startIdx);
   821     int32_t parsedOffset = UNKNOWN_OFFSET;  // stores successfully parsed offset for later use
   822     int32_t parsedPos = -1;                 // stores successfully parsed offset position for later use
   824     // Try localized GMT format first if necessary
   825     if (fallbackLocalizedGMT || fallbackShortLocalizedGMT) {
   826         UBool hasDigitOffset = FALSE;
   827         offset = parseOffsetLocalizedGMT(text, tmpPos, fallbackShortLocalizedGMT, &hasDigitOffset);
   828         if (tmpPos.getErrorIndex() == -1) {
   829             // Even when the input text was successfully parsed as a localized GMT format text,
   830             // we may still need to evaluate the specified style if -
   831             //   1) GMT zero format was used, and
   832             //   2) The input text was not completely processed
   833             if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
   834                 pos.setIndex(tmpPos.getIndex());
   835                 return createTimeZoneForOffset(offset);
   836             }
   837             parsedOffset = offset;
   838             parsedPos = tmpPos.getIndex();
   839         }
   840         // Note: For now, no distinction between long/short localized GMT format in the parser.
   841         // This might be changed in future.
   842         // evaluated |= (fallbackLocalizedGMT ? STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] : STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]);
   843         evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] | STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT];
   844     }
   846     UErrorCode status = U_ZERO_ERROR;
   847     UnicodeString tzID;
   849     // Try the specified style
   850     switch (style) {
   851     case UTZFMT_STYLE_LOCALIZED_GMT:
   852         {
   853             tmpPos.setIndex(startIdx);
   854             tmpPos.setErrorIndex(-1);
   856             offset = parseOffsetLocalizedGMT(text, tmpPos);
   857             if (tmpPos.getErrorIndex() == -1) {
   858                 pos.setIndex(tmpPos.getIndex());
   859                 return createTimeZoneForOffset(offset);
   860             }
   862             // Note: For now, no distinction between long/short localized GMT format in the parser.
   863             // This might be changed in future.
   864             evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT];
   866             break;
   867         }
   868     case UTZFMT_STYLE_LOCALIZED_GMT_SHORT:
   869         {
   870             tmpPos.setIndex(startIdx);
   871             tmpPos.setErrorIndex(-1);
   873             offset = parseOffsetShortLocalizedGMT(text, tmpPos);
   874             if (tmpPos.getErrorIndex() == -1) {
   875                 pos.setIndex(tmpPos.getIndex());
   876                 return createTimeZoneForOffset(offset);
   877             }
   879             // Note: For now, no distinction between long/short localized GMT format in the parser.
   880             // This might be changed in future.
   881             evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT];
   883             break;
   884         }
   885     case UTZFMT_STYLE_ISO_BASIC_SHORT:
   886     case UTZFMT_STYLE_ISO_BASIC_FIXED:
   887     case UTZFMT_STYLE_ISO_BASIC_FULL:
   888     case UTZFMT_STYLE_ISO_EXTENDED_FIXED:
   889     case UTZFMT_STYLE_ISO_EXTENDED_FULL:
   890         {
   891             tmpPos.setIndex(startIdx);
   892             tmpPos.setErrorIndex(-1);
   894             offset = parseOffsetISO8601(text, tmpPos);
   895             if (tmpPos.getErrorIndex() == -1) {
   896                 pos.setIndex(tmpPos.getIndex());
   897                 return createTimeZoneForOffset(offset);
   898             }
   900             break;
   901         }
   903     case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT:
   904     case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED:
   905     case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL:
   906     case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED:
   907     case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL:
   908         {
   909             tmpPos.setIndex(startIdx);
   910             tmpPos.setErrorIndex(-1);
   912             // Exclude the case of UTC Indicator "Z" here
   913             UBool hasDigitOffset = FALSE;
   914             offset = parseOffsetISO8601(text, tmpPos, FALSE, &hasDigitOffset);
   915             if (tmpPos.getErrorIndex() == -1 && hasDigitOffset) {
   916                 pos.setIndex(tmpPos.getIndex());
   917                 return createTimeZoneForOffset(offset);
   918             }
   920             break;
   921         }
   923     case UTZFMT_STYLE_SPECIFIC_LONG:
   924     case UTZFMT_STYLE_SPECIFIC_SHORT:
   925         {
   926             // Specific styles
   927             int32_t nameTypes = 0;
   928             if (style == UTZFMT_STYLE_SPECIFIC_LONG) {
   929                 nameTypes = (UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT);
   930             } else {
   931                 U_ASSERT(style == UTZFMT_STYLE_SPECIFIC_SHORT);
   932                 nameTypes = (UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT);
   933             }
   934             LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, nameTypes, status));
   935             if (U_FAILURE(status)) {
   936                 pos.setErrorIndex(startIdx);
   937                 return NULL;
   938             }
   939             if (!specificMatches.isNull()) {
   940                 int32_t matchIdx = -1;
   941                 int32_t matchPos = -1;
   942                 for (int32_t i = 0; i < specificMatches->size(); i++) {
   943                     matchPos  = startIdx + specificMatches->getMatchLengthAt(i);
   944                     if (matchPos > parsedPos) {
   945                         matchIdx = i;
   946                         parsedPos = matchPos;
   947                     }
   948                 }
   949                 if (matchIdx >= 0) {
   950                     if (timeType) {
   951                         *timeType = getTimeType(specificMatches->getNameTypeAt(matchIdx));
   952                     }
   953                     pos.setIndex(matchPos);
   954                     getTimeZoneID(specificMatches.getAlias(), matchIdx, tzID);
   955                     U_ASSERT(!tzID.isEmpty());
   956                     return TimeZone::createTimeZone(tzID);
   957                 }
   958             }
   959             break;
   960         }
   961     case UTZFMT_STYLE_GENERIC_LONG:
   962     case UTZFMT_STYLE_GENERIC_SHORT:
   963     case UTZFMT_STYLE_GENERIC_LOCATION:
   964         {
   965             int32_t genericNameTypes = 0;
   966             switch (style) {
   967             case UTZFMT_STYLE_GENERIC_LOCATION:
   968                 genericNameTypes = UTZGNM_LOCATION;
   969                 break;
   971             case UTZFMT_STYLE_GENERIC_LONG:
   972                 genericNameTypes = UTZGNM_LONG | UTZGNM_LOCATION;
   973                 break;
   975             case UTZFMT_STYLE_GENERIC_SHORT:
   976                 genericNameTypes = UTZGNM_SHORT | UTZGNM_LOCATION;
   977                 break;
   979             default:
   980                 U_ASSERT(FALSE);
   981             }
   983             int32_t len = 0;
   984             UTimeZoneFormatTimeType tt = UTZFMT_TIME_TYPE_UNKNOWN;
   985             const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status);
   986             if (U_SUCCESS(status)) {
   987                 len = gnames->findBestMatch(text, startIdx, genericNameTypes, tzID, tt, status);
   988             }
   989             if (U_FAILURE(status)) {
   990                 pos.setErrorIndex(startIdx);
   991                 return NULL;
   992             }
   993             if (len > 0) {
   994                 // Found a match
   995                 if (timeType) {
   996                     *timeType = tt;
   997                 }
   998                 pos.setIndex(startIdx + len);
   999                 U_ASSERT(!tzID.isEmpty());
  1000                 return TimeZone::createTimeZone(tzID);
  1003             break;
  1005     case UTZFMT_STYLE_ZONE_ID:
  1007             tmpPos.setIndex(startIdx);
  1008             tmpPos.setErrorIndex(-1);
  1010             parseZoneID(text, tmpPos, tzID);
  1011             if (tmpPos.getErrorIndex() == -1) {
  1012                 pos.setIndex(tmpPos.getIndex());
  1013                 return TimeZone::createTimeZone(tzID);
  1015             break;
  1017     case UTZFMT_STYLE_ZONE_ID_SHORT:
  1019             tmpPos.setIndex(startIdx);
  1020             tmpPos.setErrorIndex(-1);
  1022             parseShortZoneID(text, tmpPos, tzID);
  1023             if (tmpPos.getErrorIndex() == -1) {
  1024                 pos.setIndex(tmpPos.getIndex());
  1025                 return TimeZone::createTimeZone(tzID);
  1027             break;
  1029     case UTZFMT_STYLE_EXEMPLAR_LOCATION:
  1031             tmpPos.setIndex(startIdx);
  1032             tmpPos.setErrorIndex(-1);
  1034             parseExemplarLocation(text, tmpPos, tzID);
  1035             if (tmpPos.getErrorIndex() == -1) {
  1036                 pos.setIndex(tmpPos.getIndex());
  1037                 return TimeZone::createTimeZone(tzID);
  1039             break;
  1042     evaluated |= STYLE_PARSE_FLAGS[style];
  1045     if (parsedPos > startIdx) {
  1046         // When the specified style is one of SPECIFIC_XXX or GENERIC_XXX, we tried to parse the input
  1047         // as localized GMT format earlier. If parsedOffset is positive, it means it was successfully
  1048         // parsed as localized GMT format, but offset digits were not detected (more specifically, GMT
  1049         // zero format). Then, it tried to find a match within the set of display names, but could not
  1050         // find a match. At this point, we can safely assume the input text contains the localized
  1051         // GMT format.
  1052         U_ASSERT(parsedOffset != UNKNOWN_OFFSET);
  1053         pos.setIndex(parsedPos);
  1054         return createTimeZoneForOffset(parsedOffset);
  1057     // Failed to parse the input text as the time zone format in the specified style.
  1058     // Check the longest match among other styles below.
  1059     UnicodeString parsedID;
  1060     UTimeZoneFormatTimeType parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1062     U_ASSERT(parsedPos < 0);
  1063     U_ASSERT(parsedOffset == UNKNOWN_OFFSET);
  1065     // ISO 8601
  1066     if (parsedPos < maxPos &&
  1067         ((evaluated & ISO_Z_STYLE_FLAG) == 0 || (evaluated & ISO_LOCAL_STYLE_FLAG) == 0)) {
  1068         tmpPos.setIndex(startIdx);
  1069         tmpPos.setErrorIndex(-1);
  1071         UBool hasDigitOffset = FALSE;
  1072         offset = parseOffsetISO8601(text, tmpPos, FALSE, &hasDigitOffset);
  1073         if (tmpPos.getErrorIndex() == -1) {
  1074             if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
  1075                 pos.setIndex(tmpPos.getIndex());
  1076                 return createTimeZoneForOffset(offset);
  1078             // Note: When ISO 8601 format contains offset digits, it should not
  1079             // collide with other formats. However, ISO 8601 UTC format "Z" (single letter)
  1080             // may collide with other names. In this case, we need to evaluate other names.
  1081             if (parsedPos < tmpPos.getIndex()) {
  1082                 parsedOffset = offset;
  1083                 parsedID.setToBogus();
  1084                 parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1085                 parsedPos = tmpPos.getIndex();
  1086                 U_ASSERT(parsedPos == startIdx + 1);    // only when "Z" is used
  1091     // Localized GMT format
  1092     if (parsedPos < maxPos &&
  1093         (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT]) == 0) {
  1094         tmpPos.setIndex(startIdx);
  1095         tmpPos.setErrorIndex(-1);
  1097         UBool hasDigitOffset = FALSE;
  1098         offset = parseOffsetLocalizedGMT(text, tmpPos, FALSE, &hasDigitOffset);
  1099         if (tmpPos.getErrorIndex() == -1) {
  1100             if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
  1101                 pos.setIndex(tmpPos.getIndex());
  1102                 return createTimeZoneForOffset(offset);
  1104             // Evaluate other names - see the comment earlier in this method.
  1105             if (parsedPos < tmpPos.getIndex()) {
  1106                 parsedOffset = offset;
  1107                 parsedID.setToBogus();
  1108                 parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1109                 parsedPos = tmpPos.getIndex();
  1114     if (parsedPos < maxPos &&
  1115         (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]) == 0) {
  1116         tmpPos.setIndex(startIdx);
  1117         tmpPos.setErrorIndex(-1);
  1119         UBool hasDigitOffset = FALSE;
  1120         offset = parseOffsetLocalizedGMT(text, tmpPos, TRUE, &hasDigitOffset);
  1121         if (tmpPos.getErrorIndex() == -1) {
  1122             if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
  1123                 pos.setIndex(tmpPos.getIndex());
  1124                 return createTimeZoneForOffset(offset);
  1126             // Evaluate other names - see the comment earlier in this method.
  1127             if (parsedPos < tmpPos.getIndex()) {
  1128                 parsedOffset = offset;
  1129                 parsedID.setToBogus();
  1130                 parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1131                 parsedPos = tmpPos.getIndex();
  1136     // When ParseOption.ALL_STYLES is available, we also try to look all possible display names and IDs.
  1137     // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never
  1138     // used for America/New_York. With parseAllStyles true, this code parses "EST"
  1139     // as America/New_York.
  1141     // Note: Adding all possible names into the trie used by the implementation is quite heavy operation,
  1142     // which we want to avoid normally (note that we cache the trie, so this is applicable to the
  1143     // first time only as long as the cache does not expire).
  1145     if (parseOptions & UTZFMT_PARSE_OPTION_ALL_STYLES) {
  1146         // Try all specific names and exemplar location names
  1147         if (parsedPos < maxPos) {
  1148             LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, ALL_SIMPLE_NAME_TYPES, status));
  1149             if (U_FAILURE(status)) {
  1150                 pos.setErrorIndex(startIdx);
  1151                 return NULL;
  1153             int32_t specificMatchIdx = -1;
  1154             int32_t matchPos = -1;
  1155             if (!specificMatches.isNull()) {
  1156                 for (int32_t i = 0; i < specificMatches->size(); i++) {
  1157                     if (startIdx + specificMatches->getMatchLengthAt(i) > matchPos) {
  1158                         specificMatchIdx = i;
  1159                         matchPos = startIdx + specificMatches->getMatchLengthAt(i);
  1163             if (parsedPos < matchPos) {
  1164                 U_ASSERT(specificMatchIdx >= 0);
  1165                 parsedPos = matchPos;
  1166                 getTimeZoneID(specificMatches.getAlias(), specificMatchIdx, parsedID);
  1167                 parsedTimeType = getTimeType(specificMatches->getNameTypeAt(specificMatchIdx));
  1168                 parsedOffset = UNKNOWN_OFFSET;
  1171         // Try generic names
  1172         if (parsedPos < maxPos) {
  1173             int32_t genMatchLen = -1;
  1174             UTimeZoneFormatTimeType tt = UTZFMT_TIME_TYPE_UNKNOWN;
  1176             const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status);
  1177             if (U_SUCCESS(status)) {
  1178                 genMatchLen = gnames->findBestMatch(text, startIdx, ALL_GENERIC_NAME_TYPES, tzID, tt, status);
  1180             if (U_FAILURE(status)) {
  1181                 pos.setErrorIndex(startIdx);
  1182                 return NULL;
  1185             if (parsedPos < startIdx + genMatchLen) {
  1186                 parsedPos = startIdx + genMatchLen;
  1187                 parsedID.setTo(tzID);
  1188                 parsedTimeType = tt;
  1189                 parsedOffset = UNKNOWN_OFFSET;
  1193         // Try time zone ID
  1194         if (parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_ZONE_ID]) == 0) {
  1195             tmpPos.setIndex(startIdx);
  1196             tmpPos.setErrorIndex(-1);
  1198             parseZoneID(text, tmpPos, tzID);
  1199             if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
  1200                 parsedPos = tmpPos.getIndex();
  1201                 parsedID.setTo(tzID);
  1202                 parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1203                 parsedOffset = UNKNOWN_OFFSET;
  1206         // Try short time zone ID
  1207         if (parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_ZONE_ID]) == 0) {
  1208             tmpPos.setIndex(startIdx);
  1209             tmpPos.setErrorIndex(-1);
  1211             parseShortZoneID(text, tmpPos, tzID);
  1212             if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
  1213                 parsedPos = tmpPos.getIndex();
  1214                 parsedID.setTo(tzID);
  1215                 parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
  1216                 parsedOffset = UNKNOWN_OFFSET;
  1221     if (parsedPos > startIdx) {
  1222         // Parsed successfully
  1223         TimeZone* parsedTZ;
  1224         if (parsedID.length() > 0) {
  1225             parsedTZ = TimeZone::createTimeZone(parsedID);
  1226         } else {
  1227             U_ASSERT(parsedOffset != UNKNOWN_OFFSET);
  1228             parsedTZ = createTimeZoneForOffset(parsedOffset);
  1230         if (timeType) {
  1231             *timeType = parsedTimeType;
  1233         pos.setIndex(parsedPos);
  1234         return parsedTZ;
  1237     pos.setErrorIndex(startIdx);
  1238     return NULL;
  1241 void
  1242 TimeZoneFormat::parseObject(const UnicodeString& source, Formattable& result,
  1243         ParsePosition& parse_pos) const {
  1244     result.adoptObject(parse(UTZFMT_STYLE_GENERIC_LOCATION, source, parse_pos, UTZFMT_PARSE_OPTION_ALL_STYLES));
  1248 // ------------------------------------------------------------------
  1249 // Private zone name format/parse implementation
  1251 UnicodeString&
  1252 TimeZoneFormat::formatGeneric(const TimeZone& tz, int32_t genType, UDate date, UnicodeString& name) const {
  1253     UErrorCode status = U_ZERO_ERROR;
  1254     const TimeZoneGenericNames* gnames = getTimeZoneGenericNames(status);
  1255     if (U_FAILURE(status)) {
  1256         name.setToBogus();
  1257         return name;
  1260     if (genType == UTZGNM_LOCATION) {
  1261         const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
  1262         if (canonicalID == NULL) {
  1263             name.setToBogus();
  1264             return name;
  1266         return gnames->getGenericLocationName(UnicodeString(canonicalID), name);
  1268     return gnames->getDisplayName(tz, (UTimeZoneGenericNameType)genType, date, name);
  1271 UnicodeString&
  1272 TimeZoneFormat::formatSpecific(const TimeZone& tz, UTimeZoneNameType stdType, UTimeZoneNameType dstType,
  1273         UDate date, UnicodeString& name, UTimeZoneFormatTimeType *timeType) const {
  1274     if (fTimeZoneNames == NULL) {
  1275         name.setToBogus();
  1276         return name;
  1279     UErrorCode status = U_ZERO_ERROR;
  1280     UBool isDaylight = tz.inDaylightTime(date, status);
  1281     const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
  1283     if (U_FAILURE(status) || canonicalID == NULL) {
  1284         name.setToBogus();
  1285         return name;
  1288     if (isDaylight) {
  1289         fTimeZoneNames->getDisplayName(UnicodeString(canonicalID), dstType, date, name);
  1290     } else {
  1291         fTimeZoneNames->getDisplayName(UnicodeString(canonicalID), stdType, date, name);
  1294     if (timeType && !name.isEmpty()) {
  1295         *timeType = isDaylight ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD;
  1297     return name;
  1300 const TimeZoneGenericNames*
  1301 TimeZoneFormat::getTimeZoneGenericNames(UErrorCode& status) const {
  1302     if (U_FAILURE(status)) {
  1303         return NULL;
  1306     umtx_lock(&gLock);
  1307     if (fTimeZoneGenericNames == NULL) {
  1308         TimeZoneFormat *nonConstThis = const_cast<TimeZoneFormat *>(this);
  1309         nonConstThis->fTimeZoneGenericNames = TimeZoneGenericNames::createInstance(fLocale, status);
  1311     umtx_unlock(&gLock);
  1313     return fTimeZoneGenericNames;
  1316 UnicodeString&
  1317 TimeZoneFormat::formatExemplarLocation(const TimeZone& tz, UnicodeString& name) const {
  1318     UnicodeString location;
  1319     const UChar* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
  1321     if (canonicalID) {
  1322         fTimeZoneNames->getExemplarLocationName(UnicodeString(canonicalID), location);
  1324     if (location.length() > 0) {
  1325         name.setTo(location);
  1326     } else {
  1327         // Use "unknown" location
  1328         fTimeZoneNames->getExemplarLocationName(UnicodeString(UNKNOWN_ZONE_ID), location);
  1329         if (location.length() > 0) {
  1330             name.setTo(location);
  1331         } else {
  1332             // last resort
  1333             name.setTo(UNKNOWN_LOCATION, -1);
  1336     return name;
  1340 // ------------------------------------------------------------------
  1341 // Zone offset format and parse
  1343 UnicodeString&
  1344 TimeZoneFormat::formatOffsetISO8601Basic(int32_t offset, UBool useUtcIndicator, UBool isShort, UBool ignoreSeconds,
  1345         UnicodeString& result, UErrorCode& status) const {
  1346     return formatOffsetISO8601(offset, TRUE, useUtcIndicator, isShort, ignoreSeconds, result, status);
  1349 UnicodeString&
  1350 TimeZoneFormat::formatOffsetISO8601Extended(int32_t offset, UBool useUtcIndicator, UBool isShort, UBool ignoreSeconds,
  1351         UnicodeString& result, UErrorCode& status) const {
  1352     return formatOffsetISO8601(offset, FALSE, useUtcIndicator, isShort, ignoreSeconds, result, status);
  1355 UnicodeString&
  1356 TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset, UnicodeString& result, UErrorCode& status) const {
  1357     return formatOffsetLocalizedGMT(offset, FALSE, result, status);
  1360 UnicodeString&
  1361 TimeZoneFormat::formatOffsetShortLocalizedGMT(int32_t offset, UnicodeString& result, UErrorCode& status) const {
  1362     return formatOffsetLocalizedGMT(offset, TRUE, result, status);
  1365 int32_t
  1366 TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& pos) const {
  1367     return parseOffsetISO8601(text, pos, FALSE);
  1370 int32_t
  1371 TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos) const {
  1372     return parseOffsetLocalizedGMT(text, pos, FALSE, NULL);
  1375 int32_t
  1376 TimeZoneFormat::parseOffsetShortLocalizedGMT(const UnicodeString& text, ParsePosition& pos) const {
  1377     return parseOffsetLocalizedGMT(text, pos, TRUE, NULL);
  1380 // ------------------------------------------------------------------
  1381 // Private zone offset format/parse implementation
  1383 UnicodeString&
  1384 TimeZoneFormat::formatOffsetISO8601(int32_t offset, UBool isBasic, UBool useUtcIndicator,
  1385         UBool isShort, UBool ignoreSeconds, UnicodeString& result, UErrorCode& status) const {
  1386     if (U_FAILURE(status)) {
  1387         result.setToBogus();
  1388         return result;
  1390     int32_t absOffset = offset < 0 ? -offset : offset;
  1391     if (useUtcIndicator && (absOffset < MILLIS_PER_SECOND || (ignoreSeconds && absOffset < MILLIS_PER_MINUTE))) {
  1392         result.setTo(ISO8601_UTC);
  1393         return result;
  1396     OffsetFields minFields = isShort ? FIELDS_H : FIELDS_HM;
  1397     OffsetFields maxFields = ignoreSeconds ? FIELDS_HM : FIELDS_HMS;
  1398     UChar sep = isBasic ? 0 : ISO8601_SEP;
  1400     // Note: FIELDS_HMS as maxFields is a CLDR/ICU extension. ISO 8601 specification does
  1401     // not support seconds field.
  1403     if (absOffset >= MAX_OFFSET) {
  1404         result.setToBogus();
  1405         status = U_ILLEGAL_ARGUMENT_ERROR;
  1406         return result;
  1409     int fields[3];
  1410     fields[0] = absOffset / MILLIS_PER_HOUR;
  1411     absOffset = absOffset % MILLIS_PER_HOUR;
  1412     fields[1] = absOffset / MILLIS_PER_MINUTE;
  1413     absOffset = absOffset % MILLIS_PER_MINUTE;
  1414     fields[2] = absOffset / MILLIS_PER_SECOND;
  1416     U_ASSERT(fields[0] >= 0 && fields[0] <= MAX_OFFSET_HOUR);
  1417     U_ASSERT(fields[1] >= 0 && fields[1] <= MAX_OFFSET_MINUTE);
  1418     U_ASSERT(fields[2] >= 0 && fields[2] <= MAX_OFFSET_SECOND);
  1420     int32_t lastIdx = maxFields;
  1421     while (lastIdx > minFields) {
  1422         if (fields[lastIdx] != 0) {
  1423             break;
  1425         lastIdx--;
  1428     UChar sign = PLUS;
  1429     if (offset < 0) {
  1430         // if all output fields are 0s, do not use negative sign
  1431         for (int32_t idx = 0; idx <= lastIdx; idx++) {
  1432             if (fields[idx] != 0) {
  1433                 sign = MINUS;
  1434                 break;
  1438     result.setTo(sign);
  1440     for (int32_t idx = 0; idx <= lastIdx; idx++) {
  1441         if (sep && idx != 0) {
  1442             result.append(sep);
  1444         result.append((UChar)(0x0030 + fields[idx]/10));
  1445         result.append((UChar)(0x0030 + fields[idx]%10));
  1448     return result;
  1451 UnicodeString&
  1452 TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset, UBool isShort, UnicodeString& result, UErrorCode& status) const {
  1453     if (U_FAILURE(status)) {
  1454         result.setToBogus();
  1455         return result;
  1457     if (offset <= -MAX_OFFSET || offset >= MAX_OFFSET) {
  1458         result.setToBogus();
  1459         status = U_ILLEGAL_ARGUMENT_ERROR;
  1460         return result;
  1463     if (offset == 0) {
  1464         result.setTo(fGMTZeroFormat);
  1465         return result;
  1468     UBool positive = TRUE;
  1469     if (offset < 0) {
  1470         offset = -offset;
  1471         positive = FALSE;
  1474     int32_t offsetH = offset / MILLIS_PER_HOUR;
  1475     offset = offset % MILLIS_PER_HOUR;
  1476     int32_t offsetM = offset / MILLIS_PER_MINUTE;
  1477     offset = offset % MILLIS_PER_MINUTE;
  1478     int32_t offsetS = offset / MILLIS_PER_SECOND;
  1480     U_ASSERT(offsetH <= MAX_OFFSET_HOUR && offsetM <= MAX_OFFSET_MINUTE && offsetS <= MAX_OFFSET_SECOND);
  1482     const UVector* offsetPatternItems = NULL;
  1483     if (positive) {
  1484         if (offsetS != 0) {
  1485             offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HMS];
  1486         } else if (offsetM != 0 || !isShort) {
  1487             offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HM];
  1488         } else {
  1489             offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_H];
  1491     } else {
  1492         if (offsetS != 0) {
  1493             offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HMS];
  1494         } else if (offsetM != 0 || !isShort) {
  1495             offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HM];
  1496         } else {
  1497             offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_H];
  1501     U_ASSERT(offsetPatternItems != NULL);
  1503     // Building the GMT format string
  1504     result.setTo(fGMTPatternPrefix);
  1506     for (int32_t i = 0; i < offsetPatternItems->size(); i++) {
  1507         const GMTOffsetField* item = (GMTOffsetField*)offsetPatternItems->elementAt(i);
  1508         GMTOffsetField::FieldType type = item->getType();
  1510         switch (type) {
  1511         case GMTOffsetField::TEXT:
  1512             result.append(item->getPatternText(), -1);
  1513             break;
  1515         case GMTOffsetField::HOUR:
  1516             appendOffsetDigits(result, offsetH, (isShort ? 1 : 2));
  1517             break;
  1519         case GMTOffsetField::MINUTE:
  1520             appendOffsetDigits(result, offsetM, 2);
  1521             break;
  1523         case GMTOffsetField::SECOND:
  1524             appendOffsetDigits(result, offsetS, 2);
  1525             break;
  1529     result.append(fGMTPatternSuffix);
  1530     return result;
  1533 int32_t
  1534 TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& pos, UBool extendedOnly, UBool* hasDigitOffset /* = NULL */) const {
  1535     if (hasDigitOffset) {
  1536         *hasDigitOffset = FALSE;
  1538     int32_t start = pos.getIndex();
  1539     if (start >= text.length()) {
  1540         pos.setErrorIndex(start);
  1541         return 0;
  1544     UChar firstChar = text.charAt(start);
  1545     if (firstChar == ISO8601_UTC || firstChar == (UChar)(ISO8601_UTC + 0x20)) {
  1546         // "Z" (or "z") - indicates UTC
  1547         pos.setIndex(start + 1);
  1548         return 0;
  1551     int32_t sign = 1;
  1552     if (firstChar == PLUS) {
  1553         sign = 1;
  1554     } else if (firstChar == MINUS) {
  1555         sign = -1;
  1556     } else {
  1557         // Not an ISO 8601 offset string
  1558         pos.setErrorIndex(start);
  1559         return 0;
  1561     ParsePosition posOffset(start + 1);
  1562     int32_t offset = parseAsciiOffsetFields(text, posOffset, ISO8601_SEP, FIELDS_H, FIELDS_HMS);
  1563     if (posOffset.getErrorIndex() == -1 && !extendedOnly && (posOffset.getIndex() - start <= 3)) {
  1564         // If the text is successfully parsed as extended format with the options above, it can be also parsed
  1565         // as basic format. For example, "0230" can be parsed as offset 2:00 (only first digits are valid for
  1566         // extended format), but it can be parsed as offset 2:30 with basic format. We use longer result.
  1567         ParsePosition posBasic(start + 1);
  1568         int32_t tmpOffset = parseAbuttingAsciiOffsetFields(text, posBasic, FIELDS_H, FIELDS_HMS, FALSE);
  1569         if (posBasic.getErrorIndex() == -1 && posBasic.getIndex() > posOffset.getIndex()) {
  1570             offset = tmpOffset;
  1571             posOffset.setIndex(posBasic.getIndex());
  1575     if (posOffset.getErrorIndex() != -1) {
  1576         pos.setErrorIndex(start);
  1577         return 0;
  1580     pos.setIndex(posOffset.getIndex());
  1581     if (hasDigitOffset) {
  1582         *hasDigitOffset = TRUE;
  1584     return sign * offset;
  1587 int32_t
  1588 TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos, UBool isShort, UBool* hasDigitOffset) const {
  1589     int32_t start = pos.getIndex();
  1590     int32_t offset = 0;
  1591     int32_t parsedLength = 0;
  1593     if (hasDigitOffset) {
  1594         *hasDigitOffset = FALSE;
  1597     offset = parseOffsetLocalizedGMTPattern(text, start, isShort, parsedLength);
  1599     // For now, parseOffsetLocalizedGMTPattern handles both long and short
  1600     // formats, no matter isShort is true or false. This might be changed in future
  1601     // when strict parsing is necessary, or different set of patterns are used for
  1602     // short/long formats.
  1603 #if 0
  1604     if (parsedLength == 0) {
  1605         offset = parseOffsetLocalizedGMTPattern(text, start, !isShort, parsedLength);
  1607 #endif
  1609     if (parsedLength > 0) {
  1610         if (hasDigitOffset) {
  1611             *hasDigitOffset = TRUE;
  1613         pos.setIndex(start + parsedLength);
  1614         return offset;
  1617     // Try the default patterns
  1618     offset = parseOffsetDefaultLocalizedGMT(text, start, parsedLength);
  1619     if (parsedLength > 0) {
  1620         if (hasDigitOffset) {
  1621             *hasDigitOffset = TRUE;
  1623         pos.setIndex(start + parsedLength);
  1624         return offset;
  1627     // Check if this is a GMT zero format
  1628     if (text.caseCompare(start, fGMTZeroFormat.length(), fGMTZeroFormat, 0) == 0) {
  1629         pos.setIndex(start + fGMTZeroFormat.length());
  1630         return 0;
  1633     // Check if this is a default GMT zero format
  1634     for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) {
  1635         const UChar* defGMTZero = ALT_GMT_STRINGS[i];
  1636         int32_t defGMTZeroLen = u_strlen(defGMTZero);
  1637         if (text.caseCompare(start, defGMTZeroLen, defGMTZero, 0) == 0) {
  1638             pos.setIndex(start + defGMTZeroLen);
  1639             return 0;
  1643     // Nothing matched
  1644     pos.setErrorIndex(start);
  1645     return 0;
  1648 int32_t
  1649 TimeZoneFormat::parseOffsetLocalizedGMTPattern(const UnicodeString& text, int32_t start, UBool /*isShort*/, int32_t& parsedLen) const {
  1650     int32_t idx = start;
  1651     int32_t offset = 0;
  1652     UBool parsed = FALSE;
  1654     do {
  1655         // Prefix part
  1656         int32_t len = fGMTPatternPrefix.length();
  1657         if (len > 0 && text.caseCompare(idx, len, fGMTPatternPrefix, 0) != 0) {
  1658             // prefix match failed
  1659             break;
  1661         idx += len;
  1663         // Offset part
  1664         offset = parseOffsetFields(text, idx, FALSE, len);
  1665         if (len == 0) {
  1666             // offset field match failed
  1667             break;
  1669         idx += len;
  1671         len = fGMTPatternSuffix.length();
  1672         if (len > 0 && text.caseCompare(idx, len, fGMTPatternSuffix, 0) != 0) {
  1673             // no suffix match
  1674             break;
  1676         idx += len;
  1677         parsed = TRUE;
  1678     } while (FALSE);
  1680     parsedLen = parsed ? idx - start : 0;
  1681     return offset;
  1684 int32_t
  1685 TimeZoneFormat::parseOffsetFields(const UnicodeString& text, int32_t start, UBool /*isShort*/, int32_t& parsedLen) const {
  1686     int32_t outLen = 0;
  1687     int32_t offset = 0;
  1688     int32_t sign = 1;
  1690     parsedLen = 0;
  1692     int32_t offsetH, offsetM, offsetS;
  1693     offsetH = offsetM = offsetS = 0;
  1695     for (int32_t patidx = 0; PARSE_GMT_OFFSET_TYPES[patidx] >= 0; patidx++) {
  1696         int32_t gmtPatType = PARSE_GMT_OFFSET_TYPES[patidx];
  1697         UVector* items = fGMTOffsetPatternItems[gmtPatType];
  1698         U_ASSERT(items != NULL);
  1700         outLen = parseOffsetFieldsWithPattern(text, start, items, FALSE, offsetH, offsetM, offsetS);
  1701         if (outLen > 0) {
  1702             sign = (gmtPatType == UTZFMT_PAT_POSITIVE_H || gmtPatType == UTZFMT_PAT_POSITIVE_HM || gmtPatType == UTZFMT_PAT_POSITIVE_HMS) ?
  1703                 1 : -1;
  1704             break;
  1708     if (outLen > 0 && fAbuttingOffsetHoursAndMinutes) {
  1709         // When hours field is sabutting minutes field,
  1710         // the parse result above may not be appropriate.
  1711         // For example, "01020" is parsed as 01:02: above,
  1712         // but it should be parsed as 00:10:20.
  1713         int32_t tmpLen = 0;
  1714         int32_t tmpSign = 1;
  1715         int32_t tmpH, tmpM, tmpS;
  1717         for (int32_t patidx = 0; PARSE_GMT_OFFSET_TYPES[patidx] >= 0; patidx++) {
  1718             int32_t gmtPatType = PARSE_GMT_OFFSET_TYPES[patidx];
  1719             UVector* items = fGMTOffsetPatternItems[gmtPatType];
  1720             U_ASSERT(items != NULL);
  1722             // forcing parse to use single hour digit
  1723             tmpLen = parseOffsetFieldsWithPattern(text, start, items, TRUE, tmpH, tmpM, tmpS);
  1724             if (tmpLen > 0) {
  1725                 tmpSign = (gmtPatType == UTZFMT_PAT_POSITIVE_H || gmtPatType == UTZFMT_PAT_POSITIVE_HM || gmtPatType == UTZFMT_PAT_POSITIVE_HMS) ?
  1726                     1 : -1;
  1727                 break;
  1730         if (tmpLen > outLen) {
  1731             // Better parse result with single hour digit
  1732             outLen = tmpLen;
  1733             sign = tmpSign;
  1734             offsetH = tmpH;
  1735             offsetM = tmpM;
  1736             offsetS = tmpS;
  1740     if (outLen > 0) {
  1741         offset = ((((offsetH * 60) + offsetM) * 60) + offsetS) * 1000 * sign;
  1742         parsedLen = outLen;
  1745     return offset;
  1748 int32_t
  1749 TimeZoneFormat::parseOffsetFieldsWithPattern(const UnicodeString& text, int32_t start,
  1750         UVector* patternItems, UBool forceSingleHourDigit, int32_t& hour, int32_t& min, int32_t& sec) const {
  1751     UBool failed = FALSE;
  1752     int32_t offsetH, offsetM, offsetS;
  1753     offsetH = offsetM = offsetS = 0;
  1754     int32_t idx = start;
  1756     for (int32_t i = 0; i < patternItems->size(); i++) {
  1757         int32_t len = 0;
  1758         const GMTOffsetField* field = (const GMTOffsetField*)patternItems->elementAt(i);
  1759         GMTOffsetField::FieldType fieldType = field->getType();
  1760         if (fieldType == GMTOffsetField::TEXT) {
  1761             const UChar* patStr = field->getPatternText();
  1762             len = u_strlen(patStr);
  1763             if (text.caseCompare(idx, len, patStr, 0) != 0) {
  1764                 failed = TRUE;
  1765                 break;
  1767             idx += len;
  1768         } else {
  1769             if (fieldType == GMTOffsetField::HOUR) {
  1770                 uint8_t maxDigits = forceSingleHourDigit ? 1 : 2;
  1771                 offsetH = parseOffsetFieldWithLocalizedDigits(text, idx, 1, maxDigits, 0, MAX_OFFSET_HOUR, len);
  1772             } else if (fieldType == GMTOffsetField::MINUTE) {
  1773                 offsetM = parseOffsetFieldWithLocalizedDigits(text, idx, 2, 2, 0, MAX_OFFSET_MINUTE, len);
  1774             } else if (fieldType == GMTOffsetField::SECOND) {
  1775                 offsetS = parseOffsetFieldWithLocalizedDigits(text, idx, 2, 2, 0, MAX_OFFSET_SECOND, len);
  1778             if (len == 0) {
  1779                 failed = TRUE;
  1780                 break;
  1782             idx += len;
  1786     if (failed) {
  1787         hour = min = sec = 0;
  1788         return 0;
  1791     hour = offsetH;
  1792     min = offsetM;
  1793     sec = offsetS;
  1795     return idx - start;
  1798 int32_t
  1799 TimeZoneFormat::parseAbuttingOffsetFields(const UnicodeString& text, int32_t start, int32_t& parsedLen) const {
  1800     int32_t digits[MAX_OFFSET_DIGITS];
  1801     int32_t parsed[MAX_OFFSET_DIGITS];  // accumulative offsets
  1803     // Parse digits into int[]
  1804     int32_t idx = start;
  1805     int32_t len = 0;
  1806     int32_t numDigits = 0;
  1807     for (int32_t i = 0; i < MAX_OFFSET_DIGITS; i++) {
  1808         digits[i] = parseSingleLocalizedDigit(text, idx, len);
  1809         if (digits[i] < 0) {
  1810             break;
  1812         idx += len;
  1813         parsed[i] = idx - start;
  1814         numDigits++;
  1817     if (numDigits == 0) {
  1818         parsedLen = 0;
  1819         return 0;
  1822     int32_t offset = 0;
  1823     while (numDigits > 0) {
  1824         int32_t hour = 0;
  1825         int32_t min = 0;
  1826         int32_t sec = 0;
  1828         U_ASSERT(numDigits > 0 && numDigits <= MAX_OFFSET_DIGITS);
  1829         switch (numDigits) {
  1830         case 1: // H
  1831             hour = digits[0];
  1832             break;
  1833         case 2: // HH
  1834             hour = digits[0] * 10 + digits[1];
  1835             break;
  1836         case 3: // Hmm
  1837             hour = digits[0];
  1838             min = digits[1] * 10 + digits[2];
  1839             break;
  1840         case 4: // HHmm
  1841             hour = digits[0] * 10 + digits[1];
  1842             min = digits[2] * 10 + digits[3];
  1843             break;
  1844         case 5: // Hmmss
  1845             hour = digits[0];
  1846             min = digits[1] * 10 + digits[2];
  1847             sec = digits[3] * 10 + digits[4];
  1848             break;
  1849         case 6: // HHmmss
  1850             hour = digits[0] * 10 + digits[1];
  1851             min = digits[2] * 10 + digits[3];
  1852             sec = digits[4] * 10 + digits[5];
  1853             break;
  1855         if (hour <= MAX_OFFSET_HOUR && min <= MAX_OFFSET_MINUTE && sec <= MAX_OFFSET_SECOND) {
  1856             // found a valid combination
  1857             offset = hour * MILLIS_PER_HOUR + min * MILLIS_PER_MINUTE + sec * MILLIS_PER_SECOND;
  1858             parsedLen = parsed[numDigits - 1];
  1859             break;
  1861         numDigits--;
  1863     return offset;
  1866 int32_t
  1867 TimeZoneFormat::parseOffsetDefaultLocalizedGMT(const UnicodeString& text, int start, int32_t& parsedLen) const {
  1868     int32_t idx = start;
  1869     int32_t offset = 0;
  1870     int32_t parsed = 0;
  1872     do {
  1873         // check global default GMT alternatives
  1874         int32_t gmtLen = 0;
  1876         for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) {
  1877             const UChar* gmt = ALT_GMT_STRINGS[i];
  1878             int32_t len = u_strlen(gmt);
  1879             if (text.caseCompare(start, len, gmt, 0) == 0) {
  1880                 gmtLen = len;
  1881                 break;
  1884         if (gmtLen == 0) {
  1885             break;
  1887         idx += gmtLen;
  1889         // offset needs a sign char and a digit at minimum
  1890         if (idx + 1 >= text.length()) {
  1891             break;
  1894         // parse sign
  1895         int32_t sign = 1;
  1896         UChar c = text.charAt(idx);
  1897         if (c == PLUS) {
  1898             sign = 1;
  1899         } else if (c == MINUS) {
  1900             sign = -1;
  1901         } else {
  1902             break;
  1904         idx++;
  1906         // offset part
  1907         // try the default pattern with the separator first
  1908         int32_t lenWithSep = 0;
  1909         int32_t offsetWithSep = parseDefaultOffsetFields(text, idx, DEFAULT_GMT_OFFSET_SEP, lenWithSep);
  1910         if (lenWithSep == text.length() - idx) {
  1911             // maximum match
  1912             offset = offsetWithSep * sign;
  1913             idx += lenWithSep;
  1914         } else {
  1915             // try abutting field pattern
  1916             int32_t lenAbut = 0;
  1917             int32_t offsetAbut = parseAbuttingOffsetFields(text, idx, lenAbut);
  1919             if (lenWithSep > lenAbut) {
  1920                 offset = offsetWithSep * sign;
  1921                 idx += lenWithSep;
  1922             } else {
  1923                 offset = offsetAbut * sign;
  1924                 idx += lenAbut;
  1927         parsed = idx - start;
  1928     } while (false);
  1930     parsedLen = parsed;
  1931     return offset;
  1934 int32_t
  1935 TimeZoneFormat::parseDefaultOffsetFields(const UnicodeString& text, int32_t start, UChar separator, int32_t& parsedLen) const {
  1936     int32_t max = text.length();
  1937     int32_t idx = start;
  1938     int32_t len = 0;
  1939     int32_t hour = 0, min = 0, sec = 0;
  1941     parsedLen = 0;
  1943     do {
  1944         hour = parseOffsetFieldWithLocalizedDigits(text, idx, 1, 2, 0, MAX_OFFSET_HOUR, len);
  1945         if (len == 0) {
  1946             break;
  1948         idx += len;
  1950         if (idx + 1 < max && text.charAt(idx) == separator) {
  1951             min = parseOffsetFieldWithLocalizedDigits(text, idx + 1, 2, 2, 0, MAX_OFFSET_MINUTE, len);
  1952             if (len == 0) {
  1953                 break;
  1955             idx += (1 + len);
  1957             if (idx + 1 < max && text.charAt(idx) == separator) {
  1958                 sec = parseOffsetFieldWithLocalizedDigits(text, idx + 1, 2, 2, 0, MAX_OFFSET_SECOND, len);
  1959                 if (len == 0) {
  1960                     break;
  1962                 idx += (1 + len);
  1965     } while (FALSE);
  1967     if (idx == start) {
  1968         return 0;
  1971     parsedLen = idx - start;
  1972     return hour * MILLIS_PER_HOUR + min * MILLIS_PER_MINUTE + sec * MILLIS_PER_SECOND;
  1975 int32_t
  1976 TimeZoneFormat::parseOffsetFieldWithLocalizedDigits(const UnicodeString& text, int32_t start, uint8_t minDigits, uint8_t maxDigits, uint16_t minVal, uint16_t maxVal, int32_t& parsedLen) const {
  1977     parsedLen = 0;
  1979     int32_t decVal = 0;
  1980     int32_t numDigits = 0;
  1981     int32_t idx = start;
  1982     int32_t digitLen = 0;
  1984     while (idx < text.length() && numDigits < maxDigits) {
  1985         int32_t digit = parseSingleLocalizedDigit(text, idx, digitLen);
  1986         if (digit < 0) {
  1987             break;
  1989         int32_t tmpVal = decVal * 10 + digit;
  1990         if (tmpVal > maxVal) {
  1991             break;
  1993         decVal = tmpVal;
  1994         numDigits++;
  1995         idx += digitLen;
  1998     // Note: maxVal is checked in the while loop
  1999     if (numDigits < minDigits || decVal < minVal) {
  2000         decVal = -1;
  2001         numDigits = 0;
  2002     } else {
  2003         parsedLen = idx - start;
  2006     return decVal;
  2009 int32_t
  2010 TimeZoneFormat::parseSingleLocalizedDigit(const UnicodeString& text, int32_t start, int32_t& len) const {
  2011     int32_t digit = -1;
  2012     len = 0;
  2013     if (start < text.length()) {
  2014         UChar32 cp = text.char32At(start);
  2016         // First, try digits configured for this instance
  2017         for (int32_t i = 0; i < 10; i++) {
  2018             if (cp == fGMTOffsetDigits[i]) {
  2019                 digit = i;
  2020                 break;
  2023         // If failed, check if this is a Unicode digit
  2024         if (digit < 0) {
  2025             int32_t tmp = u_charDigitValue(cp);
  2026             digit = (tmp >= 0 && tmp <= 9) ? tmp : -1;
  2029         if (digit >= 0) {
  2030             int32_t next = text.moveIndex32(start, 1);
  2031             len = next - start;
  2034     return digit;
  2037 UnicodeString&
  2038 TimeZoneFormat::formatOffsetWithAsciiDigits(int32_t offset, UChar sep, OffsetFields minFields, OffsetFields maxFields, UnicodeString& result) {
  2039     U_ASSERT(maxFields >= minFields);
  2040     U_ASSERT(offset > -MAX_OFFSET && offset < MAX_OFFSET);
  2042     UChar sign = PLUS;
  2043     if (offset < 0) {
  2044         sign = MINUS;
  2045         offset = -offset;
  2047     result.setTo(sign);
  2049     int fields[3];
  2050     fields[0] = offset / MILLIS_PER_HOUR;
  2051     offset = offset % MILLIS_PER_HOUR;
  2052     fields[1] = offset / MILLIS_PER_MINUTE;
  2053     offset = offset % MILLIS_PER_MINUTE;
  2054     fields[2] = offset / MILLIS_PER_SECOND;
  2056     U_ASSERT(fields[0] >= 0 && fields[0] <= MAX_OFFSET_HOUR);
  2057     U_ASSERT(fields[1] >= 0 && fields[1] <= MAX_OFFSET_MINUTE);
  2058     U_ASSERT(fields[2] >= 0 && fields[2] <= MAX_OFFSET_SECOND);
  2060     int32_t lastIdx = maxFields;
  2061     while (lastIdx > minFields) {
  2062         if (fields[lastIdx] != 0) {
  2063             break;
  2065         lastIdx--;
  2068     for (int32_t idx = 0; idx <= lastIdx; idx++) {
  2069         if (sep && idx != 0) {
  2070             result.append(sep);
  2072         result.append((UChar)(0x0030 + fields[idx]/10));
  2073         result.append((UChar)(0x0030 + fields[idx]%10));
  2076     return result;
  2079 int32_t
  2080 TimeZoneFormat::parseAbuttingAsciiOffsetFields(const UnicodeString& text, ParsePosition& pos, OffsetFields minFields, OffsetFields maxFields, UBool fixedHourWidth) {
  2081     int32_t start = pos.getIndex();
  2083     int32_t minDigits = 2 * (minFields + 1) - (fixedHourWidth ? 0 : 1);
  2084     int32_t maxDigits = 2 * (maxFields + 1);
  2086     U_ASSERT(maxDigits <= MAX_OFFSET_DIGITS);
  2088     int32_t digits[MAX_OFFSET_DIGITS] = {};
  2089     int32_t numDigits = 0;
  2090     int32_t idx = start;
  2091     while (numDigits < maxDigits && idx < text.length()) {
  2092         UChar uch = text.charAt(idx);
  2093         int32_t digit = DIGIT_VAL(uch);
  2094         if (digit < 0) {
  2095             break;
  2097         digits[numDigits] = digit;
  2098         numDigits++;
  2099         idx++;
  2102     if (fixedHourWidth && (numDigits & 1)) {
  2103         // Fixed digits, so the number of digits must be even number. Truncating.
  2104         numDigits--;
  2107     if (numDigits < minDigits) {
  2108         pos.setErrorIndex(start);
  2109         return 0;
  2112     int32_t hour = 0, min = 0, sec = 0;
  2113     UBool bParsed = FALSE;
  2114     while (numDigits >= minDigits) {
  2115         switch (numDigits) {
  2116         case 1: //H
  2117             hour = digits[0];
  2118             break;
  2119         case 2: //HH
  2120             hour = digits[0] * 10 + digits[1];
  2121             break;
  2122         case 3: //Hmm
  2123             hour = digits[0];
  2124             min = digits[1] * 10 + digits[2];
  2125             break;
  2126         case 4: //HHmm
  2127             hour = digits[0] * 10 + digits[1];
  2128             min = digits[2] * 10 + digits[3];
  2129             break;
  2130         case 5: //Hmmss
  2131             hour = digits[0];
  2132             min = digits[1] * 10 + digits[2];
  2133             sec = digits[3] * 10 + digits[4];
  2134             break;
  2135         case 6: //HHmmss
  2136             hour = digits[0] * 10 + digits[1];
  2137             min = digits[2] * 10 + digits[3];
  2138             sec = digits[4] * 10 + digits[5];
  2139             break;
  2142         if (hour <= MAX_OFFSET_HOUR && min <= MAX_OFFSET_MINUTE && sec <= MAX_OFFSET_SECOND) {
  2143             // Successfully parsed
  2144             bParsed = true;
  2145             break;
  2148         // Truncating
  2149         numDigits -= (fixedHourWidth ? 2 : 1);
  2150         hour = min = sec = 0;
  2153     if (!bParsed) {
  2154         pos.setErrorIndex(start);
  2155         return 0;
  2157     pos.setIndex(start + numDigits);
  2158     return ((((hour * 60) + min) * 60) + sec) * 1000;
  2161 int32_t
  2162 TimeZoneFormat::parseAsciiOffsetFields(const UnicodeString& text, ParsePosition& pos, UChar sep, OffsetFields minFields, OffsetFields maxFields) {
  2163     int32_t start = pos.getIndex();
  2164     int32_t fieldVal[] = {0, 0, 0};
  2165     int32_t fieldLen[] = {0, -1, -1};
  2166     for (int32_t idx = start, fieldIdx = 0; idx < text.length() && fieldIdx <= maxFields; idx++) {
  2167         UChar c = text.charAt(idx);
  2168         if (c == sep) {
  2169             if (fieldIdx == 0) {
  2170                 if (fieldLen[0] == 0) {
  2171                     // no hours field
  2172                     break;
  2174                 // 1 digit hour, move to next field
  2175             } else {
  2176                 if (fieldLen[fieldIdx] != -1) {
  2177                     // premature minute or seconds field
  2178                     break;
  2180                 fieldLen[fieldIdx] = 0;
  2182             continue;
  2183         } else if (fieldLen[fieldIdx] == -1) {
  2184             // no separator after 2 digit field
  2185             break;
  2187         int32_t digit = DIGIT_VAL(c);
  2188         if (digit < 0) {
  2189             // not a digit
  2190             break;
  2192         fieldVal[fieldIdx] = fieldVal[fieldIdx] * 10 + digit;
  2193         fieldLen[fieldIdx]++;
  2194         if (fieldLen[fieldIdx] >= 2) {
  2195             // parsed 2 digits, move to next field
  2196             fieldIdx++;
  2200     int32_t offset = 0;
  2201     int32_t parsedLen = 0;
  2202     int32_t parsedFields = -1;
  2203     do {
  2204         // hour
  2205         if (fieldLen[0] == 0) {
  2206             break;
  2208         if (fieldVal[0] > MAX_OFFSET_HOUR) {
  2209             offset = (fieldVal[0] / 10) * MILLIS_PER_HOUR;
  2210             parsedFields = FIELDS_H;
  2211             parsedLen = 1;
  2212             break;
  2214         offset = fieldVal[0] * MILLIS_PER_HOUR;
  2215         parsedLen = fieldLen[0];
  2216         parsedFields = FIELDS_H;
  2218         // minute
  2219         if (fieldLen[1] != 2 || fieldVal[1] > MAX_OFFSET_MINUTE) {
  2220             break;
  2222         offset += fieldVal[1] * MILLIS_PER_MINUTE;
  2223         parsedLen += (1 + fieldLen[1]);
  2224         parsedFields = FIELDS_HM;
  2226         // second
  2227         if (fieldLen[2] != 2 || fieldVal[2] > MAX_OFFSET_SECOND) {
  2228             break;
  2230         offset += fieldVal[2] * MILLIS_PER_SECOND;
  2231         parsedLen += (1 + fieldLen[2]);
  2232         parsedFields = FIELDS_HMS;
  2233     } while (false);
  2235     if (parsedFields < minFields) {
  2236         pos.setErrorIndex(start);
  2237         return 0;
  2240     pos.setIndex(start + parsedLen);
  2241     return offset;
  2244 void
  2245 TimeZoneFormat::appendOffsetDigits(UnicodeString& buf, int32_t n, uint8_t minDigits) const {
  2246     U_ASSERT(n >= 0 && n < 60);
  2247     int32_t numDigits = n >= 10 ? 2 : 1;
  2248     for (int32_t i = 0; i < minDigits - numDigits; i++) {
  2249         buf.append(fGMTOffsetDigits[0]);
  2251     if (numDigits == 2) {
  2252         buf.append(fGMTOffsetDigits[n / 10]);
  2254     buf.append(fGMTOffsetDigits[n % 10]);
  2257 // ------------------------------------------------------------------
  2258 // Private misc
  2259 void
  2260 TimeZoneFormat::initGMTPattern(const UnicodeString& gmtPattern, UErrorCode& status) {
  2261     if (U_FAILURE(status)) {
  2262         return;
  2264     // This implementation not perfect, but sufficient practically.
  2265     int32_t idx = gmtPattern.indexOf(ARG0, ARG0_LEN, 0);
  2266     if (idx < 0) {
  2267         status = U_ILLEGAL_ARGUMENT_ERROR;
  2268         return;
  2270     fGMTPattern.setTo(gmtPattern);
  2271     unquote(gmtPattern.tempSubString(0, idx), fGMTPatternPrefix);
  2272     unquote(gmtPattern.tempSubString(idx + ARG0_LEN), fGMTPatternSuffix);
  2275 UnicodeString&
  2276 TimeZoneFormat::unquote(const UnicodeString& pattern, UnicodeString& result) {
  2277     if (pattern.indexOf(SINGLEQUOTE) < 0) {
  2278         result.setTo(pattern);
  2279         return result;
  2281     result.remove();
  2282     UBool isPrevQuote = FALSE;
  2283     UBool inQuote = FALSE;
  2284     for (int32_t i = 0; i < pattern.length(); i++) {
  2285         UChar c = pattern.charAt(i);
  2286         if (c == SINGLEQUOTE) {
  2287             if (isPrevQuote) {
  2288                 result.append(c);
  2289                 isPrevQuote = FALSE;
  2290             } else {
  2291                 isPrevQuote = TRUE;
  2293             inQuote = !inQuote;
  2294         } else {
  2295             isPrevQuote = FALSE;
  2296             result.append(c);
  2299     return result;
  2302 UVector*
  2303 TimeZoneFormat::parseOffsetPattern(const UnicodeString& pattern, OffsetFields required, UErrorCode& status) {
  2304     if (U_FAILURE(status)) {
  2305         return NULL;
  2307     UVector* result = new UVector(deleteGMTOffsetField, NULL, status);
  2308     if (result == NULL) {
  2309         status = U_MEMORY_ALLOCATION_ERROR;
  2310         return NULL;
  2313     int32_t checkBits = 0;
  2314     UBool isPrevQuote = FALSE;
  2315     UBool inQuote = FALSE;
  2316     UnicodeString text;
  2317     GMTOffsetField::FieldType itemType = GMTOffsetField::TEXT;
  2318     int32_t itemLength = 1;
  2320     for (int32_t i = 0; i < pattern.length(); i++) {
  2321         UChar ch = pattern.charAt(i);
  2322         if (ch == SINGLEQUOTE) {
  2323             if (isPrevQuote) {
  2324                 text.append(SINGLEQUOTE);
  2325                 isPrevQuote = FALSE;
  2326             } else {
  2327                 isPrevQuote = TRUE;
  2328                 if (itemType != GMTOffsetField::TEXT) {
  2329                     if (GMTOffsetField::isValid(itemType, itemLength)) {
  2330                         GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, (uint8_t)itemLength, status);
  2331                         result->addElement(fld, status);
  2332                         if (U_FAILURE(status)) {
  2333                             break;
  2335                     } else {
  2336                         status = U_ILLEGAL_ARGUMENT_ERROR;
  2337                         break;
  2339                     itemType = GMTOffsetField::TEXT;
  2342             inQuote = !inQuote;
  2343         } else {
  2344             isPrevQuote = FALSE;
  2345             if (inQuote) {
  2346                 text.append(ch);
  2347             } else {
  2348                 GMTOffsetField::FieldType tmpType = GMTOffsetField::getTypeByLetter(ch);
  2349                 if (tmpType != GMTOffsetField::TEXT) {
  2350                     // an offset time pattern character
  2351                     if (tmpType == itemType) {
  2352                         itemLength++;
  2353                     } else {
  2354                         if (itemType == GMTOffsetField::TEXT) {
  2355                             if (text.length() > 0) {
  2356                                 GMTOffsetField* textfld = GMTOffsetField::createText(text, status);
  2357                                 result->addElement(textfld, status);
  2358                                 if (U_FAILURE(status)) {
  2359                                     break;
  2361                                 text.remove();
  2363                         } else {
  2364                             if (GMTOffsetField::isValid(itemType, itemLength)) {
  2365                                 GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
  2366                                 result->addElement(fld, status);
  2367                                 if (U_FAILURE(status)) {
  2368                                     break;
  2370                             } else {
  2371                                 status = U_ILLEGAL_ARGUMENT_ERROR;
  2372                                 break;
  2375                         itemType = tmpType;
  2376                         itemLength = 1;
  2377                         checkBits |= tmpType;
  2379                 } else {
  2380                     // a string literal
  2381                     if (itemType != GMTOffsetField::TEXT) {
  2382                         if (GMTOffsetField::isValid(itemType, itemLength)) {
  2383                             GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
  2384                             result->addElement(fld, status);
  2385                             if (U_FAILURE(status)) {
  2386                                 break;
  2388                         } else {
  2389                             status = U_ILLEGAL_ARGUMENT_ERROR;
  2390                             break;
  2392                         itemType = GMTOffsetField::TEXT;
  2394                     text.append(ch);
  2399     // handle last item
  2400     if (U_SUCCESS(status)) {
  2401         if (itemType == GMTOffsetField::TEXT) {
  2402             if (text.length() > 0) {
  2403                 GMTOffsetField* tfld = GMTOffsetField::createText(text, status);
  2404                 result->addElement(tfld, status);
  2406         } else {
  2407             if (GMTOffsetField::isValid(itemType, itemLength)) {
  2408                 GMTOffsetField* fld = GMTOffsetField::createTimeField(itemType, itemLength, status);
  2409                 result->addElement(fld, status);
  2410             } else {
  2411                 status = U_ILLEGAL_ARGUMENT_ERROR;
  2415         // Check all required fields are set
  2416         if (U_SUCCESS(status)) {
  2417             int32_t reqBits = 0;
  2418             switch (required) {
  2419             case FIELDS_H:
  2420                 reqBits = GMTOffsetField::HOUR;
  2421                 break;
  2422             case FIELDS_HM:
  2423                 reqBits = GMTOffsetField::HOUR | GMTOffsetField::MINUTE;
  2424                 break;
  2425             case FIELDS_HMS:
  2426                 reqBits = GMTOffsetField::HOUR | GMTOffsetField::MINUTE | GMTOffsetField::SECOND;
  2427                 break;
  2429             if (checkBits == reqBits) {
  2430                 // all required fields are set, no extra fields
  2431                 return result;
  2436     // error
  2437     delete result;
  2438     return NULL;
  2441 UnicodeString&
  2442 TimeZoneFormat::expandOffsetPattern(const UnicodeString& offsetHM, UnicodeString& result, UErrorCode& status) {
  2443     result.setToBogus();
  2444     if (U_FAILURE(status)) {
  2445         return result;
  2447     U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN) == 2);
  2449     int32_t idx_mm = offsetHM.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN, 2, 0);
  2450     if (idx_mm < 0) {
  2451         // Bad time zone hour pattern data
  2452         status = U_ILLEGAL_ARGUMENT_ERROR;
  2453         return result;
  2456     UnicodeString sep;
  2457     int32_t idx_H = offsetHM.tempSubString(0, idx_mm).lastIndexOf((UChar)0x0048 /* H */);
  2458     if (idx_H >= 0) {
  2459         sep = offsetHM.tempSubString(idx_H + 1, idx_mm - (idx_H + 1));
  2461     result.setTo(offsetHM.tempSubString(0, idx_mm + 2));
  2462     result.append(sep);
  2463     result.append(DEFAULT_GMT_OFFSET_SECOND_PATTERN, -1);
  2464     result.append(offsetHM.tempSubString(idx_mm + 2));
  2465     return result;
  2468 UnicodeString&
  2469 TimeZoneFormat::truncateOffsetPattern(const UnicodeString& offsetHM, UnicodeString& result, UErrorCode& status) {
  2470     result.setToBogus();
  2471     if (U_FAILURE(status)) {
  2472         return result;
  2474     U_ASSERT(u_strlen(DEFAULT_GMT_OFFSET_MINUTE_PATTERN) == 2);
  2476     int32_t idx_mm = offsetHM.indexOf(DEFAULT_GMT_OFFSET_MINUTE_PATTERN, 2, 0);
  2477     if (idx_mm < 0) {
  2478         // Bad time zone hour pattern data
  2479         status = U_ILLEGAL_ARGUMENT_ERROR;
  2480         return result;
  2482     UChar HH[] = {0x0048, 0x0048};
  2483     int32_t idx_HH = offsetHM.tempSubString(0, idx_mm).lastIndexOf(HH, 2, 0);
  2484     if (idx_HH >= 0) {
  2485         return result.setTo(offsetHM.tempSubString(0, idx_HH + 2));
  2487     int32_t idx_H = offsetHM.tempSubString(0, idx_mm).lastIndexOf((UChar)0x0048, 0);
  2488     if (idx_H >= 0) {
  2489         return result.setTo(offsetHM.tempSubString(0, idx_H + 1));
  2491     // Bad time zone hour pattern data
  2492     status = U_ILLEGAL_ARGUMENT_ERROR;
  2493     return result;
  2496 void
  2497 TimeZoneFormat::initGMTOffsetPatterns(UErrorCode& status) {
  2498     for (int32_t type = 0; type < UTZFMT_PAT_COUNT; type++) {
  2499         switch (type) {
  2500         case UTZFMT_PAT_POSITIVE_H:
  2501         case UTZFMT_PAT_NEGATIVE_H:
  2502             fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_H, status);
  2503             break;
  2504         case UTZFMT_PAT_POSITIVE_HM:
  2505         case UTZFMT_PAT_NEGATIVE_HM:
  2506             fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_HM, status);
  2507             break;
  2508         case UTZFMT_PAT_POSITIVE_HMS:
  2509         case UTZFMT_PAT_NEGATIVE_HMS:
  2510             fGMTOffsetPatternItems[type] = parseOffsetPattern(fGMTOffsetPatterns[type], FIELDS_HMS, status);
  2511             break;
  2514     checkAbuttingHoursAndMinutes();
  2517 void
  2518 TimeZoneFormat::checkAbuttingHoursAndMinutes() {
  2519     fAbuttingOffsetHoursAndMinutes= FALSE;
  2520     for (int32_t type = 0; type < UTZFMT_PAT_COUNT; type++) {
  2521         UBool afterH = FALSE;
  2522         UVector *items = fGMTOffsetPatternItems[type];
  2523         for (int32_t i = 0; i < items->size(); i++) {
  2524             const GMTOffsetField* item = (GMTOffsetField*)items->elementAt(i);
  2525             GMTOffsetField::FieldType type = item->getType();
  2526             if (type != GMTOffsetField::TEXT) {
  2527                 if (afterH) {
  2528                     fAbuttingOffsetHoursAndMinutes = TRUE;
  2529                     break;
  2530                 } else if (type == GMTOffsetField::HOUR) {
  2531                     afterH = TRUE;
  2533             } else if (afterH) {
  2534                 break;
  2537         if (fAbuttingOffsetHoursAndMinutes) {
  2538             break;
  2543 UBool
  2544 TimeZoneFormat::toCodePoints(const UnicodeString& str, UChar32* codeArray, int32_t size) {
  2545     int32_t count = str.countChar32();
  2546     if (count != size) {
  2547         return FALSE;
  2550     for (int32_t idx = 0, start = 0; idx < size; idx++) {
  2551         codeArray[idx] = str.char32At(start);
  2552         start = str.moveIndex32(start, 1);
  2555     return TRUE;
  2558 TimeZone*
  2559 TimeZoneFormat::createTimeZoneForOffset(int32_t offset) const {
  2560     if (offset == 0) {
  2561         // when offset is 0, we should use "Etc/GMT"
  2562         return TimeZone::createTimeZone(UnicodeString(TZID_GMT));
  2564     return ZoneMeta::createCustomTimeZone(offset);
  2567 UTimeZoneFormatTimeType
  2568 TimeZoneFormat::getTimeType(UTimeZoneNameType nameType) {
  2569     switch (nameType) {
  2570     case UTZNM_LONG_STANDARD:
  2571     case UTZNM_SHORT_STANDARD:
  2572         return UTZFMT_TIME_TYPE_STANDARD;
  2574     case UTZNM_LONG_DAYLIGHT:
  2575     case UTZNM_SHORT_DAYLIGHT:
  2576         return UTZFMT_TIME_TYPE_DAYLIGHT;
  2578     default:
  2579         U_ASSERT(FALSE);
  2581     return UTZFMT_TIME_TYPE_UNKNOWN;
  2584 UnicodeString&
  2585 TimeZoneFormat::getTimeZoneID(const TimeZoneNames::MatchInfoCollection* matches, int32_t idx, UnicodeString& tzID) const {
  2586     if (!matches->getTimeZoneIDAt(idx, tzID)) {
  2587         UnicodeString mzID;
  2588         if (matches->getMetaZoneIDAt(idx, mzID)) {
  2589             fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, tzID);
  2592     return tzID;
  2596 class ZoneIdMatchHandler : public TextTrieMapSearchResultHandler {
  2597 public:
  2598     ZoneIdMatchHandler();
  2599     virtual ~ZoneIdMatchHandler();
  2601     UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status);
  2602     const UChar* getID();
  2603     int32_t getMatchLen();
  2604 private:
  2605     int32_t fLen;
  2606     const UChar* fID;
  2607 };
  2609 ZoneIdMatchHandler::ZoneIdMatchHandler() 
  2610 : fLen(0), fID(NULL) {
  2613 ZoneIdMatchHandler::~ZoneIdMatchHandler() {
  2616 UBool
  2617 ZoneIdMatchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
  2618     if (U_FAILURE(status)) {
  2619         return FALSE;
  2621     if (node->hasValues()) {
  2622         const UChar* id = (const UChar*)node->getValue(0);
  2623         if (id != NULL) {
  2624             if (fLen < matchLength) {
  2625                 fID = id;
  2626                 fLen = matchLength;
  2630     return TRUE;
  2633 const UChar*
  2634 ZoneIdMatchHandler::getID() {
  2635     return fID;
  2638 int32_t
  2639 ZoneIdMatchHandler::getMatchLen() {
  2640     return fLen;
  2644 static void U_CALLCONV initZoneIdTrie(UErrorCode &status) {
  2645     U_ASSERT(gZoneIdTrie == NULL);
  2646     ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT, tzfmt_cleanup);
  2647     gZoneIdTrie = new TextTrieMap(TRUE, NULL);    // No deleter, because values are pooled by ZoneMeta
  2648     if (gZoneIdTrie == NULL) {
  2649         status = U_MEMORY_ALLOCATION_ERROR;
  2650         return;
  2652     StringEnumeration *tzenum = TimeZone::createEnumeration();
  2653     const UnicodeString *id;
  2654     while ((id = tzenum->snext(status))) {
  2655         const UChar* uid = ZoneMeta::findTimeZoneID(*id);
  2656         if (uid) {
  2657             gZoneIdTrie->put(uid, const_cast<UChar *>(uid), status);
  2660     delete tzenum;
  2664 UnicodeString&
  2665 TimeZoneFormat::parseZoneID(const UnicodeString& text, ParsePosition& pos, UnicodeString& tzID) const {
  2666     UErrorCode status = U_ZERO_ERROR;
  2667     umtx_initOnce(gZoneIdTrieInitOnce, &initZoneIdTrie, status);
  2669     int32_t start = pos.getIndex();
  2670     int32_t len = 0;
  2671     tzID.setToBogus();
  2673     if (U_SUCCESS(status)) {
  2674         LocalPointer<ZoneIdMatchHandler> handler(new ZoneIdMatchHandler());
  2675         gZoneIdTrie->search(text, start, handler.getAlias(), status); 
  2676         len = handler->getMatchLen();
  2677         if (len > 0) {
  2678             tzID.setTo(handler->getID(), -1);
  2682     if (len > 0) {
  2683         pos.setIndex(start + len);
  2684     } else {
  2685         pos.setErrorIndex(start);
  2688     return tzID;
  2691 static void U_CALLCONV initShortZoneIdTrie(UErrorCode &status) {
  2692     U_ASSERT(gShortZoneIdTrie == NULL);
  2693     ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONEFORMAT, tzfmt_cleanup);
  2694     StringEnumeration *tzenum = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
  2695     if (U_SUCCESS(status)) {
  2696         gShortZoneIdTrie = new TextTrieMap(TRUE, NULL);    // No deleter, because values are pooled by ZoneMeta
  2697         if (gShortZoneIdTrie == NULL) {
  2698             status = U_MEMORY_ALLOCATION_ERROR;
  2699         } else {
  2700             const UnicodeString *id;
  2701             while ((id = tzenum->snext(status))) {
  2702                 const UChar* uID = ZoneMeta::findTimeZoneID(*id);
  2703                 const UChar* shortID = ZoneMeta::getShortID(*id);
  2704                 if (shortID && uID) {
  2705                     gShortZoneIdTrie->put(shortID, const_cast<UChar *>(uID), status);
  2710     delete tzenum;
  2714 UnicodeString&
  2715 TimeZoneFormat::parseShortZoneID(const UnicodeString& text, ParsePosition& pos, UnicodeString& tzID) const {
  2716     UErrorCode status = U_ZERO_ERROR;
  2717     umtx_initOnce(gShortZoneIdTrieInitOnce, &initShortZoneIdTrie, status);
  2719     int32_t start = pos.getIndex();
  2720     int32_t len = 0;
  2721     tzID.setToBogus();
  2723     if (U_SUCCESS(status)) {
  2724         LocalPointer<ZoneIdMatchHandler> handler(new ZoneIdMatchHandler());
  2725         gShortZoneIdTrie->search(text, start, handler.getAlias(), status); 
  2726         len = handler->getMatchLen();
  2727         if (len > 0) {
  2728             tzID.setTo(handler->getID(), -1);
  2732     if (len > 0) {
  2733         pos.setIndex(start + len);
  2734     } else {
  2735         pos.setErrorIndex(start);
  2738     return tzID;
  2742 UnicodeString&
  2743 TimeZoneFormat::parseExemplarLocation(const UnicodeString& text, ParsePosition& pos, UnicodeString& tzID) const {
  2744     int32_t startIdx = pos.getIndex();
  2745     int32_t parsedPos = -1;
  2746     tzID.setToBogus();
  2748     UErrorCode status = U_ZERO_ERROR;
  2749     LocalPointer<TimeZoneNames::MatchInfoCollection> exemplarMatches(fTimeZoneNames->find(text, startIdx, UTZNM_EXEMPLAR_LOCATION, status));
  2750     if (U_FAILURE(status)) {
  2751         pos.setErrorIndex(startIdx);
  2752         return tzID;
  2754     int32_t matchIdx = -1;
  2755     if (!exemplarMatches.isNull()) {
  2756         for (int32_t i = 0; i < exemplarMatches->size(); i++) {
  2757             if (startIdx + exemplarMatches->getMatchLengthAt(i) > parsedPos) {
  2758                 matchIdx = i;
  2759                 parsedPos = startIdx + exemplarMatches->getMatchLengthAt(i);
  2762         if (parsedPos > 0) {
  2763             pos.setIndex(parsedPos);
  2764             getTimeZoneID(exemplarMatches.getAlias(), matchIdx, tzID);
  2768     if (tzID.length() == 0) {
  2769         pos.setErrorIndex(startIdx);
  2772     return tzID;
  2775 U_NAMESPACE_END
  2777 #endif

mercurial