Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / icu / source / i18n / olsontz.cpp
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
34 static void debug_tz_loc(const char *f, int32_t l)
35 {
36   fprintf(stderr, "%s:%d: ", f, l);
37 }
38
39 static 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
52 static 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
66 U_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
80 static const int32_t ZEROS[] = {0,0};
81
82 UOBJECT_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  */
97 void 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  */
118 OlsonTimeZone::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  */
266 OlsonTimeZone::OlsonTimeZone(const OlsonTimeZone& other) :
267     BasicTimeZone(other), finalZone(0) {
268     *this = other;
269 }
270
271 /**
272  * Assignment operator
273  */
274 OlsonTimeZone& 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  */
304 OlsonTimeZone::~OlsonTimeZone() {
305     deleteTransitionRules();
306     delete finalZone;
307 }
308
309 /**
310  * Returns true if the two TimeZone objects are equal.
311  */
312 UBool 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  */
322 TimeZone* OlsonTimeZone::clone() const {
323     return new OlsonTimeZone(*this);
324 }
325
326 /**
327  * TimeZone API.
328  */
329 int32_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  */
347 int32_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  */
389 void 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
401 void
402 OlsonTimeZone::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  */
418 void 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  */
428 int32_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
437 void 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
448 int64_t
449 OlsonTimeZone::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
472 void
473 OlsonTimeZone::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  */
558 UBool 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 }
591 int32_t 
592 OlsonTimeZone::getDSTSavings() const{
593     if (finalZone != NULL){
594         return finalZone->getDSTSavings();
595     }
596     return TimeZone::getDSTSavings();
597 }
598 /**
599  * TimeZone API.
600  */
601 UBool 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
607 UBool
608 OlsonTimeZone::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
652 void
653 OlsonTimeZone::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
664 void
665 OlsonTimeZone::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
693 static void U_CALLCONV initRules(OlsonTimeZone *This, UErrorCode &status) {
694     This->initTransitionRules(status);
695 }
696     
697 void
698 OlsonTimeZone::checkTransitionRules(UErrorCode& status) const {
699     OlsonTimeZone *ncThis = const_cast<OlsonTimeZone *>(this);
700     umtx_initOnce(ncThis->transitionRulesInitOnce, &initRules, ncThis, status);
701 }
702
703 void
704 OlsonTimeZone::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
878 UBool
879 OlsonTimeZone::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
943 UBool
944 OlsonTimeZone::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
1004 int32_t
1005 OlsonTimeZone::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
1034 void
1035 OlsonTimeZone::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
1077 U_NAMESPACE_END
1078
1079 #endif // !UCONFIG_NO_FORMATTING
1080
1081 //eof