2 **********************************************************************
3 * Copyright (c) 2003-2009, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 **********************************************************************
7 * Created: July 21 2003
9 **********************************************************************
14 #if !UCONFIG_NO_FORMATTING
16 #include "unicode/ures.h"
17 #include "unicode/simpletz.h"
18 #include "unicode/gregocal.h"
23 #include <float.h> // DBL_MAX
27 # include "uresimp.h" // for debugging
29 static void debug_tz_loc(const char *f
, int32_t l
)
31 fprintf(stderr
, "%s:%d: ", f
, l
);
34 static void debug_tz_msg(const char *pat
, ...)
38 vfprintf(stderr
, pat
, ap
);
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;}
44 #define U_DEBUG_TZ_MSG(x)
49 #define SECONDS_PER_DAY (24*60*60)
51 static const int32_t ZEROS
[] = {0,0};
53 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OlsonTimeZone
)
56 * Default constructor. Creates a time zone with an empty ID and
57 * a fixed GMT offset of zero.
59 /*OlsonTimeZone::OlsonTimeZone() : finalYear(INT32_MAX), finalMillis(DBL_MAX), finalZone(0), transitionRulesInitialized(FALSE) {
60 clearTransitionRules();
65 * Construct a GMT+0 zone with no transitions. This is done when a
66 * constructor fails so the resultant object is well-behaved.
68 void OlsonTimeZone::constructEmpty() {
71 transitionTimes
= typeOffsets
= ZEROS
;
72 typeData
= (const uint8_t*) ZEROS
;
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
82 OlsonTimeZone::OlsonTimeZone(const UResourceBundle
* top
,
83 const UResourceBundle
* res
,
85 finalYear(INT32_MAX
), finalMillis(DBL_MAX
), finalZone(0), transitionRulesInitialized(FALSE
)
87 clearTransitionRules();
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
;
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
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
103 int32_t size
= ures_getSize(res
);
104 if (size
< 3 || size
> 6) {
105 ec
= U_INVALID_FORMAT_ERROR
;
108 // Transitions list may be empty
110 UResourceBundle
* r
= ures_getByIndex(res
, 0, NULL
, &ec
);
111 transitionTimes
= ures_getIntVector(r
, &i
, &ec
);
112 if ((i
<0 || i
>0x7FFF) && U_SUCCESS(ec
)) {
113 ec
= U_INVALID_FORMAT_ERROR
;
115 transitionCount
= (int16_t) i
;
117 // Type offsets list must be of even size, with size >= 2
118 r
= ures_getByIndex(res
, 1, r
, &ec
);
119 typeOffsets
= ures_getIntVector(r
, &i
, &ec
);
120 if ((i
<2 || i
>0x7FFE || ((i
&1)!=0)) && U_SUCCESS(ec
)) {
121 ec
= U_INVALID_FORMAT_ERROR
;
123 typeCount
= (int16_t) i
>> 1;
125 // Type data must be of the same size as the transitions list
126 r
= ures_getByIndex(res
, 2, r
, &ec
);
128 typeData
= ures_getBinary(r
, &len
, &ec
);
130 if (len
!= transitionCount
&& U_SUCCESS(ec
)) {
131 ec
= U_INVALID_FORMAT_ERROR
;
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
)));
138 for(jj
=0;jj
<transitionCount
;jj
++) {
139 int32_t year
, month
, dom
, dow
;
141 double days
= Math::floorDivide(((double)transitionTimes
[jj
])*1000.0, (double)U_MILLIS_PER_DAY
, millis
);
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]));
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]));
155 // Process final rule and data, if any
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
);
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
);
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
;
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
),
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
);
198 // Make sure finalZone was created
199 if (finalZone
== NULL
) {
200 ec
= U_MEMORY_ALLOCATION_ERROR
;
203 ec
= U_INVALID_FORMAT_ERROR
;
208 ec
= U_INVALID_FORMAT_ERROR
;
222 OlsonTimeZone::OlsonTimeZone(const OlsonTimeZone
& other
) :
223 BasicTimeZone(other
), finalZone(0) {
228 * Assignment operator
230 OlsonTimeZone
& 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
;
239 finalZone
= (other
.finalZone
!= 0) ?
240 (SimpleTimeZone
*) other
.finalZone
->clone() : 0;
241 clearTransitionRules();
248 OlsonTimeZone::~OlsonTimeZone() {
249 deleteTransitionRules();
254 * Returns true if the two TimeZone objects are equal.
256 UBool
OlsonTimeZone::operator==(const TimeZone
& other
) const {
257 return ((this == &other
) ||
258 (getDynamicClassID() == other
.getDynamicClassID() &&
259 TimeZone::operator==(other
) &&
260 hasSameRules(other
)));
266 TimeZone
* OlsonTimeZone::clone() const {
267 return new OlsonTimeZone(*this);
273 int32_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
) {
278 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
282 return getOffset(era
, year
, month
, dom
, dow
, millis
,
283 Grego::monthLength(year
, month
),
291 int32_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 {
299 if ((era
!= GregorianCalendar::AD
&& era
!= GregorianCalendar::BC
)
300 || month
< UCAL_JANUARY
301 || month
> UCAL_DECEMBER
305 || dow
> UCAL_SATURDAY
307 || millis
>= U_MILLIS_PER_DAY
309 || monthLength
> 31) {
310 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
314 if (era
== GregorianCalendar::BC
) {
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
);
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
;
334 void OlsonTimeZone::getOffset(UDate date
, UBool local
, int32_t& rawoff
,
335 int32_t& dstoff
, UErrorCode
& ec
) const {
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) {
343 finalZone
->getOffset(date
, local
, rawoff
, dstoff
, ec
);
345 getHistoricalOffset(date
, local
, kFormer
, kLatter
, rawoff
, dstoff
);
350 OlsonTimeZone::getOffsetFromLocal(UDate date
, int32_t nonExistingTimeOpt
, int32_t duplicatedTimeOpt
,
351 int32_t& rawoff
, int32_t& dstoff
, UErrorCode
& ec
) /*const*/ {
355 if (date
>= finalMillis
&& finalZone
!= 0) {
356 finalZone
->getOffsetFromLocal(date
, nonExistingTimeOpt
, duplicatedTimeOpt
, rawoff
, dstoff
, ec
);
358 getHistoricalOffset(date
, TRUE
, nonExistingTimeOpt
, duplicatedTimeOpt
, rawoff
, dstoff
);
366 void 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).
376 int32_t OlsonTimeZone::getRawOffset() const {
377 UErrorCode ec
= U_ZERO_ERROR
;
379 getOffset((double) uprv_getUTCtime() * U_MILLIS_PER_SECOND
,
380 FALSE
, raw
, dst
, ec
);
384 #if defined U_DEBUG_TZ
385 void printTime(double ms
) {
386 int32_t year
, month
, dom
, dow
;
388 double days
= Math::floorDivide(((double)ms
), (double)U_MILLIS_PER_DAY
, millis
);
390 Grego::dayToFields(days
, year
, month
, dom
, dow
);
391 U_DEBUG_TZ_MSG((" getHistoricalOffset: time %.1f (%04d.%02d.%02d+%.1fh)\n", ms
,
392 year
, month
+1, dom
, (millis
/kOneHour
)));
397 OlsonTimeZone::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
));
402 #if defined U_DEBUG_TZ
403 printTime(date
*1000.0);
405 if (transitionCount
!= 0) {
406 double sec
= uprv_floor(date
/ U_MILLIS_PER_SECOND
);
407 // Linear search from the end is the fastest approach, since
408 // most lookups will happen at/near the end.
410 for (i
= transitionCount
- 1; i
> 0; --i
) {
411 int32_t transition
= transitionTimes
[i
];
414 int32_t offsetBefore
= zoneOffset(typeData
[i
-1]);
415 UBool dstBefore
= dstOffset(typeData
[i
-1]) != 0;
417 int32_t offsetAfter
= zoneOffset(typeData
[i
]);
418 UBool dstAfter
= dstOffset(typeData
[i
]) != 0;
420 UBool dstToStd
= dstBefore
&& !dstAfter
;
421 UBool stdToDst
= !dstBefore
&& dstAfter
;
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
;
434 // Interprets the time with rule before the transition,
435 // default for non-existing time range
436 transition
+= offsetAfter
;
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
;
449 // Interprets the time with rule after the transition,
450 // default for duplicated local time range
451 transition
+= offsetAfter
;
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
],
457 zoneOffset(typeData
[i
-1])));
458 #if defined U_DEBUG_TZ
459 printTime(transition
*1000.0);
460 printTime(transitionTimes
[i
]*1000.0);
464 U_DEBUG_TZ_MSG(("miss@%d: time=%.1f, localtransition=%d (orig %d) dz %d\n", i
, sec
, transition
, transitionTimes
[i
],
465 zoneOffset(typeData
[i
-1])));
466 #if defined U_DEBUG_TZ
467 printTime(transition
*1000.0);
468 printTime(transitionTimes
[i
]*1000.0);
473 U_ASSERT(i
>=0 && i
<transitionCount
);
475 // Check invariants for GMT times; if these pass for GMT times
476 // the local logic should be working too.
477 U_ASSERT(local
|| sec
< transitionTimes
[0] || sec
>= transitionTimes
[i
]);
478 U_ASSERT(local
|| i
== transitionCount
-1 || sec
< transitionTimes
[i
+1]);
480 U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst) - trans %d\n",
481 date
, local
?"T":"F", NonExistingTimeOpt
, DuplicatedTimeOpt
, i
));
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
;
490 // No transitions, single pair of offsets only
491 rawoff
= rawOffset(0) * U_MILLIS_PER_SECOND
;
492 dstoff
= dstOffset(0) * U_MILLIS_PER_SECOND
;
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
));
501 UBool
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.
508 int32_t days
= (int32_t)Math::floorDivide(uprv_getUTCtime(), (double)U_MILLIS_PER_DAY
); // epoch days
510 int32_t year
, month
, dom
, dow
;
512 Grego::dayToFields(days
, year
, month
, dom
, dow
);
514 if (year
> finalYear
) { // [sic] >, not >=; see above
515 U_ASSERT(finalZone
!= 0 && finalZone
->useDaylightTime());
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
;
523 // Return TRUE if DST is observed at any time during the current
525 for (int16_t i
=0; i
<transitionCount
; ++i
) {
526 if (transitionTimes
[i
] >= limit
) {
529 if ((transitionTimes
[i
] >= start
&& dstOffset(typeData
[i
]) != 0)
530 || (transitionTimes
[i
] > start
&& i
> 0 && dstOffset(typeData
[i
- 1]) != 0)) {
537 OlsonTimeZone::getDSTSavings() const{
539 return finalZone
->getDSTSavings();
541 return TimeZone::getDSTSavings();
546 UBool
OlsonTimeZone::inDaylightTime(UDate date
, UErrorCode
& ec
) const {
548 getOffset(date
, FALSE
, raw
, dst
, ec
);
553 OlsonTimeZone::hasSameRules(const TimeZone
&other
) const {
554 if (this == &other
) {
557 if (other
.getDynamicClassID() != OlsonTimeZone::getStaticClassID()) {
560 const OlsonTimeZone
* z
= (const OlsonTimeZone
*) &other
;
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
) {
569 // If the pointers are not equal, the zones may still
570 // be equal if their rules and transitions are equal
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
)) &&
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);
588 OlsonTimeZone::clearTransitionRules(void) {
590 firstTZTransition
= NULL
;
591 firstFinalTZTransition
= NULL
;
592 historicRules
= NULL
;
593 historicRuleCount
= 0;
594 finalZoneWithStartYear
= NULL
;
595 firstTZTransitionIdx
= 0;
596 transitionRulesInitialized
= FALSE
;
600 OlsonTimeZone::deleteTransitionRules(void) {
601 if (initialRule
!= NULL
) {
604 if (firstTZTransition
!= NULL
) {
605 delete firstTZTransition
;
607 if (firstFinalTZTransition
!= NULL
) {
608 delete firstFinalTZTransition
;
610 if (finalZoneWithStartYear
!= NULL
) {
611 delete finalZoneWithStartYear
;
613 if (historicRules
!= NULL
) {
614 for (int i
= 0; i
< historicRuleCount
; i
++) {
615 if (historicRules
[i
] != NULL
) {
616 delete historicRules
[i
];
619 uprv_free(historicRules
);
621 clearTransitionRules();
625 OlsonTimeZone::initTransitionRules(UErrorCode
& status
) {
626 if(U_FAILURE(status
)) {
629 if (transitionRulesInitialized
) {
632 deleteTransitionRules();
636 UnicodeString stdName
= tzid
+ UNICODE_STRING_SIMPLE("(STD)");
637 UnicodeString dstName
= tzid
+ UNICODE_STRING_SIMPLE("(DST)");
640 if (transitionCount
> 0) {
641 int16_t transitionIdx
, typeIdx
;
643 // Note: Since 2007c, the very first transition data is a dummy entry
644 // added for resolving a offset calculation problem.
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();
658 firstTZTransitionIdx
= 0;
659 for (transitionIdx
= 1; transitionIdx
< transitionCount
; transitionIdx
++) {
660 firstTZTransitionIdx
++;
661 if (typeIdx
!= (int16_t)typeData
[transitionIdx
]) {
665 if (transitionIdx
== transitionCount
) {
666 // Actually no transitions...
668 // Build historic rule array
669 UDate
* times
= (UDate
*)uprv_malloc(sizeof(UDate
)*transitionCount
); /* large enough to store all transition times */
671 status
= U_MEMORY_ALLOCATION_ERROR
;
672 deleteTransitionRules();
675 for (typeIdx
= 0; typeIdx
< typeCount
; typeIdx
++) {
676 // Gather all start times for each pair of offsets
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
;
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();
700 for (int i
= 0; i
< historicRuleCount
; i
++) {
701 // Initialize TimeArrayTimeZoneRule pointers as NULL
702 historicRules
[i
] = NULL
;
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();
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();
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();
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()) {
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.
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();
761 // finalYear is 1 year before the actual final year.
762 // See the comment in the construction method.
763 finalZoneWithStartYear
->setStartYear(finalYear
+ 1);
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();
774 startTime
= tzt
.getTime();
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();
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();
793 TimeZoneRule
*prevRule
= NULL
;
794 if (transitionCount
> 0) {
795 prevRule
= historicRules
[typeData
[transitionCount
- 1]];
797 if (prevRule
== NULL
) {
798 // No historic transitions, but only finalZone available
799 prevRule
= initialRule
;
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();
808 firstFinalTZTransition
->setTime(startTime
);
809 firstFinalTZTransition
->adoptFrom(prevRule
->clone());
810 firstFinalTZTransition
->adoptTo(firstFinalRule
);
812 transitionRulesInitialized
= TRUE
;
816 OlsonTimeZone::getNextTransition(UDate base
, UBool inclusive
, TimeZoneTransition
& result
) /*const*/ {
817 UErrorCode status
= U_ZERO_ERROR
;
818 initTransitionRules(status
);
819 if (U_FAILURE(status
)) {
823 if (finalZone
!= NULL
) {
824 if (inclusive
&& base
== firstFinalTZTransition
->getTime()) {
825 result
= *firstFinalTZTransition
;
827 } else if (base
>= firstFinalTZTransition
->getTime()) {
828 if (finalZone
->useDaylightTime()) {
829 //return finalZone->getNextTransition(base, inclusive, result);
830 return finalZoneWithStartYear
->getNextTransition(base
, inclusive
, result
);
832 // No more transitions
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
)) {
846 if (ttidx
== transitionCount
- 1) {
847 if (firstFinalTZTransition
!= NULL
) {
848 result
= *firstFinalTZTransition
;
853 } else if (ttidx
< firstTZTransitionIdx
) {
854 result
= *firstTZTransition
;
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
;
862 // The transitions loaded from zoneinfo.res may contain non-transition data
863 UnicodeString fromName
, toName
;
864 from
->getName(fromName
);
866 if (fromName
== toName
&& from
->getRawOffset() == to
->getRawOffset()
867 && from
->getDSTSavings() == to
->getDSTSavings()) {
868 return getNextTransition(startTime
, false, result
);
870 result
.setTime(startTime
);
871 result
.adoptFrom(from
->clone());
872 result
.adoptTo(to
->clone());
880 OlsonTimeZone::getPreviousTransition(UDate base
, UBool inclusive
, TimeZoneTransition
& result
) /*const*/ {
881 UErrorCode status
= U_ZERO_ERROR
;
882 initTransitionRules(status
);
883 if (U_FAILURE(status
)) {
887 if (finalZone
!= NULL
) {
888 if (inclusive
&& base
== firstFinalTZTransition
->getTime()) {
889 result
= *firstFinalTZTransition
;
891 } else if (base
> firstFinalTZTransition
->getTime()) {
892 if (finalZone
->useDaylightTime()) {
893 //return finalZone->getPreviousTransition(base, inclusive, result);
894 return finalZoneWithStartYear
->getPreviousTransition(base
, inclusive
, result
);
896 result
= *firstFinalTZTransition
;
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
)) {
911 if (ttidx
< firstTZTransitionIdx
) {
912 // No more transitions
914 } else if (ttidx
== firstTZTransitionIdx
) {
915 result
= *firstTZTransition
;
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
;
923 // The transitions loaded from zoneinfo.res may contain non-transition data
924 UnicodeString fromName
, toName
;
925 from
->getName(fromName
);
927 if (fromName
== toName
&& from
->getRawOffset() == to
->getRawOffset()
928 && from
->getDSTSavings() == to
->getDSTSavings()) {
929 return getPreviousTransition(startTime
, false, result
);
931 result
.setTime(startTime
);
932 result
.adoptFrom(from
->clone());
933 result
.adoptTo(to
->clone());
941 OlsonTimeZone::countTransitionRules(UErrorCode
& status
) /*const*/ {
942 if (U_FAILURE(status
)) {
945 initTransitionRules(status
);
946 if (U_FAILURE(status
)) {
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
) {
960 if (finalZone
!= NULL
) {
961 if (finalZone
->useDaylightTime()) {
971 OlsonTimeZone::getTimeZoneRules(const InitialTimeZoneRule
*& initial
,
972 const TimeZoneRule
* trsrules
[],
974 UErrorCode
& status
) /*const*/ {
975 if (U_FAILURE(status
)) {
978 initTransitionRules(status
);
979 if (U_FAILURE(status
)) {
984 initial
= initialRule
;
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
) {
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
)) {
1009 // Set the result length
1015 #endif // !UCONFIG_NO_FORMATTING