• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <typeinfo>  // 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 
debug_tz_loc(const char * f,int32_t l)32 static void debug_tz_loc(const char *f, int32_t l)
33 {
34   fprintf(stderr, "%s:%d: ", f, l);
35 }
36 
debug_tz_msg(const char * pat,...)37 static 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 
arrayEqual(const void * a1,const void * a2,int32_t size)50 static 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 
64 U_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 
78 static const int32_t ZEROS[] = {0,0};
79 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OlsonTimeZone)80 UOBJECT_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  */
95 void 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  */
OlsonTimeZone(const UResourceBundle * top,const UResourceBundle * res,UErrorCode & ec)114 OlsonTimeZone::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  */
OlsonTimeZone(const OlsonTimeZone & other)258 OlsonTimeZone::OlsonTimeZone(const OlsonTimeZone& other) :
259     BasicTimeZone(other), finalZone(0) {
260     *this = other;
261 }
262 
263 /**
264  * Assignment operator
265  */
operator =(const OlsonTimeZone & other)266 OlsonTimeZone& 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  */
~OlsonTimeZone()294 OlsonTimeZone::~OlsonTimeZone() {
295     deleteTransitionRules();
296     delete finalZone;
297 }
298 
299 /**
300  * Returns true if the two TimeZone objects are equal.
301  */
operator ==(const TimeZone & other) const302 UBool 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  */
clone() const312 TimeZone* OlsonTimeZone::clone() const {
313     return new OlsonTimeZone(*this);
314 }
315 
316 /**
317  * TimeZone API.
318  */
getOffset(uint8_t era,int32_t year,int32_t month,int32_t dom,uint8_t dow,int32_t millis,UErrorCode & ec) const319 int32_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  */
getOffset(uint8_t era,int32_t year,int32_t month,int32_t dom,uint8_t dow,int32_t millis,int32_t monthLength,UErrorCode & ec) const337 int32_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  */
getOffset(UDate date,UBool local,int32_t & rawoff,int32_t & dstoff,UErrorCode & ec) const379 void 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 
391 void
getOffsetFromLocal(UDate date,int32_t nonExistingTimeOpt,int32_t duplicatedTimeOpt,int32_t & rawoff,int32_t & dstoff,UErrorCode & ec)392 OlsonTimeZone::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  */
setRawOffset(int32_t)408 void 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  */
getRawOffset() const418 int32_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
printTime(double ms)427 void 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 
438 int64_t
transitionTimeInSeconds(int16_t transIdx) const439 OlsonTimeZone::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 
457 void
getHistoricalOffset(UDate date,UBool local,int32_t NonExistingTimeOpt,int32_t DuplicatedTimeOpt,int32_t & rawoff,int32_t & dstoff) const458 OlsonTimeZone::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  */
useDaylightTime() const543 UBool 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 }
576 int32_t
getDSTSavings() const577 OlsonTimeZone::getDSTSavings() const{
578     if (finalZone != NULL){
579         return finalZone->getDSTSavings();
580     }
581     return TimeZone::getDSTSavings();
582 }
583 /**
584  * TimeZone API.
585  */
inDaylightTime(UDate date,UErrorCode & ec) const586 UBool 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 
592 UBool
hasSameRules(const TimeZone & other) const593 OlsonTimeZone::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 
637 void
clearTransitionRules(void)638 OlsonTimeZone::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 
649 void
deleteTransitionRules(void)650 OlsonTimeZone::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 
674 void
initTransitionRules(UErrorCode & status)675 OlsonTimeZone::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 
853 UBool
getNextTransition(UDate base,UBool inclusive,TimeZoneTransition & result)854 OlsonTimeZone::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 
918 UBool
getPreviousTransition(UDate base,UBool inclusive,TimeZoneTransition & result)919 OlsonTimeZone::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 
979 int32_t
countTransitionRules(UErrorCode & status)980 OlsonTimeZone::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 
1009 void
getTimeZoneRules(const InitialTimeZoneRule * & initial,const TimeZoneRule * trsrules[],int32_t & trscount,UErrorCode & status)1010 OlsonTimeZone::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 
1052 U_NAMESPACE_END
1053 
1054 #endif // !UCONFIG_NO_FORMATTING
1055 
1056 //eof
1057