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