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