1/*
2**********************************************************************
3* Copyright (c) 2003-2013, International Business Machines
4* Corporation and others.  All Rights Reserved.
5**********************************************************************
6* Author: Alan Liu
7* Created: July 21 2003
8* Since: ICU 2.8
9**********************************************************************
10*/
11
12#include "utypeinfo.h"  // for 'typeid' to work
13
14#include "olsontz.h"
15
16#if !UCONFIG_NO_FORMATTING
17
18#include "unicode/ures.h"
19#include "unicode/simpletz.h"
20#include "unicode/gregocal.h"
21#include "gregoimp.h"
22#include "cmemory.h"
23#include "uassert.h"
24#include "uvector.h"
25#include <float.h> // DBL_MAX
26#include "uresimp.h" // struct UResourceBundle
27#include "zonemeta.h"
28#include "umutex.h"
29
30#ifdef U_DEBUG_TZ
31# include <stdio.h>
32# include "uresimp.h" // for debugging
33
34static void debug_tz_loc(const char *f, int32_t l)
35{
36  fprintf(stderr, "%s:%d: ", f, l);
37}
38
39static void debug_tz_msg(const char *pat, ...)
40{
41  va_list ap;
42  va_start(ap, pat);
43  vfprintf(stderr, pat, ap);
44  fflush(stderr);
45}
46// must use double parens, i.e.:  U_DEBUG_TZ_MSG(("four is: %d",4));
47#define U_DEBUG_TZ_MSG(x) {debug_tz_loc(__FILE__,__LINE__);debug_tz_msg x;}
48#else
49#define U_DEBUG_TZ_MSG(x)
50#endif
51
52static UBool arrayEqual(const void *a1, const void *a2, int32_t size) {
53    if (a1 == NULL && a2 == NULL) {
54        return TRUE;
55    }
56    if ((a1 != NULL && a2 == NULL) || (a1 == NULL && a2 != NULL)) {
57        return FALSE;
58    }
59    if (a1 == a2) {
60        return TRUE;
61    }
62
63    return (uprv_memcmp(a1, a2, size) == 0);
64}
65
66U_NAMESPACE_BEGIN
67
68#define kTRANS          "trans"
69#define kTRANSPRE32     "transPre32"
70#define kTRANSPOST32    "transPost32"
71#define kTYPEOFFSETS    "typeOffsets"
72#define kTYPEMAP        "typeMap"
73#define kLINKS          "links"
74#define kFINALRULE      "finalRule"
75#define kFINALRAW       "finalRaw"
76#define kFINALYEAR      "finalYear"
77
78#define SECONDS_PER_DAY (24*60*60)
79
80static const int32_t ZEROS[] = {0,0};
81
82UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OlsonTimeZone)
83
84/**
85 * Default constructor.  Creates a time zone with an empty ID and
86 * a fixed GMT offset of zero.
87 */
88/*OlsonTimeZone::OlsonTimeZone() : finalYear(INT32_MAX), finalMillis(DBL_MAX), finalZone(0), transitionRulesInitialized(FALSE) {
89    clearTransitionRules();
90    constructEmpty();
91}*/
92
93/**
94 * Construct a GMT+0 zone with no transitions.  This is done when a
95 * constructor fails so the resultant object is well-behaved.
96 */
97void OlsonTimeZone::constructEmpty() {
98    canonicalID = NULL;
99
100    transitionCountPre32 = transitionCount32 = transitionCountPost32 = 0;
101    transitionTimesPre32 = transitionTimes32 = transitionTimesPost32 = NULL;
102
103    typeMapData = NULL;
104
105    typeCount = 1;
106    typeOffsets = ZEROS;
107
108    finalZone = NULL;
109}
110
111/**
112 * Construct from a resource bundle
113 * @param top the top-level zoneinfo resource bundle.  This is used
114 * to lookup the rule that `res' may refer to, if there is one.
115 * @param res the resource bundle of the zone to be constructed
116 * @param ec input-output error code
117 */
118OlsonTimeZone::OlsonTimeZone(const UResourceBundle* top,
119                             const UResourceBundle* res,
120                             const UnicodeString& tzid,
121                             UErrorCode& ec) :
122  BasicTimeZone(tzid), finalZone(NULL)
123{
124    clearTransitionRules();
125    U_DEBUG_TZ_MSG(("OlsonTimeZone(%s)\n", ures_getKey((UResourceBundle*)res)));
126    if ((top == NULL || res == NULL) && U_SUCCESS(ec)) {
127        ec = U_ILLEGAL_ARGUMENT_ERROR;
128    }
129    if (U_SUCCESS(ec)) {
130        // TODO -- clean up -- Doesn't work if res points to an alias
131        //        // TODO remove nonconst casts below when ures_* API is fixed
132        //        setID(ures_getKey((UResourceBundle*) res)); // cast away const
133
134        int32_t len;
135        UResourceBundle r;
136        ures_initStackObject(&r);
137
138        // Pre-32bit second transitions
139        ures_getByKey(res, kTRANSPRE32, &r, &ec);
140        transitionTimesPre32 = ures_getIntVector(&r, &len, &ec);
141        transitionCountPre32 = len >> 1;
142        if (ec == U_MISSING_RESOURCE_ERROR) {
143            // No pre-32bit transitions
144            transitionTimesPre32 = NULL;
145            transitionCountPre32 = 0;
146            ec = U_ZERO_ERROR;
147        } else if (U_SUCCESS(ec) && (len < 0 || len > 0x7FFF || (len & 1) != 0) /* len must be even */) {
148            ec = U_INVALID_FORMAT_ERROR;
149        }
150
151        // 32bit second transitions
152        ures_getByKey(res, kTRANS, &r, &ec);
153        transitionTimes32 = ures_getIntVector(&r, &len, &ec);
154        transitionCount32 = len;
155        if (ec == U_MISSING_RESOURCE_ERROR) {
156            // No 32bit transitions
157            transitionTimes32 = NULL;
158            transitionCount32 = 0;
159            ec = U_ZERO_ERROR;
160        } else if (U_SUCCESS(ec) && (len < 0 || len > 0x7FFF)) {
161            ec = U_INVALID_FORMAT_ERROR;
162        }
163
164        // Post-32bit second transitions
165        ures_getByKey(res, kTRANSPOST32, &r, &ec);
166        transitionTimesPost32 = ures_getIntVector(&r, &len, &ec);
167        transitionCountPost32 = len >> 1;
168        if (ec == U_MISSING_RESOURCE_ERROR) {
169            // No pre-32bit transitions
170            transitionTimesPost32 = NULL;
171            transitionCountPost32 = 0;
172            ec = U_ZERO_ERROR;
173        } else if (U_SUCCESS(ec) && (len < 0 || len > 0x7FFF || (len & 1) != 0) /* len must be even */) {
174            ec = U_INVALID_FORMAT_ERROR;
175        }
176
177        // Type offsets list must be of even size, with size >= 2
178        ures_getByKey(res, kTYPEOFFSETS, &r, &ec);
179        typeOffsets = ures_getIntVector(&r, &len, &ec);
180        if (U_SUCCESS(ec) && (len < 2 || len > 0x7FFE || (len & 1) != 0)) {
181            ec = U_INVALID_FORMAT_ERROR;
182        }
183        typeCount = (int16_t) len >> 1;
184
185        // Type map data must be of the same size as the transition count
186        typeMapData =  NULL;
187        if (transitionCount() > 0) {
188            ures_getByKey(res, kTYPEMAP, &r, &ec);
189            typeMapData = ures_getBinary(&r, &len, &ec);
190            if (ec == U_MISSING_RESOURCE_ERROR) {
191                // no type mapping data
192                ec = U_INVALID_FORMAT_ERROR;
193            } else if (U_SUCCESS(ec) && len != transitionCount()) {
194                ec = U_INVALID_FORMAT_ERROR;
195            }
196        }
197
198        // Process final rule and data, if any
199        const UChar *ruleIdUStr = ures_getStringByKey(res, kFINALRULE, &len, &ec);
200        ures_getByKey(res, kFINALRAW, &r, &ec);
201        int32_t ruleRaw = ures_getInt(&r, &ec);
202        ures_getByKey(res, kFINALYEAR, &r, &ec);
203        int32_t ruleYear = ures_getInt(&r, &ec);
204        if (U_SUCCESS(ec)) {
205            UnicodeString ruleID(TRUE, ruleIdUStr, len);
206            UResourceBundle *rule = TimeZone::loadRule(top, ruleID, NULL, ec);
207            const int32_t *ruleData = ures_getIntVector(rule, &len, &ec);
208            if (U_SUCCESS(ec) && len == 11) {
209                UnicodeString emptyStr;
210                finalZone = new SimpleTimeZone(
211                    ruleRaw * U_MILLIS_PER_SECOND,
212                    emptyStr,
213                    (int8_t)ruleData[0], (int8_t)ruleData[1], (int8_t)ruleData[2],
214                    ruleData[3] * U_MILLIS_PER_SECOND,
215                    (SimpleTimeZone::TimeMode) ruleData[4],
216                    (int8_t)ruleData[5], (int8_t)ruleData[6], (int8_t)ruleData[7],
217                    ruleData[8] * U_MILLIS_PER_SECOND,
218                    (SimpleTimeZone::TimeMode) ruleData[9],
219                    ruleData[10] * U_MILLIS_PER_SECOND, ec);
220                if (finalZone == NULL) {
221                    ec = U_MEMORY_ALLOCATION_ERROR;
222                } else {
223                    finalStartYear = ruleYear;
224
225                    // Note: Setting finalStartYear to the finalZone is problematic.  When a date is around
226                    // year boundary, SimpleTimeZone may return false result when DST is observed at the
227                    // beginning of year.  We could apply safe margin (day or two), but when one of recurrent
228                    // rules falls around year boundary, it could return false result.  Without setting the
229                    // start year, finalZone works fine around the year boundary of the start year.
230
231                    // finalZone->setStartYear(finalStartYear);
232
233
234                    // Compute the millis for Jan 1, 0:00 GMT of the finalYear
235
236                    // Note: finalStartMillis is used for detecting either if
237                    // historic transition data or finalZone to be used.  In an
238                    // extreme edge case - for example, two transitions fall into
239                    // small windows of time around the year boundary, this may
240                    // result incorrect offset computation.  But I think it will
241                    // never happen practically.  Yoshito - Feb 20, 2010
242                    finalStartMillis = Grego::fieldsToDay(finalStartYear, 0, 1) * U_MILLIS_PER_DAY;
243                }
244            } else {
245                ec = U_INVALID_FORMAT_ERROR;
246            }
247            ures_close(rule);
248        } else if (ec == U_MISSING_RESOURCE_ERROR) {
249            // No final zone
250            ec = U_ZERO_ERROR;
251        }
252        ures_close(&r);
253
254        // initialize canonical ID
255        canonicalID = ZoneMeta::getCanonicalCLDRID(tzid, ec);
256    }
257
258    if (U_FAILURE(ec)) {
259        constructEmpty();
260    }
261}
262
263/**
264 * Copy constructor
265 */
266OlsonTimeZone::OlsonTimeZone(const OlsonTimeZone& other) :
267    BasicTimeZone(other), finalZone(0) {
268    *this = other;
269}
270
271/**
272 * Assignment operator
273 */
274OlsonTimeZone& OlsonTimeZone::operator=(const OlsonTimeZone& other) {
275    canonicalID = other.canonicalID;
276
277    transitionTimesPre32 = other.transitionTimesPre32;
278    transitionTimes32 = other.transitionTimes32;
279    transitionTimesPost32 = other.transitionTimesPost32;
280
281    transitionCountPre32 = other.transitionCountPre32;
282    transitionCount32 = other.transitionCount32;
283    transitionCountPost32 = other.transitionCountPost32;
284
285    typeCount = other.typeCount;
286    typeOffsets = other.typeOffsets;
287    typeMapData = other.typeMapData;
288
289    delete finalZone;
290    finalZone = (other.finalZone != 0) ?
291        (SimpleTimeZone*) other.finalZone->clone() : 0;
292
293    finalStartYear = other.finalStartYear;
294    finalStartMillis = other.finalStartMillis;
295
296    clearTransitionRules();
297
298    return *this;
299}
300
301/**
302 * Destructor
303 */
304OlsonTimeZone::~OlsonTimeZone() {
305    deleteTransitionRules();
306    delete finalZone;
307}
308
309/**
310 * Returns true if the two TimeZone objects are equal.
311 */
312UBool OlsonTimeZone::operator==(const TimeZone& other) const {
313    return ((this == &other) ||
314            (typeid(*this) == typeid(other) &&
315            TimeZone::operator==(other) &&
316            hasSameRules(other)));
317}
318
319/**
320 * TimeZone API.
321 */
322TimeZone* OlsonTimeZone::clone() const {
323    return new OlsonTimeZone(*this);
324}
325
326/**
327 * TimeZone API.
328 */
329int32_t OlsonTimeZone::getOffset(uint8_t era, int32_t year, int32_t month,
330                                 int32_t dom, uint8_t dow,
331                                 int32_t millis, UErrorCode& ec) const {
332    if (month < UCAL_JANUARY || month > UCAL_DECEMBER) {
333        if (U_SUCCESS(ec)) {
334            ec = U_ILLEGAL_ARGUMENT_ERROR;
335        }
336        return 0;
337    } else {
338        return getOffset(era, year, month, dom, dow, millis,
339                         Grego::monthLength(year, month),
340                         ec);
341    }
342}
343
344/**
345 * TimeZone API.
346 */
347int32_t OlsonTimeZone::getOffset(uint8_t era, int32_t year, int32_t month,
348                                 int32_t dom, uint8_t dow,
349                                 int32_t millis, int32_t monthLength,
350                                 UErrorCode& ec) const {
351    if (U_FAILURE(ec)) {
352        return 0;
353    }
354
355    if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC)
356        || month < UCAL_JANUARY
357        || month > UCAL_DECEMBER
358        || dom < 1
359        || dom > monthLength
360        || dow < UCAL_SUNDAY
361        || dow > UCAL_SATURDAY
362        || millis < 0
363        || millis >= U_MILLIS_PER_DAY
364        || monthLength < 28
365        || monthLength > 31) {
366        ec = U_ILLEGAL_ARGUMENT_ERROR;
367        return 0;
368    }
369
370    if (era == GregorianCalendar::BC) {
371        year = -year;
372    }
373
374    if (finalZone != NULL && year >= finalStartYear) {
375        return finalZone->getOffset(era, year, month, dom, dow,
376                                    millis, monthLength, ec);
377    }
378
379    // Compute local epoch millis from input fields
380    UDate date = (UDate)(Grego::fieldsToDay(year, month, dom) * U_MILLIS_PER_DAY + millis);
381    int32_t rawoff, dstoff;
382    getHistoricalOffset(date, TRUE, kDaylight, kStandard, rawoff, dstoff);
383    return rawoff + dstoff;
384}
385
386/**
387 * TimeZone API.
388 */
389void OlsonTimeZone::getOffset(UDate date, UBool local, int32_t& rawoff,
390                              int32_t& dstoff, UErrorCode& ec) const {
391    if (U_FAILURE(ec)) {
392        return;
393    }
394    if (finalZone != NULL && date >= finalStartMillis) {
395        finalZone->getOffset(date, local, rawoff, dstoff, ec);
396    } else {
397        getHistoricalOffset(date, local, kFormer, kLatter, rawoff, dstoff);
398    }
399}
400
401void
402OlsonTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
403                                  int32_t& rawoff, int32_t& dstoff, UErrorCode& ec) const {
404    if (U_FAILURE(ec)) {
405        return;
406    }
407    if (finalZone != NULL && date >= finalStartMillis) {
408        finalZone->getOffsetFromLocal(date, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff, ec);
409    } else {
410        getHistoricalOffset(date, TRUE, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff);
411    }
412}
413
414
415/**
416 * TimeZone API.
417 */
418void OlsonTimeZone::setRawOffset(int32_t /*offsetMillis*/) {
419    // We don't support this operation, since OlsonTimeZones are
420    // immutable (except for the ID, which is in the base class).
421
422    // Nothing to do!
423}
424
425/**
426 * TimeZone API.
427 */
428int32_t OlsonTimeZone::getRawOffset() const {
429    UErrorCode ec = U_ZERO_ERROR;
430    int32_t raw, dst;
431    getOffset((double) uprv_getUTCtime() * U_MILLIS_PER_SECOND,
432              FALSE, raw, dst, ec);
433    return raw;
434}
435
436#if defined U_DEBUG_TZ
437void printTime(double ms) {
438            int32_t year, month, dom, dow;
439            double millis=0;
440            double days = ClockMath::floorDivide(((double)ms), (double)U_MILLIS_PER_DAY, millis);
441
442            Grego::dayToFields(days, year, month, dom, dow);
443            U_DEBUG_TZ_MSG(("   getHistoricalOffset:  time %.1f (%04d.%02d.%02d+%.1fh)\n", ms,
444                            year, month+1, dom, (millis/kOneHour)));
445    }
446#endif
447
448int64_t
449OlsonTimeZone::transitionTimeInSeconds(int16_t transIdx) const {
450    U_ASSERT(transIdx >= 0 && transIdx < transitionCount());
451
452    if (transIdx < transitionCountPre32) {
453        return (((int64_t)((uint32_t)transitionTimesPre32[transIdx << 1])) << 32)
454            | ((int64_t)((uint32_t)transitionTimesPre32[(transIdx << 1) + 1]));
455    }
456
457    transIdx -= transitionCountPre32;
458    if (transIdx < transitionCount32) {
459        return (int64_t)transitionTimes32[transIdx];
460    }
461
462    transIdx -= transitionCount32;
463    return (((int64_t)((uint32_t)transitionTimesPost32[transIdx << 1])) << 32)
464        | ((int64_t)((uint32_t)transitionTimesPost32[(transIdx << 1) + 1]));
465}
466
467// Maximum absolute offset in seconds (86400 seconds = 1 day)
468// getHistoricalOffset uses this constant as safety margin of
469// quick zone transition checking.
470#define MAX_OFFSET_SECONDS 86400
471
472void
473OlsonTimeZone::getHistoricalOffset(UDate date, UBool local,
474                                   int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt,
475                                   int32_t& rawoff, int32_t& dstoff) const {
476    U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst)\n",
477        date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt));
478#if defined U_DEBUG_TZ
479        printTime(date*1000.0);
480#endif
481    int16_t transCount = transitionCount();
482
483    if (transCount > 0) {
484        double sec = uprv_floor(date / U_MILLIS_PER_SECOND);
485        if (!local && sec < transitionTimeInSeconds(0)) {
486            // Before the first transition time
487            rawoff = initialRawOffset() * U_MILLIS_PER_SECOND;
488            dstoff = initialDstOffset() * U_MILLIS_PER_SECOND;
489        } else {
490            // Linear search from the end is the fastest approach, since
491            // most lookups will happen at/near the end.
492            int16_t transIdx;
493            for (transIdx = transCount - 1; transIdx >= 0; transIdx--) {
494                int64_t transition = transitionTimeInSeconds(transIdx);
495
496                if (local && (sec >= (transition - MAX_OFFSET_SECONDS))) {
497                    int32_t offsetBefore = zoneOffsetAt(transIdx - 1);
498                    UBool dstBefore = dstOffsetAt(transIdx - 1) != 0;
499
500                    int32_t offsetAfter = zoneOffsetAt(transIdx);
501                    UBool dstAfter = dstOffsetAt(transIdx) != 0;
502
503                    UBool dstToStd = dstBefore && !dstAfter;
504                    UBool stdToDst = !dstBefore && dstAfter;
505
506                    if (offsetAfter - offsetBefore >= 0) {
507                        // Positive transition, which makes a non-existing local time range
508                        if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd)
509                                || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
510                            transition += offsetBefore;
511                        } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst)
512                                || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
513                            transition += offsetAfter;
514                        } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) {
515                            transition += offsetBefore;
516                        } else {
517                            // Interprets the time with rule before the transition,
518                            // default for non-existing time range
519                            transition += offsetAfter;
520                        }
521                    } else {
522                        // Negative transition, which makes a duplicated local time range
523                        if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd)
524                                || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
525                            transition += offsetAfter;
526                        } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst)
527                                || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
528                            transition += offsetBefore;
529                        } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) {
530                            transition += offsetBefore;
531                        } else {
532                            // Interprets the time with rule after the transition,
533                            // default for duplicated local time range
534                            transition += offsetAfter;
535                        }
536                    }
537                }
538                if (sec >= transition) {
539                    break;
540                }
541            }
542            // transIdx could be -1 when local=true
543            rawoff = rawOffsetAt(transIdx) * U_MILLIS_PER_SECOND;
544            dstoff = dstOffsetAt(transIdx) * U_MILLIS_PER_SECOND;
545        }
546    } else {
547        // No transitions, single pair of offsets only
548        rawoff = initialRawOffset() * U_MILLIS_PER_SECOND;
549        dstoff = initialDstOffset() * U_MILLIS_PER_SECOND;
550    }
551    U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst) - raw=%d, dst=%d\n",
552        date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt, rawoff, dstoff));
553}
554
555/**
556 * TimeZone API.
557 */
558UBool OlsonTimeZone::useDaylightTime() const {
559    // If DST was observed in 1942 (for example) but has never been
560    // observed from 1943 to the present, most clients will expect
561    // this method to return FALSE.  This method determines whether
562    // DST is in use in the current year (at any point in the year)
563    // and returns TRUE if so.
564
565    UDate current = uprv_getUTCtime();
566    if (finalZone != NULL && current >= finalStartMillis) {
567        return finalZone->useDaylightTime();
568    }
569
570    int32_t year, month, dom, dow, doy, mid;
571    Grego::timeToFields(current, year, month, dom, dow, doy, mid);
572
573    // Find start of this year, and start of next year
574    double start = Grego::fieldsToDay(year, 0, 1) * SECONDS_PER_DAY;
575    double limit = Grego::fieldsToDay(year+1, 0, 1) * SECONDS_PER_DAY;
576
577    // Return TRUE if DST is observed at any time during the current
578    // year.
579    for (int16_t i = 0; i < transitionCount(); ++i) {
580        double transition = (double)transitionTimeInSeconds(i);
581        if (transition >= limit) {
582            break;
583        }
584        if ((transition >= start && dstOffsetAt(i) != 0)
585                || (transition > start && dstOffsetAt(i - 1) != 0)) {
586            return TRUE;
587        }
588    }
589    return FALSE;
590}
591int32_t
592OlsonTimeZone::getDSTSavings() const{
593    if (finalZone != NULL){
594        return finalZone->getDSTSavings();
595    }
596    return TimeZone::getDSTSavings();
597}
598/**
599 * TimeZone API.
600 */
601UBool OlsonTimeZone::inDaylightTime(UDate date, UErrorCode& ec) const {
602    int32_t raw, dst;
603    getOffset(date, FALSE, raw, dst, ec);
604    return dst != 0;
605}
606
607UBool
608OlsonTimeZone::hasSameRules(const TimeZone &other) const {
609    if (this == &other) {
610        return TRUE;
611    }
612    const OlsonTimeZone* z = dynamic_cast<const OlsonTimeZone*>(&other);
613    if (z == NULL) {
614        return FALSE;
615    }
616
617    // [sic] pointer comparison: typeMapData points into
618    // memory-mapped or DLL space, so if two zones have the same
619    // pointer, they are equal.
620    if (typeMapData == z->typeMapData) {
621        return TRUE;
622    }
623
624    // If the pointers are not equal, the zones may still
625    // be equal if their rules and transitions are equal
626    if ((finalZone == NULL && z->finalZone != NULL)
627        || (finalZone != NULL && z->finalZone == NULL)
628        || (finalZone != NULL && z->finalZone != NULL && *finalZone != *z->finalZone)) {
629        return FALSE;
630    }
631
632    if (finalZone != NULL) {
633        if (finalStartYear != z->finalStartYear || finalStartMillis != z->finalStartMillis) {
634            return FALSE;
635        }
636    }
637    if (typeCount != z->typeCount
638        || transitionCountPre32 != z->transitionCountPre32
639        || transitionCount32 != z->transitionCount32
640        || transitionCountPost32 != z->transitionCountPost32) {
641        return FALSE;
642    }
643
644    return
645        arrayEqual(transitionTimesPre32, z->transitionTimesPre32, sizeof(transitionTimesPre32[0]) * transitionCountPre32 << 1)
646        && arrayEqual(transitionTimes32, z->transitionTimes32, sizeof(transitionTimes32[0]) * transitionCount32)
647        && arrayEqual(transitionTimesPost32, z->transitionTimesPost32, sizeof(transitionTimesPost32[0]) * transitionCountPost32 << 1)
648        && arrayEqual(typeOffsets, z->typeOffsets, sizeof(typeOffsets[0]) * typeCount << 1)
649        && arrayEqual(typeMapData, z->typeMapData, sizeof(typeMapData[0]) * transitionCount());
650}
651
652void
653OlsonTimeZone::clearTransitionRules(void) {
654    initialRule = NULL;
655    firstTZTransition = NULL;
656    firstFinalTZTransition = NULL;
657    historicRules = NULL;
658    historicRuleCount = 0;
659    finalZoneWithStartYear = NULL;
660    firstTZTransitionIdx = 0;
661    transitionRulesInitOnce.reset();
662}
663
664void
665OlsonTimeZone::deleteTransitionRules(void) {
666    if (initialRule != NULL) {
667        delete initialRule;
668    }
669    if (firstTZTransition != NULL) {
670        delete firstTZTransition;
671    }
672    if (firstFinalTZTransition != NULL) {
673        delete firstFinalTZTransition;
674    }
675    if (finalZoneWithStartYear != NULL) {
676        delete finalZoneWithStartYear;
677    }
678    if (historicRules != NULL) {
679        for (int i = 0; i < historicRuleCount; i++) {
680            if (historicRules[i] != NULL) {
681                delete historicRules[i];
682            }
683        }
684        uprv_free(historicRules);
685    }
686    clearTransitionRules();
687}
688
689/*
690 * Lazy transition rules initializer
691 */
692
693static void U_CALLCONV initRules(OlsonTimeZone *This, UErrorCode &status) {
694    This->initTransitionRules(status);
695}
696
697void
698OlsonTimeZone::checkTransitionRules(UErrorCode& status) const {
699    OlsonTimeZone *ncThis = const_cast<OlsonTimeZone *>(this);
700    umtx_initOnce(ncThis->transitionRulesInitOnce, &initRules, ncThis, status);
701}
702
703void
704OlsonTimeZone::initTransitionRules(UErrorCode& status) {
705    if(U_FAILURE(status)) {
706        return;
707    }
708    deleteTransitionRules();
709    UnicodeString tzid;
710    getID(tzid);
711
712    UnicodeString stdName = tzid + UNICODE_STRING_SIMPLE("(STD)");
713    UnicodeString dstName = tzid + UNICODE_STRING_SIMPLE("(DST)");
714
715    int32_t raw, dst;
716
717    // Create initial rule
718    raw = initialRawOffset() * U_MILLIS_PER_SECOND;
719    dst = initialDstOffset() * U_MILLIS_PER_SECOND;
720    initialRule = new InitialTimeZoneRule((dst == 0 ? stdName : dstName), raw, dst);
721    // Check to make sure initialRule was created
722    if (initialRule == NULL) {
723        status = U_MEMORY_ALLOCATION_ERROR;
724        deleteTransitionRules();
725        return;
726    }
727
728    int32_t transCount = transitionCount();
729    if (transCount > 0) {
730        int16_t transitionIdx, typeIdx;
731
732        // We probably no longer need to check the first "real" transition
733        // here, because the new tzcode remove such transitions already.
734        // For now, keeping this code for just in case. Feb 19, 2010 Yoshito
735        firstTZTransitionIdx = 0;
736        for (transitionIdx = 0; transitionIdx < transCount; transitionIdx++) {
737            if (typeMapData[transitionIdx] != 0) { // type 0 is the initial type
738                break;
739            }
740            firstTZTransitionIdx++;
741        }
742        if (transitionIdx == transCount) {
743            // Actually no transitions...
744        } else {
745            // Build historic rule array
746            UDate* times = (UDate*)uprv_malloc(sizeof(UDate)*transCount); /* large enough to store all transition times */
747            if (times == NULL) {
748                status = U_MEMORY_ALLOCATION_ERROR;
749                deleteTransitionRules();
750                return;
751            }
752            for (typeIdx = 0; typeIdx < typeCount; typeIdx++) {
753                // Gather all start times for each pair of offsets
754                int32_t nTimes = 0;
755                for (transitionIdx = firstTZTransitionIdx; transitionIdx < transCount; transitionIdx++) {
756                    if (typeIdx == (int16_t)typeMapData[transitionIdx]) {
757                        UDate tt = (UDate)transitionTime(transitionIdx);
758                        if (finalZone == NULL || tt <= finalStartMillis) {
759                            // Exclude transitions after finalMillis
760                            times[nTimes++] = tt;
761                        }
762                    }
763                }
764                if (nTimes > 0) {
765                    // Create a TimeArrayTimeZoneRule
766                    raw = typeOffsets[typeIdx << 1] * U_MILLIS_PER_SECOND;
767                    dst = typeOffsets[(typeIdx << 1) + 1] * U_MILLIS_PER_SECOND;
768                    if (historicRules == NULL) {
769                        historicRuleCount = typeCount;
770                        historicRules = (TimeArrayTimeZoneRule**)uprv_malloc(sizeof(TimeArrayTimeZoneRule*)*historicRuleCount);
771                        if (historicRules == NULL) {
772                            status = U_MEMORY_ALLOCATION_ERROR;
773                            deleteTransitionRules();
774                            uprv_free(times);
775                            return;
776                        }
777                        for (int i = 0; i < historicRuleCount; i++) {
778                            // Initialize TimeArrayTimeZoneRule pointers as NULL
779                            historicRules[i] = NULL;
780                        }
781                    }
782                    historicRules[typeIdx] = new TimeArrayTimeZoneRule((dst == 0 ? stdName : dstName),
783                        raw, dst, times, nTimes, DateTimeRule::UTC_TIME);
784                    // Check for memory allocation error
785                    if (historicRules[typeIdx] == NULL) {
786                        status = U_MEMORY_ALLOCATION_ERROR;
787                        deleteTransitionRules();
788                        return;
789                    }
790                }
791            }
792            uprv_free(times);
793
794            // Create initial transition
795            typeIdx = (int16_t)typeMapData[firstTZTransitionIdx];
796            firstTZTransition = new TimeZoneTransition((UDate)transitionTime(firstTZTransitionIdx),
797                    *initialRule, *historicRules[typeIdx]);
798            // Check to make sure firstTZTransition was created.
799            if (firstTZTransition == NULL) {
800                status = U_MEMORY_ALLOCATION_ERROR;
801                deleteTransitionRules();
802                return;
803            }
804        }
805    }
806    if (finalZone != NULL) {
807        // Get the first occurence of final rule starts
808        UDate startTime = (UDate)finalStartMillis;
809        TimeZoneRule *firstFinalRule = NULL;
810
811        if (finalZone->useDaylightTime()) {
812            /*
813             * Note: When an OlsonTimeZone is constructed, we should set the final year
814             * as the start year of finalZone.  However, the bounday condition used for
815             * getting offset from finalZone has some problems.
816             * For now, we do not set the valid start year when the construction time
817             * and create a clone and set the start year when extracting rules.
818             */
819            finalZoneWithStartYear = (SimpleTimeZone*)finalZone->clone();
820            // Check to make sure finalZone was actually cloned.
821            if (finalZoneWithStartYear == NULL) {
822                status = U_MEMORY_ALLOCATION_ERROR;
823                deleteTransitionRules();
824                return;
825            }
826            finalZoneWithStartYear->setStartYear(finalStartYear);
827
828            TimeZoneTransition tzt;
829            finalZoneWithStartYear->getNextTransition(startTime, false, tzt);
830            firstFinalRule  = tzt.getTo()->clone();
831            // Check to make sure firstFinalRule received proper clone.
832            if (firstFinalRule == NULL) {
833                status = U_MEMORY_ALLOCATION_ERROR;
834                deleteTransitionRules();
835                return;
836            }
837            startTime = tzt.getTime();
838        } else {
839            // final rule with no transitions
840            finalZoneWithStartYear = (SimpleTimeZone*)finalZone->clone();
841            // Check to make sure finalZone was actually cloned.
842            if (finalZoneWithStartYear == NULL) {
843                status = U_MEMORY_ALLOCATION_ERROR;
844                deleteTransitionRules();
845                return;
846            }
847            finalZone->getID(tzid);
848            firstFinalRule = new TimeArrayTimeZoneRule(tzid,
849                finalZone->getRawOffset(), 0, &startTime, 1, DateTimeRule::UTC_TIME);
850            // Check firstFinalRule was properly created.
851            if (firstFinalRule == NULL) {
852                status = U_MEMORY_ALLOCATION_ERROR;
853                deleteTransitionRules();
854                return;
855            }
856        }
857        TimeZoneRule *prevRule = NULL;
858        if (transCount > 0) {
859            prevRule = historicRules[typeMapData[transCount - 1]];
860        }
861        if (prevRule == NULL) {
862            // No historic transitions, but only finalZone available
863            prevRule = initialRule;
864        }
865        firstFinalTZTransition = new TimeZoneTransition();
866        // Check to make sure firstFinalTZTransition was created before dereferencing
867        if (firstFinalTZTransition == NULL) {
868            status = U_MEMORY_ALLOCATION_ERROR;
869            deleteTransitionRules();
870            return;
871        }
872        firstFinalTZTransition->setTime(startTime);
873        firstFinalTZTransition->adoptFrom(prevRule->clone());
874        firstFinalTZTransition->adoptTo(firstFinalRule);
875    }
876}
877
878UBool
879OlsonTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
880    UErrorCode status = U_ZERO_ERROR;
881    checkTransitionRules(status);
882    if (U_FAILURE(status)) {
883        return FALSE;
884    }
885
886    if (finalZone != NULL) {
887        if (inclusive && base == firstFinalTZTransition->getTime()) {
888            result = *firstFinalTZTransition;
889            return TRUE;
890        } else if (base >= firstFinalTZTransition->getTime()) {
891            if (finalZone->useDaylightTime()) {
892                //return finalZone->getNextTransition(base, inclusive, result);
893                return finalZoneWithStartYear->getNextTransition(base, inclusive, result);
894            } else {
895                // No more transitions
896                return FALSE;
897            }
898        }
899    }
900    if (historicRules != NULL) {
901        // Find a historical transition
902        int16_t transCount = transitionCount();
903        int16_t ttidx = transCount - 1;
904        for (; ttidx >= firstTZTransitionIdx; ttidx--) {
905            UDate t = (UDate)transitionTime(ttidx);
906            if (base > t || (!inclusive && base == t)) {
907                break;
908            }
909        }
910        if (ttidx == transCount - 1)  {
911            if (firstFinalTZTransition != NULL) {
912                result = *firstFinalTZTransition;
913                return TRUE;
914            } else {
915                return FALSE;
916            }
917        } else if (ttidx < firstTZTransitionIdx) {
918            result = *firstTZTransition;
919            return TRUE;
920        } else {
921            // Create a TimeZoneTransition
922            TimeZoneRule *to = historicRules[typeMapData[ttidx + 1]];
923            TimeZoneRule *from = historicRules[typeMapData[ttidx]];
924            UDate startTime = (UDate)transitionTime(ttidx+1);
925
926            // The transitions loaded from zoneinfo.res may contain non-transition data
927            UnicodeString fromName, toName;
928            from->getName(fromName);
929            to->getName(toName);
930            if (fromName == toName && from->getRawOffset() == to->getRawOffset()
931                    && from->getDSTSavings() == to->getDSTSavings()) {
932                return getNextTransition(startTime, false, result);
933            }
934            result.setTime(startTime);
935            result.adoptFrom(from->clone());
936            result.adoptTo(to->clone());
937            return TRUE;
938        }
939    }
940    return FALSE;
941}
942
943UBool
944OlsonTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
945    UErrorCode status = U_ZERO_ERROR;
946    checkTransitionRules(status);
947    if (U_FAILURE(status)) {
948        return FALSE;
949    }
950
951    if (finalZone != NULL) {
952        if (inclusive && base == firstFinalTZTransition->getTime()) {
953            result = *firstFinalTZTransition;
954            return TRUE;
955        } else if (base > firstFinalTZTransition->getTime()) {
956            if (finalZone->useDaylightTime()) {
957                //return finalZone->getPreviousTransition(base, inclusive, result);
958                return finalZoneWithStartYear->getPreviousTransition(base, inclusive, result);
959            } else {
960                result = *firstFinalTZTransition;
961                return TRUE;
962            }
963        }
964    }
965
966    if (historicRules != NULL) {
967        // Find a historical transition
968        int16_t ttidx = transitionCount() - 1;
969        for (; ttidx >= firstTZTransitionIdx; ttidx--) {
970            UDate t = (UDate)transitionTime(ttidx);
971            if (base > t || (inclusive && base == t)) {
972                break;
973            }
974        }
975        if (ttidx < firstTZTransitionIdx) {
976            // No more transitions
977            return FALSE;
978        } else if (ttidx == firstTZTransitionIdx) {
979            result = *firstTZTransition;
980            return TRUE;
981        } else {
982            // Create a TimeZoneTransition
983            TimeZoneRule *to = historicRules[typeMapData[ttidx]];
984            TimeZoneRule *from = historicRules[typeMapData[ttidx-1]];
985            UDate startTime = (UDate)transitionTime(ttidx);
986
987            // The transitions loaded from zoneinfo.res may contain non-transition data
988            UnicodeString fromName, toName;
989            from->getName(fromName);
990            to->getName(toName);
991            if (fromName == toName && from->getRawOffset() == to->getRawOffset()
992                    && from->getDSTSavings() == to->getDSTSavings()) {
993                return getPreviousTransition(startTime, false, result);
994            }
995            result.setTime(startTime);
996            result.adoptFrom(from->clone());
997            result.adoptTo(to->clone());
998            return TRUE;
999        }
1000    }
1001    return FALSE;
1002}
1003
1004int32_t
1005OlsonTimeZone::countTransitionRules(UErrorCode& status) const {
1006    if (U_FAILURE(status)) {
1007        return 0;
1008    }
1009    checkTransitionRules(status);
1010    if (U_FAILURE(status)) {
1011        return 0;
1012    }
1013
1014    int32_t count = 0;
1015    if (historicRules != NULL) {
1016        // historicRules may contain null entries when original zoneinfo data
1017        // includes non transition data.
1018        for (int32_t i = 0; i < historicRuleCount; i++) {
1019            if (historicRules[i] != NULL) {
1020                count++;
1021            }
1022        }
1023    }
1024    if (finalZone != NULL) {
1025        if (finalZone->useDaylightTime()) {
1026            count += 2;
1027        } else {
1028            count++;
1029        }
1030    }
1031    return count;
1032}
1033
1034void
1035OlsonTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
1036                                const TimeZoneRule* trsrules[],
1037                                int32_t& trscount,
1038                                UErrorCode& status) const {
1039    if (U_FAILURE(status)) {
1040        return;
1041    }
1042    checkTransitionRules(status);
1043    if (U_FAILURE(status)) {
1044        return;
1045    }
1046
1047    // Initial rule
1048    initial = initialRule;
1049
1050    // Transition rules
1051    int32_t cnt = 0;
1052    if (historicRules != NULL && trscount > cnt) {
1053        // historicRules may contain null entries when original zoneinfo data
1054        // includes non transition data.
1055        for (int32_t i = 0; i < historicRuleCount; i++) {
1056            if (historicRules[i] != NULL) {
1057                trsrules[cnt++] = historicRules[i];
1058                if (cnt >= trscount) {
1059                    break;
1060                }
1061            }
1062        }
1063    }
1064    if (finalZoneWithStartYear != NULL && trscount > cnt) {
1065        const InitialTimeZoneRule *tmpini;
1066        int32_t tmpcnt = trscount - cnt;
1067        finalZoneWithStartYear->getTimeZoneRules(tmpini, &trsrules[cnt], tmpcnt, status);
1068        if (U_FAILURE(status)) {
1069            return;
1070        }
1071        cnt += tmpcnt;
1072    }
1073    // Set the result length
1074    trscount = cnt;
1075}
1076
1077U_NAMESPACE_END
1078
1079#endif // !UCONFIG_NO_FORMATTING
1080
1081//eof
1082