intl/icu/source/i18n/timezone.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/intl/icu/source/i18n/timezone.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1669 @@
     1.4 +/*
     1.5 +*******************************************************************************
     1.6 +* Copyright (C) 1997-2013, International Business Machines Corporation and
     1.7 +* others. All Rights Reserved.
     1.8 +*******************************************************************************
     1.9 +*
    1.10 +* File TIMEZONE.CPP
    1.11 +*
    1.12 +* Modification History:
    1.13 +*
    1.14 +*   Date        Name        Description
    1.15 +*   12/05/96    clhuang     Creation.
    1.16 +*   04/21/97    aliu        General clean-up and bug fixing.
    1.17 +*   05/08/97    aliu        Fixed Hashtable code per code review.
    1.18 +*   07/09/97    helena      Changed createInstance to createDefault.
    1.19 +*   07/29/97    aliu        Updated with all-new list of 96 UNIX-derived
    1.20 +*                           TimeZones.  Changed mechanism to load from static
    1.21 +*                           array rather than resource bundle.
    1.22 +*   07/07/1998  srl         Bugfixes from the Java side: UTC GMT CAT NST
    1.23 +*                           Added getDisplayName API
    1.24 +*                           going to add custom parsing.
    1.25 +*
    1.26 +*                           ISSUES:
    1.27 +*                               - should getDisplayName cache something?
    1.28 +*                               - should custom time zones be cached? [probably]
    1.29 +*  08/10/98     stephen     Brought getDisplayName() API in-line w/ conventions
    1.30 +*  08/19/98     stephen     Changed createTimeZone() to never return 0
    1.31 +*  09/02/98     stephen     Added getOffset(monthLen) and hasSameRules()
    1.32 +*  09/15/98     stephen     Added getStaticClassID()
    1.33 +*  02/22/99     stephen     Removed character literals for EBCDIC safety
    1.34 +*  05/04/99     stephen     Changed initDefault() for Mutex issues
    1.35 +*  07/12/99     helena      HPUX 11 CC Port.
    1.36 +*  12/03/99     aliu        Moved data out of static table into icudata.dll.
    1.37 +*                           Substantial rewrite of zone lookup, default zone, and
    1.38 +*                           available IDs code.  Misc. cleanup.
    1.39 +*********************************************************************************/
    1.40 +
    1.41 +#include "utypeinfo.h"  // for 'typeid' to work
    1.42 +
    1.43 +#include "unicode/utypes.h"
    1.44 +#include "unicode/ustring.h"
    1.45 +#include "uassert.h"
    1.46 +#include "ustr_imp.h"
    1.47 +
    1.48 +#ifdef U_DEBUG_TZ
    1.49 +# include <stdio.h>
    1.50 +# include "uresimp.h" // for debugging
    1.51 +
    1.52 +static void debug_tz_loc(const char *f, int32_t l)
    1.53 +{
    1.54 +  fprintf(stderr, "%s:%d: ", f, l);
    1.55 +}
    1.56 +
    1.57 +static void debug_tz_msg(const char *pat, ...)
    1.58 +{
    1.59 +  va_list ap;
    1.60 +  va_start(ap, pat);
    1.61 +  vfprintf(stderr, pat, ap);
    1.62 +  fflush(stderr);
    1.63 +}
    1.64 +static char gStrBuf[256];
    1.65 +#define U_DEBUG_TZ_STR(x) u_austrncpy(gStrBuf,x,sizeof(gStrBuf)-1)
    1.66 +// must use double parens, i.e.:  U_DEBUG_TZ_MSG(("four is: %d",4));
    1.67 +#define U_DEBUG_TZ_MSG(x) {debug_tz_loc(__FILE__,__LINE__);debug_tz_msg x;}
    1.68 +#else
    1.69 +#define U_DEBUG_TZ_MSG(x)
    1.70 +#endif
    1.71 +
    1.72 +#if !UCONFIG_NO_FORMATTING
    1.73 +
    1.74 +#include "unicode/simpletz.h"
    1.75 +#include "unicode/calendar.h"
    1.76 +#include "unicode/gregocal.h"
    1.77 +#include "unicode/ures.h"
    1.78 +#include "unicode/tzfmt.h"
    1.79 +#include "unicode/numfmt.h"
    1.80 +#include "gregoimp.h"
    1.81 +#include "uresimp.h" // struct UResourceBundle
    1.82 +#include "olsontz.h"
    1.83 +#include "mutex.h"
    1.84 +#include "unicode/udata.h"
    1.85 +#include "ucln_in.h"
    1.86 +#include "cstring.h"
    1.87 +#include "cmemory.h"
    1.88 +#include "unicode/strenum.h"
    1.89 +#include "uassert.h"
    1.90 +#include "zonemeta.h"
    1.91 +
    1.92 +#define kZONEINFO "zoneinfo64"
    1.93 +#define kREGIONS  "Regions"
    1.94 +#define kZONES    "Zones"
    1.95 +#define kRULES    "Rules"
    1.96 +#define kNAMES    "Names"
    1.97 +#define kTZVERSION  "TZVersion"
    1.98 +#define kLINKS    "links"
    1.99 +#define kMAX_CUSTOM_HOUR    23
   1.100 +#define kMAX_CUSTOM_MIN     59
   1.101 +#define kMAX_CUSTOM_SEC     59
   1.102 +#define MINUS 0x002D
   1.103 +#define PLUS 0x002B
   1.104 +#define ZERO_DIGIT 0x0030
   1.105 +#define COLON 0x003A
   1.106 +
   1.107 +// Static data and constants
   1.108 +
   1.109 +static const UChar         WORLD[] = {0x30, 0x30, 0x31, 0x00}; /* "001" */
   1.110 +
   1.111 +static const UChar         GMT_ID[] = {0x47, 0x4D, 0x54, 0x00}; /* "GMT" */
   1.112 +static const UChar         UNKNOWN_ZONE_ID[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E, 0x00}; /* "Etc/Unknown" */
   1.113 +static const int32_t       GMT_ID_LENGTH = 3;
   1.114 +static const int32_t       UNKNOWN_ZONE_ID_LENGTH = 11;
   1.115 +
   1.116 +static icu::TimeZone* DEFAULT_ZONE = NULL;
   1.117 +static icu::UInitOnce gDefaultZoneInitOnce = U_INITONCE_INITIALIZER;
   1.118 +
   1.119 +static icu::TimeZone* _GMT = NULL;
   1.120 +static icu::TimeZone* _UNKNOWN_ZONE = NULL;
   1.121 +static icu::UInitOnce gStaticZonesInitOnce = U_INITONCE_INITIALIZER;
   1.122 +
   1.123 +static char TZDATA_VERSION[16];
   1.124 +static icu::UInitOnce gTZDataVersionInitOnce = U_INITONCE_INITIALIZER;
   1.125 +
   1.126 +static int32_t* MAP_SYSTEM_ZONES = NULL;
   1.127 +static int32_t* MAP_CANONICAL_SYSTEM_ZONES = NULL;
   1.128 +static int32_t* MAP_CANONICAL_SYSTEM_LOCATION_ZONES = NULL;
   1.129 +
   1.130 +static int32_t LEN_SYSTEM_ZONES = 0;
   1.131 +static int32_t LEN_CANONICAL_SYSTEM_ZONES = 0;
   1.132 +static int32_t LEN_CANONICAL_SYSTEM_LOCATION_ZONES = 0;
   1.133 +
   1.134 +static icu::UInitOnce gSystemZonesInitOnce = U_INITONCE_INITIALIZER;
   1.135 +static icu::UInitOnce gCanonicalZonesInitOnce = U_INITONCE_INITIALIZER;
   1.136 +static icu::UInitOnce gCanonicalLocationZonesInitOnce = U_INITONCE_INITIALIZER;
   1.137 +
   1.138 +U_CDECL_BEGIN
   1.139 +static UBool U_CALLCONV timeZone_cleanup(void)
   1.140 +{
   1.141 +    U_NAMESPACE_USE
   1.142 +    delete DEFAULT_ZONE;
   1.143 +    DEFAULT_ZONE = NULL;
   1.144 +    gDefaultZoneInitOnce.reset();
   1.145 +
   1.146 +    delete _GMT;
   1.147 +    _GMT = NULL;
   1.148 +    delete _UNKNOWN_ZONE;
   1.149 +    _UNKNOWN_ZONE = NULL;
   1.150 +    gStaticZonesInitOnce.reset();
   1.151 +
   1.152 +    uprv_memset(TZDATA_VERSION, 0, sizeof(TZDATA_VERSION));
   1.153 +    gTZDataVersionInitOnce.reset();
   1.154 +
   1.155 +    LEN_SYSTEM_ZONES = 0;
   1.156 +    uprv_free(MAP_SYSTEM_ZONES);
   1.157 +    MAP_SYSTEM_ZONES = 0;
   1.158 +    gSystemZonesInitOnce.reset();
   1.159 +
   1.160 +    LEN_CANONICAL_SYSTEM_ZONES = 0;
   1.161 +    uprv_free(MAP_CANONICAL_SYSTEM_ZONES);
   1.162 +    MAP_CANONICAL_SYSTEM_ZONES = 0;
   1.163 +    gCanonicalZonesInitOnce.reset();
   1.164 +
   1.165 +    LEN_CANONICAL_SYSTEM_LOCATION_ZONES = 0;
   1.166 +    uprv_free(MAP_CANONICAL_SYSTEM_LOCATION_ZONES);
   1.167 +    MAP_CANONICAL_SYSTEM_LOCATION_ZONES = 0;
   1.168 +    gCanonicalLocationZonesInitOnce.reset();
   1.169 +
   1.170 +    return TRUE;
   1.171 +}
   1.172 +U_CDECL_END
   1.173 +
   1.174 +U_NAMESPACE_BEGIN
   1.175 +
   1.176 +static int32_t findInStringArray(UResourceBundle* array, const UnicodeString& id, UErrorCode &status)
   1.177 +{
   1.178 +    UnicodeString copy;
   1.179 +    const UChar *u;
   1.180 +    int32_t len;
   1.181 +
   1.182 +    int32_t start = 0;
   1.183 +    int32_t limit = ures_getSize(array);
   1.184 +    int32_t mid;
   1.185 +    int32_t lastMid = INT32_MAX;
   1.186 +    if(U_FAILURE(status) || (limit < 1)) {
   1.187 +        return -1;
   1.188 +    }
   1.189 +    U_DEBUG_TZ_MSG(("fisa: Looking for %s, between %d and %d\n", U_DEBUG_TZ_STR(UnicodeString(id).getTerminatedBuffer()), start, limit));
   1.190 +
   1.191 +    for (;;) {
   1.192 +        mid = (int32_t)((start + limit) / 2);
   1.193 +        if (lastMid == mid) {   /* Have we moved? */
   1.194 +            break;  /* We haven't moved, and it wasn't found. */
   1.195 +        }
   1.196 +        lastMid = mid;
   1.197 +        u = ures_getStringByIndex(array, mid, &len, &status);
   1.198 +        if (U_FAILURE(status)) {
   1.199 +            break;
   1.200 +        }
   1.201 +        U_DEBUG_TZ_MSG(("tz: compare to %s, %d .. [%d] .. %d\n", U_DEBUG_TZ_STR(u), start, mid, limit));
   1.202 +        copy.setTo(TRUE, u, len);
   1.203 +        int r = id.compare(copy);
   1.204 +        if(r==0) {
   1.205 +            U_DEBUG_TZ_MSG(("fisa: found at %d\n", mid));
   1.206 +            return mid;
   1.207 +        } else if(r<0) {
   1.208 +            limit = mid;
   1.209 +        } else {
   1.210 +            start = mid;
   1.211 +        }
   1.212 +    }
   1.213 +    U_DEBUG_TZ_MSG(("fisa: not found\n"));
   1.214 +    return -1;
   1.215 +}
   1.216 +
   1.217 +/**
   1.218 + * Fetch a specific zone by name.  Replaces the getByKey call.
   1.219 + * @param top Top timezone resource
   1.220 + * @param id Time zone ID
   1.221 + * @param oldbundle Bundle for reuse (or NULL).   see 'ures_open()'
   1.222 + * @return the zone's bundle if found, or undefined if error.  Reuses oldbundle.
   1.223 + */
   1.224 +static UResourceBundle* getZoneByName(const UResourceBundle* top, const UnicodeString& id, UResourceBundle *oldbundle, UErrorCode& status) {
   1.225 +    // load the Rules object
   1.226 +    UResourceBundle *tmp = ures_getByKey(top, kNAMES, NULL, &status);
   1.227 +
   1.228 +    // search for the string
   1.229 +    int32_t idx = findInStringArray(tmp, id, status);
   1.230 +
   1.231 +    if((idx == -1) && U_SUCCESS(status)) {
   1.232 +        // not found
   1.233 +        status = U_MISSING_RESOURCE_ERROR;
   1.234 +        //ures_close(oldbundle);
   1.235 +        //oldbundle = NULL;
   1.236 +    } else {
   1.237 +        U_DEBUG_TZ_MSG(("gzbn: oldbundle= size %d, type %d, %s\n", ures_getSize(tmp), ures_getType(tmp), u_errorName(status)));
   1.238 +        tmp = ures_getByKey(top, kZONES, tmp, &status); // get Zones object from top
   1.239 +        U_DEBUG_TZ_MSG(("gzbn: loaded ZONES, size %d, type %d, path %s %s\n", ures_getSize(tmp), ures_getType(tmp), ures_getPath(tmp), u_errorName(status)));
   1.240 +        oldbundle = ures_getByIndex(tmp, idx, oldbundle, &status); // get nth Zone object
   1.241 +        U_DEBUG_TZ_MSG(("gzbn: loaded z#%d, size %d, type %d, path %s, %s\n", idx, ures_getSize(oldbundle), ures_getType(oldbundle), ures_getPath(oldbundle),  u_errorName(status)));
   1.242 +    }
   1.243 +    ures_close(tmp);
   1.244 +    if(U_FAILURE(status)) {
   1.245 +        //ures_close(oldbundle);
   1.246 +        return NULL;
   1.247 +    } else {
   1.248 +        return oldbundle;
   1.249 +    }
   1.250 +}
   1.251 +
   1.252 +
   1.253 +UResourceBundle* TimeZone::loadRule(const UResourceBundle* top, const UnicodeString& ruleid, UResourceBundle* oldbundle, UErrorCode& status) {
   1.254 +    char key[64];
   1.255 +    ruleid.extract(0, sizeof(key)-1, key, (int32_t)sizeof(key)-1, US_INV);
   1.256 +    U_DEBUG_TZ_MSG(("loadRule(%s)\n", key));
   1.257 +    UResourceBundle *r = ures_getByKey(top, kRULES, oldbundle, &status);
   1.258 +    U_DEBUG_TZ_MSG(("loadRule(%s) -> kRULES [%s]\n", key, u_errorName(status)));
   1.259 +    r = ures_getByKey(r, key, r, &status);
   1.260 +    U_DEBUG_TZ_MSG(("loadRule(%s) -> item [%s]\n", key, u_errorName(status)));
   1.261 +    return r;
   1.262 +}
   1.263 +
   1.264 +/**
   1.265 + * Given an ID, open the appropriate resource for the given time zone.
   1.266 + * Dereference aliases if necessary.
   1.267 + * @param id zone id
   1.268 + * @param res resource, which must be ready for use (initialized but not open)
   1.269 + * @param ec input-output error code
   1.270 + * @return top-level resource bundle
   1.271 + */
   1.272 +static UResourceBundle* openOlsonResource(const UnicodeString& id,
   1.273 +                                          UResourceBundle& res,
   1.274 +                                          UErrorCode& ec)
   1.275 +{
   1.276 +#if U_DEBUG_TZ
   1.277 +    char buf[128];
   1.278 +    id.extract(0, sizeof(buf)-1, buf, sizeof(buf), "");
   1.279 +#endif
   1.280 +    UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec);
   1.281 +    U_DEBUG_TZ_MSG(("pre: res sz=%d\n", ures_getSize(&res)));
   1.282 +    /* &res = */ getZoneByName(top, id, &res, ec);
   1.283 +    // Dereference if this is an alias.  Docs say result should be 1
   1.284 +    // but it is 0 in 2.8 (?).
   1.285 +    U_DEBUG_TZ_MSG(("Loading zone '%s' (%s, size %d) - %s\n", buf, ures_getKey((UResourceBundle*)&res), ures_getSize(&res), u_errorName(ec)));
   1.286 +    if (ures_getType(&res) == URES_INT) {
   1.287 +        int32_t deref = ures_getInt(&res, &ec) + 0;
   1.288 +        U_DEBUG_TZ_MSG(("getInt: %s - type is %d\n", u_errorName(ec), ures_getType(&res)));
   1.289 +        UResourceBundle *ares = ures_getByKey(top, kZONES, NULL, &ec); // dereference Zones section
   1.290 +        ures_getByIndex(ares, deref, &res, &ec);
   1.291 +        ures_close(ares);
   1.292 +        U_DEBUG_TZ_MSG(("alias to #%d (%s) - %s\n", deref, "??", u_errorName(ec)));
   1.293 +    } else {
   1.294 +        U_DEBUG_TZ_MSG(("not an alias - size %d\n", ures_getSize(&res)));
   1.295 +    }
   1.296 +    U_DEBUG_TZ_MSG(("%s - final status is %s\n", buf, u_errorName(ec)));
   1.297 +    return top;
   1.298 +}
   1.299 +
   1.300 +// -------------------------------------
   1.301 +
   1.302 +namespace {
   1.303 +
   1.304 +void U_CALLCONV initStaticTimeZones() {
   1.305 +    // Initialize _GMT independently of other static data; it should
   1.306 +    // be valid even if we can't load the time zone UDataMemory.
   1.307 +    ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
   1.308 +    _UNKNOWN_ZONE = new SimpleTimeZone(0, UnicodeString(TRUE, UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH));
   1.309 +    _GMT = new SimpleTimeZone(0, UnicodeString(TRUE, GMT_ID, GMT_ID_LENGTH));
   1.310 +}
   1.311 +
   1.312 +}  // anonymous namespace
   1.313 +
   1.314 +const TimeZone& U_EXPORT2
   1.315 +TimeZone::getUnknown()
   1.316 +{
   1.317 +    umtx_initOnce(gStaticZonesInitOnce, &initStaticTimeZones);
   1.318 +    return *_UNKNOWN_ZONE;
   1.319 +}
   1.320 +
   1.321 +const TimeZone* U_EXPORT2
   1.322 +TimeZone::getGMT(void)
   1.323 +{
   1.324 +    umtx_initOnce(gStaticZonesInitOnce, &initStaticTimeZones);
   1.325 +    return _GMT;
   1.326 +}
   1.327 +
   1.328 +// *****************************************************************************
   1.329 +// class TimeZone
   1.330 +// *****************************************************************************
   1.331 +
   1.332 +UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(TimeZone)
   1.333 +
   1.334 +TimeZone::TimeZone()
   1.335 +    :   UObject(), fID()
   1.336 +{
   1.337 +}
   1.338 +
   1.339 +// -------------------------------------
   1.340 +
   1.341 +TimeZone::TimeZone(const UnicodeString &id)
   1.342 +    :   UObject(), fID(id)
   1.343 +{
   1.344 +}
   1.345 +
   1.346 +// -------------------------------------
   1.347 +
   1.348 +TimeZone::~TimeZone()
   1.349 +{
   1.350 +}
   1.351 +
   1.352 +// -------------------------------------
   1.353 +
   1.354 +TimeZone::TimeZone(const TimeZone &source)
   1.355 +    :   UObject(source), fID(source.fID)
   1.356 +{
   1.357 +}
   1.358 +
   1.359 +// -------------------------------------
   1.360 +
   1.361 +TimeZone &
   1.362 +TimeZone::operator=(const TimeZone &right)
   1.363 +{
   1.364 +    if (this != &right) fID = right.fID;
   1.365 +    return *this;
   1.366 +}
   1.367 +
   1.368 +// -------------------------------------
   1.369 +
   1.370 +UBool
   1.371 +TimeZone::operator==(const TimeZone& that) const
   1.372 +{
   1.373 +    return typeid(*this) == typeid(that) &&
   1.374 +        fID == that.fID;
   1.375 +}
   1.376 +
   1.377 +// -------------------------------------
   1.378 +
   1.379 +namespace {
   1.380 +TimeZone*
   1.381 +createSystemTimeZone(const UnicodeString& id, UErrorCode& ec) {
   1.382 +    if (U_FAILURE(ec)) {
   1.383 +        return NULL;
   1.384 +    }
   1.385 +    TimeZone* z = 0;
   1.386 +    UResourceBundle res;
   1.387 +    ures_initStackObject(&res);
   1.388 +    U_DEBUG_TZ_MSG(("pre-err=%s\n", u_errorName(ec)));
   1.389 +    UResourceBundle *top = openOlsonResource(id, res, ec);
   1.390 +    U_DEBUG_TZ_MSG(("post-err=%s\n", u_errorName(ec)));
   1.391 +    if (U_SUCCESS(ec)) {
   1.392 +        z = new OlsonTimeZone(top, &res, id, ec);
   1.393 +        if (z == NULL) {
   1.394 +          U_DEBUG_TZ_MSG(("cstz: olson time zone failed to initialize - err %s\n", u_errorName(ec)));
   1.395 +        }
   1.396 +    }
   1.397 +    ures_close(&res);
   1.398 +    ures_close(top);
   1.399 +    if (U_FAILURE(ec)) {
   1.400 +        U_DEBUG_TZ_MSG(("cstz: failed to create, err %s\n", u_errorName(ec)));
   1.401 +        delete z;
   1.402 +        z = 0;
   1.403 +    }
   1.404 +    return z;
   1.405 +}
   1.406 +
   1.407 +/**
   1.408 + * Lookup the given name in our system zone table.  If found,
   1.409 + * instantiate a new zone of that name and return it.  If not
   1.410 + * found, return 0.
   1.411 + */
   1.412 +TimeZone*
   1.413 +createSystemTimeZone(const UnicodeString& id) {
   1.414 +    UErrorCode ec = U_ZERO_ERROR;
   1.415 +    return createSystemTimeZone(id, ec);
   1.416 +}
   1.417 +
   1.418 +}
   1.419 +
   1.420 +TimeZone* U_EXPORT2
   1.421 +TimeZone::createTimeZone(const UnicodeString& ID)
   1.422 +{
   1.423 +    /* We first try to lookup the zone ID in our system list.  If this
   1.424 +     * fails, we try to parse it as a custom string GMT[+-]hh:mm.  If
   1.425 +     * all else fails, we return GMT, which is probably not what the
   1.426 +     * user wants, but at least is a functioning TimeZone object.
   1.427 +     *
   1.428 +     * We cannot return NULL, because that would break compatibility
   1.429 +     * with the JDK.
   1.430 +     */
   1.431 +    TimeZone* result = createSystemTimeZone(ID);
   1.432 +
   1.433 +    if (result == 0) {
   1.434 +        U_DEBUG_TZ_MSG(("failed to load system time zone with id - falling to custom"));
   1.435 +        result = createCustomTimeZone(ID);
   1.436 +    }
   1.437 +    if (result == 0) {
   1.438 +        U_DEBUG_TZ_MSG(("failed to load time zone with id - falling to Etc/Unknown(GMT)"));
   1.439 +        result = getUnknown().clone();
   1.440 +    }
   1.441 +    return result;
   1.442 +}
   1.443 +
   1.444 +// -------------------------------------
   1.445 +
   1.446 +/**
   1.447 + * Initialize DEFAULT_ZONE from the system default time zone.  
   1.448 + * Upon return, DEFAULT_ZONE will not be NULL, unless operator new()
   1.449 + * returns NULL.
   1.450 + */
   1.451 +static void U_CALLCONV initDefault()
   1.452 +{
   1.453 +    ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
   1.454 +
   1.455 +    // If setDefault() has already been called we can skip getting the
   1.456 +    // default zone information from the system.
   1.457 +    if (DEFAULT_ZONE != NULL) {
   1.458 +        return;
   1.459 +    }
   1.460 +    
   1.461 +    // We access system timezone data through TPlatformUtilities,
   1.462 +    // including tzset(), timezone, and tzname[].
   1.463 +    int32_t rawOffset = 0;
   1.464 +    const char *hostID;
   1.465 +
   1.466 +    // First, try to create a system timezone, based
   1.467 +    // on the string ID in tzname[0].
   1.468 +
   1.469 +    // NOTE:  this code is safely single threaded, being only
   1.470 +    // run via umtx_initOnce().
   1.471 +    //
   1.472 +    // Some of the locale/timezone OS functions may not be thread safe,
   1.473 +    //
   1.474 +    // The operating system might actually use ICU to implement timezones.
   1.475 +    // So we may have ICU calling ICU here, like on AIX.
   1.476 +    // There shouldn't be a problem with this; initOnce does not hold a mutex
   1.477 +    // while the init function is being run.
   1.478 +
   1.479 +    uprv_tzset(); // Initialize tz... system data
   1.480 +
   1.481 +    // Get the timezone ID from the host.  This function should do
   1.482 +    // any required host-specific remapping; e.g., on Windows this
   1.483 +    // function maps the Date and Time control panel setting to an
   1.484 +    // ICU timezone ID.
   1.485 +    hostID = uprv_tzname(0);
   1.486 +
   1.487 +    // Invert sign because UNIX semantics are backwards
   1.488 +    rawOffset = uprv_timezone() * -U_MILLIS_PER_SECOND;
   1.489 +
   1.490 +    TimeZone* default_zone = NULL;
   1.491 +
   1.492 +    /* Make sure that the string is NULL terminated to prevent BoundsChecker/Purify warnings. */
   1.493 +    UnicodeString hostStrID(hostID, -1, US_INV);
   1.494 +    hostStrID.append((UChar)0);
   1.495 +    hostStrID.truncate(hostStrID.length()-1);
   1.496 +    default_zone = createSystemTimeZone(hostStrID);
   1.497 +
   1.498 +#if U_PLATFORM_USES_ONLY_WIN32_API
   1.499 +    // hostID points to a heap-allocated location on Windows.
   1.500 +    uprv_free(const_cast<char *>(hostID));
   1.501 +#endif
   1.502 +
   1.503 +    int32_t hostIDLen = hostStrID.length();
   1.504 +    if (default_zone != NULL && rawOffset != default_zone->getRawOffset()
   1.505 +        && (3 <= hostIDLen && hostIDLen <= 4))
   1.506 +    {
   1.507 +        // Uh oh. This probably wasn't a good id.
   1.508 +        // It was probably an ambiguous abbreviation
   1.509 +        delete default_zone;
   1.510 +        default_zone = NULL;
   1.511 +    }
   1.512 +
   1.513 +    // Construct a fixed standard zone with the host's ID
   1.514 +    // and raw offset.
   1.515 +    if (default_zone == NULL) {
   1.516 +        default_zone = new SimpleTimeZone(rawOffset, hostStrID);
   1.517 +    }
   1.518 +
   1.519 +    // If we _still_ don't have a time zone, use GMT.
   1.520 +    if (default_zone == NULL) {
   1.521 +        const TimeZone* temptz = TimeZone::getGMT();
   1.522 +        // If we can't use GMT, get out.
   1.523 +        if (temptz == NULL) {
   1.524 +            return;
   1.525 +        }
   1.526 +        default_zone = temptz->clone();
   1.527 +    }
   1.528 +
   1.529 +    // The only way for DEFAULT_ZONE to be non-null at this point is if the user
   1.530 +    // made a thread-unsafe call to setDefault() or adoptDefault() in another
   1.531 +    // thread while this thread was doing something that required getting the default.
   1.532 +    U_ASSERT(DEFAULT_ZONE == NULL);
   1.533 +
   1.534 +    DEFAULT_ZONE = default_zone;
   1.535 +}
   1.536 +
   1.537 +// -------------------------------------
   1.538 +
   1.539 +TimeZone* U_EXPORT2
   1.540 +TimeZone::createDefault()
   1.541 +{
   1.542 +    umtx_initOnce(gDefaultZoneInitOnce, initDefault);
   1.543 +    return (DEFAULT_ZONE != NULL) ? DEFAULT_ZONE->clone() : NULL;
   1.544 +}
   1.545 +
   1.546 +// -------------------------------------
   1.547 +
   1.548 +void U_EXPORT2
   1.549 +TimeZone::adoptDefault(TimeZone* zone)
   1.550 +{
   1.551 +    if (zone != NULL)
   1.552 +    {
   1.553 +        TimeZone *old = DEFAULT_ZONE;
   1.554 +        DEFAULT_ZONE = zone;
   1.555 +        delete old;
   1.556 +        ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
   1.557 +    }
   1.558 +}
   1.559 +// -------------------------------------
   1.560 +
   1.561 +void U_EXPORT2
   1.562 +TimeZone::setDefault(const TimeZone& zone)
   1.563 +{
   1.564 +    adoptDefault(zone.clone());
   1.565 +}
   1.566 +
   1.567 +//----------------------------------------------------------------------
   1.568 +
   1.569 +
   1.570 +static void U_CALLCONV initMap(USystemTimeZoneType type, UErrorCode& ec) {
   1.571 +    ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
   1.572 +
   1.573 +    UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec);
   1.574 +    res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section
   1.575 +    if (U_SUCCESS(ec)) {
   1.576 +        int32_t size = ures_getSize(res);
   1.577 +        int32_t *m = (int32_t *)uprv_malloc(size * sizeof(int32_t));
   1.578 +        if (m == NULL) {
   1.579 +            ec = U_MEMORY_ALLOCATION_ERROR;
   1.580 +        } else {
   1.581 +            int32_t numEntries = 0;
   1.582 +            for (int32_t i = 0; i < size; i++) {
   1.583 +                UnicodeString id = ures_getUnicodeStringByIndex(res, i, &ec);
   1.584 +                if (U_FAILURE(ec)) {
   1.585 +                    break;
   1.586 +                }
   1.587 +                if (0 == id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH)) {
   1.588 +                    // exclude Etc/Unknown
   1.589 +                    continue;
   1.590 +                }
   1.591 +                if (type == UCAL_ZONE_TYPE_CANONICAL || type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) {
   1.592 +                    UnicodeString canonicalID;
   1.593 +                    ZoneMeta::getCanonicalCLDRID(id, canonicalID, ec);
   1.594 +                    if (U_FAILURE(ec)) {
   1.595 +                        break;
   1.596 +                    }
   1.597 +                    if (canonicalID != id) {
   1.598 +                        // exclude aliases
   1.599 +                        continue;
   1.600 +                    }
   1.601 +                }
   1.602 +                if (type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) {
   1.603 +                    const UChar *region = TimeZone::getRegion(id, ec);
   1.604 +                    if (U_FAILURE(ec)) {
   1.605 +                        break;
   1.606 +                    }
   1.607 +                    if (u_strcmp(region, WORLD) == 0) {
   1.608 +                       // exclude non-location ("001")
   1.609 +                        continue;
   1.610 +                    }
   1.611 +                }
   1.612 +                m[numEntries++] = i;
   1.613 +            }
   1.614 +            if (U_SUCCESS(ec)) {
   1.615 +                int32_t *tmp = m;
   1.616 +                m = (int32_t *)uprv_realloc(tmp, numEntries * sizeof(int32_t));
   1.617 +                if (m == NULL) {
   1.618 +                    // realloc failed.. use the original one even it has unused
   1.619 +                    // area at the end
   1.620 +                    m = tmp;
   1.621 +                }
   1.622 +
   1.623 +                switch(type) {
   1.624 +                case UCAL_ZONE_TYPE_ANY:
   1.625 +                    U_ASSERT(MAP_SYSTEM_ZONES == NULL);
   1.626 +                    MAP_SYSTEM_ZONES = m;
   1.627 +                    LEN_SYSTEM_ZONES = numEntries;
   1.628 +                    break;
   1.629 +                case UCAL_ZONE_TYPE_CANONICAL:
   1.630 +                    U_ASSERT(MAP_CANONICAL_SYSTEM_ZONES == NULL);
   1.631 +                    MAP_CANONICAL_SYSTEM_ZONES = m;
   1.632 +                    LEN_CANONICAL_SYSTEM_ZONES = numEntries;
   1.633 +                    break;
   1.634 +                case UCAL_ZONE_TYPE_CANONICAL_LOCATION:
   1.635 +                    U_ASSERT(MAP_CANONICAL_SYSTEM_LOCATION_ZONES == NULL);
   1.636 +                    MAP_CANONICAL_SYSTEM_LOCATION_ZONES = m;
   1.637 +                    LEN_CANONICAL_SYSTEM_LOCATION_ZONES = numEntries;
   1.638 +                    break;
   1.639 +                }
   1.640 +            }
   1.641 +        }
   1.642 +    }
   1.643 +    ures_close(res);
   1.644 +}
   1.645 +
   1.646 +
   1.647 +/**
   1.648 + * This is the default implementation for subclasses that do not
   1.649 + * override this method.  This implementation calls through to the
   1.650 + * 8-argument getOffset() method after suitable computations, and
   1.651 + * correctly adjusts GMT millis to local millis when necessary.
   1.652 + */
   1.653 +void TimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
   1.654 +                         int32_t& dstOffset, UErrorCode& ec) const {
   1.655 +    if (U_FAILURE(ec)) {
   1.656 +        return;
   1.657 +    }
   1.658 +
   1.659 +    rawOffset = getRawOffset();
   1.660 +    if (!local) {
   1.661 +        date += rawOffset; // now in local standard millis
   1.662 +    }
   1.663 +
   1.664 +    // When local == TRUE, date might not be in local standard
   1.665 +    // millis.  getOffset taking 7 parameters used here assume
   1.666 +    // the given time in day is local standard time.
   1.667 +    // At STD->DST transition, there is a range of time which
   1.668 +    // does not exist.  When 'date' is in this time range
   1.669 +    // (and local == TRUE), this method interprets the specified
   1.670 +    // local time as DST.  At DST->STD transition, there is a
   1.671 +    // range of time which occurs twice.  In this case, this
   1.672 +    // method interprets the specified local time as STD.
   1.673 +    // To support the behavior above, we need to call getOffset
   1.674 +    // (with 7 args) twice when local == true and DST is
   1.675 +    // detected in the initial call.
   1.676 +    for (int32_t pass=0; ; ++pass) {
   1.677 +        int32_t year, month, dom, dow;
   1.678 +        double day = uprv_floor(date / U_MILLIS_PER_DAY);
   1.679 +        int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
   1.680 +
   1.681 +        Grego::dayToFields(day, year, month, dom, dow);
   1.682 +
   1.683 +        dstOffset = getOffset(GregorianCalendar::AD, year, month, dom,
   1.684 +                              (uint8_t) dow, millis,
   1.685 +                              Grego::monthLength(year, month),
   1.686 +                              ec) - rawOffset;
   1.687 +
   1.688 +        // Recompute if local==TRUE, dstOffset!=0.
   1.689 +        if (pass!=0 || !local || dstOffset == 0) {
   1.690 +            break;
   1.691 +        }
   1.692 +        // adjust to local standard millis
   1.693 +        date -= dstOffset;
   1.694 +    }
   1.695 +}
   1.696 +
   1.697 +// -------------------------------------
   1.698 +
   1.699 +// New available IDs API as of ICU 2.4.  Uses StringEnumeration API.
   1.700 +
   1.701 +class TZEnumeration : public StringEnumeration {
   1.702 +private:
   1.703 +
   1.704 +    // Map into to zones.  Our results are zone[map[i]] for
   1.705 +    // i=0..len-1, where zone[i] is the i-th Olson zone.  If map==NULL
   1.706 +    // then our results are zone[i] for i=0..len-1.  Len will be zero
   1.707 +    // if the zone data could not be loaded.
   1.708 +    int32_t* map;
   1.709 +    int32_t* localMap;
   1.710 +    int32_t  len;
   1.711 +    int32_t  pos;
   1.712 +
   1.713 +    TZEnumeration(int32_t* mapData, int32_t mapLen, UBool adoptMapData) : pos(0) {
   1.714 +        map = mapData;
   1.715 +        localMap = adoptMapData ? mapData : NULL;
   1.716 +        len = mapLen;
   1.717 +    }
   1.718 +
   1.719 +    UBool getID(int32_t i) {
   1.720 +        UErrorCode ec = U_ZERO_ERROR;
   1.721 +        int32_t idLen = 0;
   1.722 +        const UChar* id = NULL;
   1.723 +        UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec);
   1.724 +        top = ures_getByKey(top, kNAMES, top, &ec); // dereference Zones section
   1.725 +        id = ures_getStringByIndex(top, i, &idLen, &ec);
   1.726 +        if(U_FAILURE(ec)) {
   1.727 +            unistr.truncate(0);
   1.728 +        }
   1.729 +        else {
   1.730 +            unistr.fastCopyFrom(UnicodeString(TRUE, id, idLen));
   1.731 +        }
   1.732 +        ures_close(top);
   1.733 +        return U_SUCCESS(ec);
   1.734 +    }
   1.735 +
   1.736 +    static int32_t* getMap(USystemTimeZoneType type, int32_t& len, UErrorCode& ec) {
   1.737 +        len = 0;
   1.738 +        if (U_FAILURE(ec)) {
   1.739 +            return NULL;
   1.740 +        }
   1.741 +        int32_t* m = NULL;
   1.742 +        switch (type) {
   1.743 +        case UCAL_ZONE_TYPE_ANY:
   1.744 +            umtx_initOnce(gSystemZonesInitOnce, &initMap, type, ec);
   1.745 +            m = MAP_SYSTEM_ZONES;
   1.746 +            len = LEN_SYSTEM_ZONES;
   1.747 +            break;
   1.748 +        case UCAL_ZONE_TYPE_CANONICAL:
   1.749 +            umtx_initOnce(gCanonicalZonesInitOnce, &initMap, type, ec);
   1.750 +            m = MAP_CANONICAL_SYSTEM_ZONES;
   1.751 +            len = LEN_CANONICAL_SYSTEM_ZONES;
   1.752 +            break;
   1.753 +        case UCAL_ZONE_TYPE_CANONICAL_LOCATION:
   1.754 +            umtx_initOnce(gCanonicalLocationZonesInitOnce, &initMap, type, ec);
   1.755 +            m = MAP_CANONICAL_SYSTEM_LOCATION_ZONES;
   1.756 +            len = LEN_CANONICAL_SYSTEM_LOCATION_ZONES;
   1.757 +            break;
   1.758 +        default:
   1.759 +            ec = U_ILLEGAL_ARGUMENT_ERROR;
   1.760 +            m = NULL;
   1.761 +            len = 0;
   1.762 +            break;
   1.763 +        }
   1.764 +        return m;
   1.765 +    }
   1.766 +
   1.767 +public:
   1.768 +
   1.769 +#define DEFAULT_FILTERED_MAP_SIZE 8
   1.770 +#define MAP_INCREMENT_SIZE 8
   1.771 +
   1.772 +    static TZEnumeration* create(USystemTimeZoneType type, const char* region, const int32_t* rawOffset, UErrorCode& ec) {
   1.773 +        if (U_FAILURE(ec)) {
   1.774 +            return NULL;
   1.775 +        }
   1.776 +
   1.777 +        int32_t baseLen;
   1.778 +        int32_t *baseMap = getMap(type, baseLen, ec);
   1.779 +
   1.780 +        if (U_FAILURE(ec)) {
   1.781 +            return NULL;
   1.782 +        }
   1.783 +
   1.784 +        // If any additional conditions are available,
   1.785 +        // create instance local map filtered by the conditions.
   1.786 +
   1.787 +        int32_t *filteredMap = NULL;
   1.788 +        int32_t numEntries = 0;
   1.789 +
   1.790 +        if (region != NULL || rawOffset != NULL) {
   1.791 +            int32_t filteredMapSize = DEFAULT_FILTERED_MAP_SIZE;
   1.792 +            filteredMap = (int32_t *)uprv_malloc(filteredMapSize * sizeof(int32_t));
   1.793 +            if (filteredMap == NULL) {
   1.794 +                ec = U_MEMORY_ALLOCATION_ERROR;
   1.795 +                return NULL;
   1.796 +            }
   1.797 +
   1.798 +            // Walk through the base map
   1.799 +            UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec);
   1.800 +            res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section
   1.801 +            for (int32_t i = 0; i < baseLen; i++) {
   1.802 +                int32_t zidx = baseMap[i];
   1.803 +                UnicodeString id = ures_getUnicodeStringByIndex(res, zidx, &ec);
   1.804 +                if (U_FAILURE(ec)) {
   1.805 +                    break;
   1.806 +                }
   1.807 +                if (region != NULL) {
   1.808 +                    // Filter by region
   1.809 +                    char tzregion[4]; // max 3 letters + null term
   1.810 +                    TimeZone::getRegion(id, tzregion, sizeof(tzregion), ec);
   1.811 +                    if (U_FAILURE(ec)) {
   1.812 +                        break;
   1.813 +                    }
   1.814 +                    if (uprv_stricmp(tzregion, region) != 0) {
   1.815 +                        // region does not match
   1.816 +                        continue;
   1.817 +                    }
   1.818 +                }
   1.819 +                if (rawOffset != NULL) {
   1.820 +                    // Filter by raw offset
   1.821 +                    // Note: This is VERY inefficient
   1.822 +                    TimeZone *z = createSystemTimeZone(id, ec);
   1.823 +                    if (U_FAILURE(ec)) {
   1.824 +                        break;
   1.825 +                    }
   1.826 +                    int32_t tzoffset = z->getRawOffset();
   1.827 +                    delete z;
   1.828 +
   1.829 +                    if (tzoffset != *rawOffset) {
   1.830 +                        continue;
   1.831 +                    }
   1.832 +                }
   1.833 +
   1.834 +                if (filteredMapSize <= numEntries) {
   1.835 +                    filteredMapSize += MAP_INCREMENT_SIZE;
   1.836 +                    int32_t *tmp = (int32_t *)uprv_realloc(filteredMap, filteredMapSize * sizeof(int32_t));
   1.837 +                    if (tmp == NULL) {
   1.838 +                        ec = U_MEMORY_ALLOCATION_ERROR;
   1.839 +                        break;
   1.840 +                    } else {
   1.841 +                        filteredMap = tmp;
   1.842 +                    }
   1.843 +                }
   1.844 +
   1.845 +                filteredMap[numEntries++] = zidx;
   1.846 +            }
   1.847 +
   1.848 +            if (U_FAILURE(ec)) {
   1.849 +                uprv_free(filteredMap);
   1.850 +                filteredMap = NULL;
   1.851 +            }
   1.852 +
   1.853 +            ures_close(res);
   1.854 +        }
   1.855 +
   1.856 +        TZEnumeration *result = NULL;
   1.857 +        if (U_SUCCESS(ec)) {
   1.858 +            // Finally, create a new enumeration instance
   1.859 +            if (filteredMap == NULL) {
   1.860 +                result = new TZEnumeration(baseMap, baseLen, FALSE);
   1.861 +            } else {
   1.862 +                result = new TZEnumeration(filteredMap, numEntries, TRUE);
   1.863 +                filteredMap = NULL;
   1.864 +            }
   1.865 +            if (result == NULL) {
   1.866 +                ec = U_MEMORY_ALLOCATION_ERROR;
   1.867 +            }
   1.868 +        }
   1.869 +
   1.870 +        if (filteredMap != NULL) {
   1.871 +            uprv_free(filteredMap);
   1.872 +        }
   1.873 +
   1.874 +        return result;
   1.875 +    }
   1.876 +
   1.877 +    TZEnumeration(const TZEnumeration &other) : StringEnumeration(), map(NULL), localMap(NULL), len(0), pos(0) {
   1.878 +        if (other.localMap != NULL) {
   1.879 +            localMap = (int32_t *)uprv_malloc(other.len * sizeof(int32_t));
   1.880 +            if (localMap != NULL) {
   1.881 +                len = other.len;
   1.882 +                uprv_memcpy(localMap, other.localMap, len * sizeof(int32_t));
   1.883 +                pos = other.pos;
   1.884 +                map = localMap;
   1.885 +            } else {
   1.886 +                len = 0;
   1.887 +                pos = 0;
   1.888 +                map = NULL;
   1.889 +            }
   1.890 +        } else {
   1.891 +            map = other.map;
   1.892 +            localMap = NULL;
   1.893 +            len = other.len;
   1.894 +            pos = other.pos;
   1.895 +        }
   1.896 +    }
   1.897 +
   1.898 +    virtual ~TZEnumeration();
   1.899 +
   1.900 +    virtual StringEnumeration *clone() const {
   1.901 +        return new TZEnumeration(*this);
   1.902 +    }
   1.903 +
   1.904 +    virtual int32_t count(UErrorCode& status) const {
   1.905 +        return U_FAILURE(status) ? 0 : len;
   1.906 +    }
   1.907 +
   1.908 +    virtual const UnicodeString* snext(UErrorCode& status) {
   1.909 +        if (U_SUCCESS(status) && map != NULL && pos < len) {
   1.910 +            getID(map[pos]);
   1.911 +            ++pos;
   1.912 +            return &unistr;
   1.913 +        }
   1.914 +        return 0;
   1.915 +    }
   1.916 +
   1.917 +    virtual void reset(UErrorCode& /*status*/) {
   1.918 +        pos = 0;
   1.919 +    }
   1.920 +
   1.921 +public:
   1.922 +    static UClassID U_EXPORT2 getStaticClassID(void);
   1.923 +    virtual UClassID getDynamicClassID(void) const;
   1.924 +};
   1.925 +
   1.926 +TZEnumeration::~TZEnumeration() {
   1.927 +    if (localMap != NULL) {
   1.928 +        uprv_free(localMap);
   1.929 +    }
   1.930 +}
   1.931 +
   1.932 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TZEnumeration)
   1.933 +
   1.934 +StringEnumeration* U_EXPORT2
   1.935 +TimeZone::createTimeZoneIDEnumeration(
   1.936 +            USystemTimeZoneType zoneType,
   1.937 +            const char* region,
   1.938 +            const int32_t* rawOffset,
   1.939 +            UErrorCode& ec) {
   1.940 +    return TZEnumeration::create(zoneType, region, rawOffset, ec);
   1.941 +}
   1.942 +
   1.943 +StringEnumeration* U_EXPORT2
   1.944 +TimeZone::createEnumeration() {
   1.945 +    UErrorCode ec = U_ZERO_ERROR;
   1.946 +    return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, NULL, ec);
   1.947 +}
   1.948 +
   1.949 +StringEnumeration* U_EXPORT2
   1.950 +TimeZone::createEnumeration(int32_t rawOffset) {
   1.951 +    UErrorCode ec = U_ZERO_ERROR;
   1.952 +    return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, &rawOffset, ec);
   1.953 +}
   1.954 +
   1.955 +StringEnumeration* U_EXPORT2
   1.956 +TimeZone::createEnumeration(const char* country) {
   1.957 +    UErrorCode ec = U_ZERO_ERROR;
   1.958 +    return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, country, NULL, ec);
   1.959 +}
   1.960 +
   1.961 +// ---------------------------------------
   1.962 +
   1.963 +int32_t U_EXPORT2
   1.964 +TimeZone::countEquivalentIDs(const UnicodeString& id) {
   1.965 +    int32_t result = 0;
   1.966 +    UErrorCode ec = U_ZERO_ERROR;
   1.967 +    UResourceBundle res;
   1.968 +    ures_initStackObject(&res);
   1.969 +    U_DEBUG_TZ_MSG(("countEquivalentIDs..\n"));
   1.970 +    UResourceBundle *top = openOlsonResource(id, res, ec);
   1.971 +    if (U_SUCCESS(ec)) {
   1.972 +        UResourceBundle r;
   1.973 +        ures_initStackObject(&r);
   1.974 +        ures_getByKey(&res, kLINKS, &r, &ec);
   1.975 +        ures_getIntVector(&r, &result, &ec);
   1.976 +        ures_close(&r);
   1.977 +    }
   1.978 +    ures_close(&res);
   1.979 +    ures_close(top);
   1.980 +    return result;
   1.981 +}
   1.982 +
   1.983 +// ---------------------------------------
   1.984 +
   1.985 +const UnicodeString U_EXPORT2
   1.986 +TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) {
   1.987 +    U_DEBUG_TZ_MSG(("gEI(%d)\n", index));
   1.988 +    UnicodeString result;
   1.989 +    UErrorCode ec = U_ZERO_ERROR;
   1.990 +    UResourceBundle res;
   1.991 +    ures_initStackObject(&res);
   1.992 +    UResourceBundle *top = openOlsonResource(id, res, ec);
   1.993 +    int32_t zone = -1;
   1.994 +    if (U_SUCCESS(ec)) {
   1.995 +        UResourceBundle r;
   1.996 +        ures_initStackObject(&r);
   1.997 +        int32_t size;
   1.998 +        ures_getByKey(&res, kLINKS, &r, &ec);
   1.999 +        const int32_t* v = ures_getIntVector(&r, &size, &ec);
  1.1000 +        if (U_SUCCESS(ec)) {
  1.1001 +            if (index >= 0 && index < size) {
  1.1002 +                zone = v[index];
  1.1003 +            }
  1.1004 +        }
  1.1005 +        ures_close(&r);
  1.1006 +    }
  1.1007 +    ures_close(&res);
  1.1008 +    if (zone >= 0) {
  1.1009 +        UResourceBundle *ares = ures_getByKey(top, kNAMES, NULL, &ec); // dereference Zones section
  1.1010 +        if (U_SUCCESS(ec)) {
  1.1011 +            int32_t idLen = 0;
  1.1012 +            const UChar* id = ures_getStringByIndex(ares, zone, &idLen, &ec);
  1.1013 +            result.fastCopyFrom(UnicodeString(TRUE, id, idLen));
  1.1014 +            U_DEBUG_TZ_MSG(("gei(%d) -> %d, len%d, %s\n", index, zone, result.length(), u_errorName(ec)));
  1.1015 +        }
  1.1016 +        ures_close(ares);
  1.1017 +    }
  1.1018 +    ures_close(top);
  1.1019 +#if defined(U_DEBUG_TZ)
  1.1020 +    if(result.length() ==0) {
  1.1021 +      U_DEBUG_TZ_MSG(("equiv [__, #%d] -> 0 (%s)\n", index, u_errorName(ec)));
  1.1022 +    }
  1.1023 +#endif
  1.1024 +    return result;
  1.1025 +}
  1.1026 +
  1.1027 +// ---------------------------------------
  1.1028 +
  1.1029 +// These methods are used by ZoneMeta class only.
  1.1030 +
  1.1031 +const UChar*
  1.1032 +TimeZone::findID(const UnicodeString& id) {
  1.1033 +    const UChar *result = NULL;
  1.1034 +    UErrorCode ec = U_ZERO_ERROR;
  1.1035 +    UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec);
  1.1036 +
  1.1037 +    // resolve zone index by name
  1.1038 +    UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec);
  1.1039 +    int32_t idx = findInStringArray(names, id, ec);
  1.1040 +    result = ures_getStringByIndex(names, idx, NULL, &ec);
  1.1041 +    if (U_FAILURE(ec)) {
  1.1042 +        result = NULL;
  1.1043 +    }
  1.1044 +    ures_close(names);
  1.1045 +    ures_close(rb);
  1.1046 +    return result;
  1.1047 +}
  1.1048 +
  1.1049 +
  1.1050 +const UChar*
  1.1051 +TimeZone::dereferOlsonLink(const UnicodeString& id) {
  1.1052 +    const UChar *result = NULL;
  1.1053 +    UErrorCode ec = U_ZERO_ERROR;
  1.1054 +    UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec);
  1.1055 +
  1.1056 +    // resolve zone index by name
  1.1057 +    UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec);
  1.1058 +    int32_t idx = findInStringArray(names, id, ec);
  1.1059 +    result = ures_getStringByIndex(names, idx, NULL, &ec);
  1.1060 +
  1.1061 +    // open the zone bundle by index
  1.1062 +    ures_getByKey(rb, kZONES, rb, &ec);
  1.1063 +    ures_getByIndex(rb, idx, rb, &ec); 
  1.1064 +
  1.1065 +    if (U_SUCCESS(ec)) {
  1.1066 +        if (ures_getType(rb) == URES_INT) {
  1.1067 +            // this is a link - dereference the link
  1.1068 +            int32_t deref = ures_getInt(rb, &ec);
  1.1069 +            const UChar* tmp = ures_getStringByIndex(names, deref, NULL, &ec);
  1.1070 +            if (U_SUCCESS(ec)) {
  1.1071 +                result = tmp;
  1.1072 +            }
  1.1073 +        }
  1.1074 +    }
  1.1075 +
  1.1076 +    ures_close(names);
  1.1077 +    ures_close(rb);
  1.1078 +
  1.1079 +    return result;
  1.1080 +}
  1.1081 +
  1.1082 +const UChar*
  1.1083 +TimeZone::getRegion(const UnicodeString& id) {
  1.1084 +    UErrorCode status = U_ZERO_ERROR;
  1.1085 +    return getRegion(id, status);
  1.1086 +}
  1.1087 +
  1.1088 +const UChar*
  1.1089 +TimeZone::getRegion(const UnicodeString& id, UErrorCode& status) {
  1.1090 +    if (U_FAILURE(status)) {
  1.1091 +        return NULL;
  1.1092 +    }
  1.1093 +    const UChar *result = NULL;
  1.1094 +    UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &status);
  1.1095 +
  1.1096 +    // resolve zone index by name
  1.1097 +    UResourceBundle *res = ures_getByKey(rb, kNAMES, NULL, &status);
  1.1098 +    int32_t idx = findInStringArray(res, id, status);
  1.1099 +
  1.1100 +    // get region mapping
  1.1101 +    ures_getByKey(rb, kREGIONS, res, &status);
  1.1102 +    const UChar *tmp = ures_getStringByIndex(res, idx, NULL, &status);
  1.1103 +    if (U_SUCCESS(status)) {
  1.1104 +        result = tmp;
  1.1105 +    }
  1.1106 +
  1.1107 +    ures_close(res);
  1.1108 +    ures_close(rb);
  1.1109 +
  1.1110 +    return result;
  1.1111 +}
  1.1112 +
  1.1113 +
  1.1114 +// ---------------------------------------
  1.1115 +int32_t
  1.1116 +TimeZone::getRegion(const UnicodeString& id, char *region, int32_t capacity, UErrorCode& status)
  1.1117 +{
  1.1118 +    int32_t resultLen = 0;
  1.1119 +    *region = 0;
  1.1120 +    if (U_FAILURE(status)) {
  1.1121 +        return 0;
  1.1122 +    }
  1.1123 +
  1.1124 +    const UChar *uregion = NULL;
  1.1125 +    // "Etc/Unknown" is not a system zone ID,
  1.1126 +    // but in the zone data
  1.1127 +    if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) != 0) {
  1.1128 +        uregion = getRegion(id);
  1.1129 +    }
  1.1130 +    if (uregion == NULL) {
  1.1131 +        status = U_ILLEGAL_ARGUMENT_ERROR;
  1.1132 +        return 0;
  1.1133 +    }
  1.1134 +    resultLen = u_strlen(uregion);
  1.1135 +    // A region code is represented by invariant characters
  1.1136 +    u_UCharsToChars(uregion, region, uprv_min(resultLen, capacity));
  1.1137 +
  1.1138 +    if (capacity < resultLen) {
  1.1139 +        status = U_BUFFER_OVERFLOW_ERROR;
  1.1140 +        return resultLen;
  1.1141 +    }
  1.1142 +
  1.1143 +    return u_terminateChars(region, capacity, resultLen, &status);
  1.1144 +}
  1.1145 +
  1.1146 +// ---------------------------------------
  1.1147 +
  1.1148 +
  1.1149 +UnicodeString&
  1.1150 +TimeZone::getDisplayName(UnicodeString& result) const
  1.1151 +{
  1.1152 +    return getDisplayName(FALSE,LONG,Locale::getDefault(), result);
  1.1153 +}
  1.1154 +
  1.1155 +UnicodeString&
  1.1156 +TimeZone::getDisplayName(const Locale& locale, UnicodeString& result) const
  1.1157 +{
  1.1158 +    return getDisplayName(FALSE, LONG, locale, result);
  1.1159 +}
  1.1160 +
  1.1161 +UnicodeString&
  1.1162 +TimeZone::getDisplayName(UBool daylight, EDisplayType style, UnicodeString& result)  const
  1.1163 +{
  1.1164 +    return getDisplayName(daylight,style, Locale::getDefault(), result);
  1.1165 +}
  1.1166 +//--------------------------------------
  1.1167 +int32_t
  1.1168 +TimeZone::getDSTSavings()const {
  1.1169 +    if (useDaylightTime()) {
  1.1170 +        return 3600000;
  1.1171 +    }
  1.1172 +    return 0;
  1.1173 +}
  1.1174 +//---------------------------------------
  1.1175 +UnicodeString&
  1.1176 +TimeZone::getDisplayName(UBool daylight, EDisplayType style, const Locale& locale, UnicodeString& result) const
  1.1177 +{
  1.1178 +    UErrorCode status = U_ZERO_ERROR;
  1.1179 +    UDate date = Calendar::getNow();
  1.1180 +    UTimeZoneFormatTimeType timeType;
  1.1181 +    int32_t offset;
  1.1182 +
  1.1183 +    if (style == GENERIC_LOCATION || style == LONG_GENERIC || style == SHORT_GENERIC) {
  1.1184 +        LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
  1.1185 +        if (U_FAILURE(status)) {
  1.1186 +            result.remove();
  1.1187 +            return result;
  1.1188 +        }
  1.1189 +        // Generic format
  1.1190 +        switch (style) {
  1.1191 +        case GENERIC_LOCATION:
  1.1192 +            tzfmt->format(UTZFMT_STYLE_GENERIC_LOCATION, *this, date, result, &timeType);
  1.1193 +            break;
  1.1194 +        case LONG_GENERIC:
  1.1195 +            tzfmt->format(UTZFMT_STYLE_GENERIC_LONG, *this, date, result, &timeType);
  1.1196 +            break;
  1.1197 +        case SHORT_GENERIC:
  1.1198 +            tzfmt->format(UTZFMT_STYLE_GENERIC_SHORT, *this, date, result, &timeType);
  1.1199 +            break;
  1.1200 +        default:
  1.1201 +            U_ASSERT(FALSE);
  1.1202 +        }
  1.1203 +        // Generic format many use Localized GMT as the final fallback.
  1.1204 +        // When Localized GMT format is used, the result might not be
  1.1205 +        // appropriate for the requested daylight value.
  1.1206 +        if ((daylight && timeType == UTZFMT_TIME_TYPE_STANDARD) || (!daylight && timeType == UTZFMT_TIME_TYPE_DAYLIGHT)) {
  1.1207 +            offset = daylight ? getRawOffset() + getDSTSavings() : getRawOffset();
  1.1208 +            if (style == SHORT_GENERIC) {
  1.1209 +                tzfmt->formatOffsetShortLocalizedGMT(offset, result, status);
  1.1210 +            } else {
  1.1211 +                tzfmt->formatOffsetLocalizedGMT(offset, result, status);
  1.1212 +            }
  1.1213 +        }
  1.1214 +    } else if (style == LONG_GMT || style == SHORT_GMT) {
  1.1215 +        LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
  1.1216 +        if (U_FAILURE(status)) {
  1.1217 +            result.remove();
  1.1218 +            return result;
  1.1219 +        }
  1.1220 +        offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset();
  1.1221 +        switch (style) {
  1.1222 +        case LONG_GMT:
  1.1223 +            tzfmt->formatOffsetLocalizedGMT(offset, result, status);
  1.1224 +            break;
  1.1225 +        case SHORT_GMT:
  1.1226 +            tzfmt->formatOffsetISO8601Basic(offset, FALSE, FALSE, FALSE, result, status);
  1.1227 +            break;
  1.1228 +        default:
  1.1229 +            U_ASSERT(FALSE);
  1.1230 +        }
  1.1231 +
  1.1232 +    } else {
  1.1233 +        U_ASSERT(style == LONG || style == SHORT || style == SHORT_COMMONLY_USED);
  1.1234 +        UTimeZoneNameType nameType = UTZNM_UNKNOWN;
  1.1235 +        switch (style) {
  1.1236 +        case LONG:
  1.1237 +            nameType = daylight ? UTZNM_LONG_DAYLIGHT : UTZNM_LONG_STANDARD;
  1.1238 +            break;
  1.1239 +        case SHORT:
  1.1240 +        case SHORT_COMMONLY_USED:
  1.1241 +            nameType = daylight ? UTZNM_SHORT_DAYLIGHT : UTZNM_SHORT_STANDARD;
  1.1242 +            break;
  1.1243 +        default:
  1.1244 +            U_ASSERT(FALSE);
  1.1245 +        }
  1.1246 +        LocalPointer<TimeZoneNames> tznames(TimeZoneNames::createInstance(locale, status));
  1.1247 +        if (U_FAILURE(status)) {
  1.1248 +            result.remove();
  1.1249 +            return result;
  1.1250 +        }
  1.1251 +        UnicodeString canonicalID(ZoneMeta::getCanonicalCLDRID(*this));
  1.1252 +        tznames->getDisplayName(canonicalID, nameType, date, result);
  1.1253 +        if (result.isEmpty()) {
  1.1254 +            // Fallback to localized GMT
  1.1255 +            LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(locale, status));
  1.1256 +            offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset();
  1.1257 +            if (style == LONG) {
  1.1258 +                tzfmt->formatOffsetLocalizedGMT(offset, result, status);
  1.1259 +            } else {
  1.1260 +                tzfmt->formatOffsetShortLocalizedGMT(offset, result, status);
  1.1261 +            }
  1.1262 +        }
  1.1263 +    }
  1.1264 +    if (U_FAILURE(status)) {
  1.1265 +        result.remove();
  1.1266 +    }
  1.1267 +    return  result;
  1.1268 +}
  1.1269 +
  1.1270 +/**
  1.1271 + * Parse a custom time zone identifier and return a corresponding zone.
  1.1272 + * @param id a string of the form GMT[+-]hh:mm, GMT[+-]hhmm, or
  1.1273 + * GMT[+-]hh.
  1.1274 + * @return a newly created SimpleTimeZone with the given offset and
  1.1275 + * no Daylight Savings Time, or null if the id cannot be parsed.
  1.1276 +*/
  1.1277 +TimeZone*
  1.1278 +TimeZone::createCustomTimeZone(const UnicodeString& id)
  1.1279 +{
  1.1280 +    int32_t sign, hour, min, sec;
  1.1281 +    if (parseCustomID(id, sign, hour, min, sec)) {
  1.1282 +        UnicodeString customID;
  1.1283 +        formatCustomID(hour, min, sec, (sign < 0), customID);
  1.1284 +        int32_t offset = sign * ((hour * 60 + min) * 60 + sec) * 1000;
  1.1285 +        return new SimpleTimeZone(offset, customID);
  1.1286 +    }
  1.1287 +    return NULL;
  1.1288 +}
  1.1289 +
  1.1290 +UnicodeString&
  1.1291 +TimeZone::getCustomID(const UnicodeString& id, UnicodeString& normalized, UErrorCode& status) {
  1.1292 +    normalized.remove();
  1.1293 +    if (U_FAILURE(status)) {
  1.1294 +        return normalized;
  1.1295 +    }
  1.1296 +    int32_t sign, hour, min, sec;
  1.1297 +    if (parseCustomID(id, sign, hour, min, sec)) {
  1.1298 +        formatCustomID(hour, min, sec, (sign < 0), normalized);
  1.1299 +    }
  1.1300 +    return normalized;
  1.1301 +}
  1.1302 +
  1.1303 +UBool
  1.1304 +TimeZone::parseCustomID(const UnicodeString& id, int32_t& sign,
  1.1305 +                        int32_t& hour, int32_t& min, int32_t& sec) {
  1.1306 +    static const int32_t         kParseFailed = -99999;
  1.1307 +
  1.1308 +    NumberFormat* numberFormat = 0;
  1.1309 +    UnicodeString idUppercase = id;
  1.1310 +    idUppercase.toUpper("");
  1.1311 +
  1.1312 +    if (id.length() > GMT_ID_LENGTH &&
  1.1313 +        idUppercase.startsWith(GMT_ID, GMT_ID_LENGTH))
  1.1314 +    {
  1.1315 +        ParsePosition pos(GMT_ID_LENGTH);
  1.1316 +        sign = 1;
  1.1317 +        hour = 0;
  1.1318 +        min = 0;
  1.1319 +        sec = 0;
  1.1320 +
  1.1321 +        if (id[pos.getIndex()] == MINUS /*'-'*/) {
  1.1322 +            sign = -1;
  1.1323 +        } else if (id[pos.getIndex()] != PLUS /*'+'*/) {
  1.1324 +            return FALSE;
  1.1325 +        }
  1.1326 +        pos.setIndex(pos.getIndex() + 1);
  1.1327 +
  1.1328 +        UErrorCode success = U_ZERO_ERROR;
  1.1329 +        numberFormat = NumberFormat::createInstance(success);
  1.1330 +        if(U_FAILURE(success)){
  1.1331 +            return FALSE;
  1.1332 +        }
  1.1333 +        numberFormat->setParseIntegerOnly(TRUE);
  1.1334 +        //numberFormat->setLenient(TRUE); // TODO: May need to set this, depends on latest timezone parsing
  1.1335 +
  1.1336 +        // Look for either hh:mm, hhmm, or hh
  1.1337 +        int32_t start = pos.getIndex();
  1.1338 +        Formattable n(kParseFailed);
  1.1339 +        numberFormat->parse(id, n, pos);
  1.1340 +        if (pos.getIndex() == start) {
  1.1341 +            delete numberFormat;
  1.1342 +            return FALSE;
  1.1343 +        }
  1.1344 +        hour = n.getLong();
  1.1345 +
  1.1346 +        if (pos.getIndex() < id.length()) {
  1.1347 +            if (pos.getIndex() - start > 2
  1.1348 +                || id[pos.getIndex()] != COLON) {
  1.1349 +                delete numberFormat;
  1.1350 +                return FALSE;
  1.1351 +            }
  1.1352 +            // hh:mm
  1.1353 +            pos.setIndex(pos.getIndex() + 1);
  1.1354 +            int32_t oldPos = pos.getIndex();
  1.1355 +            n.setLong(kParseFailed);
  1.1356 +            numberFormat->parse(id, n, pos);
  1.1357 +            if ((pos.getIndex() - oldPos) != 2) {
  1.1358 +                // must be 2 digits
  1.1359 +                delete numberFormat;
  1.1360 +                return FALSE;
  1.1361 +            }
  1.1362 +            min = n.getLong();
  1.1363 +            if (pos.getIndex() < id.length()) {
  1.1364 +                if (id[pos.getIndex()] != COLON) {
  1.1365 +                    delete numberFormat;
  1.1366 +                    return FALSE;
  1.1367 +                }
  1.1368 +                // [:ss]
  1.1369 +                pos.setIndex(pos.getIndex() + 1);
  1.1370 +                oldPos = pos.getIndex();
  1.1371 +                n.setLong(kParseFailed);
  1.1372 +                numberFormat->parse(id, n, pos);
  1.1373 +                if (pos.getIndex() != id.length()
  1.1374 +                        || (pos.getIndex() - oldPos) != 2) {
  1.1375 +                    delete numberFormat;
  1.1376 +                    return FALSE;
  1.1377 +                }
  1.1378 +                sec = n.getLong();
  1.1379 +            }
  1.1380 +        } else {
  1.1381 +            // Supported formats are below -
  1.1382 +            //
  1.1383 +            // HHmmss
  1.1384 +            // Hmmss
  1.1385 +            // HHmm
  1.1386 +            // Hmm
  1.1387 +            // HH
  1.1388 +            // H
  1.1389 +
  1.1390 +            int32_t length = pos.getIndex() - start;
  1.1391 +            if (length <= 0 || 6 < length) {
  1.1392 +                // invalid length
  1.1393 +                delete numberFormat;
  1.1394 +                return FALSE;
  1.1395 +            }
  1.1396 +            switch (length) {
  1.1397 +                case 1:
  1.1398 +                case 2:
  1.1399 +                    // already set to hour
  1.1400 +                    break;
  1.1401 +                case 3:
  1.1402 +                case 4:
  1.1403 +                    min = hour % 100;
  1.1404 +                    hour /= 100;
  1.1405 +                    break;
  1.1406 +                case 5:
  1.1407 +                case 6:
  1.1408 +                    sec = hour % 100;
  1.1409 +                    min = (hour/100) % 100;
  1.1410 +                    hour /= 10000;
  1.1411 +                    break;
  1.1412 +            }
  1.1413 +        }
  1.1414 +
  1.1415 +        delete numberFormat;
  1.1416 +
  1.1417 +        if (hour > kMAX_CUSTOM_HOUR || min > kMAX_CUSTOM_MIN || sec > kMAX_CUSTOM_SEC) {
  1.1418 +            return FALSE;
  1.1419 +        }
  1.1420 +        return TRUE;
  1.1421 +    }
  1.1422 +    return FALSE;
  1.1423 +}
  1.1424 +
  1.1425 +UnicodeString&
  1.1426 +TimeZone::formatCustomID(int32_t hour, int32_t min, int32_t sec,
  1.1427 +                         UBool negative, UnicodeString& id) {
  1.1428 +    // Create time zone ID - GMT[+|-]hhmm[ss]
  1.1429 +    id.setTo(GMT_ID, GMT_ID_LENGTH);
  1.1430 +    if (hour | min | sec) {
  1.1431 +        if (negative) {
  1.1432 +            id += (UChar)MINUS;
  1.1433 +        } else {
  1.1434 +            id += (UChar)PLUS;
  1.1435 +        }
  1.1436 +
  1.1437 +        if (hour < 10) {
  1.1438 +            id += (UChar)ZERO_DIGIT;
  1.1439 +        } else {
  1.1440 +            id += (UChar)(ZERO_DIGIT + hour/10);
  1.1441 +        }
  1.1442 +        id += (UChar)(ZERO_DIGIT + hour%10);
  1.1443 +        id += (UChar)COLON;
  1.1444 +        if (min < 10) {
  1.1445 +            id += (UChar)ZERO_DIGIT;
  1.1446 +        } else {
  1.1447 +            id += (UChar)(ZERO_DIGIT + min/10);
  1.1448 +        }
  1.1449 +        id += (UChar)(ZERO_DIGIT + min%10);
  1.1450 +
  1.1451 +        if (sec) {
  1.1452 +            id += (UChar)COLON;
  1.1453 +            if (sec < 10) {
  1.1454 +                id += (UChar)ZERO_DIGIT;
  1.1455 +            } else {
  1.1456 +                id += (UChar)(ZERO_DIGIT + sec/10);
  1.1457 +            }
  1.1458 +            id += (UChar)(ZERO_DIGIT + sec%10);
  1.1459 +        }
  1.1460 +    }
  1.1461 +    return id;
  1.1462 +}
  1.1463 +
  1.1464 +
  1.1465 +UBool
  1.1466 +TimeZone::hasSameRules(const TimeZone& other) const
  1.1467 +{
  1.1468 +    return (getRawOffset() == other.getRawOffset() &&
  1.1469 +            useDaylightTime() == other.useDaylightTime());
  1.1470 +}
  1.1471 +
  1.1472 +static void U_CALLCONV initTZDataVersion(UErrorCode &status) {
  1.1473 +    ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
  1.1474 +    int32_t len = 0;
  1.1475 +    UResourceBundle *bundle = ures_openDirect(NULL, kZONEINFO, &status);
  1.1476 +    const UChar *tzver = ures_getStringByKey(bundle, kTZVERSION, &len, &status);
  1.1477 +
  1.1478 +    if (U_SUCCESS(status)) {
  1.1479 +        if (len >= (int32_t)sizeof(TZDATA_VERSION)) {
  1.1480 +            // Ensure that there is always space for a trailing nul in TZDATA_VERSION
  1.1481 +            len = sizeof(TZDATA_VERSION) - 1;
  1.1482 +        }
  1.1483 +        u_UCharsToChars(tzver, TZDATA_VERSION, len);
  1.1484 +    }
  1.1485 +    ures_close(bundle);
  1.1486 +
  1.1487 +}
  1.1488 +
  1.1489 +const char*
  1.1490 +TimeZone::getTZDataVersion(UErrorCode& status)
  1.1491 +{
  1.1492 +    umtx_initOnce(gTZDataVersionInitOnce, &initTZDataVersion, status);
  1.1493 +    return (const char*)TZDATA_VERSION;
  1.1494 +}
  1.1495 +
  1.1496 +UnicodeString&
  1.1497 +TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UErrorCode& status)
  1.1498 +{
  1.1499 +    UBool isSystemID = FALSE;
  1.1500 +    return getCanonicalID(id, canonicalID, isSystemID, status);
  1.1501 +}
  1.1502 +
  1.1503 +UnicodeString&
  1.1504 +TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UBool& isSystemID,
  1.1505 +                         UErrorCode& status)
  1.1506 +{
  1.1507 +    canonicalID.remove();
  1.1508 +    isSystemID = FALSE;
  1.1509 +    if (U_FAILURE(status)) {
  1.1510 +        return canonicalID;
  1.1511 +    }
  1.1512 +    if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) == 0) {
  1.1513 +        // special case - Etc/Unknown is a canonical ID, but not system ID
  1.1514 +        canonicalID.fastCopyFrom(id);
  1.1515 +        isSystemID = FALSE;
  1.1516 +    } else {
  1.1517 +        ZoneMeta::getCanonicalCLDRID(id, canonicalID, status);
  1.1518 +        if (U_SUCCESS(status)) {
  1.1519 +            isSystemID = TRUE;
  1.1520 +        } else {
  1.1521 +            // Not a system ID
  1.1522 +            status = U_ZERO_ERROR;
  1.1523 +            getCustomID(id, canonicalID, status);
  1.1524 +        }
  1.1525 +    }
  1.1526 +    return canonicalID;
  1.1527 +}
  1.1528 +
  1.1529 +#ifndef U_HIDE_DRAFT_API
  1.1530 +UnicodeString&
  1.1531 +TimeZone::getWindowsID(const UnicodeString& id, UnicodeString& winid, UErrorCode& status) {
  1.1532 +    winid.remove();
  1.1533 +    if (U_FAILURE(status)) {
  1.1534 +        return winid;
  1.1535 +    }
  1.1536 +
  1.1537 +    // canonicalize the input ID
  1.1538 +    UnicodeString canonicalID;
  1.1539 +    UBool isSystemID = FALSE;
  1.1540 +
  1.1541 +    getCanonicalID(id, canonicalID, isSystemID, status);
  1.1542 +    if (U_FAILURE(status) || !isSystemID) {
  1.1543 +        // mapping data is only applicable to tz database IDs
  1.1544 +        return winid;
  1.1545 +    }
  1.1546 +
  1.1547 +    UResourceBundle *mapTimezones = ures_openDirect(NULL, "windowsZones", &status);
  1.1548 +    ures_getByKey(mapTimezones, "mapTimezones", mapTimezones, &status);
  1.1549 +
  1.1550 +    if (U_FAILURE(status)) {
  1.1551 +        return winid;
  1.1552 +    }
  1.1553 +
  1.1554 +    UResourceBundle *winzone = NULL;
  1.1555 +    UBool found = FALSE;
  1.1556 +    while (ures_hasNext(mapTimezones) && !found) {
  1.1557 +        winzone = ures_getNextResource(mapTimezones, winzone, &status);
  1.1558 +        if (U_FAILURE(status)) {
  1.1559 +            break;
  1.1560 +        }
  1.1561 +        if (ures_getType(winzone) != URES_TABLE) {
  1.1562 +            continue;
  1.1563 +        }
  1.1564 +        UResourceBundle *regionalData = NULL;
  1.1565 +        while (ures_hasNext(winzone) && !found) {
  1.1566 +            regionalData = ures_getNextResource(winzone, regionalData, &status);
  1.1567 +            if (U_FAILURE(status)) {
  1.1568 +                break;
  1.1569 +            }
  1.1570 +            if (ures_getType(regionalData) != URES_STRING) {
  1.1571 +                continue;
  1.1572 +            }
  1.1573 +            int32_t len;
  1.1574 +            const UChar *tzids = ures_getString(regionalData, &len, &status);
  1.1575 +            if (U_FAILURE(status)) {
  1.1576 +                break;
  1.1577 +            }
  1.1578 +
  1.1579 +            const UChar *start = tzids;
  1.1580 +            UBool hasNext = TRUE;
  1.1581 +            while (hasNext) {
  1.1582 +                const UChar *end = u_strchr(start, (UChar)0x20);
  1.1583 +                if (end == NULL) {
  1.1584 +                    end = tzids + len;
  1.1585 +                    hasNext = FALSE;
  1.1586 +                }
  1.1587 +                if (canonicalID.compare(start, end - start) == 0) {
  1.1588 +                    winid = UnicodeString(ures_getKey(winzone), -1 , US_INV);
  1.1589 +                    found = TRUE;
  1.1590 +                    break;
  1.1591 +                }
  1.1592 +                start = end + 1;
  1.1593 +            }
  1.1594 +        }
  1.1595 +        ures_close(regionalData);
  1.1596 +    }
  1.1597 +    ures_close(winzone);
  1.1598 +    ures_close(mapTimezones);
  1.1599 +
  1.1600 +    return winid;
  1.1601 +}
  1.1602 +
  1.1603 +#define MAX_WINDOWS_ID_SIZE 128
  1.1604 +
  1.1605 +UnicodeString&
  1.1606 +TimeZone::getIDForWindowsID(const UnicodeString& winid, const char* region, UnicodeString& id, UErrorCode& status) {
  1.1607 +    id.remove();
  1.1608 +    if (U_FAILURE(status)) {
  1.1609 +        return id;
  1.1610 +    }
  1.1611 +
  1.1612 +    UResourceBundle *zones = ures_openDirect(NULL, "windowsZones", &status);
  1.1613 +    ures_getByKey(zones, "mapTimezones", zones, &status);
  1.1614 +    if (U_FAILURE(status)) {
  1.1615 +        ures_close(zones);
  1.1616 +        return id;
  1.1617 +    }
  1.1618 +
  1.1619 +    UErrorCode tmperr = U_ZERO_ERROR;
  1.1620 +    char winidKey[MAX_WINDOWS_ID_SIZE];
  1.1621 +    int32_t winKeyLen = winid.extract(0, winid.length(), winidKey, sizeof(winidKey) - 1, US_INV);
  1.1622 +
  1.1623 +    if (winKeyLen == 0 || winKeyLen >= (int32_t)sizeof(winidKey)) {
  1.1624 +        ures_close(zones);
  1.1625 +        return id;
  1.1626 +    }
  1.1627 +    winidKey[winKeyLen] = 0;
  1.1628 +
  1.1629 +    ures_getByKey(zones, winidKey, zones, &tmperr); // use tmperr, because windows mapping might not
  1.1630 +                                                    // be avaiable by design
  1.1631 +    if (U_FAILURE(tmperr)) {
  1.1632 +        ures_close(zones);
  1.1633 +        return id;
  1.1634 +    }
  1.1635 +
  1.1636 +    const UChar *tzid = NULL;
  1.1637 +    int32_t len = 0;
  1.1638 +    UBool gotID = FALSE;
  1.1639 +    if (region) {
  1.1640 +        const UChar *tzids = ures_getStringByKey(zones, region, &len, &tmperr); // use tmperr, because
  1.1641 +                                                                                // regional mapping is optional
  1.1642 +        if (U_SUCCESS(tmperr)) {
  1.1643 +            // first ID delimited by space is the defasult one
  1.1644 +            const UChar *end = u_strchr(tzids, (UChar)0x20);
  1.1645 +            if (end == NULL) {
  1.1646 +                id.setTo(tzids, -1);
  1.1647 +            } else {
  1.1648 +                id.setTo(tzids, end - tzids);
  1.1649 +            }
  1.1650 +            gotID = TRUE;
  1.1651 +        }
  1.1652 +    }
  1.1653 +
  1.1654 +    if (!gotID) {
  1.1655 +        tzid = ures_getStringByKey(zones, "001", &len, &status);    // using status, because "001" must be
  1.1656 +                                                                // available at this point
  1.1657 +        if (U_SUCCESS(status)) {
  1.1658 +            id.setTo(tzid, len);
  1.1659 +        }
  1.1660 +    }
  1.1661 +
  1.1662 +    ures_close(zones);
  1.1663 +    return id;
  1.1664 +}
  1.1665 +#endif /* U_HIDE_DRAFT_API */
  1.1666 +
  1.1667 +
  1.1668 +U_NAMESPACE_END
  1.1669 +
  1.1670 +#endif /* #if !UCONFIG_NO_FORMATTING */
  1.1671 +
  1.1672 +//eof

mercurial