]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/olsontz.cpp
ICU-400.38.tar.gz
[apple/icu.git] / icuSources / i18n / olsontz.cpp
CommitLineData
374ca955
A
1/*
2**********************************************************************
46f4442e 3* Copyright (c) 2003-2009, 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
12#include "olsontz.h"
13
14#if !UCONFIG_NO_FORMATTING
15
16#include "unicode/ures.h"
17#include "unicode/simpletz.h"
18#include "unicode/gregocal.h"
19#include "gregoimp.h"
20#include "cmemory.h"
21#include "uassert.h"
46f4442e 22#include "uvector.h"
374ca955
A
23#include <float.h> // DBL_MAX
24
25#ifdef U_DEBUG_TZ
26# include <stdio.h>
27# include "uresimp.h" // for debugging
28
29static void debug_tz_loc(const char *f, int32_t l)
30{
31 fprintf(stderr, "%s:%d: ", f, l);
32}
33
34static void debug_tz_msg(const char *pat, ...)
35{
36 va_list ap;
37 va_start(ap, pat);
38 vfprintf(stderr, pat, ap);
39 fflush(stderr);
40}
41// must use double parens, i.e.: U_DEBUG_TZ_MSG(("four is: %d",4));
42#define U_DEBUG_TZ_MSG(x) {debug_tz_loc(__FILE__,__LINE__);debug_tz_msg x;}
43#else
44#define U_DEBUG_TZ_MSG(x)
45#endif
46
47U_NAMESPACE_BEGIN
48
49#define SECONDS_PER_DAY (24*60*60)
50
51static const int32_t ZEROS[] = {0,0};
52
53UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OlsonTimeZone)
54
55/**
56 * Default constructor. Creates a time zone with an empty ID and
57 * a fixed GMT offset of zero.
58 */
46f4442e
A
59/*OlsonTimeZone::OlsonTimeZone() : finalYear(INT32_MAX), finalMillis(DBL_MAX), finalZone(0), transitionRulesInitialized(FALSE) {
60 clearTransitionRules();
374ca955 61 constructEmpty();
46f4442e 62}*/
374ca955
A
63
64/**
65 * Construct a GMT+0 zone with no transitions. This is done when a
66 * constructor fails so the resultant object is well-behaved.
67 */
68void OlsonTimeZone::constructEmpty() {
69 transitionCount = 0;
70 typeCount = 1;
71 transitionTimes = typeOffsets = ZEROS;
72 typeData = (const uint8_t*) ZEROS;
73}
74
75/**
76 * Construct from a resource bundle
77 * @param top the top-level zoneinfo resource bundle. This is used
78 * to lookup the rule that `res' may refer to, if there is one.
79 * @param res the resource bundle of the zone to be constructed
80 * @param ec input-output error code
81 */
82OlsonTimeZone::OlsonTimeZone(const UResourceBundle* top,
83 const UResourceBundle* res,
84 UErrorCode& ec) :
46f4442e 85 finalYear(INT32_MAX), finalMillis(DBL_MAX), finalZone(0), transitionRulesInitialized(FALSE)
374ca955 86{
46f4442e 87 clearTransitionRules();
374ca955
A
88 U_DEBUG_TZ_MSG(("OlsonTimeZone(%s)\n", ures_getKey((UResourceBundle*)res)));
89 if ((top == NULL || res == NULL) && U_SUCCESS(ec)) {
90 ec = U_ILLEGAL_ARGUMENT_ERROR;
91 }
92 if (U_SUCCESS(ec)) {
93 // TODO -- clean up -- Doesn't work if res points to an alias
94 // // TODO remove nonconst casts below when ures_* API is fixed
95 // setID(ures_getKey((UResourceBundle*) res)); // cast away const
96
97 // Size 1 is an alias TO another zone (int)
98 // HOWEVER, the caller should dereference this and never pass it in to us
99 // Size 3 is a purely historical zone (no final rules)
100 // Size 4 is like size 3, but with an alias list at the end
101 // Size 5 is a hybrid zone, with historical and final elements
102 // Size 6 is like size 5, but with an alias list at the end
73c04bcf 103 int32_t size = ures_getSize(res);
374ca955
A
104 if (size < 3 || size > 6) {
105 ec = U_INVALID_FORMAT_ERROR;
106 }
107
108 // Transitions list may be empty
109 int32_t i;
110 UResourceBundle* r = ures_getByIndex(res, 0, NULL, &ec);
111 transitionTimes = ures_getIntVector(r, &i, &ec);
374ca955
A
112 if ((i<0 || i>0x7FFF) && U_SUCCESS(ec)) {
113 ec = U_INVALID_FORMAT_ERROR;
114 }
115 transitionCount = (int16_t) i;
116
117 // Type offsets list must be of even size, with size >= 2
73c04bcf 118 r = ures_getByIndex(res, 1, r, &ec);
374ca955 119 typeOffsets = ures_getIntVector(r, &i, &ec);
374ca955
A
120 if ((i<2 || i>0x7FFE || ((i&1)!=0)) && U_SUCCESS(ec)) {
121 ec = U_INVALID_FORMAT_ERROR;
122 }
123 typeCount = (int16_t) i >> 1;
124
125 // Type data must be of the same size as the transitions list
73c04bcf 126 r = ures_getByIndex(res, 2, r, &ec);
374ca955
A
127 int32_t len;
128 typeData = ures_getBinary(r, &len, &ec);
129 ures_close(r);
130 if (len != transitionCount && U_SUCCESS(ec)) {
131 ec = U_INVALID_FORMAT_ERROR;
132 }
133
134#if defined (U_DEBUG_TZ)
135 U_DEBUG_TZ_MSG(("OlsonTimeZone(%s) - size = %d, typecount %d transitioncount %d - err %s\n", ures_getKey((UResourceBundle*)res), size, typeCount, transitionCount, u_errorName(ec)));
136 if(U_SUCCESS(ec)) {
137 int32_t jj;
138 for(jj=0;jj<transitionCount;jj++) {
73c04bcf
A
139 int32_t year, month, dom, dow;
140 double millis=0;
141 double days = Math::floorDivide(((double)transitionTimes[jj])*1000.0, (double)U_MILLIS_PER_DAY, millis);
142
143 Grego::dayToFields(days, year, month, dom, dow);
144 U_DEBUG_TZ_MSG((" Transition %d: time %d (%04d.%02d.%02d+%.1fh), typedata%d\n", jj, transitionTimes[jj],
145 year, month+1, dom, (millis/kOneHour), typeData[jj]));
146// U_DEBUG_TZ_MSG((" offset%d\n", typeOffsets[jj]));
147 int16_t f = jj;
148 f <<= 1;
149 U_DEBUG_TZ_MSG((" offsets[%d+%d]=(%d+%d)=(%d==%d)\n", (int)f,(int)f+1,(int)typeOffsets[f],(int)typeOffsets[f+1],(int)zoneOffset(jj),
150 (int)typeOffsets[f]+(int)typeOffsets[f+1]));
374ca955
A
151 }
152 }
153#endif
154
155 // Process final rule and data, if any
156 if (size >= 5) {
157 int32_t ruleidLen = 0;
158 const UChar* idUStr = ures_getStringByIndex(res, 3, &ruleidLen, &ec);
159 UnicodeString ruleid(TRUE, idUStr, ruleidLen);
160 r = ures_getByIndex(res, 4, NULL, &ec);
161 const int32_t* data = ures_getIntVector(r, &len, &ec);
162#if defined U_DEBUG_TZ
163 const char *rKey = ures_getKey(r);
164 const char *zKey = ures_getKey((UResourceBundle*)res);
165#endif
166 ures_close(r);
167 if (U_SUCCESS(ec)) {
168 if (data != 0 && len == 2) {
169 int32_t rawOffset = data[0] * U_MILLIS_PER_SECOND;
170 // Subtract one from the actual final year; we
171 // actually store final year - 1, and compare
172 // using > rather than >=. This allows us to use
173 // INT32_MAX as an exclusive upper limit for all
174 // years, including INT32_MAX.
175 U_ASSERT(data[1] > INT32_MIN);
176 finalYear = data[1] - 1;
177 // Also compute the millis for Jan 1, 0:00 GMT of the
178 // finalYear. This reduces runtime computations.
179 finalMillis = Grego::fieldsToDay(data[1], 0, 1) * U_MILLIS_PER_DAY;
180 U_DEBUG_TZ_MSG(("zone%s|%s: {%d,%d}, finalYear%d, finalMillis%.1lf\n",
181 zKey,rKey, data[0], data[1], finalYear, finalMillis));
182 r = TimeZone::loadRule(top, ruleid, NULL, ec);
183 if (U_SUCCESS(ec)) {
184 // 3, 1, -1, 7200, 0, 9, -31, -1, 7200, 0, 3600
185 data = ures_getIntVector(r, &len, &ec);
186 if (U_SUCCESS(ec) && len == 11) {
187 UnicodeString emptyStr;
73c04bcf 188 U_DEBUG_TZ_MSG(("zone%s, rule%s: {%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d}\n", zKey, ures_getKey(r),
374ca955
A
189 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10]));
190 finalZone = new SimpleTimeZone(rawOffset, emptyStr,
191 (int8_t)data[0], (int8_t)data[1], (int8_t)data[2],
192 data[3] * U_MILLIS_PER_SECOND,
193 (SimpleTimeZone::TimeMode) data[4],
194 (int8_t)data[5], (int8_t)data[6], (int8_t)data[7],
195 data[8] * U_MILLIS_PER_SECOND,
196 (SimpleTimeZone::TimeMode) data[9],
197 data[10] * U_MILLIS_PER_SECOND, ec);
46f4442e
A
198 // Make sure finalZone was created
199 if (finalZone == NULL) {
200 ec = U_MEMORY_ALLOCATION_ERROR;
201 }
374ca955
A
202 } else {
203 ec = U_INVALID_FORMAT_ERROR;
204 }
205 }
206 ures_close(r);
207 } else {
208 ec = U_INVALID_FORMAT_ERROR;
209 }
210 }
211 }
212 }
213
214 if (U_FAILURE(ec)) {
215 constructEmpty();
216 }
217}
218
219/**
220 * Copy constructor
221 */
222OlsonTimeZone::OlsonTimeZone(const OlsonTimeZone& other) :
46f4442e 223 BasicTimeZone(other), finalZone(0) {
374ca955
A
224 *this = other;
225}
226
227/**
228 * Assignment operator
229 */
230OlsonTimeZone& OlsonTimeZone::operator=(const OlsonTimeZone& other) {
231 transitionCount = other.transitionCount;
232 typeCount = other.typeCount;
233 transitionTimes = other.transitionTimes;
234 typeOffsets = other.typeOffsets;
235 typeData = other.typeData;
236 finalYear = other.finalYear;
237 finalMillis = other.finalMillis;
238 delete finalZone;
239 finalZone = (other.finalZone != 0) ?
240 (SimpleTimeZone*) other.finalZone->clone() : 0;
46f4442e 241 clearTransitionRules();
374ca955
A
242 return *this;
243}
244
245/**
246 * Destructor
247 */
248OlsonTimeZone::~OlsonTimeZone() {
46f4442e 249 deleteTransitionRules();
374ca955
A
250 delete finalZone;
251}
252
253/**
254 * Returns true if the two TimeZone objects are equal.
255 */
256UBool OlsonTimeZone::operator==(const TimeZone& other) const {
46f4442e
A
257 return ((this == &other) ||
258 (getDynamicClassID() == other.getDynamicClassID() &&
259 TimeZone::operator==(other) &&
260 hasSameRules(other)));
374ca955
A
261}
262
263/**
264 * TimeZone API.
265 */
266TimeZone* OlsonTimeZone::clone() const {
267 return new OlsonTimeZone(*this);
268}
269
270/**
271 * TimeZone API.
272 */
273int32_t OlsonTimeZone::getOffset(uint8_t era, int32_t year, int32_t month,
274 int32_t dom, uint8_t dow,
275 int32_t millis, UErrorCode& ec) const {
276 if (month < UCAL_JANUARY || month > UCAL_DECEMBER) {
277 if (U_SUCCESS(ec)) {
278 ec = U_ILLEGAL_ARGUMENT_ERROR;
279 }
280 return 0;
281 } else {
282 return getOffset(era, year, month, dom, dow, millis,
283 Grego::monthLength(year, month),
284 ec);
285 }
286}
287
288/**
289 * TimeZone API.
290 */
291int32_t OlsonTimeZone::getOffset(uint8_t era, int32_t year, int32_t month,
292 int32_t dom, uint8_t dow,
293 int32_t millis, int32_t monthLength,
294 UErrorCode& ec) const {
295 if (U_FAILURE(ec)) {
296 return 0;
297 }
298
299 if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC)
300 || month < UCAL_JANUARY
301 || month > UCAL_DECEMBER
302 || dom < 1
303 || dom > monthLength
304 || dow < UCAL_SUNDAY
305 || dow > UCAL_SATURDAY
306 || millis < 0
307 || millis >= U_MILLIS_PER_DAY
308 || monthLength < 28
309 || monthLength > 31) {
310 ec = U_ILLEGAL_ARGUMENT_ERROR;
311 return 0;
312 }
313
314 if (era == GregorianCalendar::BC) {
315 year = -year;
316 }
317
318 if (year > finalYear) { // [sic] >, not >=; see above
319 U_ASSERT(finalZone != 0);
320 return finalZone->getOffset(era, year, month, dom, dow,
321 millis, monthLength, ec);
322 }
323
46f4442e
A
324 // Compute local epoch millis from input fields
325 UDate date = (UDate)(Grego::fieldsToDay(year, month, dom) * U_MILLIS_PER_DAY + millis);
326 int32_t rawoff, dstoff;
327 getHistoricalOffset(date, TRUE, kDaylight, kStandard, rawoff, dstoff);
328 return rawoff + dstoff;
374ca955
A
329}
330
331/**
332 * TimeZone API.
333 */
334void OlsonTimeZone::getOffset(UDate date, UBool local, int32_t& rawoff,
335 int32_t& dstoff, UErrorCode& ec) const {
336 if (U_FAILURE(ec)) {
337 return;
338 }
374ca955
A
339 // The check against finalMillis will suffice most of the time, except
340 // for the case in which finalMillis == DBL_MAX, date == DBL_MAX,
341 // and finalZone == 0. For this case we add "&& finalZone != 0".
342 if (date >= finalMillis && finalZone != 0) {
46f4442e
A
343 finalZone->getOffset(date, local, rawoff, dstoff, ec);
344 } else {
345 getHistoricalOffset(date, local, kFormer, kLatter, rawoff, dstoff);
346 }
347}
374ca955 348
46f4442e
A
349void
350OlsonTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
351 int32_t& rawoff, int32_t& dstoff, UErrorCode& ec) /*const*/ {
352 if (U_FAILURE(ec)) {
374ca955
A
353 return;
354 }
46f4442e
A
355 if (date >= finalMillis && finalZone != 0) {
356 finalZone->getOffsetFromLocal(date, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff, ec);
357 } else {
358 getHistoricalOffset(date, TRUE, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff);
359 }
374ca955
A
360}
361
46f4442e 362
374ca955
A
363/**
364 * TimeZone API.
365 */
366void OlsonTimeZone::setRawOffset(int32_t /*offsetMillis*/) {
367 // We don't support this operation, since OlsonTimeZones are
368 // immutable (except for the ID, which is in the base class).
369
370 // Nothing to do!
371}
372
373/**
374 * TimeZone API.
375 */
376int32_t OlsonTimeZone::getRawOffset() const {
377 UErrorCode ec = U_ZERO_ERROR;
378 int32_t raw, dst;
379 getOffset((double) uprv_getUTCtime() * U_MILLIS_PER_SECOND,
380 FALSE, raw, dst, ec);
381 return raw;
382}
383
73c04bcf
A
384#if defined U_DEBUG_TZ
385void printTime(double ms) {
386 int32_t year, month, dom, dow;
387 double millis=0;
388 double days = Math::floorDivide(((double)ms), (double)U_MILLIS_PER_DAY, millis);
389
390 Grego::dayToFields(days, year, month, dom, dow);
46f4442e 391 U_DEBUG_TZ_MSG((" getHistoricalOffset: time %.1f (%04d.%02d.%02d+%.1fh)\n", ms,
73c04bcf
A
392 year, month+1, dom, (millis/kOneHour)));
393 }
394#endif
395
46f4442e
A
396void
397OlsonTimeZone::getHistoricalOffset(UDate date, UBool local,
398 int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt,
399 int32_t& rawoff, int32_t& dstoff) const {
400 U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst)\n",
401 date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt));
73c04bcf 402#if defined U_DEBUG_TZ
46f4442e 403 printTime(date*1000.0);
73c04bcf 404#endif
374ca955 405 if (transitionCount != 0) {
46f4442e 406 double sec = uprv_floor(date / U_MILLIS_PER_SECOND);
374ca955
A
407 // Linear search from the end is the fastest approach, since
408 // most lookups will happen at/near the end.
46f4442e 409 int16_t i;
374ca955
A
410 for (i = transitionCount - 1; i > 0; --i) {
411 int32_t transition = transitionTimes[i];
46f4442e 412
374ca955 413 if (local) {
46f4442e
A
414 int32_t offsetBefore = zoneOffset(typeData[i-1]);
415 UBool dstBefore = dstOffset(typeData[i-1]) != 0;
416
417 int32_t offsetAfter = zoneOffset(typeData[i]);
418 UBool dstAfter = dstOffset(typeData[i]) != 0;
419
420 UBool dstToStd = dstBefore && !dstAfter;
421 UBool stdToDst = !dstBefore && dstAfter;
73c04bcf 422
46f4442e
A
423 if (offsetAfter - offsetBefore >= 0) {
424 // Positive transition, which makes a non-existing local time range
425 if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd)
426 || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
427 transition += offsetBefore;
428 } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst)
429 || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
430 transition += offsetAfter;
431 } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) {
432 transition += offsetBefore;
433 } else {
434 // Interprets the time with rule before the transition,
435 // default for non-existing time range
436 transition += offsetAfter;
437 }
73c04bcf 438 } else {
46f4442e
A
439 // Negative transition, which makes a duplicated local time range
440 if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd)
441 || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
442 transition += offsetAfter;
443 } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst)
444 || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
445 transition += offsetBefore;
446 } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) {
447 transition += offsetBefore;
448 } else {
449 // Interprets the time with rule after the transition,
450 // default for duplicated local time range
451 transition += offsetAfter;
452 }
73c04bcf 453 }
374ca955 454 }
46f4442e
A
455 if (sec >= transition) {
456 U_DEBUG_TZ_MSG(("Found@%d: time=%.1f, localtransition=%d (orig %d) dz %d\n", i, sec, transition, transitionTimes[i],
73c04bcf
A
457 zoneOffset(typeData[i-1])));
458#if defined U_DEBUG_TZ
46f4442e
A
459 printTime(transition*1000.0);
460 printTime(transitionTimes[i]*1000.0);
73c04bcf 461#endif
374ca955 462 break;
73c04bcf 463 } else {
46f4442e 464 U_DEBUG_TZ_MSG(("miss@%d: time=%.1f, localtransition=%d (orig %d) dz %d\n", i, sec, transition, transitionTimes[i],
73c04bcf
A
465 zoneOffset(typeData[i-1])));
466#if defined U_DEBUG_TZ
46f4442e
A
467 printTime(transition*1000.0);
468 printTime(transitionTimes[i]*1000.0);
73c04bcf 469#endif
374ca955
A
470 }
471 }
472
473 U_ASSERT(i>=0 && i<transitionCount);
474
475 // Check invariants for GMT times; if these pass for GMT times
476 // the local logic should be working too.
46f4442e
A
477 U_ASSERT(local || sec < transitionTimes[0] || sec >= transitionTimes[i]);
478 U_ASSERT(local || i == transitionCount-1 || sec < transitionTimes[i+1]);
479
480 U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst) - trans %d\n",
481 date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt, i));
482
483 // Since ICU tzdata 2007c, the first transition data is actually not a
484 // transition, but used for representing the initial offset. So the code
485 // below works even if i == 0.
486 int16_t index = typeData[i];
487 rawoff = rawOffset(index) * U_MILLIS_PER_SECOND;
488 dstoff = dstOffset(index) * U_MILLIS_PER_SECOND;
489 } else {
490 // No transitions, single pair of offsets only
491 rawoff = rawOffset(0) * U_MILLIS_PER_SECOND;
492 dstoff = dstOffset(0) * U_MILLIS_PER_SECOND;
374ca955 493 }
46f4442e
A
494 U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst) - raw=%d, dst=%d\n",
495 date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt, rawoff, dstoff));
374ca955
A
496}
497
498/**
499 * TimeZone API.
500 */
501UBool OlsonTimeZone::useDaylightTime() const {
502 // If DST was observed in 1942 (for example) but has never been
503 // observed from 1943 to the present, most clients will expect
504 // this method to return FALSE. This method determines whether
505 // DST is in use in the current year (at any point in the year)
506 // and returns TRUE if so.
507
508 int32_t days = (int32_t)Math::floorDivide(uprv_getUTCtime(), (double)U_MILLIS_PER_DAY); // epoch days
509
510 int32_t year, month, dom, dow;
511
512 Grego::dayToFields(days, year, month, dom, dow);
513
514 if (year > finalYear) { // [sic] >, not >=; see above
515 U_ASSERT(finalZone != 0 && finalZone->useDaylightTime());
516 return TRUE;
517 }
518
519 // Find start of this year, and start of next year
520 int32_t start = (int32_t) Grego::fieldsToDay(year, 0, 1) * SECONDS_PER_DAY;
521 int32_t limit = (int32_t) Grego::fieldsToDay(year+1, 0, 1) * SECONDS_PER_DAY;
522
523 // Return TRUE if DST is observed at any time during the current
524 // year.
525 for (int16_t i=0; i<transitionCount; ++i) {
526 if (transitionTimes[i] >= limit) {
527 break;
528 }
46f4442e
A
529 if ((transitionTimes[i] >= start && dstOffset(typeData[i]) != 0)
530 || (transitionTimes[i] > start && i > 0 && dstOffset(typeData[i - 1]) != 0)) {
374ca955
A
531 return TRUE;
532 }
533 }
534 return FALSE;
535}
73c04bcf
A
536int32_t
537OlsonTimeZone::getDSTSavings() const{
538 if(finalZone!=NULL){
539 return finalZone->getDSTSavings();
540 }
541 return TimeZone::getDSTSavings();
542}
374ca955
A
543/**
544 * TimeZone API.
545 */
546UBool OlsonTimeZone::inDaylightTime(UDate date, UErrorCode& ec) const {
547 int32_t raw, dst;
548 getOffset(date, FALSE, raw, dst, ec);
549 return dst != 0;
550}
551
46f4442e
A
552UBool
553OlsonTimeZone::hasSameRules(const TimeZone &other) const {
554 if (this == &other) {
555 return TRUE;
556 }
557 if (other.getDynamicClassID() != OlsonTimeZone::getStaticClassID()) {
558 return FALSE;
559 }
560 const OlsonTimeZone* z = (const OlsonTimeZone*) &other;
561
562 // [sic] pointer comparison: typeData points into
563 // memory-mapped or DLL space, so if two zones have the same
564 // pointer, they are equal.
565 if (typeData == z->typeData) {
566 return TRUE;
567 }
568
569 // If the pointers are not equal, the zones may still
570 // be equal if their rules and transitions are equal
571 return
572 (finalYear == z->finalYear &&
573 // Don't compare finalMillis; if finalYear is ==, so is finalMillis
574 ((finalZone == 0 && z->finalZone == 0) ||
575 (finalZone != 0 && z->finalZone != 0 && *finalZone == *z->finalZone)) &&
576
577 transitionCount == z->transitionCount &&
578 typeCount == z->typeCount &&
579 uprv_memcmp(transitionTimes, z->transitionTimes,
580 sizeof(transitionTimes[0]) * transitionCount) == 0 &&
581 uprv_memcmp(typeOffsets, z->typeOffsets,
582 (sizeof(typeOffsets[0]) * typeCount) << 1) == 0 &&
583 uprv_memcmp(typeData, z->typeData,
584 (sizeof(typeData[0]) * typeCount)) == 0);
585}
586
587void
588OlsonTimeZone::clearTransitionRules(void) {
589 initialRule = NULL;
590 firstTZTransition = NULL;
591 firstFinalTZTransition = NULL;
592 historicRules = NULL;
593 historicRuleCount = 0;
594 finalZoneWithStartYear = NULL;
595 firstTZTransitionIdx = 0;
596 transitionRulesInitialized = FALSE;
597}
598
599void
600OlsonTimeZone::deleteTransitionRules(void) {
601 if (initialRule != NULL) {
602 delete initialRule;
603 }
604 if (firstTZTransition != NULL) {
605 delete firstTZTransition;
606 }
607 if (firstFinalTZTransition != NULL) {
608 delete firstFinalTZTransition;
609 }
610 if (finalZoneWithStartYear != NULL) {
611 delete finalZoneWithStartYear;
612 }
613 if (historicRules != NULL) {
614 for (int i = 0; i < historicRuleCount; i++) {
615 if (historicRules[i] != NULL) {
616 delete historicRules[i];
617 }
618 }
619 uprv_free(historicRules);
620 }
621 clearTransitionRules();
622}
623
624void
625OlsonTimeZone::initTransitionRules(UErrorCode& status) {
626 if(U_FAILURE(status)) {
627 return;
628 }
629 if (transitionRulesInitialized) {
630 return;
631 }
632 deleteTransitionRules();
633 UnicodeString tzid;
634 getID(tzid);
635
636 UnicodeString stdName = tzid + UNICODE_STRING_SIMPLE("(STD)");
637 UnicodeString dstName = tzid + UNICODE_STRING_SIMPLE("(DST)");
638
639 int32_t raw, dst;
640 if (transitionCount > 0) {
641 int16_t transitionIdx, typeIdx;
642
643 // Note: Since 2007c, the very first transition data is a dummy entry
644 // added for resolving a offset calculation problem.
645
646 // Create initial rule
647 typeIdx = (int16_t)typeData[0]; // initial type
648 raw = rawOffset(typeIdx) * U_MILLIS_PER_SECOND;
649 dst = dstOffset(typeIdx) * U_MILLIS_PER_SECOND;
650 initialRule = new InitialTimeZoneRule((dst == 0 ? stdName : dstName), raw, dst);
651 // Check to make sure initialRule was created
652 if (initialRule == NULL) {
653 status = U_MEMORY_ALLOCATION_ERROR;
654 deleteTransitionRules();
655 return;
656 }
657
658 firstTZTransitionIdx = 0;
659 for (transitionIdx = 1; transitionIdx < transitionCount; transitionIdx++) {
660 firstTZTransitionIdx++;
661 if (typeIdx != (int16_t)typeData[transitionIdx]) {
662 break;
663 }
664 }
665 if (transitionIdx == transitionCount) {
666 // Actually no transitions...
667 } else {
668 // Build historic rule array
669 UDate* times = (UDate*)uprv_malloc(sizeof(UDate)*transitionCount); /* large enough to store all transition times */
670 if (times == NULL) {
671 status = U_MEMORY_ALLOCATION_ERROR;
672 deleteTransitionRules();
673 return;
674 }
675 for (typeIdx = 0; typeIdx < typeCount; typeIdx++) {
676 // Gather all start times for each pair of offsets
677 int32_t nTimes = 0;
678 for (transitionIdx = firstTZTransitionIdx; transitionIdx < transitionCount; transitionIdx++) {
679 if (typeIdx == (int16_t)typeData[transitionIdx]) {
680 UDate tt = ((UDate)transitionTimes[transitionIdx]) * U_MILLIS_PER_SECOND;
681 if (tt < finalMillis) {
682 // Exclude transitions after finalMillis
683 times[nTimes++] = tt;
684 }
685 }
686 }
687 if (nTimes > 0) {
688 // Create a TimeArrayTimeZoneRule
689 raw = rawOffset(typeIdx) * U_MILLIS_PER_SECOND;
690 dst = dstOffset(typeIdx) * U_MILLIS_PER_SECOND;
691 if (historicRules == NULL) {
692 historicRuleCount = typeCount;
693 historicRules = (TimeArrayTimeZoneRule**)uprv_malloc(sizeof(TimeArrayTimeZoneRule*)*historicRuleCount);
694 if (historicRules == NULL) {
695 status = U_MEMORY_ALLOCATION_ERROR;
696 deleteTransitionRules();
697 uprv_free(times);
698 return;
699 }
700 for (int i = 0; i < historicRuleCount; i++) {
701 // Initialize TimeArrayTimeZoneRule pointers as NULL
702 historicRules[i] = NULL;
703 }
704 }
705 historicRules[typeIdx] = new TimeArrayTimeZoneRule((dst == 0 ? stdName : dstName),
706 raw, dst, times, nTimes, DateTimeRule::UTC_TIME);
707 // Check for memory allocation error
708 if (historicRules[typeIdx] == NULL) {
709 status = U_MEMORY_ALLOCATION_ERROR;
710 deleteTransitionRules();
711 return;
712 }
713 }
714 }
715 uprv_free(times);
716
717 // Create initial transition
718 typeIdx = (int16_t)typeData[firstTZTransitionIdx];
719 firstTZTransition = new TimeZoneTransition(((UDate)transitionTimes[firstTZTransitionIdx]) * U_MILLIS_PER_SECOND,
720 *initialRule, *historicRules[typeIdx]);
721 // Check to make sure firstTZTransition was created.
722 if (firstTZTransition == NULL) {
723 status = U_MEMORY_ALLOCATION_ERROR;
724 deleteTransitionRules();
725 return;
726 }
727 }
728 }
729 if (initialRule == NULL) {
730 // No historic transitions
731 raw = rawOffset(0) * U_MILLIS_PER_SECOND;
732 dst = dstOffset(0) * U_MILLIS_PER_SECOND;
733 initialRule = new InitialTimeZoneRule((dst == 0 ? stdName : dstName), raw, dst);
734 // Check to make sure initialRule was created.
735 if (initialRule == NULL) {
736 status = U_MEMORY_ALLOCATION_ERROR;
737 deleteTransitionRules();
738 return;
739 }
740 }
741 if (finalZone != NULL) {
742 // Get the first occurence of final rule starts
743 UDate startTime = (UDate)finalMillis;
744 TimeZoneRule *firstFinalRule = NULL;
745 if (finalZone->useDaylightTime()) {
746 /*
747 * Note: When an OlsonTimeZone is constructed, we should set the final year
748 * as the start year of finalZone. However, the bounday condition used for
749 * getting offset from finalZone has some problems. So setting the start year
750 * in the finalZone will cause a problem. For now, we do not set the valid
751 * start year when the construction time and create a clone and set the
752 * start year when extracting rules.
753 */
754 finalZoneWithStartYear = (SimpleTimeZone*)finalZone->clone();
755 // Check to make sure finalZone was actually cloned.
756 if (finalZoneWithStartYear == NULL) {
757 status = U_MEMORY_ALLOCATION_ERROR;
758 deleteTransitionRules();
759 return;
760 }
761 // finalYear is 1 year before the actual final year.
762 // See the comment in the construction method.
763 finalZoneWithStartYear->setStartYear(finalYear + 1);
764
765 TimeZoneTransition tzt;
766 finalZoneWithStartYear->getNextTransition(startTime, false, tzt);
767 firstFinalRule = tzt.getTo()->clone();
768 // Check to make sure firstFinalRule received proper clone.
769 if (firstFinalRule == NULL) {
770 status = U_MEMORY_ALLOCATION_ERROR;
771 deleteTransitionRules();
772 return;
773 }
774 startTime = tzt.getTime();
775 } else {
776 finalZoneWithStartYear = (SimpleTimeZone*)finalZone->clone();
777 // Check to make sure finalZoneWithStartYear received proper clone before dereference.
778 if (finalZoneWithStartYear == NULL) {
779 status = U_MEMORY_ALLOCATION_ERROR;
780 deleteTransitionRules();
781 return;
782 }
783 finalZone->getID(tzid);
784 firstFinalRule = new TimeArrayTimeZoneRule(tzid,
785 finalZone->getRawOffset(), 0, &startTime, 1, DateTimeRule::UTC_TIME);
786 // Check firstFinalRule was properly created.
787 if (firstFinalRule == NULL) {
788 status = U_MEMORY_ALLOCATION_ERROR;
789 deleteTransitionRules();
790 return;
791 }
792 }
793 TimeZoneRule *prevRule = NULL;
794 if (transitionCount > 0) {
795 prevRule = historicRules[typeData[transitionCount - 1]];
796 }
797 if (prevRule == NULL) {
798 // No historic transitions, but only finalZone available
799 prevRule = initialRule;
800 }
801 firstFinalTZTransition = new TimeZoneTransition();
802 // Check to make sure firstFinalTZTransition was created before dereferencing
803 if (firstFinalTZTransition == NULL) {
804 status = U_MEMORY_ALLOCATION_ERROR;
805 deleteTransitionRules();
806 return;
807 }
808 firstFinalTZTransition->setTime(startTime);
809 firstFinalTZTransition->adoptFrom(prevRule->clone());
810 firstFinalTZTransition->adoptTo(firstFinalRule);
811 }
812 transitionRulesInitialized = TRUE;
813}
814
815UBool
816OlsonTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) /*const*/ {
817 UErrorCode status = U_ZERO_ERROR;
818 initTransitionRules(status);
819 if (U_FAILURE(status)) {
820 return FALSE;
821 }
822
823 if (finalZone != NULL) {
824 if (inclusive && base == firstFinalTZTransition->getTime()) {
825 result = *firstFinalTZTransition;
826 return TRUE;
827 } else if (base >= firstFinalTZTransition->getTime()) {
828 if (finalZone->useDaylightTime()) {
829 //return finalZone->getNextTransition(base, inclusive, result);
830 return finalZoneWithStartYear->getNextTransition(base, inclusive, result);
831 } else {
832 // No more transitions
833 return FALSE;
834 }
835 }
836 }
837 if (historicRules != NULL) {
838 // Find a historical transition
839 int16_t ttidx = transitionCount - 1;
840 for (; ttidx >= firstTZTransitionIdx; ttidx--) {
841 UDate t = ((UDate)transitionTimes[ttidx]) * U_MILLIS_PER_SECOND;
842 if (base > t || (!inclusive && base == t)) {
843 break;
844 }
845 }
846 if (ttidx == transitionCount - 1) {
847 if (firstFinalTZTransition != NULL) {
848 result = *firstFinalTZTransition;
849 return TRUE;
850 } else {
851 return FALSE;
852 }
853 } else if (ttidx < firstTZTransitionIdx) {
854 result = *firstTZTransition;
855 return TRUE;
856 } else {
857 // Create a TimeZoneTransition
858 TimeZoneRule *to = historicRules[typeData[ttidx + 1]];
859 TimeZoneRule *from = historicRules[typeData[ttidx]];
860 UDate startTime = ((UDate)transitionTimes[ttidx+1]) * U_MILLIS_PER_SECOND;
861
862 // The transitions loaded from zoneinfo.res may contain non-transition data
863 UnicodeString fromName, toName;
864 from->getName(fromName);
865 to->getName(toName);
866 if (fromName == toName && from->getRawOffset() == to->getRawOffset()
867 && from->getDSTSavings() == to->getDSTSavings()) {
868 return getNextTransition(startTime, false, result);
869 }
870 result.setTime(startTime);
871 result.adoptFrom(from->clone());
872 result.adoptTo(to->clone());
873 return TRUE;
874 }
875 }
876 return FALSE;
877}
878
879UBool
880OlsonTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) /*const*/ {
881 UErrorCode status = U_ZERO_ERROR;
882 initTransitionRules(status);
883 if (U_FAILURE(status)) {
884 return FALSE;
885 }
886
887 if (finalZone != NULL) {
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->getPreviousTransition(base, inclusive, result);
894 return finalZoneWithStartYear->getPreviousTransition(base, inclusive, result);
895 } else {
896 result = *firstFinalTZTransition;
897 return TRUE;
898 }
899 }
900 }
901
902 if (historicRules != NULL) {
903 // Find a historical transition
904 int16_t ttidx = transitionCount - 1;
905 for (; ttidx >= firstTZTransitionIdx; ttidx--) {
906 UDate t = ((UDate)transitionTimes[ttidx]) * U_MILLIS_PER_SECOND;
907 if (base > t || (inclusive && base == t)) {
908 break;
909 }
910 }
911 if (ttidx < firstTZTransitionIdx) {
912 // No more transitions
913 return FALSE;
914 } else if (ttidx == firstTZTransitionIdx) {
915 result = *firstTZTransition;
916 return TRUE;
917 } else {
918 // Create a TimeZoneTransition
919 TimeZoneRule *to = historicRules[typeData[ttidx]];
920 TimeZoneRule *from = historicRules[typeData[ttidx-1]];
921 UDate startTime = ((UDate)transitionTimes[ttidx]) * U_MILLIS_PER_SECOND;
922
923 // The transitions loaded from zoneinfo.res may contain non-transition data
924 UnicodeString fromName, toName;
925 from->getName(fromName);
926 to->getName(toName);
927 if (fromName == toName && from->getRawOffset() == to->getRawOffset()
928 && from->getDSTSavings() == to->getDSTSavings()) {
929 return getPreviousTransition(startTime, false, result);
930 }
931 result.setTime(startTime);
932 result.adoptFrom(from->clone());
933 result.adoptTo(to->clone());
934 return TRUE;
935 }
936 }
937 return FALSE;
938}
939
940int32_t
941OlsonTimeZone::countTransitionRules(UErrorCode& status) /*const*/ {
942 if (U_FAILURE(status)) {
943 return 0;
944 }
945 initTransitionRules(status);
946 if (U_FAILURE(status)) {
947 return 0;
948 }
949
950 int32_t count = 0;
951 if (historicRules != NULL) {
952 // historicRules may contain null entries when original zoneinfo data
953 // includes non transition data.
954 for (int32_t i = 0; i < historicRuleCount; i++) {
955 if (historicRules[i] != NULL) {
956 count++;
957 }
958 }
959 }
960 if (finalZone != NULL) {
961 if (finalZone->useDaylightTime()) {
962 count += 2;
963 } else {
964 count++;
965 }
966 }
967 return count;
968}
969
970void
971OlsonTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
972 const TimeZoneRule* trsrules[],
973 int32_t& trscount,
974 UErrorCode& status) /*const*/ {
975 if (U_FAILURE(status)) {
976 return;
977 }
978 initTransitionRules(status);
979 if (U_FAILURE(status)) {
980 return;
981 }
982
983 // Initial rule
984 initial = initialRule;
985
986 // Transition rules
987 int32_t cnt = 0;
988 if (historicRules != NULL && trscount > cnt) {
989 // historicRules may contain null entries when original zoneinfo data
990 // includes non transition data.
991 for (int32_t i = 0; i < historicRuleCount; i++) {
992 if (historicRules[i] != NULL) {
993 trsrules[cnt++] = historicRules[i];
994 if (cnt >= trscount) {
995 break;
996 }
997 }
998 }
999 }
1000 if (finalZoneWithStartYear != NULL && trscount > cnt) {
1001 const InitialTimeZoneRule *tmpini;
1002 int32_t tmpcnt = trscount - cnt;
1003 finalZoneWithStartYear->getTimeZoneRules(tmpini, &trsrules[cnt], tmpcnt, status);
1004 if (U_FAILURE(status)) {
1005 return;
1006 }
1007 cnt += tmpcnt;
1008 }
1009 // Set the result length
1010 trscount = cnt;
1011}
1012
374ca955
A
1013U_NAMESPACE_END
1014
1015#endif // !UCONFIG_NO_FORMATTING
1016
1017//eof