2 **********************************************************************
3 * Copyright (c) 2003-2010, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 **********************************************************************
7 * Created: July 21 2003
9 **********************************************************************
12 #include <typeinfo> // for 'typeid' to work
16 #if !UCONFIG_NO_FORMATTING
18 #include "unicode/ures.h"
19 #include "unicode/simpletz.h"
20 #include "unicode/gregocal.h"
25 #include <float.h> // DBL_MAX
26 #include "uresimp.h" // struct UResourceBundle
30 # include "uresimp.h" // for debugging
32 static void debug_tz_loc(const char *f
, int32_t l
)
34 fprintf(stderr
, "%s:%d: ", f
, l
);
37 static void debug_tz_msg(const char *pat
, ...)
41 vfprintf(stderr
, pat
, ap
);
44 // must use double parens, i.e.: U_DEBUG_TZ_MSG(("four is: %d",4));
45 #define U_DEBUG_TZ_MSG(x) {debug_tz_loc(__FILE__,__LINE__);debug_tz_msg x;}
47 #define U_DEBUG_TZ_MSG(x)
50 static UBool
arrayEqual(const void *a1
, const void *a2
, int32_t size
) {
51 if (a1
== NULL
&& a2
== NULL
) {
54 if ((a1
!= NULL
&& a2
== NULL
) || (a1
== NULL
&& a2
!= NULL
)) {
61 return (uprv_memcmp(a1
, a2
, size
) == 0);
66 #define kTRANS "trans"
67 #define kTRANSPRE32 "transPre32"
68 #define kTRANSPOST32 "transPost32"
69 #define kTYPEOFFSETS "typeOffsets"
70 #define kTYPEMAP "typeMap"
71 #define kLINKS "links"
72 #define kFINALRULE "finalRule"
73 #define kFINALRAW "finalRaw"
74 #define kFINALYEAR "finalYear"
76 #define SECONDS_PER_DAY (24*60*60)
78 static const int32_t ZEROS
[] = {0,0};
80 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OlsonTimeZone
)
83 * Default constructor. Creates a time zone with an empty ID and
84 * a fixed GMT offset of zero.
86 /*OlsonTimeZone::OlsonTimeZone() : finalYear(INT32_MAX), finalMillis(DBL_MAX), finalZone(0), transitionRulesInitialized(FALSE) {
87 clearTransitionRules();
92 * Construct a GMT+0 zone with no transitions. This is done when a
93 * constructor fails so the resultant object is well-behaved.
95 void OlsonTimeZone::constructEmpty() {
96 transitionCountPre32
= transitionCount32
= transitionCountPost32
= 0;
97 transitionTimesPre32
= transitionTimes32
= transitionTimesPost32
= NULL
;
108 * Construct from a resource bundle
109 * @param top the top-level zoneinfo resource bundle. This is used
110 * to lookup the rule that `res' may refer to, if there is one.
111 * @param res the resource bundle of the zone to be constructed
112 * @param ec input-output error code
114 OlsonTimeZone::OlsonTimeZone(const UResourceBundle
* top
,
115 const UResourceBundle
* res
,
117 finalZone(NULL
), transitionRulesInitialized(FALSE
)
119 clearTransitionRules();
120 U_DEBUG_TZ_MSG(("OlsonTimeZone(%s)\n", ures_getKey((UResourceBundle
*)res
)));
121 if ((top
== NULL
|| res
== NULL
) && U_SUCCESS(ec
)) {
122 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
125 // TODO -- clean up -- Doesn't work if res points to an alias
126 // // TODO remove nonconst casts below when ures_* API is fixed
127 // setID(ures_getKey((UResourceBundle*) res)); // cast away const
131 ures_initStackObject(&r
);
133 // Pre-32bit second transitions
134 ures_getByKey(res
, kTRANSPRE32
, &r
, &ec
);
135 transitionTimesPre32
= ures_getIntVector(&r
, &len
, &ec
);
136 transitionCountPre32
= len
>> 1;
137 if (ec
== U_MISSING_RESOURCE_ERROR
) {
138 // No pre-32bit transitions
139 transitionTimesPre32
= NULL
;
140 transitionCountPre32
= 0;
142 } else if (U_SUCCESS(ec
) && (len
< 0 || len
> 0x7FFF || (len
& 1) != 0) /* len must be even */) {
143 ec
= U_INVALID_FORMAT_ERROR
;
146 // 32bit second transitions
147 ures_getByKey(res
, kTRANS
, &r
, &ec
);
148 transitionTimes32
= ures_getIntVector(&r
, &len
, &ec
);
149 transitionCount32
= len
;
150 if (ec
== U_MISSING_RESOURCE_ERROR
) {
151 // No 32bit transitions
152 transitionTimes32
= NULL
;
153 transitionCount32
= 0;
155 } else if (U_SUCCESS(ec
) && (len
< 0 || len
> 0x7FFF)) {
156 ec
= U_INVALID_FORMAT_ERROR
;
159 // Post-32bit second transitions
160 ures_getByKey(res
, kTRANSPOST32
, &r
, &ec
);
161 transitionTimesPost32
= ures_getIntVector(&r
, &len
, &ec
);
162 transitionCountPost32
= len
>> 1;
163 if (ec
== U_MISSING_RESOURCE_ERROR
) {
164 // No pre-32bit transitions
165 transitionTimesPost32
= NULL
;
166 transitionCountPost32
= 0;
168 } else if (U_SUCCESS(ec
) && (len
< 0 || len
> 0x7FFF || (len
& 1) != 0) /* len must be even */) {
169 ec
= U_INVALID_FORMAT_ERROR
;
172 // Type offsets list must be of even size, with size >= 2
173 ures_getByKey(res
, kTYPEOFFSETS
, &r
, &ec
);
174 typeOffsets
= ures_getIntVector(&r
, &len
, &ec
);
175 if (U_SUCCESS(ec
) && (len
< 2 || len
> 0x7FFE || (len
& 1) != 0)) {
176 ec
= U_INVALID_FORMAT_ERROR
;
178 typeCount
= (int16_t) len
>> 1;
180 // Type map data must be of the same size as the transition count
182 if (transitionCount() > 0) {
183 ures_getByKey(res
, kTYPEMAP
, &r
, &ec
);
184 typeMapData
= ures_getBinary(&r
, &len
, &ec
);
185 if (ec
== U_MISSING_RESOURCE_ERROR
) {
186 // no type mapping data
187 ec
= U_INVALID_FORMAT_ERROR
;
188 } else if (U_SUCCESS(ec
) && len
!= transitionCount()) {
189 ec
= U_INVALID_FORMAT_ERROR
;
193 // Process final rule and data, if any
194 const UChar
*ruleIdUStr
= ures_getStringByKey(res
, kFINALRULE
, &len
, &ec
);
195 ures_getByKey(res
, kFINALRAW
, &r
, &ec
);
196 int32_t ruleRaw
= ures_getInt(&r
, &ec
);
197 ures_getByKey(res
, kFINALYEAR
, &r
, &ec
);
198 int32_t ruleYear
= ures_getInt(&r
, &ec
);
200 UnicodeString
ruleID(TRUE
, ruleIdUStr
, len
);
201 UResourceBundle
*rule
= TimeZone::loadRule(top
, ruleID
, NULL
, ec
);
202 const int32_t *ruleData
= ures_getIntVector(rule
, &len
, &ec
);
203 if (U_SUCCESS(ec
) && len
== 11) {
204 UnicodeString emptyStr
;
205 finalZone
= new SimpleTimeZone(
206 ruleRaw
* U_MILLIS_PER_SECOND
,
208 (int8_t)ruleData
[0], (int8_t)ruleData
[1], (int8_t)ruleData
[2],
209 ruleData
[3] * U_MILLIS_PER_SECOND
,
210 (SimpleTimeZone::TimeMode
) ruleData
[4],
211 (int8_t)ruleData
[5], (int8_t)ruleData
[6], (int8_t)ruleData
[7],
212 ruleData
[8] * U_MILLIS_PER_SECOND
,
213 (SimpleTimeZone::TimeMode
) ruleData
[9],
214 ruleData
[10] * U_MILLIS_PER_SECOND
, ec
);
215 if (finalZone
== NULL
) {
216 ec
= U_MEMORY_ALLOCATION_ERROR
;
218 finalStartYear
= ruleYear
;
220 // Note: Setting finalStartYear to the finalZone is problematic. When a date is around
221 // year boundary, SimpleTimeZone may return false result when DST is observed at the
222 // beginning of year. We could apply safe margin (day or two), but when one of recurrent
223 // rules falls around year boundary, it could return false result. Without setting the
224 // start year, finalZone works fine around the year boundary of the start year.
226 // finalZone->setStartYear(finalStartYear);
229 // Compute the millis for Jan 1, 0:00 GMT of the finalYear
231 // Note: finalStartMillis is used for detecting either if
232 // historic transition data or finalZone to be used. In an
233 // extreme edge case - for example, two transitions fall into
234 // small windows of time around the year boundary, this may
235 // result incorrect offset computation. But I think it will
236 // never happen practically. Yoshito - Feb 20, 2010
237 finalStartMillis
= Grego::fieldsToDay(finalStartYear
, 0, 1) * U_MILLIS_PER_DAY
;
240 ec
= U_INVALID_FORMAT_ERROR
;
243 } else if (ec
== U_MISSING_RESOURCE_ERROR
) {
258 OlsonTimeZone::OlsonTimeZone(const OlsonTimeZone
& other
) :
259 BasicTimeZone(other
), finalZone(0) {
264 * Assignment operator
266 OlsonTimeZone
& OlsonTimeZone::operator=(const OlsonTimeZone
& other
) {
267 transitionTimesPre32
= other
.transitionTimesPre32
;
268 transitionTimes32
= other
.transitionTimes32
;
269 transitionTimesPost32
= other
.transitionTimesPost32
;
271 transitionCountPre32
= other
.transitionCountPre32
;
272 transitionCount32
= other
.transitionCount32
;
273 transitionCountPost32
= other
.transitionCountPost32
;
275 typeCount
= other
.typeCount
;
276 typeOffsets
= other
.typeOffsets
;
277 typeMapData
= other
.typeMapData
;
280 finalZone
= (other
.finalZone
!= 0) ?
281 (SimpleTimeZone
*) other
.finalZone
->clone() : 0;
283 finalStartYear
= other
.finalStartYear
;
284 finalStartMillis
= other
.finalStartMillis
;
286 clearTransitionRules();
294 OlsonTimeZone::~OlsonTimeZone() {
295 deleteTransitionRules();
300 * Returns true if the two TimeZone objects are equal.
302 UBool
OlsonTimeZone::operator==(const TimeZone
& other
) const {
303 return ((this == &other
) ||
304 (typeid(*this) == typeid(other
) &&
305 TimeZone::operator==(other
) &&
306 hasSameRules(other
)));
312 TimeZone
* OlsonTimeZone::clone() const {
313 return new OlsonTimeZone(*this);
319 int32_t OlsonTimeZone::getOffset(uint8_t era
, int32_t year
, int32_t month
,
320 int32_t dom
, uint8_t dow
,
321 int32_t millis
, UErrorCode
& ec
) const {
322 if (month
< UCAL_JANUARY
|| month
> UCAL_DECEMBER
) {
324 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
328 return getOffset(era
, year
, month
, dom
, dow
, millis
,
329 Grego::monthLength(year
, month
),
337 int32_t OlsonTimeZone::getOffset(uint8_t era
, int32_t year
, int32_t month
,
338 int32_t dom
, uint8_t dow
,
339 int32_t millis
, int32_t monthLength
,
340 UErrorCode
& ec
) const {
345 if ((era
!= GregorianCalendar::AD
&& era
!= GregorianCalendar::BC
)
346 || month
< UCAL_JANUARY
347 || month
> UCAL_DECEMBER
351 || dow
> UCAL_SATURDAY
353 || millis
>= U_MILLIS_PER_DAY
355 || monthLength
> 31) {
356 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
360 if (era
== GregorianCalendar::BC
) {
364 if (finalZone
!= NULL
&& year
>= finalStartYear
) {
365 return finalZone
->getOffset(era
, year
, month
, dom
, dow
,
366 millis
, monthLength
, ec
);
369 // Compute local epoch millis from input fields
370 UDate date
= (UDate
)(Grego::fieldsToDay(year
, month
, dom
) * U_MILLIS_PER_DAY
+ millis
);
371 int32_t rawoff
, dstoff
;
372 getHistoricalOffset(date
, TRUE
, kDaylight
, kStandard
, rawoff
, dstoff
);
373 return rawoff
+ dstoff
;
379 void OlsonTimeZone::getOffset(UDate date
, UBool local
, int32_t& rawoff
,
380 int32_t& dstoff
, UErrorCode
& ec
) const {
384 if (finalZone
!= NULL
&& date
>= finalStartMillis
) {
385 finalZone
->getOffset(date
, local
, rawoff
, dstoff
, ec
);
387 getHistoricalOffset(date
, local
, kFormer
, kLatter
, rawoff
, dstoff
);
392 OlsonTimeZone::getOffsetFromLocal(UDate date
, int32_t nonExistingTimeOpt
, int32_t duplicatedTimeOpt
,
393 int32_t& rawoff
, int32_t& dstoff
, UErrorCode
& ec
) /*const*/ {
397 if (finalZone
!= NULL
&& date
>= finalStartMillis
) {
398 finalZone
->getOffsetFromLocal(date
, nonExistingTimeOpt
, duplicatedTimeOpt
, rawoff
, dstoff
, ec
);
400 getHistoricalOffset(date
, TRUE
, nonExistingTimeOpt
, duplicatedTimeOpt
, rawoff
, dstoff
);
408 void OlsonTimeZone::setRawOffset(int32_t /*offsetMillis*/) {
409 // We don't support this operation, since OlsonTimeZones are
410 // immutable (except for the ID, which is in the base class).
418 int32_t OlsonTimeZone::getRawOffset() const {
419 UErrorCode ec
= U_ZERO_ERROR
;
421 getOffset((double) uprv_getUTCtime() * U_MILLIS_PER_SECOND
,
422 FALSE
, raw
, dst
, ec
);
426 #if defined U_DEBUG_TZ
427 void printTime(double ms
) {
428 int32_t year
, month
, dom
, dow
;
430 double days
= ClockMath::floorDivide(((double)ms
), (double)U_MILLIS_PER_DAY
, millis
);
432 Grego::dayToFields(days
, year
, month
, dom
, dow
);
433 U_DEBUG_TZ_MSG((" getHistoricalOffset: time %.1f (%04d.%02d.%02d+%.1fh)\n", ms
,
434 year
, month
+1, dom
, (millis
/kOneHour
)));
439 OlsonTimeZone::transitionTimeInSeconds(int16_t transIdx
) const {
440 U_ASSERT(transIdx
>= 0 && transIdx
< transitionCount());
442 if (transIdx
< transitionCountPre32
) {
443 return (((int64_t)((uint32_t)transitionTimesPre32
[transIdx
<< 1])) << 32)
444 | ((int64_t)((uint32_t)transitionTimesPre32
[(transIdx
<< 1) + 1]));
447 transIdx
-= transitionCountPre32
;
448 if (transIdx
< transitionCount32
) {
449 return (int64_t)transitionTimes32
[transIdx
];
452 transIdx
-= transitionCount32
;
453 return (((int64_t)((uint32_t)transitionTimesPost32
[transIdx
<< 1])) << 32)
454 | ((int64_t)((uint32_t)transitionTimesPost32
[(transIdx
<< 1) + 1]));
458 OlsonTimeZone::getHistoricalOffset(UDate date
, UBool local
,
459 int32_t NonExistingTimeOpt
, int32_t DuplicatedTimeOpt
,
460 int32_t& rawoff
, int32_t& dstoff
) const {
461 U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst)\n",
462 date
, local
?"T":"F", NonExistingTimeOpt
, DuplicatedTimeOpt
));
463 #if defined U_DEBUG_TZ
464 printTime(date
*1000.0);
466 int16_t transCount
= transitionCount();
468 if (transCount
> 0) {
469 double sec
= uprv_floor(date
/ U_MILLIS_PER_SECOND
);
470 if (!local
&& sec
< transitionTimeInSeconds(0)) {
471 // Before the first transition time
472 rawoff
= initialRawOffset() * U_MILLIS_PER_SECOND
;
473 dstoff
= initialDstOffset() * U_MILLIS_PER_SECOND
;
475 // Linear search from the end is the fastest approach, since
476 // most lookups will happen at/near the end.
478 for (transIdx
= transCount
- 1; transIdx
>= 0; transIdx
--) {
479 int64_t transition
= transitionTimeInSeconds(transIdx
);
482 int32_t offsetBefore
= zoneOffsetAt(transIdx
- 1);
483 UBool dstBefore
= dstOffsetAt(transIdx
- 1) != 0;
485 int32_t offsetAfter
= zoneOffsetAt(transIdx
);
486 UBool dstAfter
= dstOffsetAt(transIdx
) != 0;
488 UBool dstToStd
= dstBefore
&& !dstAfter
;
489 UBool stdToDst
= !dstBefore
&& dstAfter
;
491 if (offsetAfter
- offsetBefore
>= 0) {
492 // Positive transition, which makes a non-existing local time range
493 if (((NonExistingTimeOpt
& kStdDstMask
) == kStandard
&& dstToStd
)
494 || ((NonExistingTimeOpt
& kStdDstMask
) == kDaylight
&& stdToDst
)) {
495 transition
+= offsetBefore
;
496 } else if (((NonExistingTimeOpt
& kStdDstMask
) == kStandard
&& stdToDst
)
497 || ((NonExistingTimeOpt
& kStdDstMask
) == kDaylight
&& dstToStd
)) {
498 transition
+= offsetAfter
;
499 } else if ((NonExistingTimeOpt
& kFormerLatterMask
) == kLatter
) {
500 transition
+= offsetBefore
;
502 // Interprets the time with rule before the transition,
503 // default for non-existing time range
504 transition
+= offsetAfter
;
507 // Negative transition, which makes a duplicated local time range
508 if (((DuplicatedTimeOpt
& kStdDstMask
) == kStandard
&& dstToStd
)
509 || ((DuplicatedTimeOpt
& kStdDstMask
) == kDaylight
&& stdToDst
)) {
510 transition
+= offsetAfter
;
511 } else if (((DuplicatedTimeOpt
& kStdDstMask
) == kStandard
&& stdToDst
)
512 || ((DuplicatedTimeOpt
& kStdDstMask
) == kDaylight
&& dstToStd
)) {
513 transition
+= offsetBefore
;
514 } else if ((DuplicatedTimeOpt
& kFormerLatterMask
) == kFormer
) {
515 transition
+= offsetBefore
;
517 // Interprets the time with rule after the transition,
518 // default for duplicated local time range
519 transition
+= offsetAfter
;
523 if (sec
>= transition
) {
527 // transIdx could be -1 when local=true
528 rawoff
= rawOffsetAt(transIdx
) * U_MILLIS_PER_SECOND
;
529 dstoff
= dstOffsetAt(transIdx
) * U_MILLIS_PER_SECOND
;
532 // No transitions, single pair of offsets only
533 rawoff
= initialRawOffset() * U_MILLIS_PER_SECOND
;
534 dstoff
= initialDstOffset() * U_MILLIS_PER_SECOND
;
536 U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst) - raw=%d, dst=%d\n",
537 date
, local
?"T":"F", NonExistingTimeOpt
, DuplicatedTimeOpt
, rawoff
, dstoff
));
543 UBool
OlsonTimeZone::useDaylightTime() const {
544 // If DST was observed in 1942 (for example) but has never been
545 // observed from 1943 to the present, most clients will expect
546 // this method to return FALSE. This method determines whether
547 // DST is in use in the current year (at any point in the year)
548 // and returns TRUE if so.
550 UDate current
= uprv_getUTCtime();
551 if (finalZone
!= NULL
&& current
>= finalStartMillis
) {
552 return finalZone
->useDaylightTime();
555 int32_t year
, month
, dom
, dow
, doy
, mid
;
556 Grego::timeToFields(current
, year
, month
, dom
, dow
, doy
, mid
);
558 // Find start of this year, and start of next year
559 double start
= Grego::fieldsToDay(year
, 0, 1) * SECONDS_PER_DAY
;
560 double limit
= Grego::fieldsToDay(year
+1, 0, 1) * SECONDS_PER_DAY
;
562 // Return TRUE if DST is observed at any time during the current
564 for (int16_t i
= 0; i
< transitionCount(); ++i
) {
565 double transition
= transitionTime(i
);
566 if (transition
>= limit
) {
569 if ((transition
>= start
&& dstOffsetAt(i
) != 0)
570 || (transition
> start
&& dstOffsetAt(i
- 1) != 0)) {
577 OlsonTimeZone::getDSTSavings() const{
578 if (finalZone
!= NULL
){
579 return finalZone
->getDSTSavings();
581 return TimeZone::getDSTSavings();
586 UBool
OlsonTimeZone::inDaylightTime(UDate date
, UErrorCode
& ec
) const {
588 getOffset(date
, FALSE
, raw
, dst
, ec
);
593 OlsonTimeZone::hasSameRules(const TimeZone
&other
) const {
594 if (this == &other
) {
597 const OlsonTimeZone
* z
= dynamic_cast<const OlsonTimeZone
*>(&other
);
602 // [sic] pointer comparison: typeMapData points into
603 // memory-mapped or DLL space, so if two zones have the same
604 // pointer, they are equal.
605 if (typeMapData
== z
->typeMapData
) {
609 // If the pointers are not equal, the zones may still
610 // be equal if their rules and transitions are equal
611 if ((finalZone
== NULL
&& z
->finalZone
!= NULL
)
612 || (finalZone
!= NULL
&& z
->finalZone
== NULL
)
613 || (finalZone
!= NULL
&& z
->finalZone
!= NULL
&& *finalZone
!= *z
->finalZone
)) {
617 if (finalZone
!= NULL
) {
618 if (finalStartYear
!= z
->finalStartYear
|| finalStartMillis
!= z
->finalStartMillis
) {
622 if (typeCount
!= z
->typeCount
623 || transitionCountPre32
!= z
->transitionCountPre32
624 || transitionCount32
!= z
->transitionCount32
625 || transitionCountPost32
!= z
->transitionCountPost32
) {
630 arrayEqual(transitionTimesPre32
, z
->transitionTimesPre32
, sizeof(transitionTimesPre32
[0]) * transitionCountPre32
<< 1)
631 && arrayEqual(transitionTimes32
, z
->transitionTimes32
, sizeof(transitionTimes32
[0]) * transitionCount32
)
632 && arrayEqual(transitionTimesPost32
, z
->transitionTimesPost32
, sizeof(transitionTimesPost32
[0]) * transitionCountPost32
<< 1)
633 && arrayEqual(typeOffsets
, z
->typeOffsets
, sizeof(typeOffsets
[0]) * typeCount
<< 1)
634 && arrayEqual(typeMapData
, z
->typeMapData
, sizeof(typeMapData
[0]) * transitionCount());
638 OlsonTimeZone::clearTransitionRules(void) {
640 firstTZTransition
= NULL
;
641 firstFinalTZTransition
= NULL
;
642 historicRules
= NULL
;
643 historicRuleCount
= 0;
644 finalZoneWithStartYear
= NULL
;
645 firstTZTransitionIdx
= 0;
646 transitionRulesInitialized
= FALSE
;
650 OlsonTimeZone::deleteTransitionRules(void) {
651 if (initialRule
!= NULL
) {
654 if (firstTZTransition
!= NULL
) {
655 delete firstTZTransition
;
657 if (firstFinalTZTransition
!= NULL
) {
658 delete firstFinalTZTransition
;
660 if (finalZoneWithStartYear
!= NULL
) {
661 delete finalZoneWithStartYear
;
663 if (historicRules
!= NULL
) {
664 for (int i
= 0; i
< historicRuleCount
; i
++) {
665 if (historicRules
[i
] != NULL
) {
666 delete historicRules
[i
];
669 uprv_free(historicRules
);
671 clearTransitionRules();
675 OlsonTimeZone::initTransitionRules(UErrorCode
& status
) {
676 if(U_FAILURE(status
)) {
679 if (transitionRulesInitialized
) {
682 deleteTransitionRules();
686 UnicodeString stdName
= tzid
+ UNICODE_STRING_SIMPLE("(STD)");
687 UnicodeString dstName
= tzid
+ UNICODE_STRING_SIMPLE("(DST)");
691 // Create initial rule
692 raw
= initialRawOffset() * U_MILLIS_PER_SECOND
;
693 dst
= initialDstOffset() * U_MILLIS_PER_SECOND
;
694 initialRule
= new InitialTimeZoneRule((dst
== 0 ? stdName
: dstName
), raw
, dst
);
695 // Check to make sure initialRule was created
696 if (initialRule
== NULL
) {
697 status
= U_MEMORY_ALLOCATION_ERROR
;
698 deleteTransitionRules();
702 int32_t transCount
= transitionCount();
703 if (transCount
> 0) {
704 int16_t transitionIdx
, typeIdx
;
706 // We probably no longer need to check the first "real" transition
707 // here, because the new tzcode remove such transitions already.
708 // For now, keeping this code for just in case. Feb 19, 2010 Yoshito
709 firstTZTransitionIdx
= 0;
710 for (transitionIdx
= 0; transitionIdx
< transCount
; transitionIdx
++) {
711 if (typeMapData
[transitionIdx
] != 0) { // type 0 is the initial type
714 firstTZTransitionIdx
++;
716 if (transitionIdx
== transCount
) {
717 // Actually no transitions...
719 // Build historic rule array
720 UDate
* times
= (UDate
*)uprv_malloc(sizeof(UDate
)*transCount
); /* large enough to store all transition times */
722 status
= U_MEMORY_ALLOCATION_ERROR
;
723 deleteTransitionRules();
726 for (typeIdx
= 0; typeIdx
< typeCount
; typeIdx
++) {
727 // Gather all start times for each pair of offsets
729 for (transitionIdx
= firstTZTransitionIdx
; transitionIdx
< transCount
; transitionIdx
++) {
730 if (typeIdx
== (int16_t)typeMapData
[transitionIdx
]) {
731 UDate tt
= (UDate
)transitionTime(transitionIdx
);
732 if (finalZone
== NULL
|| tt
<= finalStartMillis
) {
733 // Exclude transitions after finalMillis
734 times
[nTimes
++] = tt
;
739 // Create a TimeArrayTimeZoneRule
740 raw
= typeOffsets
[typeIdx
<< 1] * U_MILLIS_PER_SECOND
;
741 dst
= typeOffsets
[(typeIdx
<< 1) + 1] * U_MILLIS_PER_SECOND
;
742 if (historicRules
== NULL
) {
743 historicRuleCount
= typeCount
;
744 historicRules
= (TimeArrayTimeZoneRule
**)uprv_malloc(sizeof(TimeArrayTimeZoneRule
*)*historicRuleCount
);
745 if (historicRules
== NULL
) {
746 status
= U_MEMORY_ALLOCATION_ERROR
;
747 deleteTransitionRules();
751 for (int i
= 0; i
< historicRuleCount
; i
++) {
752 // Initialize TimeArrayTimeZoneRule pointers as NULL
753 historicRules
[i
] = NULL
;
756 historicRules
[typeIdx
] = new TimeArrayTimeZoneRule((dst
== 0 ? stdName
: dstName
),
757 raw
, dst
, times
, nTimes
, DateTimeRule::UTC_TIME
);
758 // Check for memory allocation error
759 if (historicRules
[typeIdx
] == NULL
) {
760 status
= U_MEMORY_ALLOCATION_ERROR
;
761 deleteTransitionRules();
768 // Create initial transition
769 typeIdx
= (int16_t)typeMapData
[firstTZTransitionIdx
];
770 firstTZTransition
= new TimeZoneTransition((UDate
)transitionTime(firstTZTransitionIdx
),
771 *initialRule
, *historicRules
[typeIdx
]);
772 // Check to make sure firstTZTransition was created.
773 if (firstTZTransition
== NULL
) {
774 status
= U_MEMORY_ALLOCATION_ERROR
;
775 deleteTransitionRules();
780 if (finalZone
!= NULL
) {
781 // Get the first occurence of final rule starts
782 UDate startTime
= (UDate
)finalStartMillis
;
783 TimeZoneRule
*firstFinalRule
= NULL
;
785 if (finalZone
->useDaylightTime()) {
787 * Note: When an OlsonTimeZone is constructed, we should set the final year
788 * as the start year of finalZone. However, the bounday condition used for
789 * getting offset from finalZone has some problems.
790 * For now, we do not set the valid start year when the construction time
791 * and create a clone and set the start year when extracting rules.
793 finalZoneWithStartYear
= (SimpleTimeZone
*)finalZone
->clone();
794 // Check to make sure finalZone was actually cloned.
795 if (finalZoneWithStartYear
== NULL
) {
796 status
= U_MEMORY_ALLOCATION_ERROR
;
797 deleteTransitionRules();
800 finalZoneWithStartYear
->setStartYear(finalStartYear
);
802 TimeZoneTransition tzt
;
803 finalZoneWithStartYear
->getNextTransition(startTime
, false, tzt
);
804 firstFinalRule
= tzt
.getTo()->clone();
805 // Check to make sure firstFinalRule received proper clone.
806 if (firstFinalRule
== NULL
) {
807 status
= U_MEMORY_ALLOCATION_ERROR
;
808 deleteTransitionRules();
811 startTime
= tzt
.getTime();
813 // final rule with no transitions
814 finalZoneWithStartYear
= (SimpleTimeZone
*)finalZone
->clone();
815 // Check to make sure finalZone was actually cloned.
816 if (finalZoneWithStartYear
== NULL
) {
817 status
= U_MEMORY_ALLOCATION_ERROR
;
818 deleteTransitionRules();
821 finalZone
->getID(tzid
);
822 firstFinalRule
= new TimeArrayTimeZoneRule(tzid
,
823 finalZone
->getRawOffset(), 0, &startTime
, 1, DateTimeRule::UTC_TIME
);
824 // Check firstFinalRule was properly created.
825 if (firstFinalRule
== NULL
) {
826 status
= U_MEMORY_ALLOCATION_ERROR
;
827 deleteTransitionRules();
831 TimeZoneRule
*prevRule
= NULL
;
832 if (transCount
> 0) {
833 prevRule
= historicRules
[typeMapData
[transCount
- 1]];
835 if (prevRule
== NULL
) {
836 // No historic transitions, but only finalZone available
837 prevRule
= initialRule
;
839 firstFinalTZTransition
= new TimeZoneTransition();
840 // Check to make sure firstFinalTZTransition was created before dereferencing
841 if (firstFinalTZTransition
== NULL
) {
842 status
= U_MEMORY_ALLOCATION_ERROR
;
843 deleteTransitionRules();
846 firstFinalTZTransition
->setTime(startTime
);
847 firstFinalTZTransition
->adoptFrom(prevRule
->clone());
848 firstFinalTZTransition
->adoptTo(firstFinalRule
);
850 transitionRulesInitialized
= TRUE
;
854 OlsonTimeZone::getNextTransition(UDate base
, UBool inclusive
, TimeZoneTransition
& result
) /*const*/ {
855 UErrorCode status
= U_ZERO_ERROR
;
856 initTransitionRules(status
);
857 if (U_FAILURE(status
)) {
861 if (finalZone
!= NULL
) {
862 if (inclusive
&& base
== firstFinalTZTransition
->getTime()) {
863 result
= *firstFinalTZTransition
;
865 } else if (base
>= firstFinalTZTransition
->getTime()) {
866 if (finalZone
->useDaylightTime()) {
867 //return finalZone->getNextTransition(base, inclusive, result);
868 return finalZoneWithStartYear
->getNextTransition(base
, inclusive
, result
);
870 // No more transitions
875 if (historicRules
!= NULL
) {
876 // Find a historical transition
877 int16_t transCount
= transitionCount();
878 int16_t ttidx
= transCount
- 1;
879 for (; ttidx
>= firstTZTransitionIdx
; ttidx
--) {
880 UDate t
= (UDate
)transitionTime(ttidx
);
881 if (base
> t
|| (!inclusive
&& base
== t
)) {
885 if (ttidx
== transCount
- 1) {
886 if (firstFinalTZTransition
!= NULL
) {
887 result
= *firstFinalTZTransition
;
892 } else if (ttidx
< firstTZTransitionIdx
) {
893 result
= *firstTZTransition
;
896 // Create a TimeZoneTransition
897 TimeZoneRule
*to
= historicRules
[typeMapData
[ttidx
+ 1]];
898 TimeZoneRule
*from
= historicRules
[typeMapData
[ttidx
]];
899 UDate startTime
= (UDate
)transitionTime(ttidx
+1);
901 // The transitions loaded from zoneinfo.res may contain non-transition data
902 UnicodeString fromName
, toName
;
903 from
->getName(fromName
);
905 if (fromName
== toName
&& from
->getRawOffset() == to
->getRawOffset()
906 && from
->getDSTSavings() == to
->getDSTSavings()) {
907 return getNextTransition(startTime
, false, result
);
909 result
.setTime(startTime
);
910 result
.adoptFrom(from
->clone());
911 result
.adoptTo(to
->clone());
919 OlsonTimeZone::getPreviousTransition(UDate base
, UBool inclusive
, TimeZoneTransition
& result
) /*const*/ {
920 UErrorCode status
= U_ZERO_ERROR
;
921 initTransitionRules(status
);
922 if (U_FAILURE(status
)) {
926 if (finalZone
!= NULL
) {
927 if (inclusive
&& base
== firstFinalTZTransition
->getTime()) {
928 result
= *firstFinalTZTransition
;
930 } else if (base
> firstFinalTZTransition
->getTime()) {
931 if (finalZone
->useDaylightTime()) {
932 //return finalZone->getPreviousTransition(base, inclusive, result);
933 return finalZoneWithStartYear
->getPreviousTransition(base
, inclusive
, result
);
935 result
= *firstFinalTZTransition
;
941 if (historicRules
!= NULL
) {
942 // Find a historical transition
943 int16_t ttidx
= transitionCount() - 1;
944 for (; ttidx
>= firstTZTransitionIdx
; ttidx
--) {
945 UDate t
= (UDate
)transitionTime(ttidx
);
946 if (base
> t
|| (inclusive
&& base
== t
)) {
950 if (ttidx
< firstTZTransitionIdx
) {
951 // No more transitions
953 } else if (ttidx
== firstTZTransitionIdx
) {
954 result
= *firstTZTransition
;
957 // Create a TimeZoneTransition
958 TimeZoneRule
*to
= historicRules
[typeMapData
[ttidx
]];
959 TimeZoneRule
*from
= historicRules
[typeMapData
[ttidx
-1]];
960 UDate startTime
= (UDate
)transitionTime(ttidx
);
962 // The transitions loaded from zoneinfo.res may contain non-transition data
963 UnicodeString fromName
, toName
;
964 from
->getName(fromName
);
966 if (fromName
== toName
&& from
->getRawOffset() == to
->getRawOffset()
967 && from
->getDSTSavings() == to
->getDSTSavings()) {
968 return getPreviousTransition(startTime
, false, result
);
970 result
.setTime(startTime
);
971 result
.adoptFrom(from
->clone());
972 result
.adoptTo(to
->clone());
980 OlsonTimeZone::countTransitionRules(UErrorCode
& status
) /*const*/ {
981 if (U_FAILURE(status
)) {
984 initTransitionRules(status
);
985 if (U_FAILURE(status
)) {
990 if (historicRules
!= NULL
) {
991 // historicRules may contain null entries when original zoneinfo data
992 // includes non transition data.
993 for (int32_t i
= 0; i
< historicRuleCount
; i
++) {
994 if (historicRules
[i
] != NULL
) {
999 if (finalZone
!= NULL
) {
1000 if (finalZone
->useDaylightTime()) {
1010 OlsonTimeZone::getTimeZoneRules(const InitialTimeZoneRule
*& initial
,
1011 const TimeZoneRule
* trsrules
[],
1013 UErrorCode
& status
) /*const*/ {
1014 if (U_FAILURE(status
)) {
1017 initTransitionRules(status
);
1018 if (U_FAILURE(status
)) {
1023 initial
= initialRule
;
1027 if (historicRules
!= NULL
&& trscount
> cnt
) {
1028 // historicRules may contain null entries when original zoneinfo data
1029 // includes non transition data.
1030 for (int32_t i
= 0; i
< historicRuleCount
; i
++) {
1031 if (historicRules
[i
] != NULL
) {
1032 trsrules
[cnt
++] = historicRules
[i
];
1033 if (cnt
>= trscount
) {
1039 if (finalZoneWithStartYear
!= NULL
&& trscount
> cnt
) {
1040 const InitialTimeZoneRule
*tmpini
;
1041 int32_t tmpcnt
= trscount
- cnt
;
1042 finalZoneWithStartYear
->getTimeZoneRules(tmpini
, &trsrules
[cnt
], tmpcnt
, status
);
1043 if (U_FAILURE(status
)) {
1048 // Set the result length
1054 #endif // !UCONFIG_NO_FORMATTING