2 *******************************************************************************
3 * Copyright (C) 2007-2011, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
8 #include <typeinfo> // for 'typeid' to work
10 #include "unicode/utypes.h"
12 #if !UCONFIG_NO_FORMATTING
14 #include "unicode/rbtz.h"
15 #include "unicode/gregocal.h"
23 * A struct representing a time zone transition
31 static UBool
compareRules(UVector
* rules1
, UVector
* rules2
) {
32 if (rules1
== NULL
&& rules2
== NULL
) {
34 } else if (rules1
== NULL
|| rules2
== NULL
) {
37 int32_t size
= rules1
->size();
38 if (size
!= rules2
->size()) {
41 for (int32_t i
= 0; i
< size
; i
++) {
42 TimeZoneRule
*r1
= (TimeZoneRule
*)rules1
->elementAt(i
);
43 TimeZoneRule
*r2
= (TimeZoneRule
*)rules2
->elementAt(i
);
51 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedTimeZone
)
53 RuleBasedTimeZone::RuleBasedTimeZone(const UnicodeString
& id
, InitialTimeZoneRule
* initialRule
)
54 : BasicTimeZone(id
), fInitialRule(initialRule
), fHistoricRules(NULL
), fFinalRules(NULL
),
55 fHistoricTransitions(NULL
), fUpToDate(FALSE
) {
58 RuleBasedTimeZone::RuleBasedTimeZone(const RuleBasedTimeZone
& source
)
59 : BasicTimeZone(source
), fInitialRule(source
.fInitialRule
->clone()),
60 fHistoricTransitions(NULL
), fUpToDate(FALSE
) {
61 fHistoricRules
= copyRules(source
.fHistoricRules
);
62 fFinalRules
= copyRules(source
.fFinalRules
);
63 if (source
.fUpToDate
) {
64 UErrorCode status
= U_ZERO_ERROR
;
69 RuleBasedTimeZone::~RuleBasedTimeZone() {
75 RuleBasedTimeZone::operator=(const RuleBasedTimeZone
& right
) {
77 BasicTimeZone::operator=(right
);
79 fInitialRule
= right
.fInitialRule
->clone();
80 fHistoricRules
= copyRules(right
.fHistoricRules
);
81 fFinalRules
= copyRules(right
.fFinalRules
);
89 RuleBasedTimeZone::operator==(const TimeZone
& that
) const {
93 if (typeid(*this) != typeid(that
)
94 || BasicTimeZone::operator==(that
) == FALSE
) {
97 RuleBasedTimeZone
*rbtz
= (RuleBasedTimeZone
*)&that
;
98 if (*fInitialRule
!= *(rbtz
->fInitialRule
)) {
101 if (compareRules(fHistoricRules
, rbtz
->fHistoricRules
)
102 && compareRules(fFinalRules
, rbtz
->fFinalRules
)) {
109 RuleBasedTimeZone::operator!=(const TimeZone
& that
) const {
110 return !operator==(that
);
114 RuleBasedTimeZone::addTransitionRule(TimeZoneRule
* rule
, UErrorCode
& status
) {
115 if (U_FAILURE(status
)) {
118 AnnualTimeZoneRule
* atzrule
= dynamic_cast<AnnualTimeZoneRule
*>(rule
);
119 if (atzrule
!= NULL
&& atzrule
->getEndYear() == AnnualTimeZoneRule::MAX_YEAR
) {
121 if (fFinalRules
== NULL
) {
122 fFinalRules
= new UVector(status
);
123 if (U_FAILURE(status
)) {
126 } else if (fFinalRules
->size() >= 2) {
127 // Cannot handle more than two final rules
128 status
= U_INVALID_STATE_ERROR
;
131 fFinalRules
->addElement((void*)rule
, status
);
134 if (fHistoricRules
== NULL
) {
135 fHistoricRules
= new UVector(status
);
136 if (U_FAILURE(status
)) {
140 fHistoricRules
->addElement((void*)rule
, status
);
142 // Mark dirty, so transitions are recalculated at next complete() call
147 RuleBasedTimeZone::complete(UErrorCode
& status
) {
148 if (U_FAILURE(status
)) {
154 // Make sure either no final rules or a pair of AnnualTimeZoneRules
156 if (fFinalRules
!= NULL
&& fFinalRules
->size() != 2) {
157 status
= U_INVALID_STATE_ERROR
;
162 // Create a TimezoneTransition and add to the list
163 if (fHistoricRules
!= NULL
|| fFinalRules
!= NULL
) {
164 TimeZoneRule
*curRule
= fInitialRule
;
165 UDate lastTransitionTime
= MIN_MILLIS
;
167 // Build the transition array which represents historical time zone
169 if (fHistoricRules
!= NULL
&& fHistoricRules
->size() > 0) {
171 int32_t historicCount
= fHistoricRules
->size();
172 done
= (UBool
*)uprv_malloc(sizeof(UBool
) * historicCount
);
174 status
= U_MEMORY_ALLOCATION_ERROR
;
177 for (i
= 0; i
< historicCount
; i
++) {
181 int32_t curStdOffset
= curRule
->getRawOffset();
182 int32_t curDstSavings
= curRule
->getDSTSavings();
183 UDate nextTransitionTime
= MAX_MILLIS
;
184 TimeZoneRule
*nextRule
= NULL
;
185 TimeZoneRule
*r
= NULL
;
188 UnicodeString curName
, name
;
189 curRule
->getName(curName
);
191 for (i
= 0; i
< historicCount
; i
++) {
195 r
= (TimeZoneRule
*)fHistoricRules
->elementAt(i
);
196 avail
= r
->getNextStart(lastTransitionTime
, curStdOffset
, curDstSavings
, false, tt
);
198 // No more transitions from this rule - skip this rule next time
202 if (*r
== *curRule
||
203 (name
== curName
&& r
->getRawOffset() == curRule
->getRawOffset()
204 && r
->getDSTSavings() == curRule
->getDSTSavings())) {
207 if (tt
< nextTransitionTime
) {
208 nextTransitionTime
= tt
;
214 if (nextRule
== NULL
) {
215 // Check if all historic rules are done
216 UBool bDoneAll
= TRUE
;
217 for (int32_t j
= 0; j
< historicCount
; j
++) {
228 if (fFinalRules
!= NULL
) {
229 // Check if one of final rules has earlier transition date
230 for (i
= 0; i
< 2 /* fFinalRules->size() */; i
++) {
231 TimeZoneRule
*fr
= (TimeZoneRule
*)fFinalRules
->elementAt(i
);
232 if (*fr
== *curRule
) {
235 r
= (TimeZoneRule
*)fFinalRules
->elementAt(i
);
236 avail
= r
->getNextStart(lastTransitionTime
, curStdOffset
, curDstSavings
, false, tt
);
238 if (tt
< nextTransitionTime
) {
239 nextTransitionTime
= tt
;
246 if (nextRule
== NULL
) {
251 if (fHistoricTransitions
== NULL
) {
252 fHistoricTransitions
= new UVector(status
);
253 if (U_FAILURE(status
)) {
257 Transition
*trst
= (Transition
*)uprv_malloc(sizeof(Transition
));
259 status
= U_MEMORY_ALLOCATION_ERROR
;
262 trst
->time
= nextTransitionTime
;
263 trst
->from
= curRule
;
265 fHistoricTransitions
->addElement(trst
, status
);
266 if (U_FAILURE(status
)) {
269 lastTransitionTime
= nextTransitionTime
;
273 if (fFinalRules
!= NULL
) {
274 if (fHistoricTransitions
== NULL
) {
275 fHistoricTransitions
= new UVector(status
);
276 if (U_FAILURE(status
)) {
280 // Append the first transition for each
281 TimeZoneRule
*rule0
= (TimeZoneRule
*)fFinalRules
->elementAt(0);
282 TimeZoneRule
*rule1
= (TimeZoneRule
*)fFinalRules
->elementAt(1);
284 UBool avail0
= rule0
->getNextStart(lastTransitionTime
, curRule
->getRawOffset(), curRule
->getDSTSavings(), false, tt0
);
285 UBool avail1
= rule1
->getNextStart(lastTransitionTime
, curRule
->getRawOffset(), curRule
->getDSTSavings(), false, tt1
);
286 if (!avail0
|| !avail1
) {
287 // Should not happen, because both rules are permanent
288 status
= U_INVALID_STATE_ERROR
;
291 Transition
*final0
= (Transition
*)uprv_malloc(sizeof(Transition
));
292 if (final0
== NULL
) {
293 status
= U_MEMORY_ALLOCATION_ERROR
;
296 Transition
*final1
= (Transition
*)uprv_malloc(sizeof(Transition
));
297 if (final1
== NULL
) {
299 status
= U_MEMORY_ALLOCATION_ERROR
;
304 final0
->from
= curRule
;
306 rule1
->getNextStart(tt0
, rule0
->getRawOffset(), rule0
->getDSTSavings(), false, final1
->time
);
307 final1
->from
= rule0
;
311 final0
->from
= curRule
;
313 rule0
->getNextStart(tt1
, rule1
->getRawOffset(), rule1
->getDSTSavings(), false, final1
->time
);
314 final1
->from
= rule1
;
317 fHistoricTransitions
->addElement(final0
, status
);
318 if (U_FAILURE(status
)) {
321 fHistoricTransitions
->addElement(final1
, status
);
322 if (U_FAILURE(status
)) {
342 RuleBasedTimeZone::clone(void) const {
343 return new RuleBasedTimeZone(*this);
347 RuleBasedTimeZone::getOffset(uint8_t era
, int32_t year
, int32_t month
, int32_t day
,
348 uint8_t dayOfWeek
, int32_t millis
, UErrorCode
& status
) const {
349 if (U_FAILURE(status
)) {
352 if (month
< UCAL_JANUARY
|| month
> UCAL_DECEMBER
) {
353 status
= U_ILLEGAL_ARGUMENT_ERROR
;
356 return getOffset(era
, year
, month
, day
, dayOfWeek
, millis
,
357 Grego::monthLength(year
, month
), status
);
362 RuleBasedTimeZone::getOffset(uint8_t era
, int32_t year
, int32_t month
, int32_t day
,
363 uint8_t /*dayOfWeek*/, int32_t millis
,
364 int32_t /*monthLength*/, UErrorCode
& status
) const {
365 // dayOfWeek and monthLength are unused
366 if (U_FAILURE(status
)) {
369 if (era
== GregorianCalendar::BC
) {
370 // Convert to extended year
373 int32_t rawOffset
, dstOffset
;
374 UDate time
= (UDate
)Grego::fieldsToDay(year
, month
, day
) * U_MILLIS_PER_DAY
+ millis
;
375 getOffsetInternal(time
, TRUE
, kDaylight
, kStandard
, rawOffset
, dstOffset
, status
);
376 if (U_FAILURE(status
)) {
379 return (rawOffset
+ dstOffset
);
383 RuleBasedTimeZone::getOffset(UDate date
, UBool local
, int32_t& rawOffset
,
384 int32_t& dstOffset
, UErrorCode
& status
) const {
385 getOffsetInternal(date
, local
, kFormer
, kLatter
, rawOffset
, dstOffset
, status
);
389 RuleBasedTimeZone::getOffsetFromLocal(UDate date
, int32_t nonExistingTimeOpt
, int32_t duplicatedTimeOpt
,
390 int32_t& rawOffset
, int32_t& dstOffset
, UErrorCode
& status
) /*const*/ {
391 getOffsetInternal(date
, TRUE
, nonExistingTimeOpt
, duplicatedTimeOpt
, rawOffset
, dstOffset
, status
);
396 * The internal getOffset implementation
399 RuleBasedTimeZone::getOffsetInternal(UDate date
, UBool local
,
400 int32_t NonExistingTimeOpt
, int32_t DuplicatedTimeOpt
,
401 int32_t& rawOffset
, int32_t& dstOffset
,
402 UErrorCode
& status
) const {
406 if (U_FAILURE(status
)) {
410 // Transitions are not yet resolved. We cannot do it here
411 // because this method is const. Thus, do nothing and return
413 status
= U_INVALID_STATE_ERROR
;
416 const TimeZoneRule
*rule
= NULL
;
417 if (fHistoricTransitions
== NULL
) {
420 UDate tstart
= getTransitionTime((Transition
*)fHistoricTransitions
->elementAt(0),
421 local
, NonExistingTimeOpt
, DuplicatedTimeOpt
);
425 int32_t idx
= fHistoricTransitions
->size() - 1;
426 UDate tend
= getTransitionTime((Transition
*)fHistoricTransitions
->elementAt(idx
),
427 local
, NonExistingTimeOpt
, DuplicatedTimeOpt
);
429 if (fFinalRules
!= NULL
) {
430 rule
= findRuleInFinal(date
, local
, NonExistingTimeOpt
, DuplicatedTimeOpt
);
433 // no final rules or the given time is before the first transition
434 // specified by the final rules -> use the last rule
435 rule
= ((Transition
*)fHistoricTransitions
->elementAt(idx
))->to
;
438 // Find a historical transition
440 if (date
>= getTransitionTime((Transition
*)fHistoricTransitions
->elementAt(idx
),
441 local
, NonExistingTimeOpt
, DuplicatedTimeOpt
)) {
446 rule
= ((Transition
*)fHistoricTransitions
->elementAt(idx
))->to
;
451 rawOffset
= rule
->getRawOffset();
452 dstOffset
= rule
->getDSTSavings();
457 RuleBasedTimeZone::setRawOffset(int32_t /*offsetMillis*/) {
458 // We don't support this operation at this moment.
463 RuleBasedTimeZone::getRawOffset(void) const {
464 // Note: This implementation returns standard GMT offset
465 // as of current time.
466 UErrorCode status
= U_ZERO_ERROR
;
468 getOffset(uprv_getUTCtime() * U_MILLIS_PER_SECOND
,
469 FALSE
, raw
, dst
, status
);
474 RuleBasedTimeZone::useDaylightTime(void) const {
475 // Note: This implementation returns true when
476 // daylight saving time is used as of now or
477 // after the next transition.
478 UErrorCode status
= U_ZERO_ERROR
;
479 UDate now
= uprv_getUTCtime() * U_MILLIS_PER_SECOND
;
481 getOffset(now
, FALSE
, raw
, dst
, status
);
485 // If DST is not used now, check if DST is used after the next transition
487 TimeZoneRule
*from
, *to
;
488 UBool avail
= findNext(now
, FALSE
, time
, from
, to
);
489 if (avail
&& to
->getDSTSavings() != 0) {
496 RuleBasedTimeZone::inDaylightTime(UDate date
, UErrorCode
& status
) const {
497 if (U_FAILURE(status
)) {
501 getOffset(date
, FALSE
, raw
, dst
, status
);
509 RuleBasedTimeZone::hasSameRules(const TimeZone
& other
) const {
510 if (this == &other
) {
513 if (typeid(*this) != typeid(other
)) {
516 const RuleBasedTimeZone
& that
= (const RuleBasedTimeZone
&)other
;
517 if (*fInitialRule
!= *(that
.fInitialRule
)) {
520 if (compareRules(fHistoricRules
, that
.fHistoricRules
)
521 && compareRules(fFinalRules
, that
.fFinalRules
)) {
528 RuleBasedTimeZone::getNextTransition(UDate base
, UBool inclusive
, TimeZoneTransition
& result
) /*const*/ {
529 UErrorCode status
= U_ZERO_ERROR
;
531 if (U_FAILURE(status
)) {
534 UDate transitionTime
;
535 TimeZoneRule
*fromRule
, *toRule
;
536 UBool found
= findNext(base
, inclusive
, transitionTime
, fromRule
, toRule
);
538 result
.setTime(transitionTime
);
539 result
.setFrom((const TimeZoneRule
&)*fromRule
);
540 result
.setTo((const TimeZoneRule
&)*toRule
);
547 RuleBasedTimeZone::getPreviousTransition(UDate base
, UBool inclusive
, TimeZoneTransition
& result
) /*const*/ {
548 UErrorCode status
= U_ZERO_ERROR
;
550 if (U_FAILURE(status
)) {
553 UDate transitionTime
;
554 TimeZoneRule
*fromRule
, *toRule
;
555 UBool found
= findPrev(base
, inclusive
, transitionTime
, fromRule
, toRule
);
557 result
.setTime(transitionTime
);
558 result
.setFrom((const TimeZoneRule
&)*fromRule
);
559 result
.setTo((const TimeZoneRule
&)*toRule
);
566 RuleBasedTimeZone::countTransitionRules(UErrorCode
& /*status*/) /*const*/ {
568 if (fHistoricRules
!= NULL
) {
569 count
+= fHistoricRules
->size();
571 if (fFinalRules
!= NULL
) {
572 count
+= fFinalRules
->size();
578 RuleBasedTimeZone::getTimeZoneRules(const InitialTimeZoneRule
*& initial
,
579 const TimeZoneRule
* trsrules
[],
581 UErrorCode
& status
) /*const*/ {
582 if (U_FAILURE(status
)) {
586 initial
= fInitialRule
;
591 if (fHistoricRules
!= NULL
&& cnt
< trscount
) {
592 int32_t historicCount
= fHistoricRules
->size();
594 while (cnt
< trscount
&& idx
< historicCount
) {
595 trsrules
[cnt
++] = (const TimeZoneRule
*)fHistoricRules
->elementAt(idx
++);
598 if (fFinalRules
!= NULL
&& cnt
< trscount
) {
599 int32_t finalCount
= fFinalRules
->size();
601 while (cnt
< trscount
&& idx
< finalCount
) {
602 trsrules
[cnt
++] = (const TimeZoneRule
*)fFinalRules
->elementAt(idx
++);
605 // Set the result length
610 RuleBasedTimeZone::deleteRules(void) {
613 if (fHistoricRules
!= NULL
) {
614 while (!fHistoricRules
->isEmpty()) {
615 delete (TimeZoneRule
*)(fHistoricRules
->orphanElementAt(0));
617 delete fHistoricRules
;
618 fHistoricRules
= NULL
;
620 if (fFinalRules
!= NULL
) {
621 while (!fFinalRules
->isEmpty()) {
622 delete (AnnualTimeZoneRule
*)(fFinalRules
->orphanElementAt(0));
630 RuleBasedTimeZone::deleteTransitions(void) {
631 if (fHistoricTransitions
!= NULL
) {
632 while (!fHistoricTransitions
->isEmpty()) {
633 Transition
*trs
= (Transition
*)fHistoricTransitions
->orphanElementAt(0);
636 delete fHistoricTransitions
;
638 fHistoricTransitions
= NULL
;
642 RuleBasedTimeZone::copyRules(UVector
* source
) {
643 if (source
== NULL
) {
646 UErrorCode ec
= U_ZERO_ERROR
;
647 int32_t size
= source
->size();
648 UVector
*rules
= new UVector(size
, ec
);
653 for (i
= 0; i
< size
; i
++) {
654 rules
->addElement(((TimeZoneRule
*)source
->elementAt(i
))->clone(), ec
);
660 // In case of error, clean up
661 for (i
= 0; i
< rules
->size(); i
++) {
662 TimeZoneRule
*rule
= (TimeZoneRule
*)rules
->orphanElementAt(i
);
672 RuleBasedTimeZone::findRuleInFinal(UDate date
, UBool local
,
673 int32_t NonExistingTimeOpt
, int32_t DuplicatedTimeOpt
) const {
674 if (fFinalRules
== NULL
) {
678 AnnualTimeZoneRule
* fr0
= (AnnualTimeZoneRule
*)fFinalRules
->elementAt(0);
679 AnnualTimeZoneRule
* fr1
= (AnnualTimeZoneRule
*)fFinalRules
->elementAt(1);
680 if (fr0
== NULL
|| fr1
== NULL
) {
684 UDate start0
, start1
;
690 localDelta
= getLocalDelta(fr1
->getRawOffset(), fr1
->getDSTSavings(),
691 fr0
->getRawOffset(), fr0
->getDSTSavings(),
692 NonExistingTimeOpt
, DuplicatedTimeOpt
);
695 UBool avail0
= fr0
->getPreviousStart(base
, fr1
->getRawOffset(), fr1
->getDSTSavings(), TRUE
, start0
);
699 localDelta
= getLocalDelta(fr0
->getRawOffset(), fr0
->getDSTSavings(),
700 fr1
->getRawOffset(), fr1
->getDSTSavings(),
701 NonExistingTimeOpt
, DuplicatedTimeOpt
);
704 UBool avail1
= fr1
->getPreviousStart(base
, fr0
->getRawOffset(), fr0
->getDSTSavings(), TRUE
, start1
);
706 if (!avail0
|| !avail1
) {
712 // Both rules take effect after the given time
716 return (start0
> start1
) ? fr0
: fr1
;
720 RuleBasedTimeZone::findNext(UDate base
, UBool inclusive
, UDate
& transitionTime
,
721 TimeZoneRule
*& fromRule
, TimeZoneRule
*& toRule
) const {
722 if (fHistoricTransitions
== NULL
) {
725 UBool isFinal
= FALSE
;
728 Transition
*tzt
= (Transition
*)fHistoricTransitions
->elementAt(0);
729 UDate tt
= tzt
->time
;
730 if (tt
> base
|| (inclusive
&& tt
== base
)) {
734 int32_t idx
= fHistoricTransitions
->size() - 1;
735 tzt
= (Transition
*)fHistoricTransitions
->elementAt(idx
);
737 if (inclusive
&& tt
== base
) {
740 } else if (tt
<= base
) {
741 if (fFinalRules
!= NULL
) {
742 // Find a transion time with finalRules
743 TimeZoneRule
*r0
= (TimeZoneRule
*)fFinalRules
->elementAt(0);
744 TimeZoneRule
*r1
= (TimeZoneRule
*)fFinalRules
->elementAt(1);
745 UDate start0
, start1
;
746 UBool avail0
= r0
->getNextStart(base
, r1
->getRawOffset(), r1
->getDSTSavings(), inclusive
, start0
);
747 UBool avail1
= r1
->getNextStart(base
, r0
->getRawOffset(), r0
->getDSTSavings(), inclusive
, start1
);
748 // avail0/avail1 should be always TRUE
749 if (!avail0
&& !avail1
) {
752 if (!avail1
|| start0
< start1
) {
753 result
.time
= start0
;
757 result
.time
= start1
;
765 // Find a transition within the historic transitions
767 Transition
*prev
= tzt
;
769 tzt
= (Transition
*)fHistoricTransitions
->elementAt(idx
);
771 if (tt
< base
|| (!inclusive
&& tt
== base
)) {
777 result
.time
= prev
->time
;
778 result
.from
= prev
->from
;
779 result
.to
= prev
->to
;
784 // For now, this implementation ignore transitions with only zone name changes.
785 if (result
.from
->getRawOffset() == result
.to
->getRawOffset()
786 && result
.from
->getDSTSavings() == result
.to
->getDSTSavings()) {
790 // No offset changes. Try next one if not final
791 return findNext(result
.time
, FALSE
/* always exclusive */,
792 transitionTime
, fromRule
, toRule
);
795 transitionTime
= result
.time
;
796 fromRule
= result
.from
;
804 RuleBasedTimeZone::findPrev(UDate base
, UBool inclusive
, UDate
& transitionTime
,
805 TimeZoneRule
*& fromRule
, TimeZoneRule
*& toRule
) const {
806 if (fHistoricTransitions
== NULL
) {
811 Transition
*tzt
= (Transition
*)fHistoricTransitions
->elementAt(0);
812 UDate tt
= tzt
->time
;
813 if (inclusive
&& tt
== base
) {
816 } else if (tt
< base
) {
817 int32_t idx
= fHistoricTransitions
->size() - 1;
818 tzt
= (Transition
*)fHistoricTransitions
->elementAt(idx
);
820 if (inclusive
&& tt
== base
) {
823 } else if (tt
< base
) {
824 if (fFinalRules
!= NULL
) {
825 // Find a transion time with finalRules
826 TimeZoneRule
*r0
= (TimeZoneRule
*)fFinalRules
->elementAt(0);
827 TimeZoneRule
*r1
= (TimeZoneRule
*)fFinalRules
->elementAt(1);
828 UDate start0
, start1
;
829 UBool avail0
= r0
->getPreviousStart(base
, r1
->getRawOffset(), r1
->getDSTSavings(), inclusive
, start0
);
830 UBool avail1
= r1
->getPreviousStart(base
, r0
->getRawOffset(), r0
->getDSTSavings(), inclusive
, start1
);
831 // avail0/avail1 should be always TRUE
832 if (!avail0
&& !avail1
) {
835 if (!avail1
|| start0
> start1
) {
836 result
.time
= start0
;
840 result
.time
= start1
;
849 // Find a transition within the historic transitions
852 tzt
= (Transition
*)fHistoricTransitions
->elementAt(idx
);
854 if (tt
< base
|| (inclusive
&& tt
== base
)) {
864 // For now, this implementation ignore transitions with only zone name changes.
865 if (result
.from
->getRawOffset() == result
.to
->getRawOffset()
866 && result
.from
->getDSTSavings() == result
.to
->getDSTSavings()) {
867 // No offset changes. Try next one if not final
868 return findPrev(result
.time
, FALSE
/* always exclusive */,
869 transitionTime
, fromRule
, toRule
);
871 transitionTime
= result
.time
;
872 fromRule
= result
.from
;
880 RuleBasedTimeZone::getTransitionTime(Transition
* transition
, UBool local
,
881 int32_t NonExistingTimeOpt
, int32_t DuplicatedTimeOpt
) const {
882 UDate time
= transition
->time
;
884 time
+= getLocalDelta(transition
->from
->getRawOffset(), transition
->from
->getDSTSavings(),
885 transition
->to
->getRawOffset(), transition
->to
->getDSTSavings(),
886 NonExistingTimeOpt
, DuplicatedTimeOpt
);
892 RuleBasedTimeZone::getLocalDelta(int32_t rawBefore
, int32_t dstBefore
, int32_t rawAfter
, int32_t dstAfter
,
893 int32_t NonExistingTimeOpt
, int32_t DuplicatedTimeOpt
) const {
896 int32_t offsetBefore
= rawBefore
+ dstBefore
;
897 int32_t offsetAfter
= rawAfter
+ dstAfter
;
899 UBool dstToStd
= (dstBefore
!= 0) && (dstAfter
== 0);
900 UBool stdToDst
= (dstBefore
== 0) && (dstAfter
!= 0);
902 if (offsetAfter
- offsetBefore
>= 0) {
903 // Positive transition, which makes a non-existing local time range
904 if (((NonExistingTimeOpt
& kStdDstMask
) == kStandard
&& dstToStd
)
905 || ((NonExistingTimeOpt
& kStdDstMask
) == kDaylight
&& stdToDst
)) {
906 delta
= offsetBefore
;
907 } else if (((NonExistingTimeOpt
& kStdDstMask
) == kStandard
&& stdToDst
)
908 || ((NonExistingTimeOpt
& kStdDstMask
) == kDaylight
&& dstToStd
)) {
910 } else if ((NonExistingTimeOpt
& kFormerLatterMask
) == kLatter
) {
911 delta
= offsetBefore
;
913 // Interprets the time with rule before the transition,
914 // default for non-existing time range
918 // Negative transition, which makes a duplicated local time range
919 if (((DuplicatedTimeOpt
& kStdDstMask
) == kStandard
&& dstToStd
)
920 || ((DuplicatedTimeOpt
& kStdDstMask
) == kDaylight
&& stdToDst
)) {
922 } else if (((DuplicatedTimeOpt
& kStdDstMask
) == kStandard
&& stdToDst
)
923 || ((DuplicatedTimeOpt
& kStdDstMask
) == kDaylight
&& dstToStd
)) {
924 delta
= offsetBefore
;
925 } else if ((DuplicatedTimeOpt
& kFormerLatterMask
) == kFormer
) {
926 delta
= offsetBefore
;
928 // Interprets the time with rule after the transition,
929 // default for duplicated local time range
938 #endif /* #if !UCONFIG_NO_FORMATTING */