intl/icu/source/i18n/olsontz.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/intl/icu/source/i18n/olsontz.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1081 @@
     1.4 +/*
     1.5 +**********************************************************************
     1.6 +* Copyright (c) 2003-2013, International Business Machines
     1.7 +* Corporation and others.  All Rights Reserved.
     1.8 +**********************************************************************
     1.9 +* Author: Alan Liu
    1.10 +* Created: July 21 2003
    1.11 +* Since: ICU 2.8
    1.12 +**********************************************************************
    1.13 +*/
    1.14 +
    1.15 +#include "utypeinfo.h"  // for 'typeid' to work
    1.16 +
    1.17 +#include "olsontz.h"
    1.18 +
    1.19 +#if !UCONFIG_NO_FORMATTING
    1.20 +
    1.21 +#include "unicode/ures.h"
    1.22 +#include "unicode/simpletz.h"
    1.23 +#include "unicode/gregocal.h"
    1.24 +#include "gregoimp.h"
    1.25 +#include "cmemory.h"
    1.26 +#include "uassert.h"
    1.27 +#include "uvector.h"
    1.28 +#include <float.h> // DBL_MAX
    1.29 +#include "uresimp.h" // struct UResourceBundle
    1.30 +#include "zonemeta.h"
    1.31 +#include "umutex.h"
    1.32 +
    1.33 +#ifdef U_DEBUG_TZ
    1.34 +# include <stdio.h>
    1.35 +# include "uresimp.h" // for debugging
    1.36 +
    1.37 +static void debug_tz_loc(const char *f, int32_t l)
    1.38 +{
    1.39 +  fprintf(stderr, "%s:%d: ", f, l);
    1.40 +}
    1.41 +
    1.42 +static void debug_tz_msg(const char *pat, ...)
    1.43 +{
    1.44 +  va_list ap;
    1.45 +  va_start(ap, pat);
    1.46 +  vfprintf(stderr, pat, ap);
    1.47 +  fflush(stderr);
    1.48 +}
    1.49 +// must use double parens, i.e.:  U_DEBUG_TZ_MSG(("four is: %d",4));
    1.50 +#define U_DEBUG_TZ_MSG(x) {debug_tz_loc(__FILE__,__LINE__);debug_tz_msg x;}
    1.51 +#else
    1.52 +#define U_DEBUG_TZ_MSG(x)
    1.53 +#endif
    1.54 +
    1.55 +static UBool arrayEqual(const void *a1, const void *a2, int32_t size) {
    1.56 +    if (a1 == NULL && a2 == NULL) {
    1.57 +        return TRUE;
    1.58 +    }
    1.59 +    if ((a1 != NULL && a2 == NULL) || (a1 == NULL && a2 != NULL)) {
    1.60 +        return FALSE;
    1.61 +    }
    1.62 +    if (a1 == a2) {
    1.63 +        return TRUE;
    1.64 +    }
    1.65 +
    1.66 +    return (uprv_memcmp(a1, a2, size) == 0);
    1.67 +}
    1.68 +
    1.69 +U_NAMESPACE_BEGIN
    1.70 +
    1.71 +#define kTRANS          "trans"
    1.72 +#define kTRANSPRE32     "transPre32"
    1.73 +#define kTRANSPOST32    "transPost32"
    1.74 +#define kTYPEOFFSETS    "typeOffsets"
    1.75 +#define kTYPEMAP        "typeMap"
    1.76 +#define kLINKS          "links"
    1.77 +#define kFINALRULE      "finalRule"
    1.78 +#define kFINALRAW       "finalRaw"
    1.79 +#define kFINALYEAR      "finalYear"
    1.80 +
    1.81 +#define SECONDS_PER_DAY (24*60*60)
    1.82 +
    1.83 +static const int32_t ZEROS[] = {0,0};
    1.84 +
    1.85 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OlsonTimeZone)
    1.86 +
    1.87 +/**
    1.88 + * Default constructor.  Creates a time zone with an empty ID and
    1.89 + * a fixed GMT offset of zero.
    1.90 + */
    1.91 +/*OlsonTimeZone::OlsonTimeZone() : finalYear(INT32_MAX), finalMillis(DBL_MAX), finalZone(0), transitionRulesInitialized(FALSE) {
    1.92 +    clearTransitionRules();
    1.93 +    constructEmpty();
    1.94 +}*/
    1.95 +
    1.96 +/**
    1.97 + * Construct a GMT+0 zone with no transitions.  This is done when a
    1.98 + * constructor fails so the resultant object is well-behaved.
    1.99 + */
   1.100 +void OlsonTimeZone::constructEmpty() {
   1.101 +    canonicalID = NULL;
   1.102 +
   1.103 +    transitionCountPre32 = transitionCount32 = transitionCountPost32 = 0;
   1.104 +    transitionTimesPre32 = transitionTimes32 = transitionTimesPost32 = NULL;
   1.105 +
   1.106 +    typeMapData = NULL;
   1.107 +
   1.108 +    typeCount = 1;
   1.109 +    typeOffsets = ZEROS;
   1.110 +
   1.111 +    finalZone = NULL;
   1.112 +}
   1.113 +
   1.114 +/**
   1.115 + * Construct from a resource bundle
   1.116 + * @param top the top-level zoneinfo resource bundle.  This is used
   1.117 + * to lookup the rule that `res' may refer to, if there is one.
   1.118 + * @param res the resource bundle of the zone to be constructed
   1.119 + * @param ec input-output error code
   1.120 + */
   1.121 +OlsonTimeZone::OlsonTimeZone(const UResourceBundle* top,
   1.122 +                             const UResourceBundle* res,
   1.123 +                             const UnicodeString& tzid,
   1.124 +                             UErrorCode& ec) :
   1.125 +  BasicTimeZone(tzid), finalZone(NULL)
   1.126 +{
   1.127 +    clearTransitionRules();
   1.128 +    U_DEBUG_TZ_MSG(("OlsonTimeZone(%s)\n", ures_getKey((UResourceBundle*)res)));
   1.129 +    if ((top == NULL || res == NULL) && U_SUCCESS(ec)) {
   1.130 +        ec = U_ILLEGAL_ARGUMENT_ERROR;
   1.131 +    }
   1.132 +    if (U_SUCCESS(ec)) {
   1.133 +        // TODO -- clean up -- Doesn't work if res points to an alias
   1.134 +        //        // TODO remove nonconst casts below when ures_* API is fixed
   1.135 +        //        setID(ures_getKey((UResourceBundle*) res)); // cast away const
   1.136 +
   1.137 +        int32_t len;
   1.138 +        UResourceBundle r;
   1.139 +        ures_initStackObject(&r);
   1.140 +
   1.141 +        // Pre-32bit second transitions
   1.142 +        ures_getByKey(res, kTRANSPRE32, &r, &ec);
   1.143 +        transitionTimesPre32 = ures_getIntVector(&r, &len, &ec);
   1.144 +        transitionCountPre32 = len >> 1;
   1.145 +        if (ec == U_MISSING_RESOURCE_ERROR) {
   1.146 +            // No pre-32bit transitions
   1.147 +            transitionTimesPre32 = NULL;
   1.148 +            transitionCountPre32 = 0;
   1.149 +            ec = U_ZERO_ERROR;
   1.150 +        } else if (U_SUCCESS(ec) && (len < 0 || len > 0x7FFF || (len & 1) != 0) /* len must be even */) {
   1.151 +            ec = U_INVALID_FORMAT_ERROR;
   1.152 +        }
   1.153 +
   1.154 +        // 32bit second transitions
   1.155 +        ures_getByKey(res, kTRANS, &r, &ec);
   1.156 +        transitionTimes32 = ures_getIntVector(&r, &len, &ec);
   1.157 +        transitionCount32 = len;
   1.158 +        if (ec == U_MISSING_RESOURCE_ERROR) {
   1.159 +            // No 32bit transitions
   1.160 +            transitionTimes32 = NULL;
   1.161 +            transitionCount32 = 0;
   1.162 +            ec = U_ZERO_ERROR;
   1.163 +        } else if (U_SUCCESS(ec) && (len < 0 || len > 0x7FFF)) {
   1.164 +            ec = U_INVALID_FORMAT_ERROR;
   1.165 +        }
   1.166 +
   1.167 +        // Post-32bit second transitions
   1.168 +        ures_getByKey(res, kTRANSPOST32, &r, &ec);
   1.169 +        transitionTimesPost32 = ures_getIntVector(&r, &len, &ec);
   1.170 +        transitionCountPost32 = len >> 1;
   1.171 +        if (ec == U_MISSING_RESOURCE_ERROR) {
   1.172 +            // No pre-32bit transitions
   1.173 +            transitionTimesPost32 = NULL;
   1.174 +            transitionCountPost32 = 0;
   1.175 +            ec = U_ZERO_ERROR;
   1.176 +        } else if (U_SUCCESS(ec) && (len < 0 || len > 0x7FFF || (len & 1) != 0) /* len must be even */) {
   1.177 +            ec = U_INVALID_FORMAT_ERROR;
   1.178 +        }
   1.179 +
   1.180 +        // Type offsets list must be of even size, with size >= 2
   1.181 +        ures_getByKey(res, kTYPEOFFSETS, &r, &ec);
   1.182 +        typeOffsets = ures_getIntVector(&r, &len, &ec);
   1.183 +        if (U_SUCCESS(ec) && (len < 2 || len > 0x7FFE || (len & 1) != 0)) {
   1.184 +            ec = U_INVALID_FORMAT_ERROR;
   1.185 +        }
   1.186 +        typeCount = (int16_t) len >> 1;
   1.187 +
   1.188 +        // Type map data must be of the same size as the transition count
   1.189 +        typeMapData =  NULL;
   1.190 +        if (transitionCount() > 0) {
   1.191 +            ures_getByKey(res, kTYPEMAP, &r, &ec);
   1.192 +            typeMapData = ures_getBinary(&r, &len, &ec);
   1.193 +            if (ec == U_MISSING_RESOURCE_ERROR) {
   1.194 +                // no type mapping data
   1.195 +                ec = U_INVALID_FORMAT_ERROR;
   1.196 +            } else if (U_SUCCESS(ec) && len != transitionCount()) {
   1.197 +                ec = U_INVALID_FORMAT_ERROR;
   1.198 +            }
   1.199 +        }
   1.200 +
   1.201 +        // Process final rule and data, if any
   1.202 +        const UChar *ruleIdUStr = ures_getStringByKey(res, kFINALRULE, &len, &ec);
   1.203 +        ures_getByKey(res, kFINALRAW, &r, &ec);
   1.204 +        int32_t ruleRaw = ures_getInt(&r, &ec);
   1.205 +        ures_getByKey(res, kFINALYEAR, &r, &ec);
   1.206 +        int32_t ruleYear = ures_getInt(&r, &ec);
   1.207 +        if (U_SUCCESS(ec)) {
   1.208 +            UnicodeString ruleID(TRUE, ruleIdUStr, len);
   1.209 +            UResourceBundle *rule = TimeZone::loadRule(top, ruleID, NULL, ec);
   1.210 +            const int32_t *ruleData = ures_getIntVector(rule, &len, &ec); 
   1.211 +            if (U_SUCCESS(ec) && len == 11) {
   1.212 +                UnicodeString emptyStr;
   1.213 +                finalZone = new SimpleTimeZone(
   1.214 +                    ruleRaw * U_MILLIS_PER_SECOND,
   1.215 +                    emptyStr,
   1.216 +                    (int8_t)ruleData[0], (int8_t)ruleData[1], (int8_t)ruleData[2],
   1.217 +                    ruleData[3] * U_MILLIS_PER_SECOND,
   1.218 +                    (SimpleTimeZone::TimeMode) ruleData[4],
   1.219 +                    (int8_t)ruleData[5], (int8_t)ruleData[6], (int8_t)ruleData[7],
   1.220 +                    ruleData[8] * U_MILLIS_PER_SECOND,
   1.221 +                    (SimpleTimeZone::TimeMode) ruleData[9],
   1.222 +                    ruleData[10] * U_MILLIS_PER_SECOND, ec);
   1.223 +                if (finalZone == NULL) {
   1.224 +                    ec = U_MEMORY_ALLOCATION_ERROR;
   1.225 +                } else {
   1.226 +                    finalStartYear = ruleYear;
   1.227 +
   1.228 +                    // Note: Setting finalStartYear to the finalZone is problematic.  When a date is around
   1.229 +                    // year boundary, SimpleTimeZone may return false result when DST is observed at the 
   1.230 +                    // beginning of year.  We could apply safe margin (day or two), but when one of recurrent
   1.231 +                    // rules falls around year boundary, it could return false result.  Without setting the
   1.232 +                    // start year, finalZone works fine around the year boundary of the start year.
   1.233 +
   1.234 +                    // finalZone->setStartYear(finalStartYear);
   1.235 +
   1.236 +
   1.237 +                    // Compute the millis for Jan 1, 0:00 GMT of the finalYear
   1.238 +
   1.239 +                    // Note: finalStartMillis is used for detecting either if
   1.240 +                    // historic transition data or finalZone to be used.  In an
   1.241 +                    // extreme edge case - for example, two transitions fall into
   1.242 +                    // small windows of time around the year boundary, this may
   1.243 +                    // result incorrect offset computation.  But I think it will
   1.244 +                    // never happen practically.  Yoshito - Feb 20, 2010
   1.245 +                    finalStartMillis = Grego::fieldsToDay(finalStartYear, 0, 1) * U_MILLIS_PER_DAY;
   1.246 +                }
   1.247 +            } else {
   1.248 +                ec = U_INVALID_FORMAT_ERROR;
   1.249 +            }
   1.250 +            ures_close(rule);
   1.251 +        } else if (ec == U_MISSING_RESOURCE_ERROR) {
   1.252 +            // No final zone
   1.253 +            ec = U_ZERO_ERROR;
   1.254 +        }
   1.255 +        ures_close(&r);
   1.256 +
   1.257 +        // initialize canonical ID
   1.258 +        canonicalID = ZoneMeta::getCanonicalCLDRID(tzid, ec);
   1.259 +    }
   1.260 +
   1.261 +    if (U_FAILURE(ec)) {
   1.262 +        constructEmpty();
   1.263 +    }
   1.264 +}
   1.265 +
   1.266 +/**
   1.267 + * Copy constructor
   1.268 + */
   1.269 +OlsonTimeZone::OlsonTimeZone(const OlsonTimeZone& other) :
   1.270 +    BasicTimeZone(other), finalZone(0) {
   1.271 +    *this = other;
   1.272 +}
   1.273 +
   1.274 +/**
   1.275 + * Assignment operator
   1.276 + */
   1.277 +OlsonTimeZone& OlsonTimeZone::operator=(const OlsonTimeZone& other) {
   1.278 +    canonicalID = other.canonicalID;
   1.279 +
   1.280 +    transitionTimesPre32 = other.transitionTimesPre32;
   1.281 +    transitionTimes32 = other.transitionTimes32;
   1.282 +    transitionTimesPost32 = other.transitionTimesPost32;
   1.283 +
   1.284 +    transitionCountPre32 = other.transitionCountPre32;
   1.285 +    transitionCount32 = other.transitionCount32;
   1.286 +    transitionCountPost32 = other.transitionCountPost32;
   1.287 +
   1.288 +    typeCount = other.typeCount;
   1.289 +    typeOffsets = other.typeOffsets;
   1.290 +    typeMapData = other.typeMapData;
   1.291 +
   1.292 +    delete finalZone;
   1.293 +    finalZone = (other.finalZone != 0) ?
   1.294 +        (SimpleTimeZone*) other.finalZone->clone() : 0;
   1.295 +
   1.296 +    finalStartYear = other.finalStartYear;
   1.297 +    finalStartMillis = other.finalStartMillis;
   1.298 +
   1.299 +    clearTransitionRules();
   1.300 +
   1.301 +    return *this;
   1.302 +}
   1.303 +
   1.304 +/**
   1.305 + * Destructor
   1.306 + */
   1.307 +OlsonTimeZone::~OlsonTimeZone() {
   1.308 +    deleteTransitionRules();
   1.309 +    delete finalZone;
   1.310 +}
   1.311 +
   1.312 +/**
   1.313 + * Returns true if the two TimeZone objects are equal.
   1.314 + */
   1.315 +UBool OlsonTimeZone::operator==(const TimeZone& other) const {
   1.316 +    return ((this == &other) ||
   1.317 +            (typeid(*this) == typeid(other) &&
   1.318 +            TimeZone::operator==(other) &&
   1.319 +            hasSameRules(other)));
   1.320 +}
   1.321 +
   1.322 +/**
   1.323 + * TimeZone API.
   1.324 + */
   1.325 +TimeZone* OlsonTimeZone::clone() const {
   1.326 +    return new OlsonTimeZone(*this);
   1.327 +}
   1.328 +
   1.329 +/**
   1.330 + * TimeZone API.
   1.331 + */
   1.332 +int32_t OlsonTimeZone::getOffset(uint8_t era, int32_t year, int32_t month,
   1.333 +                                 int32_t dom, uint8_t dow,
   1.334 +                                 int32_t millis, UErrorCode& ec) const {
   1.335 +    if (month < UCAL_JANUARY || month > UCAL_DECEMBER) {
   1.336 +        if (U_SUCCESS(ec)) {
   1.337 +            ec = U_ILLEGAL_ARGUMENT_ERROR;
   1.338 +        }
   1.339 +        return 0;
   1.340 +    } else {
   1.341 +        return getOffset(era, year, month, dom, dow, millis,
   1.342 +                         Grego::monthLength(year, month),
   1.343 +                         ec);
   1.344 +    }
   1.345 +}
   1.346 +
   1.347 +/**
   1.348 + * TimeZone API.
   1.349 + */
   1.350 +int32_t OlsonTimeZone::getOffset(uint8_t era, int32_t year, int32_t month,
   1.351 +                                 int32_t dom, uint8_t dow,
   1.352 +                                 int32_t millis, int32_t monthLength,
   1.353 +                                 UErrorCode& ec) const {
   1.354 +    if (U_FAILURE(ec)) {
   1.355 +        return 0;
   1.356 +    }
   1.357 +
   1.358 +    if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC)
   1.359 +        || month < UCAL_JANUARY
   1.360 +        || month > UCAL_DECEMBER
   1.361 +        || dom < 1
   1.362 +        || dom > monthLength
   1.363 +        || dow < UCAL_SUNDAY
   1.364 +        || dow > UCAL_SATURDAY
   1.365 +        || millis < 0
   1.366 +        || millis >= U_MILLIS_PER_DAY
   1.367 +        || monthLength < 28
   1.368 +        || monthLength > 31) {
   1.369 +        ec = U_ILLEGAL_ARGUMENT_ERROR;
   1.370 +        return 0;
   1.371 +    }
   1.372 +
   1.373 +    if (era == GregorianCalendar::BC) {
   1.374 +        year = -year;
   1.375 +    }
   1.376 +
   1.377 +    if (finalZone != NULL && year >= finalStartYear) {
   1.378 +        return finalZone->getOffset(era, year, month, dom, dow,
   1.379 +                                    millis, monthLength, ec);
   1.380 +    }
   1.381 +
   1.382 +    // Compute local epoch millis from input fields
   1.383 +    UDate date = (UDate)(Grego::fieldsToDay(year, month, dom) * U_MILLIS_PER_DAY + millis);
   1.384 +    int32_t rawoff, dstoff;
   1.385 +    getHistoricalOffset(date, TRUE, kDaylight, kStandard, rawoff, dstoff);
   1.386 +    return rawoff + dstoff;
   1.387 +}
   1.388 +
   1.389 +/**
   1.390 + * TimeZone API.
   1.391 + */
   1.392 +void OlsonTimeZone::getOffset(UDate date, UBool local, int32_t& rawoff,
   1.393 +                              int32_t& dstoff, UErrorCode& ec) const {
   1.394 +    if (U_FAILURE(ec)) {
   1.395 +        return;
   1.396 +    }
   1.397 +    if (finalZone != NULL && date >= finalStartMillis) {
   1.398 +        finalZone->getOffset(date, local, rawoff, dstoff, ec);
   1.399 +    } else {
   1.400 +        getHistoricalOffset(date, local, kFormer, kLatter, rawoff, dstoff);
   1.401 +    }
   1.402 +}
   1.403 +
   1.404 +void
   1.405 +OlsonTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
   1.406 +                                  int32_t& rawoff, int32_t& dstoff, UErrorCode& ec) const {
   1.407 +    if (U_FAILURE(ec)) {
   1.408 +        return;
   1.409 +    }
   1.410 +    if (finalZone != NULL && date >= finalStartMillis) {
   1.411 +        finalZone->getOffsetFromLocal(date, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff, ec);
   1.412 +    } else {
   1.413 +        getHistoricalOffset(date, TRUE, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff);
   1.414 +    }
   1.415 +}
   1.416 +
   1.417 +
   1.418 +/**
   1.419 + * TimeZone API.
   1.420 + */
   1.421 +void OlsonTimeZone::setRawOffset(int32_t /*offsetMillis*/) {
   1.422 +    // We don't support this operation, since OlsonTimeZones are
   1.423 +    // immutable (except for the ID, which is in the base class).
   1.424 +
   1.425 +    // Nothing to do!
   1.426 +}
   1.427 +
   1.428 +/**
   1.429 + * TimeZone API.
   1.430 + */
   1.431 +int32_t OlsonTimeZone::getRawOffset() const {
   1.432 +    UErrorCode ec = U_ZERO_ERROR;
   1.433 +    int32_t raw, dst;
   1.434 +    getOffset((double) uprv_getUTCtime() * U_MILLIS_PER_SECOND,
   1.435 +              FALSE, raw, dst, ec);
   1.436 +    return raw;
   1.437 +}
   1.438 +
   1.439 +#if defined U_DEBUG_TZ
   1.440 +void printTime(double ms) {
   1.441 +            int32_t year, month, dom, dow;
   1.442 +            double millis=0;
   1.443 +            double days = ClockMath::floorDivide(((double)ms), (double)U_MILLIS_PER_DAY, millis);
   1.444 +            
   1.445 +            Grego::dayToFields(days, year, month, dom, dow);
   1.446 +            U_DEBUG_TZ_MSG(("   getHistoricalOffset:  time %.1f (%04d.%02d.%02d+%.1fh)\n", ms,
   1.447 +                            year, month+1, dom, (millis/kOneHour)));
   1.448 +    }
   1.449 +#endif
   1.450 +
   1.451 +int64_t
   1.452 +OlsonTimeZone::transitionTimeInSeconds(int16_t transIdx) const {
   1.453 +    U_ASSERT(transIdx >= 0 && transIdx < transitionCount()); 
   1.454 +
   1.455 +    if (transIdx < transitionCountPre32) {
   1.456 +        return (((int64_t)((uint32_t)transitionTimesPre32[transIdx << 1])) << 32)
   1.457 +            | ((int64_t)((uint32_t)transitionTimesPre32[(transIdx << 1) + 1]));
   1.458 +    }
   1.459 +
   1.460 +    transIdx -= transitionCountPre32;
   1.461 +    if (transIdx < transitionCount32) {
   1.462 +        return (int64_t)transitionTimes32[transIdx];
   1.463 +    }
   1.464 +
   1.465 +    transIdx -= transitionCount32;
   1.466 +    return (((int64_t)((uint32_t)transitionTimesPost32[transIdx << 1])) << 32)
   1.467 +        | ((int64_t)((uint32_t)transitionTimesPost32[(transIdx << 1) + 1]));
   1.468 +}
   1.469 +
   1.470 +// Maximum absolute offset in seconds (86400 seconds = 1 day)
   1.471 +// getHistoricalOffset uses this constant as safety margin of
   1.472 +// quick zone transition checking.
   1.473 +#define MAX_OFFSET_SECONDS 86400
   1.474 +
   1.475 +void
   1.476 +OlsonTimeZone::getHistoricalOffset(UDate date, UBool local,
   1.477 +                                   int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt,
   1.478 +                                   int32_t& rawoff, int32_t& dstoff) const {
   1.479 +    U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst)\n",
   1.480 +        date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt));
   1.481 +#if defined U_DEBUG_TZ
   1.482 +        printTime(date*1000.0);
   1.483 +#endif
   1.484 +    int16_t transCount = transitionCount();
   1.485 +
   1.486 +    if (transCount > 0) {
   1.487 +        double sec = uprv_floor(date / U_MILLIS_PER_SECOND);
   1.488 +        if (!local && sec < transitionTimeInSeconds(0)) {
   1.489 +            // Before the first transition time
   1.490 +            rawoff = initialRawOffset() * U_MILLIS_PER_SECOND;
   1.491 +            dstoff = initialDstOffset() * U_MILLIS_PER_SECOND;
   1.492 +        } else {
   1.493 +            // Linear search from the end is the fastest approach, since
   1.494 +            // most lookups will happen at/near the end.
   1.495 +            int16_t transIdx;
   1.496 +            for (transIdx = transCount - 1; transIdx >= 0; transIdx--) {
   1.497 +                int64_t transition = transitionTimeInSeconds(transIdx);
   1.498 +
   1.499 +                if (local && (sec >= (transition - MAX_OFFSET_SECONDS))) {
   1.500 +                    int32_t offsetBefore = zoneOffsetAt(transIdx - 1);
   1.501 +                    UBool dstBefore = dstOffsetAt(transIdx - 1) != 0;
   1.502 +
   1.503 +                    int32_t offsetAfter = zoneOffsetAt(transIdx);
   1.504 +                    UBool dstAfter = dstOffsetAt(transIdx) != 0;
   1.505 +
   1.506 +                    UBool dstToStd = dstBefore && !dstAfter;
   1.507 +                    UBool stdToDst = !dstBefore && dstAfter;
   1.508 +                    
   1.509 +                    if (offsetAfter - offsetBefore >= 0) {
   1.510 +                        // Positive transition, which makes a non-existing local time range
   1.511 +                        if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd)
   1.512 +                                || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
   1.513 +                            transition += offsetBefore;
   1.514 +                        } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst)
   1.515 +                                || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
   1.516 +                            transition += offsetAfter;
   1.517 +                        } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) {
   1.518 +                            transition += offsetBefore;
   1.519 +                        } else {
   1.520 +                            // Interprets the time with rule before the transition,
   1.521 +                            // default for non-existing time range
   1.522 +                            transition += offsetAfter;
   1.523 +                        }
   1.524 +                    } else {
   1.525 +                        // Negative transition, which makes a duplicated local time range
   1.526 +                        if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd)
   1.527 +                                || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
   1.528 +                            transition += offsetAfter;
   1.529 +                        } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst)
   1.530 +                                || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
   1.531 +                            transition += offsetBefore;
   1.532 +                        } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) {
   1.533 +                            transition += offsetBefore;
   1.534 +                        } else {
   1.535 +                            // Interprets the time with rule after the transition,
   1.536 +                            // default for duplicated local time range
   1.537 +                            transition += offsetAfter;
   1.538 +                        }
   1.539 +                    }
   1.540 +                }
   1.541 +                if (sec >= transition) {
   1.542 +                    break;
   1.543 +                }
   1.544 +            }
   1.545 +            // transIdx could be -1 when local=true
   1.546 +            rawoff = rawOffsetAt(transIdx) * U_MILLIS_PER_SECOND;
   1.547 +            dstoff = dstOffsetAt(transIdx) * U_MILLIS_PER_SECOND;
   1.548 +        }
   1.549 +    } else {
   1.550 +        // No transitions, single pair of offsets only
   1.551 +        rawoff = initialRawOffset() * U_MILLIS_PER_SECOND;
   1.552 +        dstoff = initialDstOffset() * U_MILLIS_PER_SECOND;
   1.553 +    }
   1.554 +    U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst) - raw=%d, dst=%d\n",
   1.555 +        date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt, rawoff, dstoff));
   1.556 +}
   1.557 +
   1.558 +/**
   1.559 + * TimeZone API.
   1.560 + */
   1.561 +UBool OlsonTimeZone::useDaylightTime() const {
   1.562 +    // If DST was observed in 1942 (for example) but has never been
   1.563 +    // observed from 1943 to the present, most clients will expect
   1.564 +    // this method to return FALSE.  This method determines whether
   1.565 +    // DST is in use in the current year (at any point in the year)
   1.566 +    // and returns TRUE if so.
   1.567 +
   1.568 +    UDate current = uprv_getUTCtime();
   1.569 +    if (finalZone != NULL && current >= finalStartMillis) {
   1.570 +        return finalZone->useDaylightTime();
   1.571 +    }
   1.572 +
   1.573 +    int32_t year, month, dom, dow, doy, mid;
   1.574 +    Grego::timeToFields(current, year, month, dom, dow, doy, mid);
   1.575 +
   1.576 +    // Find start of this year, and start of next year
   1.577 +    double start = Grego::fieldsToDay(year, 0, 1) * SECONDS_PER_DAY;
   1.578 +    double limit = Grego::fieldsToDay(year+1, 0, 1) * SECONDS_PER_DAY;
   1.579 +
   1.580 +    // Return TRUE if DST is observed at any time during the current
   1.581 +    // year.
   1.582 +    for (int16_t i = 0; i < transitionCount(); ++i) {
   1.583 +        double transition = (double)transitionTimeInSeconds(i);
   1.584 +        if (transition >= limit) {
   1.585 +            break;
   1.586 +        }
   1.587 +        if ((transition >= start && dstOffsetAt(i) != 0)
   1.588 +                || (transition > start && dstOffsetAt(i - 1) != 0)) {
   1.589 +            return TRUE;
   1.590 +        }
   1.591 +    }
   1.592 +    return FALSE;
   1.593 +}
   1.594 +int32_t 
   1.595 +OlsonTimeZone::getDSTSavings() const{
   1.596 +    if (finalZone != NULL){
   1.597 +        return finalZone->getDSTSavings();
   1.598 +    }
   1.599 +    return TimeZone::getDSTSavings();
   1.600 +}
   1.601 +/**
   1.602 + * TimeZone API.
   1.603 + */
   1.604 +UBool OlsonTimeZone::inDaylightTime(UDate date, UErrorCode& ec) const {
   1.605 +    int32_t raw, dst;
   1.606 +    getOffset(date, FALSE, raw, dst, ec);
   1.607 +    return dst != 0;
   1.608 +}
   1.609 +
   1.610 +UBool
   1.611 +OlsonTimeZone::hasSameRules(const TimeZone &other) const {
   1.612 +    if (this == &other) {
   1.613 +        return TRUE;
   1.614 +    }
   1.615 +    const OlsonTimeZone* z = dynamic_cast<const OlsonTimeZone*>(&other);
   1.616 +    if (z == NULL) {
   1.617 +        return FALSE;
   1.618 +    }
   1.619 +
   1.620 +    // [sic] pointer comparison: typeMapData points into
   1.621 +    // memory-mapped or DLL space, so if two zones have the same
   1.622 +    // pointer, they are equal.
   1.623 +    if (typeMapData == z->typeMapData) {
   1.624 +        return TRUE;
   1.625 +    }
   1.626 +    
   1.627 +    // If the pointers are not equal, the zones may still
   1.628 +    // be equal if their rules and transitions are equal
   1.629 +    if ((finalZone == NULL && z->finalZone != NULL)
   1.630 +        || (finalZone != NULL && z->finalZone == NULL)
   1.631 +        || (finalZone != NULL && z->finalZone != NULL && *finalZone != *z->finalZone)) {
   1.632 +        return FALSE;
   1.633 +    }
   1.634 +
   1.635 +    if (finalZone != NULL) {
   1.636 +        if (finalStartYear != z->finalStartYear || finalStartMillis != z->finalStartMillis) {
   1.637 +            return FALSE;
   1.638 +        }
   1.639 +    }
   1.640 +    if (typeCount != z->typeCount
   1.641 +        || transitionCountPre32 != z->transitionCountPre32
   1.642 +        || transitionCount32 != z->transitionCount32
   1.643 +        || transitionCountPost32 != z->transitionCountPost32) {
   1.644 +        return FALSE;
   1.645 +    }
   1.646 +
   1.647 +    return
   1.648 +        arrayEqual(transitionTimesPre32, z->transitionTimesPre32, sizeof(transitionTimesPre32[0]) * transitionCountPre32 << 1)
   1.649 +        && arrayEqual(transitionTimes32, z->transitionTimes32, sizeof(transitionTimes32[0]) * transitionCount32)
   1.650 +        && arrayEqual(transitionTimesPost32, z->transitionTimesPost32, sizeof(transitionTimesPost32[0]) * transitionCountPost32 << 1)
   1.651 +        && arrayEqual(typeOffsets, z->typeOffsets, sizeof(typeOffsets[0]) * typeCount << 1)
   1.652 +        && arrayEqual(typeMapData, z->typeMapData, sizeof(typeMapData[0]) * transitionCount());
   1.653 +}
   1.654 +
   1.655 +void
   1.656 +OlsonTimeZone::clearTransitionRules(void) {
   1.657 +    initialRule = NULL;
   1.658 +    firstTZTransition = NULL;
   1.659 +    firstFinalTZTransition = NULL;
   1.660 +    historicRules = NULL;
   1.661 +    historicRuleCount = 0;
   1.662 +    finalZoneWithStartYear = NULL;
   1.663 +    firstTZTransitionIdx = 0;
   1.664 +    transitionRulesInitOnce.reset();
   1.665 +}
   1.666 +
   1.667 +void
   1.668 +OlsonTimeZone::deleteTransitionRules(void) {
   1.669 +    if (initialRule != NULL) {
   1.670 +        delete initialRule;
   1.671 +    }
   1.672 +    if (firstTZTransition != NULL) {
   1.673 +        delete firstTZTransition;
   1.674 +    }
   1.675 +    if (firstFinalTZTransition != NULL) {
   1.676 +        delete firstFinalTZTransition;
   1.677 +    }
   1.678 +    if (finalZoneWithStartYear != NULL) {
   1.679 +        delete finalZoneWithStartYear;
   1.680 +    }
   1.681 +    if (historicRules != NULL) {
   1.682 +        for (int i = 0; i < historicRuleCount; i++) {
   1.683 +            if (historicRules[i] != NULL) {
   1.684 +                delete historicRules[i];
   1.685 +            }
   1.686 +        }
   1.687 +        uprv_free(historicRules);
   1.688 +    }
   1.689 +    clearTransitionRules();
   1.690 +}
   1.691 +
   1.692 +/*
   1.693 + * Lazy transition rules initializer
   1.694 + */
   1.695 +
   1.696 +static void U_CALLCONV initRules(OlsonTimeZone *This, UErrorCode &status) {
   1.697 +    This->initTransitionRules(status);
   1.698 +}
   1.699 +    
   1.700 +void
   1.701 +OlsonTimeZone::checkTransitionRules(UErrorCode& status) const {
   1.702 +    OlsonTimeZone *ncThis = const_cast<OlsonTimeZone *>(this);
   1.703 +    umtx_initOnce(ncThis->transitionRulesInitOnce, &initRules, ncThis, status);
   1.704 +}
   1.705 +
   1.706 +void
   1.707 +OlsonTimeZone::initTransitionRules(UErrorCode& status) {
   1.708 +    if(U_FAILURE(status)) {
   1.709 +        return;
   1.710 +    }
   1.711 +    deleteTransitionRules();
   1.712 +    UnicodeString tzid;
   1.713 +    getID(tzid);
   1.714 +
   1.715 +    UnicodeString stdName = tzid + UNICODE_STRING_SIMPLE("(STD)");
   1.716 +    UnicodeString dstName = tzid + UNICODE_STRING_SIMPLE("(DST)");
   1.717 +
   1.718 +    int32_t raw, dst;
   1.719 +
   1.720 +    // Create initial rule
   1.721 +    raw = initialRawOffset() * U_MILLIS_PER_SECOND;
   1.722 +    dst = initialDstOffset() * U_MILLIS_PER_SECOND;
   1.723 +    initialRule = new InitialTimeZoneRule((dst == 0 ? stdName : dstName), raw, dst);
   1.724 +    // Check to make sure initialRule was created
   1.725 +    if (initialRule == NULL) {
   1.726 +        status = U_MEMORY_ALLOCATION_ERROR;
   1.727 +        deleteTransitionRules();
   1.728 +        return;
   1.729 +    }
   1.730 +
   1.731 +    int32_t transCount = transitionCount();
   1.732 +    if (transCount > 0) {
   1.733 +        int16_t transitionIdx, typeIdx;
   1.734 +
   1.735 +        // We probably no longer need to check the first "real" transition
   1.736 +        // here, because the new tzcode remove such transitions already.
   1.737 +        // For now, keeping this code for just in case. Feb 19, 2010 Yoshito
   1.738 +        firstTZTransitionIdx = 0;
   1.739 +        for (transitionIdx = 0; transitionIdx < transCount; transitionIdx++) {
   1.740 +            if (typeMapData[transitionIdx] != 0) { // type 0 is the initial type
   1.741 +                break;
   1.742 +            }
   1.743 +            firstTZTransitionIdx++;
   1.744 +        }
   1.745 +        if (transitionIdx == transCount) {
   1.746 +            // Actually no transitions...
   1.747 +        } else {
   1.748 +            // Build historic rule array
   1.749 +            UDate* times = (UDate*)uprv_malloc(sizeof(UDate)*transCount); /* large enough to store all transition times */
   1.750 +            if (times == NULL) {
   1.751 +                status = U_MEMORY_ALLOCATION_ERROR;
   1.752 +                deleteTransitionRules();
   1.753 +                return;
   1.754 +            }
   1.755 +            for (typeIdx = 0; typeIdx < typeCount; typeIdx++) {
   1.756 +                // Gather all start times for each pair of offsets
   1.757 +                int32_t nTimes = 0;
   1.758 +                for (transitionIdx = firstTZTransitionIdx; transitionIdx < transCount; transitionIdx++) {
   1.759 +                    if (typeIdx == (int16_t)typeMapData[transitionIdx]) {
   1.760 +                        UDate tt = (UDate)transitionTime(transitionIdx);
   1.761 +                        if (finalZone == NULL || tt <= finalStartMillis) {
   1.762 +                            // Exclude transitions after finalMillis
   1.763 +                            times[nTimes++] = tt;
   1.764 +                        }
   1.765 +                    }
   1.766 +                }
   1.767 +                if (nTimes > 0) {
   1.768 +                    // Create a TimeArrayTimeZoneRule
   1.769 +                    raw = typeOffsets[typeIdx << 1] * U_MILLIS_PER_SECOND;
   1.770 +                    dst = typeOffsets[(typeIdx << 1) + 1] * U_MILLIS_PER_SECOND;
   1.771 +                    if (historicRules == NULL) {
   1.772 +                        historicRuleCount = typeCount;
   1.773 +                        historicRules = (TimeArrayTimeZoneRule**)uprv_malloc(sizeof(TimeArrayTimeZoneRule*)*historicRuleCount);
   1.774 +                        if (historicRules == NULL) {
   1.775 +                            status = U_MEMORY_ALLOCATION_ERROR;
   1.776 +                            deleteTransitionRules();
   1.777 +                            uprv_free(times);
   1.778 +                            return;
   1.779 +                        }
   1.780 +                        for (int i = 0; i < historicRuleCount; i++) {
   1.781 +                            // Initialize TimeArrayTimeZoneRule pointers as NULL
   1.782 +                            historicRules[i] = NULL;
   1.783 +                        }
   1.784 +                    }
   1.785 +                    historicRules[typeIdx] = new TimeArrayTimeZoneRule((dst == 0 ? stdName : dstName),
   1.786 +                        raw, dst, times, nTimes, DateTimeRule::UTC_TIME);
   1.787 +                    // Check for memory allocation error
   1.788 +                    if (historicRules[typeIdx] == NULL) {
   1.789 +                        status = U_MEMORY_ALLOCATION_ERROR;
   1.790 +                        deleteTransitionRules();
   1.791 +                        return;
   1.792 +                    }
   1.793 +                }
   1.794 +            }
   1.795 +            uprv_free(times);
   1.796 +
   1.797 +            // Create initial transition
   1.798 +            typeIdx = (int16_t)typeMapData[firstTZTransitionIdx];
   1.799 +            firstTZTransition = new TimeZoneTransition((UDate)transitionTime(firstTZTransitionIdx),
   1.800 +                    *initialRule, *historicRules[typeIdx]);
   1.801 +            // Check to make sure firstTZTransition was created.
   1.802 +            if (firstTZTransition == NULL) {
   1.803 +                status = U_MEMORY_ALLOCATION_ERROR;
   1.804 +                deleteTransitionRules();
   1.805 +                return;
   1.806 +            }
   1.807 +        }
   1.808 +    }
   1.809 +    if (finalZone != NULL) {
   1.810 +        // Get the first occurence of final rule starts
   1.811 +        UDate startTime = (UDate)finalStartMillis;
   1.812 +        TimeZoneRule *firstFinalRule = NULL;
   1.813 +
   1.814 +        if (finalZone->useDaylightTime()) {
   1.815 +            /*
   1.816 +             * Note: When an OlsonTimeZone is constructed, we should set the final year
   1.817 +             * as the start year of finalZone.  However, the bounday condition used for
   1.818 +             * getting offset from finalZone has some problems.
   1.819 +             * For now, we do not set the valid start year when the construction time
   1.820 +             * and create a clone and set the start year when extracting rules.
   1.821 +             */
   1.822 +            finalZoneWithStartYear = (SimpleTimeZone*)finalZone->clone();
   1.823 +            // Check to make sure finalZone was actually cloned.
   1.824 +            if (finalZoneWithStartYear == NULL) {
   1.825 +                status = U_MEMORY_ALLOCATION_ERROR;
   1.826 +                deleteTransitionRules();
   1.827 +                return;
   1.828 +            }
   1.829 +            finalZoneWithStartYear->setStartYear(finalStartYear);
   1.830 +
   1.831 +            TimeZoneTransition tzt;
   1.832 +            finalZoneWithStartYear->getNextTransition(startTime, false, tzt);
   1.833 +            firstFinalRule  = tzt.getTo()->clone();
   1.834 +            // Check to make sure firstFinalRule received proper clone.
   1.835 +            if (firstFinalRule == NULL) {
   1.836 +                status = U_MEMORY_ALLOCATION_ERROR;
   1.837 +                deleteTransitionRules();
   1.838 +                return;
   1.839 +            }
   1.840 +            startTime = tzt.getTime();
   1.841 +        } else {
   1.842 +            // final rule with no transitions
   1.843 +            finalZoneWithStartYear = (SimpleTimeZone*)finalZone->clone();
   1.844 +            // Check to make sure finalZone was actually cloned.
   1.845 +            if (finalZoneWithStartYear == NULL) {
   1.846 +                status = U_MEMORY_ALLOCATION_ERROR;
   1.847 +                deleteTransitionRules();
   1.848 +                return;
   1.849 +            }
   1.850 +            finalZone->getID(tzid);
   1.851 +            firstFinalRule = new TimeArrayTimeZoneRule(tzid,
   1.852 +                finalZone->getRawOffset(), 0, &startTime, 1, DateTimeRule::UTC_TIME);
   1.853 +            // Check firstFinalRule was properly created.
   1.854 +            if (firstFinalRule == NULL) {
   1.855 +                status = U_MEMORY_ALLOCATION_ERROR;
   1.856 +                deleteTransitionRules();
   1.857 +                return;
   1.858 +            }
   1.859 +        }
   1.860 +        TimeZoneRule *prevRule = NULL;
   1.861 +        if (transCount > 0) {
   1.862 +            prevRule = historicRules[typeMapData[transCount - 1]];
   1.863 +        }
   1.864 +        if (prevRule == NULL) {
   1.865 +            // No historic transitions, but only finalZone available
   1.866 +            prevRule = initialRule;
   1.867 +        }
   1.868 +        firstFinalTZTransition = new TimeZoneTransition();
   1.869 +        // Check to make sure firstFinalTZTransition was created before dereferencing
   1.870 +        if (firstFinalTZTransition == NULL) {
   1.871 +            status = U_MEMORY_ALLOCATION_ERROR;
   1.872 +            deleteTransitionRules();
   1.873 +            return;
   1.874 +        }
   1.875 +        firstFinalTZTransition->setTime(startTime);
   1.876 +        firstFinalTZTransition->adoptFrom(prevRule->clone());
   1.877 +        firstFinalTZTransition->adoptTo(firstFinalRule);
   1.878 +    }
   1.879 +}
   1.880 +
   1.881 +UBool
   1.882 +OlsonTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
   1.883 +    UErrorCode status = U_ZERO_ERROR;
   1.884 +    checkTransitionRules(status);
   1.885 +    if (U_FAILURE(status)) {
   1.886 +        return FALSE;
   1.887 +    }
   1.888 +
   1.889 +    if (finalZone != NULL) {
   1.890 +        if (inclusive && base == firstFinalTZTransition->getTime()) {
   1.891 +            result = *firstFinalTZTransition;
   1.892 +            return TRUE;
   1.893 +        } else if (base >= firstFinalTZTransition->getTime()) {
   1.894 +            if (finalZone->useDaylightTime()) {
   1.895 +                //return finalZone->getNextTransition(base, inclusive, result);
   1.896 +                return finalZoneWithStartYear->getNextTransition(base, inclusive, result);
   1.897 +            } else {
   1.898 +                // No more transitions
   1.899 +                return FALSE;
   1.900 +            }
   1.901 +        }
   1.902 +    }
   1.903 +    if (historicRules != NULL) {
   1.904 +        // Find a historical transition
   1.905 +        int16_t transCount = transitionCount();
   1.906 +        int16_t ttidx = transCount - 1;
   1.907 +        for (; ttidx >= firstTZTransitionIdx; ttidx--) {
   1.908 +            UDate t = (UDate)transitionTime(ttidx);
   1.909 +            if (base > t || (!inclusive && base == t)) {
   1.910 +                break;
   1.911 +            }
   1.912 +        }
   1.913 +        if (ttidx == transCount - 1)  {
   1.914 +            if (firstFinalTZTransition != NULL) {
   1.915 +                result = *firstFinalTZTransition;
   1.916 +                return TRUE;
   1.917 +            } else {
   1.918 +                return FALSE;
   1.919 +            }
   1.920 +        } else if (ttidx < firstTZTransitionIdx) {
   1.921 +            result = *firstTZTransition;
   1.922 +            return TRUE;
   1.923 +        } else {
   1.924 +            // Create a TimeZoneTransition
   1.925 +            TimeZoneRule *to = historicRules[typeMapData[ttidx + 1]];
   1.926 +            TimeZoneRule *from = historicRules[typeMapData[ttidx]];
   1.927 +            UDate startTime = (UDate)transitionTime(ttidx+1);
   1.928 +
   1.929 +            // The transitions loaded from zoneinfo.res may contain non-transition data
   1.930 +            UnicodeString fromName, toName;
   1.931 +            from->getName(fromName);
   1.932 +            to->getName(toName);
   1.933 +            if (fromName == toName && from->getRawOffset() == to->getRawOffset()
   1.934 +                    && from->getDSTSavings() == to->getDSTSavings()) {
   1.935 +                return getNextTransition(startTime, false, result);
   1.936 +            }
   1.937 +            result.setTime(startTime);
   1.938 +            result.adoptFrom(from->clone());
   1.939 +            result.adoptTo(to->clone());
   1.940 +            return TRUE;
   1.941 +        }
   1.942 +    }
   1.943 +    return FALSE;
   1.944 +}
   1.945 +
   1.946 +UBool
   1.947 +OlsonTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
   1.948 +    UErrorCode status = U_ZERO_ERROR;
   1.949 +    checkTransitionRules(status);
   1.950 +    if (U_FAILURE(status)) {
   1.951 +        return FALSE;
   1.952 +    }
   1.953 +
   1.954 +    if (finalZone != NULL) {
   1.955 +        if (inclusive && base == firstFinalTZTransition->getTime()) {
   1.956 +            result = *firstFinalTZTransition;
   1.957 +            return TRUE;
   1.958 +        } else if (base > firstFinalTZTransition->getTime()) {
   1.959 +            if (finalZone->useDaylightTime()) {
   1.960 +                //return finalZone->getPreviousTransition(base, inclusive, result);
   1.961 +                return finalZoneWithStartYear->getPreviousTransition(base, inclusive, result);
   1.962 +            } else {
   1.963 +                result = *firstFinalTZTransition;
   1.964 +                return TRUE;
   1.965 +            }
   1.966 +        }
   1.967 +    }
   1.968 +
   1.969 +    if (historicRules != NULL) {
   1.970 +        // Find a historical transition
   1.971 +        int16_t ttidx = transitionCount() - 1;
   1.972 +        for (; ttidx >= firstTZTransitionIdx; ttidx--) {
   1.973 +            UDate t = (UDate)transitionTime(ttidx);
   1.974 +            if (base > t || (inclusive && base == t)) {
   1.975 +                break;
   1.976 +            }
   1.977 +        }
   1.978 +        if (ttidx < firstTZTransitionIdx) {
   1.979 +            // No more transitions
   1.980 +            return FALSE;
   1.981 +        } else if (ttidx == firstTZTransitionIdx) {
   1.982 +            result = *firstTZTransition;
   1.983 +            return TRUE;
   1.984 +        } else {
   1.985 +            // Create a TimeZoneTransition
   1.986 +            TimeZoneRule *to = historicRules[typeMapData[ttidx]];
   1.987 +            TimeZoneRule *from = historicRules[typeMapData[ttidx-1]];
   1.988 +            UDate startTime = (UDate)transitionTime(ttidx);
   1.989 +
   1.990 +            // The transitions loaded from zoneinfo.res may contain non-transition data
   1.991 +            UnicodeString fromName, toName;
   1.992 +            from->getName(fromName);
   1.993 +            to->getName(toName);
   1.994 +            if (fromName == toName && from->getRawOffset() == to->getRawOffset()
   1.995 +                    && from->getDSTSavings() == to->getDSTSavings()) {
   1.996 +                return getPreviousTransition(startTime, false, result);
   1.997 +            }
   1.998 +            result.setTime(startTime);
   1.999 +            result.adoptFrom(from->clone());
  1.1000 +            result.adoptTo(to->clone());
  1.1001 +            return TRUE;
  1.1002 +        }
  1.1003 +    }
  1.1004 +    return FALSE;
  1.1005 +}
  1.1006 +
  1.1007 +int32_t
  1.1008 +OlsonTimeZone::countTransitionRules(UErrorCode& status) const {
  1.1009 +    if (U_FAILURE(status)) {
  1.1010 +        return 0;
  1.1011 +    }
  1.1012 +    checkTransitionRules(status);
  1.1013 +    if (U_FAILURE(status)) {
  1.1014 +        return 0;
  1.1015 +    }
  1.1016 +
  1.1017 +    int32_t count = 0;
  1.1018 +    if (historicRules != NULL) {
  1.1019 +        // historicRules may contain null entries when original zoneinfo data
  1.1020 +        // includes non transition data.
  1.1021 +        for (int32_t i = 0; i < historicRuleCount; i++) {
  1.1022 +            if (historicRules[i] != NULL) {
  1.1023 +                count++;
  1.1024 +            }
  1.1025 +        }
  1.1026 +    }
  1.1027 +    if (finalZone != NULL) {
  1.1028 +        if (finalZone->useDaylightTime()) {
  1.1029 +            count += 2;
  1.1030 +        } else {
  1.1031 +            count++;
  1.1032 +        }
  1.1033 +    }
  1.1034 +    return count;
  1.1035 +}
  1.1036 +
  1.1037 +void
  1.1038 +OlsonTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
  1.1039 +                                const TimeZoneRule* trsrules[],
  1.1040 +                                int32_t& trscount,
  1.1041 +                                UErrorCode& status) const {
  1.1042 +    if (U_FAILURE(status)) {
  1.1043 +        return;
  1.1044 +    }
  1.1045 +    checkTransitionRules(status);
  1.1046 +    if (U_FAILURE(status)) {
  1.1047 +        return;
  1.1048 +    }
  1.1049 +
  1.1050 +    // Initial rule
  1.1051 +    initial = initialRule;
  1.1052 +
  1.1053 +    // Transition rules
  1.1054 +    int32_t cnt = 0;
  1.1055 +    if (historicRules != NULL && trscount > cnt) {
  1.1056 +        // historicRules may contain null entries when original zoneinfo data
  1.1057 +        // includes non transition data.
  1.1058 +        for (int32_t i = 0; i < historicRuleCount; i++) {
  1.1059 +            if (historicRules[i] != NULL) {
  1.1060 +                trsrules[cnt++] = historicRules[i];
  1.1061 +                if (cnt >= trscount) {
  1.1062 +                    break;
  1.1063 +                }
  1.1064 +            }
  1.1065 +        }
  1.1066 +    }
  1.1067 +    if (finalZoneWithStartYear != NULL && trscount > cnt) {
  1.1068 +        const InitialTimeZoneRule *tmpini;
  1.1069 +        int32_t tmpcnt = trscount - cnt;
  1.1070 +        finalZoneWithStartYear->getTimeZoneRules(tmpini, &trsrules[cnt], tmpcnt, status);
  1.1071 +        if (U_FAILURE(status)) {
  1.1072 +            return;
  1.1073 +        }
  1.1074 +        cnt += tmpcnt;
  1.1075 +    }
  1.1076 +    // Set the result length
  1.1077 +    trscount = cnt;
  1.1078 +}
  1.1079 +
  1.1080 +U_NAMESPACE_END
  1.1081 +
  1.1082 +#endif // !UCONFIG_NO_FORMATTING
  1.1083 +
  1.1084 +//eof

mercurial