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