2 *******************************************************************************
3 * Copyright (C) 2007-2010, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
8 #include "unicode/utypes.h"
10 #if !UCONFIG_NO_FORMATTING
12 #include "unicode/basictz.h"
19 #define MILLIS_PER_YEAR (365*24*60*60*1000.0)
21 BasicTimeZone::BasicTimeZone()
25 BasicTimeZone::BasicTimeZone(const UnicodeString
&id
)
29 BasicTimeZone::BasicTimeZone(const BasicTimeZone
& source
)
33 BasicTimeZone::~BasicTimeZone() {
37 BasicTimeZone::hasEquivalentTransitions(/*const*/ BasicTimeZone
& tz
, UDate start
, UDate end
,
38 UBool ignoreDstAmount
, UErrorCode
& status
) /*const*/ {
39 if (U_FAILURE(status
)) {
42 if (hasSameRules(tz
)) {
45 // Check the offsets at the start time
46 int32_t raw1
, raw2
, dst1
, dst2
;
47 getOffset(start
, FALSE
, raw1
, dst1
, status
);
48 if (U_FAILURE(status
)) {
51 tz
.getOffset(start
, FALSE
, raw2
, dst2
, status
);
52 if (U_FAILURE(status
)) {
55 if (ignoreDstAmount
) {
56 if ((raw1
+ dst1
!= raw2
+ dst2
)
57 || (dst1
!= 0 && dst2
== 0)
58 || (dst1
== 0 && dst2
!= 0)) {
62 if (raw1
!= raw2
|| dst1
!= dst2
) {
66 // Check transitions in the range
68 TimeZoneTransition tr1
, tr2
;
70 UBool avail1
= getNextTransition(time
, FALSE
, tr1
);
71 UBool avail2
= tz
.getNextTransition(time
, FALSE
, tr2
);
73 if (ignoreDstAmount
) {
74 // Skip a transition which only differ the amount of DST savings
77 && tr1
.getTime() <= end
78 && (tr1
.getFrom()->getRawOffset() + tr1
.getFrom()->getDSTSavings()
79 == tr1
.getTo()->getRawOffset() + tr1
.getTo()->getDSTSavings())
80 && (tr1
.getFrom()->getDSTSavings() != 0 && tr1
.getTo()->getDSTSavings() != 0)) {
81 getNextTransition(tr1
.getTime(), FALSE
, tr1
);
88 && tr2
.getTime() <= end
89 && (tr2
.getFrom()->getRawOffset() + tr2
.getFrom()->getDSTSavings()
90 == tr2
.getTo()->getRawOffset() + tr2
.getTo()->getDSTSavings())
91 && (tr2
.getFrom()->getDSTSavings() != 0 && tr2
.getTo()->getDSTSavings() != 0)) {
92 tz
.getNextTransition(tr2
.getTime(), FALSE
, tr2
);
99 UBool inRange1
= (avail1
&& tr1
.getTime() <= end
);
100 UBool inRange2
= (avail2
&& tr2
.getTime() <= end
);
101 if (!inRange1
&& !inRange2
) {
102 // No more transition in the range
105 if (!inRange1
|| !inRange2
) {
108 if (tr1
.getTime() != tr2
.getTime()) {
111 if (ignoreDstAmount
) {
112 if (tr1
.getTo()->getRawOffset() + tr1
.getTo()->getDSTSavings()
113 != tr2
.getTo()->getRawOffset() + tr2
.getTo()->getDSTSavings()
114 || (tr1
.getTo()->getDSTSavings() != 0 && tr2
.getTo()->getDSTSavings() == 0)
115 || (tr1
.getTo()->getDSTSavings() == 0 && tr2
.getTo()->getDSTSavings() != 0)) {
119 if (tr1
.getTo()->getRawOffset() != tr2
.getTo()->getRawOffset() ||
120 tr1
.getTo()->getDSTSavings() != tr2
.getTo()->getDSTSavings()) {
124 time
= tr1
.getTime();
130 BasicTimeZone::getSimpleRulesNear(UDate date
, InitialTimeZoneRule
*& initial
,
131 AnnualTimeZoneRule
*& std
, AnnualTimeZoneRule
*& dst
, UErrorCode
& status
) /*const*/ {
135 if (U_FAILURE(status
)) {
138 int32_t initialRaw
, initialDst
;
139 UnicodeString initialName
;
141 AnnualTimeZoneRule
*ar1
= NULL
;
142 AnnualTimeZoneRule
*ar2
= NULL
;
146 TimeZoneTransition tr
;
147 // Get the next transition
148 avail
= getNextTransition(date
, FALSE
, tr
);
150 tr
.getFrom()->getName(initialName
);
151 initialRaw
= tr
.getFrom()->getRawOffset();
152 initialDst
= tr
.getFrom()->getDSTSavings();
154 // Check if the next transition is either DST->STD or STD->DST and
155 // within roughly 1 year from the specified date
156 UDate nextTransitionTime
= tr
.getTime();
157 if (((tr
.getFrom()->getDSTSavings() == 0 && tr
.getTo()->getDSTSavings() != 0)
158 || (tr
.getFrom()->getDSTSavings() != 0 && tr
.getTo()->getDSTSavings() == 0))
159 && (date
+ MILLIS_PER_YEAR
> nextTransitionTime
)) {
161 int32_t year
, month
, dom
, dow
, doy
, mid
;
164 // Get local wall time for the next transition time
165 Grego::timeToFields(nextTransitionTime
+ initialRaw
+ initialDst
,
166 year
, month
, dom
, dow
, doy
, mid
);
167 int32_t weekInMonth
= Grego::dayOfWeekInMonth(year
, month
, dom
);
169 DateTimeRule
*dtr
= new DateTimeRule(month
, weekInMonth
, dow
, mid
, DateTimeRule::WALL_TIME
);
170 tr
.getTo()->getName(name
);
172 // Note: SimpleTimeZone does not support raw offset change.
173 // So we always use raw offset of the given time for the rule,
174 // even raw offset is changed. This will result that the result
175 // zone to return wrong offset after the transition.
176 // When we encounter such case, we do not inspect next next
177 // transition for another rule.
178 ar1
= new AnnualTimeZoneRule(name
, initialRaw
, tr
.getTo()->getDSTSavings(),
179 dtr
, year
, AnnualTimeZoneRule::MAX_YEAR
);
181 if (tr
.getTo()->getRawOffset() == initialRaw
) {
182 // Get the next next transition
183 avail
= getNextTransition(nextTransitionTime
, FALSE
, tr
);
185 // Check if the next next transition is either DST->STD or STD->DST
186 // and within roughly 1 year from the next transition
187 if (((tr
.getFrom()->getDSTSavings() == 0 && tr
.getTo()->getDSTSavings() != 0)
188 || (tr
.getFrom()->getDSTSavings() != 0 && tr
.getTo()->getDSTSavings() == 0))
189 && nextTransitionTime
+ MILLIS_PER_YEAR
> tr
.getTime()) {
191 // Get local wall time for the next transition time
192 Grego::timeToFields(tr
.getTime() + tr
.getFrom()->getRawOffset() + tr
.getFrom()->getDSTSavings(),
193 year
, month
, dom
, dow
, doy
, mid
);
194 weekInMonth
= Grego::dayOfWeekInMonth(year
, month
, dom
);
195 // Generate another DOW rule
196 dtr
= new DateTimeRule(month
, weekInMonth
, dow
, mid
, DateTimeRule::WALL_TIME
);
197 tr
.getTo()->getName(name
);
198 ar2
= new AnnualTimeZoneRule(name
, tr
.getTo()->getRawOffset(), tr
.getTo()->getDSTSavings(),
199 dtr
, year
- 1, AnnualTimeZoneRule::MAX_YEAR
);
201 // Make sure this rule can be applied to the specified date
202 avail
= ar2
->getPreviousStart(date
, tr
.getFrom()->getRawOffset(), tr
.getFrom()->getDSTSavings(), TRUE
, d
);
203 if (!avail
|| d
> date
204 || initialRaw
!= tr
.getTo()->getRawOffset()
205 || initialDst
!= tr
.getTo()->getDSTSavings()) {
206 // We cannot use this rule as the second transition rule
214 // Try previous transition
215 avail
= getPreviousTransition(date
, TRUE
, tr
);
217 // Check if the previous transition is either DST->STD or STD->DST.
218 // The actual transition time does not matter here.
219 if ((tr
.getFrom()->getDSTSavings() == 0 && tr
.getTo()->getDSTSavings() != 0)
220 || (tr
.getFrom()->getDSTSavings() != 0 && tr
.getTo()->getDSTSavings() == 0)) {
222 // Generate another DOW rule
223 Grego::timeToFields(tr
.getTime() + tr
.getFrom()->getRawOffset() + tr
.getFrom()->getDSTSavings(),
224 year
, month
, dom
, dow
, doy
, mid
);
225 weekInMonth
= Grego::dayOfWeekInMonth(year
, month
, dom
);
226 dtr
= new DateTimeRule(month
, weekInMonth
, dow
, mid
, DateTimeRule::WALL_TIME
);
227 tr
.getTo()->getName(name
);
229 // second rule raw/dst offsets should match raw/dst offsets
231 ar2
= new AnnualTimeZoneRule(name
, initialRaw
, initialDst
,
232 dtr
, ar1
->getStartYear() - 1, AnnualTimeZoneRule::MAX_YEAR
);
234 // Check if this rule start after the first rule after the specified date
235 avail
= ar2
->getNextStart(date
, tr
.getFrom()->getRawOffset(), tr
.getFrom()->getDSTSavings(), FALSE
, d
);
236 if (!avail
|| d
<= nextTransitionTime
) {
237 // We cannot use this rule as the second transition rule
245 // Cannot find a good pair of AnnualTimeZoneRule
249 // The initial rule should represent the rule before the previous transition
250 ar1
->getName(initialName
);
251 initialRaw
= ar1
->getRawOffset();
252 initialDst
= ar1
->getDSTSavings();
257 // Try the previous one
258 avail
= getPreviousTransition(date
, TRUE
, tr
);
260 tr
.getTo()->getName(initialName
);
261 initialRaw
= tr
.getTo()->getRawOffset();
262 initialDst
= tr
.getTo()->getDSTSavings();
264 // No transitions in the past. Just use the current offsets
265 getOffset(date
, FALSE
, initialRaw
, initialDst
, status
);
266 if (U_FAILURE(status
)) {
271 // Set the initial rule
272 initial
= new InitialTimeZoneRule(initialName
, initialRaw
, initialDst
);
274 // Set the standard and daylight saving rules
275 if (ar1
!= NULL
&& ar2
!= NULL
) {
276 if (ar1
->getDSTSavings() != 0) {
287 BasicTimeZone::getTimeZoneRulesAfter(UDate start
, InitialTimeZoneRule
*& initial
,
288 UVector
*& transitionRules
, UErrorCode
& status
) /*const*/ {
289 if (U_FAILURE(status
)) {
293 const InitialTimeZoneRule
*orgini
;
294 const TimeZoneRule
**orgtrs
= NULL
;
295 TimeZoneTransition tzt
;
297 UVector
*orgRules
= NULL
;
299 TimeZoneRule
*r
= NULL
;
301 InitialTimeZoneRule
*res_initial
= NULL
;
302 UVector
*filteredRules
= NULL
;
306 UDate
*newTimes
= NULL
;
308 UBool bFinalStd
= FALSE
, bFinalDst
= FALSE
;
310 // Original transition rules
311 ruleCount
= countTransitionRules(status
);
312 if (U_FAILURE(status
)) {
315 orgRules
= new UVector(ruleCount
, status
);
316 if (U_FAILURE(status
)) {
319 orgtrs
= (const TimeZoneRule
**)uprv_malloc(sizeof(TimeZoneRule
*)*ruleCount
);
320 if (orgtrs
== NULL
) {
321 status
= U_MEMORY_ALLOCATION_ERROR
;
324 getTimeZoneRules(orgini
, orgtrs
, ruleCount
, status
);
325 if (U_FAILURE(status
)) {
328 for (i
= 0; i
< ruleCount
; i
++) {
329 orgRules
->addElement(orgtrs
[i
]->clone(), status
);
330 if (U_FAILURE(status
)) {
337 avail
= getPreviousTransition(start
, TRUE
, tzt
);
339 // No need to filter out rules only applicable to time before the start
340 initial
= orgini
->clone();
341 transitionRules
= orgRules
;
345 done
= (UBool
*)uprv_malloc(sizeof(UBool
)*ruleCount
);
347 status
= U_MEMORY_ALLOCATION_ERROR
;
350 filteredRules
= new UVector(status
);
351 if (U_FAILURE(status
)) {
355 // Create initial rule
356 tzt
.getTo()->getName(name
);
357 res_initial
= new InitialTimeZoneRule(name
, tzt
.getTo()->getRawOffset(),
358 tzt
.getTo()->getDSTSavings());
360 // Mark rules which does not need to be processed
361 for (i
= 0; i
< ruleCount
; i
++) {
362 r
= (TimeZoneRule
*)orgRules
->elementAt(i
);
363 avail
= r
->getNextStart(start
, res_initial
->getRawOffset(), res_initial
->getDSTSavings(), FALSE
, time
);
368 while (!bFinalStd
|| !bFinalDst
) {
369 avail
= getNextTransition(time
, FALSE
, tzt
);
373 UDate updatedTime
= tzt
.getTime();
374 if (updatedTime
== time
) {
375 // Can get here if rules for start & end of daylight time have exactly
377 // TODO: fix getNextTransition() to prevent it?
378 status
= U_INVALID_STATE_ERROR
;
383 const TimeZoneRule
*toRule
= tzt
.getTo();
384 for (i
= 0; i
< ruleCount
; i
++) {
385 r
= (TimeZoneRule
*)orgRules
->elementAt(i
);
390 if (i
>= ruleCount
) {
391 // This case should never happen
392 status
= U_INVALID_STATE_ERROR
;
398 const TimeArrayTimeZoneRule
*tar
= dynamic_cast<const TimeArrayTimeZoneRule
*>(toRule
);
399 const AnnualTimeZoneRule
*ar
;
401 // Get the previous raw offset and DST savings before the very first start time
402 TimeZoneTransition tzt0
;
405 avail
= getNextTransition(t
, FALSE
, tzt0
);
409 if (*(tzt0
.getTo()) == *tar
) {
415 // Check if the entire start times to be added
416 tar
->getFirstStart(tzt
.getFrom()->getRawOffset(), tzt
.getFrom()->getDSTSavings(), firstStart
);
417 if (firstStart
> start
) {
418 // Just add the rule as is
419 filteredRules
->addElement(tar
->clone(), status
);
420 if (U_FAILURE(status
)) {
424 // Colllect transitions after the start time
426 DateTimeRule::TimeRuleType timeType
;
429 startTimes
= tar
->countStartTimes();
430 timeType
= tar
->getTimeType();
431 for (idx
= 0; idx
< startTimes
; idx
++) {
432 tar
->getStartTimeAt(idx
, t
);
433 if (timeType
== DateTimeRule::STANDARD_TIME
) {
434 t
-= tzt
.getFrom()->getRawOffset();
436 if (timeType
== DateTimeRule::WALL_TIME
) {
437 t
-= tzt
.getFrom()->getDSTSavings();
443 int32_t asize
= startTimes
- idx
;
445 newTimes
= (UDate
*)uprv_malloc(sizeof(UDate
) * asize
);
446 if (newTimes
== NULL
) {
447 status
= U_MEMORY_ALLOCATION_ERROR
;
450 for (int32_t newidx
= 0; newidx
< asize
; newidx
++) {
451 tar
->getStartTimeAt(idx
+ newidx
, newTimes
[newidx
]);
452 if (U_FAILURE(status
)) {
459 TimeArrayTimeZoneRule
*newTar
= new TimeArrayTimeZoneRule(name
,
460 tar
->getRawOffset(), tar
->getDSTSavings(), newTimes
, asize
, timeType
);
462 filteredRules
->addElement(newTar
, status
);
463 if (U_FAILURE(status
)) {
469 } else if ((ar
= dynamic_cast<const AnnualTimeZoneRule
*>(toRule
)) != NULL
) {
470 ar
->getFirstStart(tzt
.getFrom()->getRawOffset(), tzt
.getFrom()->getDSTSavings(), firstStart
);
471 if (firstStart
== tzt
.getTime()) {
472 // Just add the rule as is
473 filteredRules
->addElement(ar
->clone(), status
);
474 if (U_FAILURE(status
)) {
478 // Calculate the transition year
479 int32_t year
, month
, dom
, dow
, doy
, mid
;
480 Grego::timeToFields(tzt
.getTime(), year
, month
, dom
, dow
, doy
, mid
);
481 // Re-create the rule
483 AnnualTimeZoneRule
*newAr
= new AnnualTimeZoneRule(name
, ar
->getRawOffset(), ar
->getDSTSavings(),
484 *(ar
->getRule()), year
, ar
->getEndYear());
485 filteredRules
->addElement(newAr
, status
);
486 if (U_FAILURE(status
)) {
490 // check if this is a final rule
491 if (ar
->getEndYear() == AnnualTimeZoneRule::MAX_YEAR
) {
492 // After bot final standard and dst rules are processed,
493 // exit this while loop.
494 if (ar
->getDSTSavings() == 0) {
505 if (orgRules
!= NULL
) {
506 while (!orgRules
->isEmpty()) {
507 r
= (TimeZoneRule
*)orgRules
->orphanElementAt(0);
516 initial
= res_initial
;
517 transitionRules
= filteredRules
;
521 if (orgtrs
!= NULL
) {
524 if (orgRules
!= NULL
) {
525 while (!orgRules
->isEmpty()) {
526 r
= (TimeZoneRule
*)orgRules
->orphanElementAt(0);
532 if (filteredRules
!= NULL
) {
533 while (!filteredRules
->isEmpty()) {
534 r
= (TimeZoneRule
*)filteredRules
->orphanElementAt(0);
537 delete filteredRules
;
544 transitionRules
= NULL
;
548 BasicTimeZone::getOffsetFromLocal(UDate
/*date*/, int32_t /*nonExistingTimeOpt*/, int32_t /*duplicatedTimeOpt*/,
549 int32_t& /*rawOffset*/, int32_t& /*dstOffset*/, UErrorCode
& status
) /*const*/ {
550 if (U_FAILURE(status
)) {
553 status
= U_UNSUPPORTED_ERROR
;
558 #endif /* #if !UCONFIG_NO_FORMATTING */