Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/intl/icu/source/i18n/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 96 kB image not shown  

Quelle  tzfmt.cpp   Sprache: C

 
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2011-2015, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*/


#include "unicode/utypes.h"

#if !UCONFIG_NO_FORMATTING

#include "unicode/calendar.h"
#include "unicode/tzfmt.h"
#include "unicode/numsys.h"
#include "unicode/strenum.h"
#include "unicode/uchar.h"
#include "unicode/udat.h"
#include "unicode/ustring.h"
#include "unicode/utf16.h"
#include "charstr.h"
#include "tzgnames.h"
#include "cmemory.h"
#include "cstring.h"
#include "putilimp.h"
#include "uassert.h"
#include "ucln_in.h"
#include "ulocimp.h"
#include "umutex.h"
#include "uresimp.h"
#include "ureslocs.h"
#include "uvector.h"
#include "zonemeta.h"
#include "tznames_impl.h"   // TextTrieMap
#include "patternprops.h"

U_NAMESPACE_BEGIN

// Bit flags used by the parse method.
// The order must match UTimeZoneFormatStyle enum.
#define ISO_Z_STYLE_FLAG 0x0080
#define ISO_LOCAL_STYLE_FLAG 0x0100
static const int16_t STYLE_PARSE_FLAGS[] = {
    0x0001, // UTZFMT_STYLE_GENERIC_LOCATION,
    0x0002, // UTZFMT_STYLE_GENERIC_LONG,
    0x0004, // UTZFMT_STYLE_GENERIC_SHORT,
    0x0008, // UTZFMT_STYLE_SPECIFIC_LONG,
    0x0010, // UTZFMT_STYLE_SPECIFIC_SHORT,
    0x0020, // UTZFMT_STYLE_LOCALIZED_GMT,
    0x0040, // UTZFMT_STYLE_LOCALIZED_GMT_SHORT,
    ISO_Z_STYLE_FLAG,       // UTZFMT_STYLE_ISO_BASIC_SHORT,
    ISO_LOCAL_STYLE_FLAG,   // UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT,
    ISO_Z_STYLE_FLAG,       // UTZFMT_STYLE_ISO_BASIC_FIXED,
    ISO_LOCAL_STYLE_FLAG,   // UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED,
    ISO_Z_STYLE_FLAG,       // UTZFMT_STYLE_ISO_BASIC_FULL,
    ISO_LOCAL_STYLE_FLAG,   // UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
    ISO_Z_STYLE_FLAG,       // UTZFMT_STYLE_ISO_EXTENDED_FIXED,
    ISO_LOCAL_STYLE_FLAG,   // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED,
    ISO_Z_STYLE_FLAG,       // UTZFMT_STYLE_ISO_EXTENDED_FULL,
    ISO_LOCAL_STYLE_FLAG,   // UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL,
    0x0200, // UTZFMT_STYLE_ZONE_ID,
    0x0400, // UTZFMT_STYLE_ZONE_ID_SHORT,
    0x0800  // UTZFMT_STYLE_EXEMPLAR_LOCATION
};

static const char gZoneStringsTag[] = "zoneStrings";
static const char gGmtFormatTag[]= "gmtFormat";
static const char gGmtZeroFormatTag[] = "gmtZeroFormat";
static const char gHourFormatTag[]= "hourFormat";

static const char16_t TZID_GMT[] = {0x0045, 0x0074, 0x0063, 0x002F, 0x0047, 0x004D, 0x0054, 0};    // Etc/GMT
static const char16_t UNKNOWN_ZONE_ID[] = {
    0x0045, 0x0074, 0x0063, 0x002F, 0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0}; // Etc/Unknown
static const char16_t UNKNOWN_SHORT_ZONE_ID[] = {0x0075, 0x006E, 0x006B, 0};   // unk
static const char16_t UNKNOWN_LOCATION[] = {0x0055, 0x006E, 0x006B, 0x006E, 0x006F, 0x0077, 0x006E, 0};    // Unknown

static const char16_t DEFAULT_GMT_PATTERN[] = {0x0047, 0x004D, 0x0054, 0x007B, 0x0030, 0x007D, 0}; // GMT{0}
//static const char16_t DEFAULT_GMT_ZERO[] = {0x0047, 0x004D, 0x0054, 0}; // GMT
static const char16_t DEFAULT_GMT_POSITIVE_HM[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // +H:mm
static const char16_t DEFAULT_GMT_POSITIVE_HMS[] = {0x002B, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // +H:mm:ss
static const char16_t DEFAULT_GMT_NEGATIVE_HM[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0}; // -H:mm
static const char16_t DEFAULT_GMT_NEGATIVE_HMS[] = {0x002D, 0x0048, 0x003A, 0x006D, 0x006D, 0x003A, 0x0073, 0x0073, 0}; // -H:mm:ss
static const char16_t DEFAULT_GMT_POSITIVE_H[] = {0x002B, 0x0048, 0}; // +H
static const char16_t DEFAULT_GMT_NEGATIVE_H[] = {0x002D, 0x0048, 0}; // -H

static const UChar32 DEFAULT_GMT_DIGITS[] = {
    0x0030, 0x0031, 0x0032, 0x0033, 0x0034,
    0x0035, 0x0036, 0x0037, 0x0038, 0x0039
};

static const char16_t DEFAULT_GMT_OFFSET_SEP = 0x003A; // ':'

static const char16_t ARG0[] = {0x007B, 0x0030, 0x007D};   // "{0}"
static const int32_t ARG0_LEN = 3;

static const char16_t DEFAULT_GMT_OFFSET_MINUTE_PATTERN[] = {0x006D, 0x006D, 0};   // "mm"
static const char16_t DEFAULT_GMT_OFFSET_SECOND_PATTERN[] = {0x0073, 0x0073, 0};   // "ss"

static const char16_t ALT_GMT_STRINGS[][4] = {
    {0x0047, 0x004D, 0x0054, 0},    // GMT
    {0x0055, 0x0054, 0x0043, 0},    // UTC
    {0x0055, 0x0054, 0, 0},         // UT
    {0, 0, 0, 0}
};

// Order of GMT offset pattern parsing, *_HMS must be evaluated first
// because *_HM is most likely a substring of *_HMS 
static const int32_t PARSE_GMT_OFFSET_TYPES[] = {
    UTZFMT_PAT_POSITIVE_HMS,
    UTZFMT_PAT_NEGATIVE_HMS,
    UTZFMT_PAT_POSITIVE_HM,
    UTZFMT_PAT_NEGATIVE_HM,
    UTZFMT_PAT_POSITIVE_H,
    UTZFMT_PAT_NEGATIVE_H,
    -1
};

static const char16_t SINGLEQUOTE  = 0x0027;
static const char16_t PLUS         = 0x002B;
static const char16_t MINUS        = 0x002D;
static const char16_t ISO8601_UTC  = 0x005A;   // 'Z'
static const char16_t ISO8601_SEP  = 0x003A;   // ':'

static const int32_t MILLIS_PER_HOUR = 60 * 60 * 1000;
static const int32_t MILLIS_PER_MINUTE = 60 * 1000;
static const int32_t MILLIS_PER_SECOND = 1000;

// Maximum offset (exclusive) in millisecond supported by offset formats
static int32_t MAX_OFFSET = 24 * MILLIS_PER_HOUR;

// Maximum values for GMT offset fields
static const int32_t MAX_OFFSET_HOUR = 23;
static const int32_t MAX_OFFSET_MINUTE = 59;
static const int32_t MAX_OFFSET_SECOND = 59;

static const int32_t UNKNOWN_OFFSET = 0x7FFFFFFF;

static const int32_t ALL_SIMPLE_NAME_TYPES = UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT | UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT | UTZNM_EXEMPLAR_LOCATION;
static const int32_t ALL_GENERIC_NAME_TYPES = UTZGNM_LOCATION | UTZGNM_LONG | UTZGNM_SHORT;

#define DIGIT_VAL(c) (0x0030 <= (c) && (c) <= 0x0039 ? (c) - 0x0030 : -1)
#define MAX_OFFSET_DIGITS 6

// Time Zone ID/Short ID trie
static TextTrieMap *gZoneIdTrie = nullptr;
static icu::UInitOnce gZoneIdTrieInitOnce {};

static TextTrieMap *gShortZoneIdTrie = nullptr;
static icu::UInitOnce gShortZoneIdTrieInitOnce {};

static UMutex gLock;

U_CDECL_BEGIN
/**
 * Cleanup callback func
 */

static UBool U_CALLCONV tzfmt_cleanup()
{
    delete gZoneIdTrie;
    gZoneIdTrie = nullptr;
    gZoneIdTrieInitOnce.reset();

    delete gShortZoneIdTrie;
    gShortZoneIdTrie = nullptr;
    gShortZoneIdTrieInitOnce.reset();

    return true;
}
U_CDECL_END

// ------------------------------------------------------------------
// GMTOffsetField
//
// This class represents a localized GMT offset pattern
// item and used by TimeZoneFormat
// ------------------------------------------------------------------
class GMTOffsetField : public UMemory {
public:
    enum FieldType {
        TEXT = 0,
        HOUR = 1,
        MINUTE = 2,
        SECOND = 4
    };

    virtual ~GMTOffsetField();

    static GMTOffsetField* createText(const UnicodeString& text, UErrorCode& status);
    static GMTOffsetField* createTimeField(FieldType type, uint8_t width, UErrorCode& status);
    static UBool isValid(FieldType type, int32_t width);
    static FieldType getTypeByLetter(char16_t ch);

    FieldType getType() const;
    uint8_t getWidth() const;
    const char16_t* getPatternText() const;

private:
    char16_t* fText;
    FieldType fType;
    uint8_t fWidth;

    GMTOffsetField();
};

GMTOffsetField::GMTOffsetField()
: fText(nullptr), fType(TEXT), fWidth(0) {
}

GMTOffsetField::~GMTOffsetField() {
    if (fText) {
        uprv_free(fText);
    }
}

GMTOffsetField*
GMTOffsetField::createText(const UnicodeString& text, UErrorCode& status) {
    if (U_FAILURE(status)) {
        return nullptr;
    }
    GMTOffsetField* result = new GMTOffsetField();
    if (result == nullptr) {
        status = U_MEMORY_ALLOCATION_ERROR;
        return nullptr;
    }

    int32_t len = text.length();
    result->fText = static_cast<char16_t*>(uprv_malloc((len + 1) * sizeof(char16_t)));
    if (result->fText == nullptr) {
        status = U_MEMORY_ALLOCATION_ERROR;
        delete result;
        return nullptr;
    }
    u_strncpy(result->fText, text.getBuffer(), len);
    result->fText[len] = 0;
    result->fType = TEXT;

    return result;
}

GMTOffsetField*
GMTOffsetField::createTimeField(FieldType type, uint8_t width, UErrorCode& status) {
    U_ASSERT(type != TEXT);
    if (U_FAILURE(status)) {
        return nullptr;
    }
    GMTOffsetField* result = new GMTOffsetField();
    if (result == nullptr) {
        status = U_MEMORY_ALLOCATION_ERROR;
        return nullptr;
    }

    result->fType = type;
    result->fWidth = width;

    return result;
}

UBool
GMTOffsetField::isValid(FieldType type, int32_t width) {
    switch (type) {
    case HOUR:
        return (width == 1 || width == 2);
    case MINUTE:
    case SECOND:
        return (width == 2);
    default:
        UPRV_UNREACHABLE_EXIT;
    }
    return (width > 0);
}

GMTOffsetField::FieldType
GMTOffsetField::getTypeByLetter(char16_t ch) {
    if (ch == 0x0048 /* H */) {
        return HOUR;
    } else if (ch == 0x006D /* m */) {
        return MINUTE;
    } else if (ch == 0x0073 /* s */) {
        return SECOND;
    }
    return TEXT;
}

inline GMTOffsetField::FieldType
GMTOffsetField::getType() const {
     return fType;
 }

inline uint8_t
GMTOffsetField::getWidth() const {
    return fWidth;
}
 
inline const char16_t*
GMTOffsetField::getPatternText() const {
    return fText;
}


U_CDECL_BEGIN
static void U_CALLCONV
deleteGMTOffsetField(void *obj) {
    delete static_cast<GMTOffsetField *>(obj);
}
U_CDECL_END


// ------------------------------------------------------------------
// TimeZoneFormat
// ------------------------------------------------------------------
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeZoneFormat)

TimeZoneFormat::TimeZoneFormat(const Locale& locale, UErrorCode& status) 
: fLocale(locale), fTimeZoneNames(nullptr), fTimeZoneGenericNames(nullptr),
  fDefParseOptionFlags(0), fTZDBTimeZoneNames(nullptr) {

    for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
        fGMTOffsetPatternItems[i] = nullptr;
    }

    const char* region = fLocale.getCountry();
    int32_t regionLen = static_cast<int32_t>(uprv_strlen(region));
    if (regionLen == 0) {
        UErrorCode tempStatus = U_ZERO_ERROR;
        CharString loc = ulocimp_addLikelySubtags(fLocale.getName(), tempStatus);

        regionLen = uloc_getCountry(loc.data(), fTargetRegion, sizeof(fTargetRegion), &tempStatus);
        if (U_SUCCESS(tempStatus)) {
            fTargetRegion[regionLen] = 0;
        } else {
            fTargetRegion[0] = 0;
        }
    } else if (regionLen < static_cast<int32_t>(sizeof(fTargetRegion))) {
        uprv_strcpy(fTargetRegion, region);
    } else {
        fTargetRegion[0] = 0;
    }

    fTimeZoneNames = TimeZoneNames::createInstance(locale, status);
    // fTimeZoneGenericNames is lazily instantiated
    if (U_FAILURE(status)) {
        return;
    }

    const char16_t* gmtPattern = nullptr;
    const char16_t* hourFormats = nullptr;

    UResourceBundle *zoneBundle = ures_open(U_ICUDATA_ZONE, locale.getName(), &status);
    UResourceBundle *zoneStringsArray = ures_getByKeyWithFallback(zoneBundle, gZoneStringsTag, nullptr, &status);
    if (U_SUCCESS(status)) {
        const char16_t* resStr;
        int32_t len;
        resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtFormatTag, &len, &status);
        if (len > 0) {
            gmtPattern = resStr;
        }
        resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gGmtZeroFormatTag, &len, &status);
        if (len > 0) {
            fGMTZeroFormat.setTo(true, resStr, len);
        }
        resStr = ures_getStringByKeyWithFallback(zoneStringsArray, gHourFormatTag, &len, &status);
        if (len > 0) {
            hourFormats = resStr;
        }
        ures_close(zoneStringsArray);
        ures_close(zoneBundle);
    }

    if (gmtPattern == nullptr) {
        gmtPattern = DEFAULT_GMT_PATTERN;
    }
    initGMTPattern(UnicodeString(true, gmtPattern, -1), status);

    UBool useDefaultOffsetPatterns = true;
    if (hourFormats) {
        char16_t* sep = u_strchr(hourFormats, static_cast<char16_t>(0x003B) /* ';' */);
        if (sep != nullptr) {
            UErrorCode tmpStatus = U_ZERO_ERROR;
            fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM].setTo(false, hourFormats, static_cast<int32_t>(sep - hourFormats));
            fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM].setTo(true, sep + 1, -1);
            expandOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HMS], tmpStatus);
            expandOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HMS], tmpStatus);
            truncateOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_H], tmpStatus);
            truncateOffsetPattern(fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM], fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_H], tmpStatus);
            if (U_SUCCESS(tmpStatus)) {
                useDefaultOffsetPatterns = false;
            }
        }
    }
    if (useDefaultOffsetPatterns) {
        fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_H].setTo(true, DEFAULT_GMT_POSITIVE_H, -1);
        fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HM].setTo(true, DEFAULT_GMT_POSITIVE_HM, -1);
        fGMTOffsetPatterns[UTZFMT_PAT_POSITIVE_HMS].setTo(true, DEFAULT_GMT_POSITIVE_HMS, -1);
        fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_H].setTo(true, DEFAULT_GMT_NEGATIVE_H, -1);
        fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HM].setTo(true, DEFAULT_GMT_NEGATIVE_HM, -1);
        fGMTOffsetPatterns[UTZFMT_PAT_NEGATIVE_HMS].setTo(true, DEFAULT_GMT_NEGATIVE_HMS, -1);
    }
    initGMTOffsetPatterns(status);

    NumberingSystem* ns = NumberingSystem::createInstance(locale, status);
    UBool useDefDigits = true;
    if (ns && !ns->isAlgorithmic()) {
        UnicodeString digits = ns->getDescription();
        useDefDigits = !toCodePoints(digits, fGMTOffsetDigits, 10);
    }
    if (useDefDigits) {
        uprv_memcpy(fGMTOffsetDigits, DEFAULT_GMT_DIGITS, sizeof(UChar32) * 10);
    }
    delete ns;
}

TimeZoneFormat::TimeZoneFormat(const TimeZoneFormat& other)
: Format(other), fTimeZoneNames(nullptr), fTimeZoneGenericNames(nullptr),
  fTZDBTimeZoneNames(nullptr) {

    for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
        fGMTOffsetPatternItems[i] = nullptr;
    }
    *this = other;
}


TimeZoneFormat::~TimeZoneFormat() {
    delete fTimeZoneNames;
    delete fTimeZoneGenericNames;
    delete fTZDBTimeZoneNames;
    for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
        delete fGMTOffsetPatternItems[i];
    }
}

TimeZoneFormat&
TimeZoneFormat::operator=(const TimeZoneFormat& other) {
    if (this == &other) {
        return *this;
    }

    delete fTimeZoneNames;
    delete fTimeZoneGenericNames;
    fTimeZoneGenericNames = nullptr;
    delete fTZDBTimeZoneNames;
    fTZDBTimeZoneNames = nullptr;

    fLocale = other.fLocale;
    uprv_memcpy(fTargetRegion, other.fTargetRegion, sizeof(fTargetRegion));

    fTimeZoneNames = other.fTimeZoneNames->clone();
    if (other.fTimeZoneGenericNames) {
        // TODO: this test has dubious thread safety.
        fTimeZoneGenericNames = other.fTimeZoneGenericNames->clone();
    }

    fGMTPattern = other.fGMTPattern;
    fGMTPatternPrefix = other.fGMTPatternPrefix;
    fGMTPatternSuffix = other.fGMTPatternSuffix;

    UErrorCode status = U_ZERO_ERROR;
    for (int32_t i = 0; i < UTZFMT_PAT_COUNT; i++) {
        fGMTOffsetPatterns[i] = other.fGMTOffsetPatterns[i];
        delete fGMTOffsetPatternItems[i];
        fGMTOffsetPatternItems[i] = nullptr;
    }
    initGMTOffsetPatterns(status);
    U_ASSERT(U_SUCCESS(status));

    fGMTZeroFormat = other.fGMTZeroFormat;

    uprv_memcpy(fGMTOffsetDigits, other.fGMTOffsetDigits, sizeof(fGMTOffsetDigits));

    fDefParseOptionFlags = other.fDefParseOptionFlags;

    return *this;
}


bool
TimeZoneFormat::operator==(const Format& other) const {
    TimeZoneFormat* tzfmt = (TimeZoneFormat*)&other;

    bool isEqual =
            fLocale == tzfmt->fLocale
            && fGMTPattern == tzfmt->fGMTPattern
            && fGMTZeroFormat == tzfmt->fGMTZeroFormat
            && *fTimeZoneNames == *tzfmt->fTimeZoneNames;

    for (int32_t i = 0; i < UTZFMT_PAT_COUNT && isEqual; i++) {
        isEqual = fGMTOffsetPatterns[i] == tzfmt->fGMTOffsetPatterns[i];
    }
    for (int32_t i = 0; i < 10 && isEqual; i++) {
        isEqual = fGMTOffsetDigits[i] == tzfmt->fGMTOffsetDigits[i];
    }
    // TODO
    // Check fTimeZoneGenericNames. For now,
    // if fTimeZoneNames is same, fTimeZoneGenericNames should
    // be also equivalent.
    return isEqual;
}

TimeZoneFormat*
TimeZoneFormat::clone() const {
    return new TimeZoneFormat(*this);
}

TimeZoneFormat* U_EXPORT2
TimeZoneFormat::createInstance(const Locale& locale, UErrorCode& status) {
    TimeZoneFormat* tzfmt = new TimeZoneFormat(locale, status);
    if (U_SUCCESS(status)) {
        return tzfmt;
    }
    delete tzfmt;
    return nullptr;
}

// ------------------------------------------------------------------
// Setter and Getter

const TimeZoneNames*
TimeZoneFormat::getTimeZoneNames() const {
    return (const TimeZoneNames*)fTimeZoneNames;
}

void
TimeZoneFormat::adoptTimeZoneNames(TimeZoneNames *tznames) {
    delete fTimeZoneNames;
    fTimeZoneNames = tznames;

    // TODO - We should also update fTimeZoneGenericNames
}

void
TimeZoneFormat::setTimeZoneNames(const TimeZoneNames &tznames) {
    delete fTimeZoneNames;
    fTimeZoneNames = tznames.clone();

    // TODO - We should also update fTimeZoneGenericNames
}

void
TimeZoneFormat::setDefaultParseOptions(uint32_t flags) {
    fDefParseOptionFlags = flags;
}

uint32_t
TimeZoneFormat::getDefaultParseOptions() const {
    return fDefParseOptionFlags;
}


UnicodeString& 
TimeZoneFormat::getGMTPattern(UnicodeString& pattern) const {
    return pattern.setTo(fGMTPattern);
}

void
TimeZoneFormat::setGMTPattern(const UnicodeString& pattern, UErrorCode& status) {
    initGMTPattern(pattern, status);
}

UnicodeString&
TimeZoneFormat::getGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, UnicodeString& pattern) const {
    return pattern.setTo(fGMTOffsetPatterns[type]);
}

void
TimeZoneFormat::setGMTOffsetPattern(UTimeZoneFormatGMTOffsetPatternType type, const UnicodeString& pattern, UErrorCode& status) {
    if (U_FAILURE(status)) {
        return;
    }
    if (pattern == fGMTOffsetPatterns[type]) {
        // No need to reset
        return;
    }

    OffsetFields required = FIELDS_HM;
    switch (type) {
    case UTZFMT_PAT_POSITIVE_H:
    case UTZFMT_PAT_NEGATIVE_H:
        required = FIELDS_H;
        break;
    case UTZFMT_PAT_POSITIVE_HM:
    case UTZFMT_PAT_NEGATIVE_HM:
        required = FIELDS_HM;
        break;
    case UTZFMT_PAT_POSITIVE_HMS:
    case UTZFMT_PAT_NEGATIVE_HMS:
        required = FIELDS_HMS;
        break;
    default:
        UPRV_UNREACHABLE_EXIT;
    }

    UVector* patternItems = parseOffsetPattern(pattern, required, status);
    if (patternItems == nullptr) {
        return;
    }

    fGMTOffsetPatterns[type].setTo(pattern);
    delete fGMTOffsetPatternItems[type];
    fGMTOffsetPatternItems[type] = patternItems;
    checkAbuttingHoursAndMinutes();
}

UnicodeString&
TimeZoneFormat::getGMTOffsetDigits(UnicodeString& digits) const {
    digits.remove();
    for (int32_t i = 0; i < 10; i++) {
        digits.append(fGMTOffsetDigits[i]);
    }
    return digits;
}

void
TimeZoneFormat::setGMTOffsetDigits(const UnicodeString& digits, UErrorCode& status) {
    if (U_FAILURE(status)) {
        return;
    }
    UChar32 digitArray[10];
    if (!toCodePoints(digits, digitArray, 10)) {
        status = U_ILLEGAL_ARGUMENT_ERROR;
        return;
    }
    uprv_memcpy(fGMTOffsetDigits, digitArray, sizeof(UChar32)*10);
}

UnicodeString&
TimeZoneFormat::getGMTZeroFormat(UnicodeString& gmtZeroFormat) const {
    return gmtZeroFormat.setTo(fGMTZeroFormat);
}

void
TimeZoneFormat::setGMTZeroFormat(const UnicodeString& gmtZeroFormat, UErrorCode&&nbsp;status) {
    if (U_SUCCESS(status)) {
        if (gmtZeroFormat.isEmpty()) {
            status = U_ILLEGAL_ARGUMENT_ERROR;
        } else if (gmtZeroFormat != fGMTZeroFormat) {
            fGMTZeroFormat.setTo(gmtZeroFormat);
        }
    }
}

// ------------------------------------------------------------------
// Format and Parse

UnicodeString&
TimeZoneFormat::format(UTimeZoneFormatStyle style, const TimeZone& tz, UDate date,
        UnicodeString& name, UTimeZoneFormatTimeType* timeType /* = nullptr */) const {
    if (timeType) {
        *timeType = UTZFMT_TIME_TYPE_UNKNOWN;
    }

    UBool noOffsetFormatFallback = false;

    switch (style) {
    case UTZFMT_STYLE_GENERIC_LOCATION:
        formatGeneric(tz, UTZGNM_LOCATION, date, name);
        break;
    case UTZFMT_STYLE_GENERIC_LONG:
        formatGeneric(tz, UTZGNM_LONG, date, name);
        break;
    case UTZFMT_STYLE_GENERIC_SHORT:
        formatGeneric(tz, UTZGNM_SHORT, date, name);
        break;
    case UTZFMT_STYLE_SPECIFIC_LONG:
        formatSpecific(tz, UTZNM_LONG_STANDARD, UTZNM_LONG_DAYLIGHT, date, name, timeType);
        break;
    case UTZFMT_STYLE_SPECIFIC_SHORT:
        formatSpecific(tz, UTZNM_SHORT_STANDARD, UTZNM_SHORT_DAYLIGHT, date, name, timeType);
        break;

    case UTZFMT_STYLE_ZONE_ID:
        tz.getID(name);
        noOffsetFormatFallback = true;
        break;
    case UTZFMT_STYLE_ZONE_ID_SHORT:
        {
            const char16_t* shortID = ZoneMeta::getShortID(tz);
            if (shortID == nullptr) {
                shortID = UNKNOWN_SHORT_ZONE_ID;
            }
            name.setTo(shortID, -1);
        }
        noOffsetFormatFallback = true;
        break;

    case UTZFMT_STYLE_EXEMPLAR_LOCATION:
        formatExemplarLocation(tz, name);
        noOffsetFormatFallback = true;
        break;

    default:
        // will be handled below
        break;
    }

    if (name.isEmpty() && !noOffsetFormatFallback) {
        UErrorCode status = U_ZERO_ERROR;
        int32_t rawOffset, dstOffset;
        tz.getOffset(date, false, rawOffset, dstOffset, status);
        int32_t offset = rawOffset + dstOffset;
        if (U_SUCCESS(status)) {
            switch (style) {
            case UTZFMT_STYLE_GENERIC_LOCATION:
            case UTZFMT_STYLE_GENERIC_LONG:
            case UTZFMT_STYLE_SPECIFIC_LONG:
            case UTZFMT_STYLE_LOCALIZED_GMT:
                formatOffsetLocalizedGMT(offset, name, status);
                break;

            case UTZFMT_STYLE_GENERIC_SHORT:
            case UTZFMT_STYLE_SPECIFIC_SHORT:
            case UTZFMT_STYLE_LOCALIZED_GMT_SHORT:
                formatOffsetShortLocalizedGMT(offset, name, status);
                break;

            case UTZFMT_STYLE_ISO_BASIC_SHORT:
                formatOffsetISO8601Basic(offset, truetruetrue, name, status);
                break;

            case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT:
                formatOffsetISO8601Basic(offset, falsetruetrue, name, status);
                break;

            case UTZFMT_STYLE_ISO_BASIC_FIXED:
                formatOffsetISO8601Basic(offset, truefalsetrue, name, status);
                break;

            case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED:
                formatOffsetISO8601Basic(offset, falsefalsetrue, name, status);
                break;

            case UTZFMT_STYLE_ISO_EXTENDED_FIXED:
                formatOffsetISO8601Extended(offset, truefalsetrue, name, status);
                break;

            case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED:
                formatOffsetISO8601Extended(offset, falsefalsetrue, name, status);
                break;

            case UTZFMT_STYLE_ISO_BASIC_FULL:
                formatOffsetISO8601Basic(offset, truefalsefalse, name, status);
                break;

            case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL:
                formatOffsetISO8601Basic(offset, falsefalsefalse, name, status);
                break;

            case UTZFMT_STYLE_ISO_EXTENDED_FULL:
                formatOffsetISO8601Extended(offset, truefalsefalse, name, status);
                break;

            case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL:
                formatOffsetISO8601Extended(offset, falsefalsefalse, name, status);
                break;

            default:
              // UTZFMT_STYLE_ZONE_ID, UTZFMT_STYLE_ZONE_ID_SHORT, UTZFMT_STYLE_EXEMPLAR_LOCATION
              break;
            }

            if (timeType) {
                *timeType = (dstOffset != 0) ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD;
            }
        }
    }

    return name;
}

UnicodeString&
TimeZoneFormat::format(const Formattable& obj, UnicodeString& appendTo,
        FieldPosition& pos, UErrorCode& status) const {
    if (U_FAILURE(status)) {
        return appendTo;
    }
    UDate date = Calendar::getNow();
    if (obj.getType() == Formattable::kObject) {
        const UObject* formatObj = obj.getObject();
        const TimeZone* tz = dynamic_cast<const TimeZone*>(formatObj);
        if (tz == nullptr) {
            const Calendar* cal = dynamic_cast<const Calendar*>(formatObj);
            if (cal != nullptr) {
                tz = &cal->getTimeZone();
                date = cal->getTime(status);
            }
        }
        if (tz != nullptr) {
            int32_t rawOffset, dstOffset;
            tz->getOffset(date, false, rawOffset, dstOffset, status);
            char16_t buf[ZONE_NAME_U16_MAX];
            UnicodeString result(buf, 0, UPRV_LENGTHOF(buf));
            formatOffsetLocalizedGMT(rawOffset + dstOffset, result, status);
            if (U_SUCCESS(status)) {
                appendTo.append(result);
                if (pos.getField() == UDAT_TIMEZONE_FIELD) {
                    pos.setBeginIndex(0);
                    pos.setEndIndex(result.length());
                }
            }
        }
    }
    return appendTo;
}

TimeZone*
TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos,
        UTimeZoneFormatTimeType* timeType /*= nullptr*/) const {
    return parse(style, text, pos, getDefaultParseOptions(), timeType);
}

TimeZone*
TimeZoneFormat::parse(UTimeZoneFormatStyle style, const UnicodeString& text, ParsePosition& pos,
        int32_t parseOptions, UTimeZoneFormatTimeType* timeType /* = nullptr */) const {
    if (timeType) {
        *timeType = UTZFMT_TIME_TYPE_UNKNOWN;
    }

    int32_t startIdx = pos.getIndex();
    int32_t maxPos = text.length();
    int32_t offset;

    // Styles using localized GMT format as fallback
    UBool fallbackLocalizedGMT = 
        (style == UTZFMT_STYLE_SPECIFIC_LONG || style == UTZFMT_STYLE_GENERIC_LONG || style == UTZFMT_STYLE_GENERIC_LOCATION);
    UBool fallbackShortLocalizedGMT =
        (style == UTZFMT_STYLE_SPECIFIC_SHORT || style == UTZFMT_STYLE_GENERIC_SHORT);

    int32_t evaluated = 0;  // bit flags representing already evaluated styles
    ParsePosition tmpPos(startIdx);

    int32_t parsedOffset = UNKNOWN_OFFSET;  // stores successfully parsed offset for later use
    int32_t parsedPos = -1;                 // stores successfully parsed offset position for later use

    // Try localized GMT format first if necessary
    if (fallbackLocalizedGMT || fallbackShortLocalizedGMT) {
        UBool hasDigitOffset = false;
        offset = parseOffsetLocalizedGMT(text, tmpPos, fallbackShortLocalizedGMT, &hasDigitOffset);
        if (tmpPos.getErrorIndex() == -1) {
            // Even when the input text was successfully parsed as a localized GMT format text,
            // we may still need to evaluate the specified style if -
            //   1) GMT zero format was used, and
            //   2) The input text was not completely processed
            if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
                pos.setIndex(tmpPos.getIndex());
                return createTimeZoneForOffset(offset);
            }
            parsedOffset = offset;
            parsedPos = tmpPos.getIndex();
        }
        // Note: For now, no distinction between long/short localized GMT format in the parser.
        // This might be changed in future.
        // evaluated |= (fallbackLocalizedGMT ? STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] : STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]);
        evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT] | STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT];
    }

    UErrorCode status = U_ZERO_ERROR;
    char16_t tzIDBuf[32];
    UnicodeString tzID(tzIDBuf, 0, UPRV_LENGTHOF(tzIDBuf));

    UBool parseTZDBAbbrev = ((parseOptions & UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS) != 0);

    // Try the specified style
    switch (style) {
    case UTZFMT_STYLE_LOCALIZED_GMT:
        {
            tmpPos.setIndex(startIdx);
            tmpPos.setErrorIndex(-1);

            offset = parseOffsetLocalizedGMT(text, tmpPos);
            if (tmpPos.getErrorIndex() == -1) {
                pos.setIndex(tmpPos.getIndex());
                return createTimeZoneForOffset(offset);
            }

            // Note: For now, no distinction between long/short localized GMT format in the parser.
            // This might be changed in future.
            evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT];

            break;
        }
    case UTZFMT_STYLE_LOCALIZED_GMT_SHORT:
        {
            tmpPos.setIndex(startIdx);
            tmpPos.setErrorIndex(-1);

            offset = parseOffsetShortLocalizedGMT(text, tmpPos);
            if (tmpPos.getErrorIndex() == -1) {
                pos.setIndex(tmpPos.getIndex());
                return createTimeZoneForOffset(offset);
            }

            // Note: For now, no distinction between long/short localized GMT format in the parser.
            // This might be changed in future.
            evaluated |= STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT];

            break;
        }
    case UTZFMT_STYLE_ISO_BASIC_SHORT:
    case UTZFMT_STYLE_ISO_BASIC_FIXED:
    case UTZFMT_STYLE_ISO_BASIC_FULL:
    case UTZFMT_STYLE_ISO_EXTENDED_FIXED:
    case UTZFMT_STYLE_ISO_EXTENDED_FULL:
        {
            tmpPos.setIndex(startIdx);
            tmpPos.setErrorIndex(-1);

            offset = parseOffsetISO8601(text, tmpPos);
            if (tmpPos.getErrorIndex() == -1) {
                pos.setIndex(tmpPos.getIndex());
                return createTimeZoneForOffset(offset);
            }

            break;
        }

    case UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT:
    case UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED:
    case UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL:
    case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED:
    case UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL:
        {
            tmpPos.setIndex(startIdx);
            tmpPos.setErrorIndex(-1);

            // Exclude the case of UTC Indicator "Z" here
            UBool hasDigitOffset = false;
            offset = parseOffsetISO8601(text, tmpPos, false, &hasDigitOffset);
            if (tmpPos.getErrorIndex() == -1 && hasDigitOffset) {
                pos.setIndex(tmpPos.getIndex());
                return createTimeZoneForOffset(offset);
            }

            break;
        }

    case UTZFMT_STYLE_SPECIFIC_LONG:
    case UTZFMT_STYLE_SPECIFIC_SHORT:
        {
            // Specific styles
            int32_t nameTypes = 0;
            if (style == UTZFMT_STYLE_SPECIFIC_LONG) {
                nameTypes = (UTZNM_LONG_STANDARD | UTZNM_LONG_DAYLIGHT);
            } else {
                U_ASSERT(style == UTZFMT_STYLE_SPECIFIC_SHORT);
                nameTypes = (UTZNM_SHORT_STANDARD | UTZNM_SHORT_DAYLIGHT);
            }
            LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, nameTypes, status));
            if (U_FAILURE(status)) {
                pos.setErrorIndex(startIdx);
                return nullptr;
            }
            if (!specificMatches.isNull()) {
                int32_t matchIdx = -1;
                int32_t matchPos = -1;
                for (int32_t i = 0; i < specificMatches->size(); i++) {
                    matchPos  = startIdx + specificMatches->getMatchLengthAt(i);
                    if (matchPos > parsedPos) {
                        matchIdx = i;
                        parsedPos = matchPos;
                    }
                }
                if (matchIdx >= 0) {
                    if (timeType) {
                        *timeType = getTimeType(specificMatches->getNameTypeAt(matchIdx));
                    }
                    pos.setIndex(matchPos);
                    getTimeZoneID(specificMatches.getAlias(), matchIdx, tzID);
                    U_ASSERT(!tzID.isEmpty());
                    return TimeZone::createTimeZone(tzID);
                }
            }

            if (parseTZDBAbbrev && style == UTZFMT_STYLE_SPECIFIC_SHORT) {
                U_ASSERT((nameTypes & UTZNM_SHORT_STANDARD) != 0);
                U_ASSERT((nameTypes & UTZNM_SHORT_DAYLIGHT) != 0);

                const TZDBTimeZoneNames *tzdbTimeZoneNames = getTZDBTimeZoneNames(status);
                if (U_SUCCESS(status)) {
                    LocalPointer<TimeZoneNames::MatchInfoCollection> tzdbNameMatches(
                        tzdbTimeZoneNames->find(text, startIdx, nameTypes, status));
                    if (U_FAILURE(status)) {
                        pos.setErrorIndex(startIdx);
                        return nullptr;
                    }
                    if (!tzdbNameMatches.isNull()) {
                        int32_t matchIdx = -1;
                        int32_t matchPos = -1;
                        for (int32_t i = 0; i < tzdbNameMatches->size(); i++) {
                            matchPos = startIdx + tzdbNameMatches->getMatchLengthAt(i);
                            if (matchPos > parsedPos) {
                                matchIdx = i;
                                parsedPos = matchPos;
                            }
                        }
                        if (matchIdx >= 0) {
                            if (timeType) {
                                *timeType = getTimeType(tzdbNameMatches->getNameTypeAt(matchIdx));
                            }
                            pos.setIndex(matchPos);
                            getTimeZoneID(tzdbNameMatches.getAlias(), matchIdx, tzID);
                            U_ASSERT(!tzID.isEmpty());
                            return TimeZone::createTimeZone(tzID);
                        }
                    }
                }
            }
            break;
        }
    case UTZFMT_STYLE_GENERIC_LONG:
    case UTZFMT_STYLE_GENERIC_SHORT:
    case UTZFMT_STYLE_GENERIC_LOCATION:
        {
            int32_t genericNameTypes = 0;
            switch (style) {
            case UTZFMT_STYLE_GENERIC_LOCATION:
                genericNameTypes = UTZGNM_LOCATION;
                break;

            case UTZFMT_STYLE_GENERIC_LONG:
                genericNameTypes = UTZGNM_LONG | UTZGNM_LOCATION;
                break;

            case UTZFMT_STYLE_GENERIC_SHORT:
                genericNameTypes = UTZGNM_SHORT | UTZGNM_LOCATION;
                break;

            default:
                UPRV_UNREACHABLE_EXIT;
            }

            int32_t len = 0;
            UTimeZoneFormatTimeType tt = UTZFMT_TIME_TYPE_UNKNOWN;
            const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status);
            if (U_SUCCESS(status)) {
                len = gnames->findBestMatch(text, startIdx, genericNameTypes, tzID, tt, status);
            }
            if (U_FAILURE(status)) {
                pos.setErrorIndex(startIdx);
                return nullptr;
            }
            if (len > 0) {
                // Found a match
                if (timeType) {
                    *timeType = tt;
                }
                pos.setIndex(startIdx + len);
                U_ASSERT(!tzID.isEmpty());
                return TimeZone::createTimeZone(tzID);
            }

            break;
        }
    case UTZFMT_STYLE_ZONE_ID:
        {
            tmpPos.setIndex(startIdx);
            tmpPos.setErrorIndex(-1);

            parseZoneID(text, tmpPos, tzID);
            if (tmpPos.getErrorIndex() == -1) {
                pos.setIndex(tmpPos.getIndex());
                return TimeZone::createTimeZone(tzID);
            }
            break;
        }
    case UTZFMT_STYLE_ZONE_ID_SHORT:
        {
            tmpPos.setIndex(startIdx);
            tmpPos.setErrorIndex(-1);

            parseShortZoneID(text, tmpPos, tzID);
            if (tmpPos.getErrorIndex() == -1) {
                pos.setIndex(tmpPos.getIndex());
                return TimeZone::createTimeZone(tzID);
            }
            break;
        }
    case UTZFMT_STYLE_EXEMPLAR_LOCATION:
        {
            tmpPos.setIndex(startIdx);
            tmpPos.setErrorIndex(-1);

            parseExemplarLocation(text, tmpPos, tzID);
            if (tmpPos.getErrorIndex() == -1) {
                pos.setIndex(tmpPos.getIndex());
                return TimeZone::createTimeZone(tzID);
            }
            break;
        }
    }
    evaluated |= STYLE_PARSE_FLAGS[style];


    if (parsedPos > startIdx) {
        // When the specified style is one of SPECIFIC_XXX or GENERIC_XXX, we tried to parse the input
        // as localized GMT format earlier. If parsedOffset is positive, it means it was successfully
        // parsed as localized GMT format, but offset digits were not detected (more specifically, GMT
        // zero format). Then, it tried to find a match within the set of display names, but could not
        // find a match. At this point, we can safely assume the input text contains the localized
        // GMT format.
        U_ASSERT(parsedOffset != UNKNOWN_OFFSET);
        pos.setIndex(parsedPos);
        return createTimeZoneForOffset(parsedOffset);
    }

    // Failed to parse the input text as the time zone format in the specified style.
    // Check the longest match among other styles below.
    char16_t parsedIDBuf[32];
    UnicodeString parsedID(parsedIDBuf, 0, UPRV_LENGTHOF(parsedIDBuf));
    UTimeZoneFormatTimeType parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;

    U_ASSERT(parsedPos < 0);
    U_ASSERT(parsedOffset == UNKNOWN_OFFSET);

    // ISO 8601
    if (parsedPos < maxPos &&
        ((evaluated & ISO_Z_STYLE_FLAG) == 0 || (evaluated & ISO_LOCAL_STYLE_FLAG) == 0)) {
        tmpPos.setIndex(startIdx);
        tmpPos.setErrorIndex(-1);

        UBool hasDigitOffset = false;
        offset = parseOffsetISO8601(text, tmpPos, false, &hasDigitOffset);
        if (tmpPos.getErrorIndex() == -1) {
            if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
                pos.setIndex(tmpPos.getIndex());
                return createTimeZoneForOffset(offset);
            }
            // Note: When ISO 8601 format contains offset digits, it should not
            // collide with other formats. However, ISO 8601 UTC format "Z" (single letter)
            // may collide with other names. In this case, we need to evaluate other names.
            if (parsedPos < tmpPos.getIndex()) {
                parsedOffset = offset;
                parsedID.setToBogus();
                parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
                parsedPos = tmpPos.getIndex();
                U_ASSERT(parsedPos == startIdx + 1);    // only when "Z" is used
            }
        }
    }

    // Localized GMT format
    if (parsedPos < maxPos &&
        (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT]) == 0) {
        tmpPos.setIndex(startIdx);
        tmpPos.setErrorIndex(-1);

        UBool hasDigitOffset = false;
        offset = parseOffsetLocalizedGMT(text, tmpPos, false, &hasDigitOffset);
        if (tmpPos.getErrorIndex() == -1) {
            if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
                pos.setIndex(tmpPos.getIndex());
                return createTimeZoneForOffset(offset);
            }
            // Evaluate other names - see the comment earlier in this method.
            if (parsedPos < tmpPos.getIndex()) {
                parsedOffset = offset;
                parsedID.setToBogus();
                parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
                parsedPos = tmpPos.getIndex();
            }
        }
    }

    if (parsedPos < maxPos &&
        (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_LOCALIZED_GMT_SHORT]) == 0) {
        tmpPos.setIndex(startIdx);
        tmpPos.setErrorIndex(-1);

        UBool hasDigitOffset = false;
        offset = parseOffsetLocalizedGMT(text, tmpPos, true, &hasDigitOffset);
        if (tmpPos.getErrorIndex() == -1) {
            if (tmpPos.getIndex() == maxPos || hasDigitOffset) {
                pos.setIndex(tmpPos.getIndex());
                return createTimeZoneForOffset(offset);
            }
            // Evaluate other names - see the comment earlier in this method.
            if (parsedPos < tmpPos.getIndex()) {
                parsedOffset = offset;
                parsedID.setToBogus();
                parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
                parsedPos = tmpPos.getIndex();
            }
        }
    }

    // When ParseOption.ALL_STYLES is available, we also try to look all possible display names and IDs.
    // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never
    // used for America/New_York. With parseAllStyles true, this code parses "EST"
    // as America/New_York.

    // Note: Adding all possible names into the trie used by the implementation is quite heavy operation,
    // which we want to avoid normally (note that we cache the trie, so this is applicable to the
    // first time only as long as the cache does not expire).

    if (parseOptions & UTZFMT_PARSE_OPTION_ALL_STYLES) {
        // Try all specific names and exemplar location names
        if (parsedPos < maxPos) {
            LocalPointer<TimeZoneNames::MatchInfoCollection> specificMatches(fTimeZoneNames->find(text, startIdx, ALL_SIMPLE_NAME_TYPES, status));
            if (U_FAILURE(status)) {
                pos.setErrorIndex(startIdx);
                return nullptr;
            }
            int32_t specificMatchIdx = -1;
            int32_t matchPos = -1;
            if (!specificMatches.isNull()) {
                for (int32_t i = 0; i < specificMatches->size(); i++) {
                    if (startIdx + specificMatches->getMatchLengthAt(i) > matchPos) {
                        specificMatchIdx = i;
                        matchPos = startIdx + specificMatches->getMatchLengthAt(i);
                    }
                }
            }
            if (parsedPos < matchPos) {
                U_ASSERT(specificMatchIdx >= 0);
                parsedPos = matchPos;
                getTimeZoneID(specificMatches.getAlias(), specificMatchIdx, parsedID);
                parsedTimeType = getTimeType(specificMatches->getNameTypeAt(specificMatchIdx));
                parsedOffset = UNKNOWN_OFFSET;
            }
        }
        if (parseTZDBAbbrev && parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_SPECIFIC_SHORT]) == 0) {
            const TZDBTimeZoneNames *tzdbTimeZoneNames = getTZDBTimeZoneNames(status);
            if (U_SUCCESS(status)) {
                LocalPointer<TimeZoneNames::MatchInfoCollection> tzdbNameMatches(
                    tzdbTimeZoneNames->find(text, startIdx, ALL_SIMPLE_NAME_TYPES, status));
                if (U_FAILURE(status)) {
                    pos.setErrorIndex(startIdx);
                    return nullptr;
                }
                int32_t tzdbNameMatchIdx = -1;
                int32_t matchPos = -1;
                if (!tzdbNameMatches.isNull()) {
                    for (int32_t i = 0; i < tzdbNameMatches->size(); i++) {
                        if (startIdx + tzdbNameMatches->getMatchLengthAt(i) > matchPos) {
                            tzdbNameMatchIdx = i;
                            matchPos = startIdx + tzdbNameMatches->getMatchLengthAt(i);
                        }
                    }
                }
                if (parsedPos < matchPos) {
                    U_ASSERT(tzdbNameMatchIdx >= 0);
                    parsedPos = matchPos;
                    getTimeZoneID(tzdbNameMatches.getAlias(), tzdbNameMatchIdx, parsedID);
                    parsedTimeType = getTimeType(tzdbNameMatches->getNameTypeAt(tzdbNameMatchIdx));
                    parsedOffset = UNKNOWN_OFFSET;
                }
            }
        }
        // Try generic names
        if (parsedPos < maxPos) {
            int32_t genMatchLen = -1;
            UTimeZoneFormatTimeType tt = UTZFMT_TIME_TYPE_UNKNOWN;

            const TimeZoneGenericNames *gnames = getTimeZoneGenericNames(status);
            if (U_SUCCESS(status)) {
                genMatchLen = gnames->findBestMatch(text, startIdx, ALL_GENERIC_NAME_TYPES, tzID, tt, status);
            }
            if (U_FAILURE(status)) {
                pos.setErrorIndex(startIdx);
                return nullptr;
            }

            if (genMatchLen > 0 && parsedPos < startIdx + genMatchLen) {
                parsedPos = startIdx + genMatchLen;
                parsedID.setTo(tzID);
                parsedTimeType = tt;
                parsedOffset = UNKNOWN_OFFSET;
            }
        }

        // Try time zone ID
        if (parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_ZONE_ID]) == 0) {
            tmpPos.setIndex(startIdx);
            tmpPos.setErrorIndex(-1);

            parseZoneID(text, tmpPos, tzID);
            if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
                parsedPos = tmpPos.getIndex();
                parsedID.setTo(tzID);
                parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
                parsedOffset = UNKNOWN_OFFSET;
            }
        }
        // Try short time zone ID
        if (parsedPos < maxPos && (evaluated & STYLE_PARSE_FLAGS[UTZFMT_STYLE_ZONE_ID]) == 0) {
            tmpPos.setIndex(startIdx);
            tmpPos.setErrorIndex(-1);

            parseShortZoneID(text, tmpPos, tzID);
            if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
                parsedPos = tmpPos.getIndex();
                parsedID.setTo(tzID);
                parsedTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
                parsedOffset = UNKNOWN_OFFSET;
            }
        }
    }

    if (parsedPos > startIdx) {
        // Parsed successfully
        TimeZone* parsedTZ;
        if (parsedID.length() > 0) {
            parsedTZ = TimeZone::createTimeZone(parsedID);
        } else {
            U_ASSERT(parsedOffset != UNKNOWN_OFFSET);
            parsedTZ = createTimeZoneForOffset(parsedOffset);
        }
        if (timeType) {
            *timeType = parsedTimeType;
        }
        pos.setIndex(parsedPos);
        return parsedTZ;
    }

    pos.setErrorIndex(startIdx);
    return nullptr;
}

void
TimeZoneFormat::parseObject(const UnicodeString& source, Formattable& result,
        ParsePosition& parse_pos) const {
    result.adoptObject(parse(UTZFMT_STYLE_GENERIC_LOCATION, source, parse_pos, UTZFMT_PARSE_OPTION_ALL_STYLES));
}


// ------------------------------------------------------------------
// Private zone name format/parse implementation

UnicodeString&
TimeZoneFormat::formatGeneric(const TimeZone& tz, int32_t genType, UDate date, UnicodeString& name) const {
    UErrorCode status = U_ZERO_ERROR;
    const TimeZoneGenericNames* gnames = getTimeZoneGenericNames(status);
    if (U_FAILURE(status)) {
        name.setToBogus();
        return name;
    }

    if (genType == UTZGNM_LOCATION) {
        const char16_t* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);
        if (canonicalID == nullptr) {
            name.setToBogus();
            return name;
        }
        return gnames->getGenericLocationName(UnicodeString(true, canonicalID, -1), name);
    }
    return gnames->getDisplayName(tz, static_cast<UTimeZoneGenericNameType>(genType), date, name);
}

UnicodeString&
TimeZoneFormat::formatSpecific(const TimeZone& tz, UTimeZoneNameType stdType, UTimeZoneNameType dstType,
        UDate date, UnicodeString& name, UTimeZoneFormatTimeType *timeType) const {
    if (fTimeZoneNames == nullptr) {
        name.setToBogus();
        return name;
    }

    UErrorCode status = U_ZERO_ERROR;
    UBool isDaylight = tz.inDaylightTime(date, status);
    const char16_t* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);

    if (U_FAILURE(status) || canonicalID == nullptr) {
        name.setToBogus();
        return name;
    }

    if (isDaylight) {
        fTimeZoneNames->getDisplayName(UnicodeString(true, canonicalID, -1), dstType, date, name);
    } else {
        fTimeZoneNames->getDisplayName(UnicodeString(true, canonicalID, -1), stdType, date, name);
    }

    if (timeType && !name.isEmpty()) {
        *timeType = isDaylight ? UTZFMT_TIME_TYPE_DAYLIGHT : UTZFMT_TIME_TYPE_STANDARD;
    }
    return name;
}

const TimeZoneGenericNames*
TimeZoneFormat::getTimeZoneGenericNames(UErrorCode& status) const {
    if (U_FAILURE(status)) {
        return nullptr;
    }

    umtx_lock(&gLock);
    if (fTimeZoneGenericNames == nullptr) {
        TimeZoneFormat *nonConstThis = const_cast<TimeZoneFormat *>(this);
        nonConstThis->fTimeZoneGenericNames = TimeZoneGenericNames::createInstance(fLocale, status);
    }
    umtx_unlock(&gLock);

    return fTimeZoneGenericNames;
}

const TZDBTimeZoneNames*
TimeZoneFormat::getTZDBTimeZoneNames(UErrorCode& status) const {
    if (U_FAILURE(status)) {
        return nullptr;
    }

    umtx_lock(&gLock);
    if (fTZDBTimeZoneNames == nullptr) {
        TZDBTimeZoneNames *tzdbNames = new TZDBTimeZoneNames(fLocale);
        if (tzdbNames == nullptr) {
            status = U_MEMORY_ALLOCATION_ERROR;
        } else {
            TimeZoneFormat *nonConstThis = const_cast<TimeZoneFormat *>(this);
            nonConstThis->fTZDBTimeZoneNames = tzdbNames;
        }
    }
    umtx_unlock(&gLock);

    return fTZDBTimeZoneNames;
}

UnicodeString&
TimeZoneFormat::formatExemplarLocation(const TimeZone& tz, UnicodeString& name) const {
    char16_t locationBuf[ZONE_NAME_U16_MAX];
    UnicodeString location(locationBuf, 0, UPRV_LENGTHOF(locationBuf));
    const char16_t* canonicalID = ZoneMeta::getCanonicalCLDRID(tz);

    if (canonicalID) {
        fTimeZoneNames->getExemplarLocationName(UnicodeString(true, canonicalID, -1), location);
    }
    if (location.length() > 0) {
        name.setTo(location);
    } else {
        // Use "unknown" location
        fTimeZoneNames->getExemplarLocationName(UnicodeString(true, UNKNOWN_ZONE_ID, -1), location);
        if (location.length() > 0) {
            name.setTo(location);
        } else {
            // last resort
            name.setTo(UNKNOWN_LOCATION, -1);
        }
    }
    return name;
}


// ------------------------------------------------------------------
// Zone offset format and parse

UnicodeString&
TimeZoneFormat::formatOffsetISO8601Basic(int32_t offset, UBool useUtcIndicator, UBool isShort, UBool ignoreSeconds,
        UnicodeString& result, UErrorCode& status) const {
    return formatOffsetISO8601(offset, true, useUtcIndicator, isShort, ignoreSeconds, result, status);
}

UnicodeString&
TimeZoneFormat::formatOffsetISO8601Extended(int32_t offset, UBool useUtcIndicator, UBool isShort, UBool ignoreSeconds,
        UnicodeString& result, UErrorCode& status) const {
    return formatOffsetISO8601(offset, false, useUtcIndicator, isShort, ignoreSeconds, result, status);
}

UnicodeString&
TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset, UnicodeString& result, UErrorCode& status) const {
    return formatOffsetLocalizedGMT(offset, false, result, status);
}

UnicodeString&
TimeZoneFormat::formatOffsetShortLocalizedGMT(int32_t offset, UnicodeString& result, UErrorCode& status) const {
    return formatOffsetLocalizedGMT(offset, true, result, status);
}

int32_t
TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& ;pos) const {
    return parseOffsetISO8601(text, pos, false);
}

int32_t
TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos) const {
    return parseOffsetLocalizedGMT(text, pos, false, nullptr);
}

int32_t
TimeZoneFormat::parseOffsetShortLocalizedGMT(const UnicodeString& text, ParsePosition& pos) const {
    return parseOffsetLocalizedGMT(text, pos, true, nullptr);
}

// ------------------------------------------------------------------
// Private zone offset format/parse implementation

UnicodeString&
TimeZoneFormat::formatOffsetISO8601(int32_t offset, UBool isBasic, UBool useUtcIndicator,
        UBool isShort, UBool ignoreSeconds, UnicodeString& result, UErrorCode& status) const {
    if (U_FAILURE(status)) {
        result.setToBogus();
        return result;
    }
    int32_t absOffset = offset < 0 ? -offset : offset;
    if (useUtcIndicator && (absOffset < MILLIS_PER_SECOND || (ignoreSeconds && absOffset < MILLIS_PER_MINUTE))) {
        result.setTo(ISO8601_UTC);
        return result;
    }

    OffsetFields minFields = isShort ? FIELDS_H : FIELDS_HM;
    OffsetFields maxFields = ignoreSeconds ? FIELDS_HM : FIELDS_HMS;
    char16_t sep = isBasic ? 0 : ISO8601_SEP;

    // Note: FIELDS_HMS as maxFields is a CLDR/ICU extension. ISO 8601 specification does
    // not support seconds field.

    if (absOffset >= MAX_OFFSET) {
        result.setToBogus();
        status = U_ILLEGAL_ARGUMENT_ERROR;
        return result;
    }

    int fields[3];
    fields[0] = absOffset / MILLIS_PER_HOUR;
    absOffset = absOffset % MILLIS_PER_HOUR;
    fields[1] = absOffset / MILLIS_PER_MINUTE;
    absOffset = absOffset % MILLIS_PER_MINUTE;
    fields[2] = absOffset / MILLIS_PER_SECOND;

    U_ASSERT(fields[0] >= 0 && fields[0] <= MAX_OFFSET_HOUR);
    U_ASSERT(fields[1] >= 0 && fields[1] <= MAX_OFFSET_MINUTE);
    U_ASSERT(fields[2] >= 0 && fields[2] <= MAX_OFFSET_SECOND);

    int32_t lastIdx = maxFields;
    while (lastIdx > minFields) {
        if (fields[lastIdx] != 0) {
            break;
        }
        lastIdx--;
    }

    char16_t sign = PLUS;
    if (offset < 0) {
        // if all output fields are 0s, do not use negative sign
        for (int32_t idx = 0; idx <= lastIdx; idx++) {
            if (fields[idx] != 0) {
                sign = MINUS;
                break;
            }
        }
    }
    result.setTo(sign);

    for (int32_t idx = 0; idx <= lastIdx; idx++) {
        if (sep && idx != 0) {
            result.append(sep);
        }
        result.append(static_cast<char16_t>(0x0030 + fields[idx] / 10));
        result.append(static_cast<char16_t>(0x0030 + fields[idx] % 10));
    }

    return result;
}

UnicodeString&
TimeZoneFormat::formatOffsetLocalizedGMT(int32_t offset, UBool isShort, UnicodeString& result, UErrorCode& status) const {
    if (U_FAILURE(status)) {
        result.setToBogus();
        return result;
    }
    if (offset <= -MAX_OFFSET || offset >= MAX_OFFSET) {
        result.setToBogus();
        status = U_ILLEGAL_ARGUMENT_ERROR;
        return result;
    }

    if (offset == 0) {
        result.setTo(fGMTZeroFormat);
        return result;
    }

    UBool positive = true;
    if (offset < 0) {
        offset = -offset;
        positive = false;
    }

    int32_t offsetH = offset / MILLIS_PER_HOUR;
    offset = offset % MILLIS_PER_HOUR;
    int32_t offsetM = offset / MILLIS_PER_MINUTE;
    offset = offset % MILLIS_PER_MINUTE;
    int32_t offsetS = offset / MILLIS_PER_SECOND;

    U_ASSERT(offsetH <= MAX_OFFSET_HOUR && offsetM <= MAX_OFFSET_MINUTE && offsetS <= MAX_OFFSET_SECOND);

    const UVector* offsetPatternItems = nullptr;
    if (positive) {
        if (offsetS != 0) {
            offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HMS];
        } else if (offsetM != 0 || !isShort) {
            offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_HM];
        } else {
            offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_POSITIVE_H];
        }
    } else {
        if (offsetS != 0) {
            offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HMS];
        } else if (offsetM != 0 || !isShort) {
            offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_HM];
        } else {
            offsetPatternItems = fGMTOffsetPatternItems[UTZFMT_PAT_NEGATIVE_H];
        }
    }

    U_ASSERT(offsetPatternItems != nullptr);

    // Building the GMT format string
    result.setTo(fGMTPatternPrefix);

    for (int32_t i = 0; i < offsetPatternItems->size(); i++) {
        const GMTOffsetField* item = static_cast<GMTOffsetField*>(offsetPatternItems->elementAt(i));
        GMTOffsetField::FieldType type = item->getType();

        switch (type) {
        case GMTOffsetField::TEXT:
            result.append(item->getPatternText(), -1);
            break;

        case GMTOffsetField::HOUR:
            appendOffsetDigits(result, offsetH, (isShort ? 1 : 2));
            break;

        case GMTOffsetField::MINUTE:
            appendOffsetDigits(result, offsetM, 2);
            break;

        case GMTOffsetField::SECOND:
            appendOffsetDigits(result, offsetS, 2);
            break;
        }
    }

    result.append(fGMTPatternSuffix);
    return result;
}

int32_t
TimeZoneFormat::parseOffsetISO8601(const UnicodeString& text, ParsePosition& ;pos, UBool extendedOnly, UBool* hasDigitOffset /* = nullptr */) const {
    if (hasDigitOffset) {
        *hasDigitOffset = false;
    }
    int32_t start = pos.getIndex();
    if (start >= text.length()) {
        pos.setErrorIndex(start);
        return 0;
    }

    char16_t firstChar = text.charAt(start);
    if (firstChar == ISO8601_UTC || firstChar == static_cast<char16_t>(ISO8601_UTC + 0x20)) {
        // "Z" (or "z") - indicates UTC
        pos.setIndex(start + 1);
        return 0;
    }

    int32_t sign = 1;
    if (firstChar == PLUS) {
        sign = 1;
    } else if (firstChar == MINUS) {
        sign = -1;
    } else {
        // Not an ISO 8601 offset string
        pos.setErrorIndex(start);
        return 0;
    }
    ParsePosition posOffset(start + 1);
    int32_t offset = parseAsciiOffsetFields(text, posOffset, ISO8601_SEP, FIELDS_H, FIELDS_HMS);
    if (posOffset.getErrorIndex() == -1 && !extendedOnly && (posOffset.getIndex() - start <= 3)) {
        // If the text is successfully parsed as extended format with the options above, it can be also parsed
        // as basic format. For example, "0230" can be parsed as offset 2:00 (only first digits are valid for
        // extended format), but it can be parsed as offset 2:30 with basic format. We use longer result.
        ParsePosition posBasic(start + 1);
        int32_t tmpOffset = parseAbuttingAsciiOffsetFields(text, posBasic, FIELDS_H, FIELDS_HMS, false);
        if (posBasic.getErrorIndex() == -1 && posBasic.getIndex() > posOffset.getIndex()) {
            offset = tmpOffset;
            posOffset.setIndex(posBasic.getIndex());
        }
    }

    if (posOffset.getErrorIndex() != -1) {
        pos.setErrorIndex(start);
        return 0;
    }

    pos.setIndex(posOffset.getIndex());
    if (hasDigitOffset) {
        *hasDigitOffset = true;
    }
    return sign * offset;
}

int32_t
TimeZoneFormat::parseOffsetLocalizedGMT(const UnicodeString& text, ParsePosition& pos, UBool isShort, UBool* hasDigitOffset) const {
    int32_t start = pos.getIndex();
    int32_t offset = 0;
    int32_t parsedLength = 0;

    if (hasDigitOffset) {
        *hasDigitOffset = false;
    }

    offset = parseOffsetLocalizedGMTPattern(text, start, isShort, parsedLength);

    // For now, parseOffsetLocalizedGMTPattern handles both long and short
    // formats, no matter isShort is true or false. This might be changed in future
    // when strict parsing is necessary, or different set of patterns are used for
    // short/long formats.
#if 0
    if (parsedLength == 0) {
        offset = parseOffsetLocalizedGMTPattern(text, start, !isShort, parsedLength);
    }
#endif

    if (parsedLength > 0) {
        if (hasDigitOffset) {
            *hasDigitOffset = true;
        }
        pos.setIndex(start + parsedLength);
        return offset;
    }

    // Try the default patterns
    offset = parseOffsetDefaultLocalizedGMT(text, start, parsedLength);
    if (parsedLength > 0) {
        if (hasDigitOffset) {
            *hasDigitOffset = true;
        }
        pos.setIndex(start + parsedLength);
        return offset;
    }

    // Check if this is a GMT zero format
    if (text.caseCompare(start, fGMTZeroFormat.length(), fGMTZeroFormat, 0) == 0) {
        pos.setIndex(start + fGMTZeroFormat.length());
        return 0;
    }

    // Check if this is a default GMT zero format
    for (int32_t i = 0; ALT_GMT_STRINGS[i][0] != 0; i++) {
        const char16_t* defGMTZero = ALT_GMT_STRINGS[i];
        int32_t defGMTZeroLen = u_strlen(defGMTZero);
        if (text.caseCompare(start, defGMTZeroLen, defGMTZero, 0) == 0) {
            pos.setIndex(start + defGMTZeroLen);
            return 0;
        }
    }

    // Nothing matched
    pos.setErrorIndex(start);
    return 0;
}

int32_t
TimeZoneFormat::parseOffsetLocalizedGMTPattern(const UnicodeString& text, int32_t start, UBool /*isShort*/, int32_t& parsedLen) const {
    int32_t idx = start;
    int32_t offset = 0;
    UBool parsed = false;

    do {
        // Prefix part
        int32_t len = fGMTPatternPrefix.length();
        if (len > 0 && text.caseCompare(idx, len, fGMTPatternPrefix, 0) != 0) {
            // prefix match failed
            break;
        }
        idx += len;

        // Offset part
        offset = parseOffsetFields(text, idx, false, len);
        if (len == 0) {
            // offset field match failed
            break;
        }
        idx += len;

        len = fGMTPatternSuffix.length();
        if (len > 0 && text.caseCompare(idx, len, fGMTPatternSuffix, 0) != 0) {
            // no suffix match
            break;
        }
        idx += len;
        parsed = true;
    } while (false);

    parsedLen = parsed ? idx - start : 0;
    return offset;
}

int32_t
TimeZoneFormat::parseOffsetFields(const UnicodeString& text, int32_t start, UBool /*isShort*/, int32_t& parsedLen) const {
    int32_t outLen = 0;
    int32_t offset = 0;
    int32_t sign = 1;

    parsedLen = 0;

    int32_t offsetH, offsetM, offsetS;
    offsetH = offsetM = offsetS = 0;

    for (int32_t patidx = 0; PARSE_GMT_OFFSET_TYPES[patidx] >= 0; patidx++) {
        int32_t gmtPatType = PARSE_GMT_OFFSET_TYPES[patidx];
        UVector* items = fGMTOffsetPatternItems[gmtPatType];
        U_ASSERT(items != nullptr);

        outLen = parseOffsetFieldsWithPattern(text, start, items, false, offsetH, offsetM, offsetS);
        if (outLen > 0) {
            sign = (gmtPatType == UTZFMT_PAT_POSITIVE_H || gmtPatType == UTZFMT_PAT_POSITIVE_HM || gmtPatType == UTZFMT_PAT_POSITIVE_HMS) ?
                1 : -1;
            break;
        }
    }

    if (outLen > 0 && fAbuttingOffsetHoursAndMinutes) {
        // When hours field is sabutting minutes field,
        // the parse result above may not be appropriate.
        // For example, "01020" is parsed as 01:02: above,
        // but it should be parsed as 00:10:20.
        int32_t tmpLen = 0;
        int32_t tmpSign = 1;
        int32_t tmpH = 0;
        int32_t tmpM = 0;
        int32_t tmpS = 0;

        for (int32_t patidx = 0; PARSE_GMT_OFFSET_TYPES[patidx] >= 0; patidx++) {
            int32_t gmtPatType = PARSE_GMT_OFFSET_TYPES[patidx];
            UVector* items = fGMTOffsetPatternItems[gmtPatType];
            U_ASSERT(items != nullptr);

            // forcing parse to use single hour digit
            tmpLen = parseOffsetFieldsWithPattern(text, start, items, true, tmpH, tmpM, tmpS);
            if (tmpLen > 0) {
                tmpSign = (gmtPatType == UTZFMT_PAT_POSITIVE_H || gmtPatType == UTZFMT_PAT_POSITIVE_HM || gmtPatType == UTZFMT_PAT_POSITIVE_HMS) ?
                    1 : -1;
                break;
            }
        }
        if (tmpLen > outLen) {
            // Better parse result with single hour digit
            outLen = tmpLen;
            sign = tmpSign;
            offsetH = tmpH;
            offsetM = tmpM;
            offsetS = tmpS;
        }
    }

    if (outLen > 0) {
        offset = ((((offsetH * 60) + offsetM) * 60) + offsetS) * 1000 * sign;
        parsedLen = outLen;
    }

    return offset;
}

int32_t
TimeZoneFormat::parseOffsetFieldsWithPattern(const UnicodeString& text, int32_t start,
        UVector* patternItems, UBool forceSingleHourDigit, int32_t& hour, int32_t& min, int32_t& sec) const {
    UBool failed = false;
    int32_t offsetH, offsetM, offsetS;
    offsetH = offsetM = offsetS = 0;
    int32_t idx = start;

    for (int32_t i = 0; i < patternItems->size(); i++) {
        int32_t len = 0;
        const GMTOffsetField* field = static_cast<const GMTOffsetField*>(patternItems->elementAt(i));
        GMTOffsetField::FieldType fieldType = field->getType();
        if (fieldType == GMTOffsetField::TEXT) {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=91 H=95 G=92

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.