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