1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2007-2013, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
10 #include "utypeinfo.h" // for 'typeid' to work
12 #include "unicode/utypes.h"
14 #if !UCONFIG_NO_FORMATTING
16 #include "unicode/rbtz.h"
17 #include "unicode/gregocal.h"
26 * A struct representing a time zone transition
34 static UBool
compareRules(UVector
* rules1
, UVector
* rules2
) {
35 if (rules1
== NULL
&& rules2
== NULL
) {
37 } else if (rules1
== NULL
|| rules2
== NULL
) {
40 int32_t size
= rules1
->size();
41 if (size
!= rules2
->size()) {
44 for (int32_t i
= 0; i
< size
; i
++) {
45 TimeZoneRule
*r1
= (TimeZoneRule
*)rules1
->elementAt(i
);
46 TimeZoneRule
*r2
= (TimeZoneRule
*)rules2
->elementAt(i
);
54 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedTimeZone
)
56 RuleBasedTimeZone::RuleBasedTimeZone(const UnicodeString
& id
, InitialTimeZoneRule
* initialRule
)
57 : BasicTimeZone(id
), fInitialRule(initialRule
), fHistoricRules(NULL
), fFinalRules(NULL
),
58 fHistoricTransitions(NULL
), fUpToDate(FALSE
) {
61 RuleBasedTimeZone::RuleBasedTimeZone(const RuleBasedTimeZone
& source
)
62 : BasicTimeZone(source
), fInitialRule(source
.fInitialRule
->clone()),
63 fHistoricTransitions(NULL
), fUpToDate(FALSE
) {
64 fHistoricRules
= copyRules(source
.fHistoricRules
);
65 fFinalRules
= copyRules(source
.fFinalRules
);
66 if (source
.fUpToDate
) {
67 UErrorCode status
= U_ZERO_ERROR
;
72 RuleBasedTimeZone::~RuleBasedTimeZone() {
78 RuleBasedTimeZone::operator=(const RuleBasedTimeZone
& right
) {
80 BasicTimeZone::operator=(right
);
82 fInitialRule
= right
.fInitialRule
->clone();
83 fHistoricRules
= copyRules(right
.fHistoricRules
);
84 fFinalRules
= copyRules(right
.fFinalRules
);
92 RuleBasedTimeZone::operator==(const TimeZone
& that
) const {
96 if (typeid(*this) != typeid(that
)
97 || BasicTimeZone::operator==(that
) == FALSE
) {
100 RuleBasedTimeZone
*rbtz
= (RuleBasedTimeZone
*)&that
;
101 if (*fInitialRule
!= *(rbtz
->fInitialRule
)) {
104 if (compareRules(fHistoricRules
, rbtz
->fHistoricRules
)
105 && compareRules(fFinalRules
, rbtz
->fFinalRules
)) {
112 RuleBasedTimeZone::operator!=(const TimeZone
& that
) const {
113 return !operator==(that
);
117 RuleBasedTimeZone::addTransitionRule(TimeZoneRule
* rule
, UErrorCode
& status
) {
118 if (U_FAILURE(status
)) {
121 AnnualTimeZoneRule
* atzrule
= dynamic_cast<AnnualTimeZoneRule
*>(rule
);
122 if (atzrule
!= NULL
&& atzrule
->getEndYear() == AnnualTimeZoneRule::MAX_YEAR
) {
124 if (fFinalRules
== NULL
) {
125 fFinalRules
= new UVector(status
);
126 if (U_FAILURE(status
)) {
129 } else if (fFinalRules
->size() >= 2) {
130 // Cannot handle more than two final rules
131 status
= U_INVALID_STATE_ERROR
;
134 fFinalRules
->addElement((void*)rule
, status
);
137 if (fHistoricRules
== NULL
) {
138 fHistoricRules
= new UVector(status
);
139 if (U_FAILURE(status
)) {
143 fHistoricRules
->addElement((void*)rule
, status
);
145 // Mark dirty, so transitions are recalculated at next complete() call
151 RuleBasedTimeZone::completeConst(UErrorCode
& status
) const {
152 static UMutex
*gLock
= STATIC_NEW(UMutex
);
153 if (U_FAILURE(status
)) {
158 RuleBasedTimeZone
*ncThis
= const_cast<RuleBasedTimeZone
*>(this);
159 ncThis
->complete(status
);
165 RuleBasedTimeZone::complete(UErrorCode
& status
) {
166 if (U_FAILURE(status
)) {
172 // Make sure either no final rules or a pair of AnnualTimeZoneRules
174 if (fFinalRules
!= NULL
&& fFinalRules
->size() != 2) {
175 status
= U_INVALID_STATE_ERROR
;
180 // Create a TimezoneTransition and add to the list
181 if (fHistoricRules
!= NULL
|| fFinalRules
!= NULL
) {
182 TimeZoneRule
*curRule
= fInitialRule
;
183 UDate lastTransitionTime
= MIN_MILLIS
;
185 // Build the transition array which represents historical time zone
187 if (fHistoricRules
!= NULL
&& fHistoricRules
->size() > 0) {
189 int32_t historicCount
= fHistoricRules
->size();
190 done
= (UBool
*)uprv_malloc(sizeof(UBool
) * historicCount
);
192 status
= U_MEMORY_ALLOCATION_ERROR
;
195 for (i
= 0; i
< historicCount
; i
++) {
199 int32_t curStdOffset
= curRule
->getRawOffset();
200 int32_t curDstSavings
= curRule
->getDSTSavings();
201 UDate nextTransitionTime
= MAX_MILLIS
;
202 TimeZoneRule
*nextRule
= NULL
;
203 TimeZoneRule
*r
= NULL
;
206 UnicodeString curName
, name
;
207 curRule
->getName(curName
);
209 for (i
= 0; i
< historicCount
; i
++) {
213 r
= (TimeZoneRule
*)fHistoricRules
->elementAt(i
);
214 avail
= r
->getNextStart(lastTransitionTime
, curStdOffset
, curDstSavings
, false, tt
);
216 // No more transitions from this rule - skip this rule next time
220 if (*r
== *curRule
||
221 (name
== curName
&& r
->getRawOffset() == curRule
->getRawOffset()
222 && r
->getDSTSavings() == curRule
->getDSTSavings())) {
225 if (tt
< nextTransitionTime
) {
226 nextTransitionTime
= tt
;
232 if (nextRule
== NULL
) {
233 // Check if all historic rules are done
234 UBool bDoneAll
= TRUE
;
235 for (int32_t j
= 0; j
< historicCount
; j
++) {
246 if (fFinalRules
!= NULL
) {
247 // Check if one of final rules has earlier transition date
248 for (i
= 0; i
< 2 /* fFinalRules->size() */; i
++) {
249 TimeZoneRule
*fr
= (TimeZoneRule
*)fFinalRules
->elementAt(i
);
250 if (*fr
== *curRule
) {
253 r
= (TimeZoneRule
*)fFinalRules
->elementAt(i
);
254 avail
= r
->getNextStart(lastTransitionTime
, curStdOffset
, curDstSavings
, false, tt
);
256 if (tt
< nextTransitionTime
) {
257 nextTransitionTime
= tt
;
264 if (nextRule
== NULL
) {
269 if (fHistoricTransitions
== NULL
) {
270 fHistoricTransitions
= new UVector(status
);
271 if (U_FAILURE(status
)) {
275 Transition
*trst
= (Transition
*)uprv_malloc(sizeof(Transition
));
277 status
= U_MEMORY_ALLOCATION_ERROR
;
280 trst
->time
= nextTransitionTime
;
281 trst
->from
= curRule
;
283 fHistoricTransitions
->addElement(trst
, status
);
284 if (U_FAILURE(status
)) {
287 lastTransitionTime
= nextTransitionTime
;
291 if (fFinalRules
!= NULL
) {
292 if (fHistoricTransitions
== NULL
) {
293 fHistoricTransitions
= new UVector(status
);
294 if (U_FAILURE(status
)) {
298 // Append the first transition for each
299 TimeZoneRule
*rule0
= (TimeZoneRule
*)fFinalRules
->elementAt(0);
300 TimeZoneRule
*rule1
= (TimeZoneRule
*)fFinalRules
->elementAt(1);
302 UBool avail0
= rule0
->getNextStart(lastTransitionTime
, curRule
->getRawOffset(), curRule
->getDSTSavings(), false, tt0
);
303 UBool avail1
= rule1
->getNextStart(lastTransitionTime
, curRule
->getRawOffset(), curRule
->getDSTSavings(), false, tt1
);
304 if (!avail0
|| !avail1
) {
305 // Should not happen, because both rules are permanent
306 status
= U_INVALID_STATE_ERROR
;
309 Transition
*final0
= (Transition
*)uprv_malloc(sizeof(Transition
));
310 if (final0
== NULL
) {
311 status
= U_MEMORY_ALLOCATION_ERROR
;
314 Transition
*final1
= (Transition
*)uprv_malloc(sizeof(Transition
));
315 if (final1
== NULL
) {
317 status
= U_MEMORY_ALLOCATION_ERROR
;
322 final0
->from
= curRule
;
324 rule1
->getNextStart(tt0
, rule0
->getRawOffset(), rule0
->getDSTSavings(), false, final1
->time
);
325 final1
->from
= rule0
;
329 final0
->from
= curRule
;
331 rule0
->getNextStart(tt1
, rule1
->getRawOffset(), rule1
->getDSTSavings(), false, final1
->time
);
332 final1
->from
= rule1
;
335 fHistoricTransitions
->addElement(final0
, status
);
336 if (U_FAILURE(status
)) {
339 fHistoricTransitions
->addElement(final1
, status
);
340 if (U_FAILURE(status
)) {
360 RuleBasedTimeZone::clone(void) const {
361 return new RuleBasedTimeZone(*this);
365 RuleBasedTimeZone::getOffset(uint8_t era
, int32_t year
, int32_t month
, int32_t day
,
366 uint8_t dayOfWeek
, int32_t millis
, UErrorCode
& status
) const {
367 if (U_FAILURE(status
)) {
370 if (month
< UCAL_JANUARY
|| month
> UCAL_DECEMBER
) {
371 status
= U_ILLEGAL_ARGUMENT_ERROR
;
374 return getOffset(era
, year
, month
, day
, dayOfWeek
, millis
,
375 Grego::monthLength(year
, month
), status
);
380 RuleBasedTimeZone::getOffset(uint8_t era
, int32_t year
, int32_t month
, int32_t day
,
381 uint8_t /*dayOfWeek*/, int32_t millis
,
382 int32_t /*monthLength*/, UErrorCode
& status
) const {
383 // dayOfWeek and monthLength are unused
384 if (U_FAILURE(status
)) {
387 if (era
== GregorianCalendar::BC
) {
388 // Convert to extended year
391 int32_t rawOffset
, dstOffset
;
392 UDate time
= (UDate
)Grego::fieldsToDay(year
, month
, day
) * U_MILLIS_PER_DAY
+ millis
;
393 getOffsetInternal(time
, TRUE
, kDaylight
, kStandard
, rawOffset
, dstOffset
, status
);
394 if (U_FAILURE(status
)) {
397 return (rawOffset
+ dstOffset
);
401 RuleBasedTimeZone::getOffset(UDate date
, UBool local
, int32_t& rawOffset
,
402 int32_t& dstOffset
, UErrorCode
& status
) const {
403 getOffsetInternal(date
, local
, kFormer
, kLatter
, rawOffset
, dstOffset
, status
);
407 RuleBasedTimeZone::getOffsetFromLocal(UDate date
, int32_t nonExistingTimeOpt
, int32_t duplicatedTimeOpt
,
408 int32_t& rawOffset
, int32_t& dstOffset
, UErrorCode
& status
) const {
409 getOffsetInternal(date
, TRUE
, nonExistingTimeOpt
, duplicatedTimeOpt
, rawOffset
, dstOffset
, status
);
414 * The internal getOffset implementation
417 RuleBasedTimeZone::getOffsetInternal(UDate date
, UBool local
,
418 int32_t NonExistingTimeOpt
, int32_t DuplicatedTimeOpt
,
419 int32_t& rawOffset
, int32_t& dstOffset
,
420 UErrorCode
& status
) const {
424 if (U_FAILURE(status
)) {
428 // Transitions are not yet resolved. We cannot do it here
429 // because this method is const. Thus, do nothing and return
431 status
= U_INVALID_STATE_ERROR
;
434 const TimeZoneRule
*rule
= NULL
;
435 if (fHistoricTransitions
== NULL
) {
438 UDate tstart
= getTransitionTime((Transition
*)fHistoricTransitions
->elementAt(0),
439 local
, NonExistingTimeOpt
, DuplicatedTimeOpt
);
443 int32_t idx
= fHistoricTransitions
->size() - 1;
444 UDate tend
= getTransitionTime((Transition
*)fHistoricTransitions
->elementAt(idx
),
445 local
, NonExistingTimeOpt
, DuplicatedTimeOpt
);
447 if (fFinalRules
!= NULL
) {
448 rule
= findRuleInFinal(date
, local
, NonExistingTimeOpt
, DuplicatedTimeOpt
);
451 // no final rules or the given time is before the first transition
452 // specified by the final rules -> use the last rule
453 rule
= ((Transition
*)fHistoricTransitions
->elementAt(idx
))->to
;
456 // Find a historical transition
458 if (date
>= getTransitionTime((Transition
*)fHistoricTransitions
->elementAt(idx
),
459 local
, NonExistingTimeOpt
, DuplicatedTimeOpt
)) {
464 rule
= ((Transition
*)fHistoricTransitions
->elementAt(idx
))->to
;
469 rawOffset
= rule
->getRawOffset();
470 dstOffset
= rule
->getDSTSavings();
475 RuleBasedTimeZone::setRawOffset(int32_t /*offsetMillis*/) {
476 // We don't support this operation at this moment.
481 RuleBasedTimeZone::getRawOffset(void) const {
482 // Note: This implementation returns standard GMT offset
483 // as of current time.
484 UErrorCode status
= U_ZERO_ERROR
;
486 getOffset(uprv_getUTCtime() * U_MILLIS_PER_SECOND
,
487 FALSE
, raw
, dst
, status
);
492 RuleBasedTimeZone::useDaylightTime(void) const {
493 // Note: This implementation returns true when
494 // daylight saving time is used as of now or
495 // after the next transition.
496 UErrorCode status
= U_ZERO_ERROR
;
497 UDate now
= uprv_getUTCtime() * U_MILLIS_PER_SECOND
;
499 getOffset(now
, FALSE
, raw
, dst
, status
);
503 // If DST is not used now, check if DST is used after the next transition
505 TimeZoneRule
*from
, *to
;
506 UBool avail
= findNext(now
, FALSE
, time
, from
, to
);
507 if (avail
&& to
->getDSTSavings() != 0) {
514 RuleBasedTimeZone::inDaylightTime(UDate date
, UErrorCode
& status
) const {
515 if (U_FAILURE(status
)) {
519 getOffset(date
, FALSE
, raw
, dst
, status
);
527 RuleBasedTimeZone::hasSameRules(const TimeZone
& other
) const {
528 if (this == &other
) {
531 if (typeid(*this) != typeid(other
)) {
534 const RuleBasedTimeZone
& that
= (const RuleBasedTimeZone
&)other
;
535 if (*fInitialRule
!= *(that
.fInitialRule
)) {
538 if (compareRules(fHistoricRules
, that
.fHistoricRules
)
539 && compareRules(fFinalRules
, that
.fFinalRules
)) {
546 RuleBasedTimeZone::getNextTransition(UDate base
, UBool inclusive
, TimeZoneTransition
& result
) const {
547 UErrorCode status
= U_ZERO_ERROR
;
548 completeConst(status
);
549 if (U_FAILURE(status
)) {
552 UDate transitionTime
;
553 TimeZoneRule
*fromRule
, *toRule
;
554 UBool found
= findNext(base
, inclusive
, transitionTime
, fromRule
, toRule
);
556 result
.setTime(transitionTime
);
557 result
.setFrom((const TimeZoneRule
&)*fromRule
);
558 result
.setTo((const TimeZoneRule
&)*toRule
);
565 RuleBasedTimeZone::getPreviousTransition(UDate base
, UBool inclusive
, TimeZoneTransition
& result
) const {
566 UErrorCode status
= U_ZERO_ERROR
;
567 completeConst(status
);
568 if (U_FAILURE(status
)) {
571 UDate transitionTime
;
572 TimeZoneRule
*fromRule
, *toRule
;
573 UBool found
= findPrev(base
, inclusive
, transitionTime
, fromRule
, toRule
);
575 result
.setTime(transitionTime
);
576 result
.setFrom((const TimeZoneRule
&)*fromRule
);
577 result
.setTo((const TimeZoneRule
&)*toRule
);
584 RuleBasedTimeZone::countTransitionRules(UErrorCode
& /*status*/) const {
586 if (fHistoricRules
!= NULL
) {
587 count
+= fHistoricRules
->size();
589 if (fFinalRules
!= NULL
) {
590 count
+= fFinalRules
->size();
596 RuleBasedTimeZone::getTimeZoneRules(const InitialTimeZoneRule
*& initial
,
597 const TimeZoneRule
* trsrules
[],
599 UErrorCode
& status
) const {
600 if (U_FAILURE(status
)) {
604 initial
= fInitialRule
;
609 if (fHistoricRules
!= NULL
&& cnt
< trscount
) {
610 int32_t historicCount
= fHistoricRules
->size();
612 while (cnt
< trscount
&& idx
< historicCount
) {
613 trsrules
[cnt
++] = (const TimeZoneRule
*)fHistoricRules
->elementAt(idx
++);
616 if (fFinalRules
!= NULL
&& cnt
< trscount
) {
617 int32_t finalCount
= fFinalRules
->size();
619 while (cnt
< trscount
&& idx
< finalCount
) {
620 trsrules
[cnt
++] = (const TimeZoneRule
*)fFinalRules
->elementAt(idx
++);
623 // Set the result length
628 RuleBasedTimeZone::deleteRules(void) {
631 if (fHistoricRules
!= NULL
) {
632 while (!fHistoricRules
->isEmpty()) {
633 delete (TimeZoneRule
*)(fHistoricRules
->orphanElementAt(0));
635 delete fHistoricRules
;
636 fHistoricRules
= NULL
;
638 if (fFinalRules
!= NULL
) {
639 while (!fFinalRules
->isEmpty()) {
640 delete (AnnualTimeZoneRule
*)(fFinalRules
->orphanElementAt(0));
648 RuleBasedTimeZone::deleteTransitions(void) {
649 if (fHistoricTransitions
!= NULL
) {
650 while (!fHistoricTransitions
->isEmpty()) {
651 Transition
*trs
= (Transition
*)fHistoricTransitions
->orphanElementAt(0);
654 delete fHistoricTransitions
;
656 fHistoricTransitions
= NULL
;
660 RuleBasedTimeZone::copyRules(UVector
* source
) {
661 if (source
== NULL
) {
664 UErrorCode ec
= U_ZERO_ERROR
;
665 int32_t size
= source
->size();
666 UVector
*rules
= new UVector(size
, ec
);
671 for (i
= 0; i
< size
; i
++) {
672 rules
->addElement(((TimeZoneRule
*)source
->elementAt(i
))->clone(), ec
);
678 // In case of error, clean up
679 for (i
= 0; i
< rules
->size(); i
++) {
680 TimeZoneRule
*rule
= (TimeZoneRule
*)rules
->orphanElementAt(i
);
690 RuleBasedTimeZone::findRuleInFinal(UDate date
, UBool local
,
691 int32_t NonExistingTimeOpt
, int32_t DuplicatedTimeOpt
) const {
692 if (fFinalRules
== NULL
) {
696 AnnualTimeZoneRule
* fr0
= (AnnualTimeZoneRule
*)fFinalRules
->elementAt(0);
697 AnnualTimeZoneRule
* fr1
= (AnnualTimeZoneRule
*)fFinalRules
->elementAt(1);
698 if (fr0
== NULL
|| fr1
== NULL
) {
702 UDate start0
, start1
;
708 localDelta
= getLocalDelta(fr1
->getRawOffset(), fr1
->getDSTSavings(),
709 fr0
->getRawOffset(), fr0
->getDSTSavings(),
710 NonExistingTimeOpt
, DuplicatedTimeOpt
);
713 UBool avail0
= fr0
->getPreviousStart(base
, fr1
->getRawOffset(), fr1
->getDSTSavings(), TRUE
, start0
);
717 localDelta
= getLocalDelta(fr0
->getRawOffset(), fr0
->getDSTSavings(),
718 fr1
->getRawOffset(), fr1
->getDSTSavings(),
719 NonExistingTimeOpt
, DuplicatedTimeOpt
);
722 UBool avail1
= fr1
->getPreviousStart(base
, fr0
->getRawOffset(), fr0
->getDSTSavings(), TRUE
, start1
);
724 if (!avail0
|| !avail1
) {
730 // Both rules take effect after the given time
734 return (start0
> start1
) ? fr0
: fr1
;
738 RuleBasedTimeZone::findNext(UDate base
, UBool inclusive
, UDate
& transitionTime
,
739 TimeZoneRule
*& fromRule
, TimeZoneRule
*& toRule
) const {
740 if (fHistoricTransitions
== NULL
) {
743 UBool isFinal
= FALSE
;
746 Transition
*tzt
= (Transition
*)fHistoricTransitions
->elementAt(0);
747 UDate tt
= tzt
->time
;
748 if (tt
> base
|| (inclusive
&& tt
== base
)) {
752 int32_t idx
= fHistoricTransitions
->size() - 1;
753 tzt
= (Transition
*)fHistoricTransitions
->elementAt(idx
);
755 if (inclusive
&& tt
== base
) {
758 } else if (tt
<= base
) {
759 if (fFinalRules
!= NULL
) {
760 // Find a transion time with finalRules
761 TimeZoneRule
*r0
= (TimeZoneRule
*)fFinalRules
->elementAt(0);
762 TimeZoneRule
*r1
= (TimeZoneRule
*)fFinalRules
->elementAt(1);
763 UDate start0
, start1
;
764 UBool avail0
= r0
->getNextStart(base
, r1
->getRawOffset(), r1
->getDSTSavings(), inclusive
, start0
);
765 UBool avail1
= r1
->getNextStart(base
, r0
->getRawOffset(), r0
->getDSTSavings(), inclusive
, start1
);
766 // avail0/avail1 should be always TRUE
767 if (!avail0
&& !avail1
) {
770 if (!avail1
|| start0
< start1
) {
771 result
.time
= start0
;
775 result
.time
= start1
;
783 // Find a transition within the historic transitions
785 Transition
*prev
= tzt
;
787 tzt
= (Transition
*)fHistoricTransitions
->elementAt(idx
);
789 if (tt
< base
|| (!inclusive
&& tt
== base
)) {
795 result
.time
= prev
->time
;
796 result
.from
= prev
->from
;
797 result
.to
= prev
->to
;
802 // For now, this implementation ignore transitions with only zone name changes.
803 if (result
.from
->getRawOffset() == result
.to
->getRawOffset()
804 && result
.from
->getDSTSavings() == result
.to
->getDSTSavings()) {
808 // No offset changes. Try next one if not final
809 return findNext(result
.time
, FALSE
/* always exclusive */,
810 transitionTime
, fromRule
, toRule
);
813 transitionTime
= result
.time
;
814 fromRule
= result
.from
;
822 RuleBasedTimeZone::findPrev(UDate base
, UBool inclusive
, UDate
& transitionTime
,
823 TimeZoneRule
*& fromRule
, TimeZoneRule
*& toRule
) const {
824 if (fHistoricTransitions
== NULL
) {
829 Transition
*tzt
= (Transition
*)fHistoricTransitions
->elementAt(0);
830 UDate tt
= tzt
->time
;
831 if (inclusive
&& tt
== base
) {
834 } else if (tt
< base
) {
835 int32_t idx
= fHistoricTransitions
->size() - 1;
836 tzt
= (Transition
*)fHistoricTransitions
->elementAt(idx
);
838 if (inclusive
&& tt
== base
) {
841 } else if (tt
< base
) {
842 if (fFinalRules
!= NULL
) {
843 // Find a transion time with finalRules
844 TimeZoneRule
*r0
= (TimeZoneRule
*)fFinalRules
->elementAt(0);
845 TimeZoneRule
*r1
= (TimeZoneRule
*)fFinalRules
->elementAt(1);
846 UDate start0
, start1
;
847 UBool avail0
= r0
->getPreviousStart(base
, r1
->getRawOffset(), r1
->getDSTSavings(), inclusive
, start0
);
848 UBool avail1
= r1
->getPreviousStart(base
, r0
->getRawOffset(), r0
->getDSTSavings(), inclusive
, start1
);
849 // avail0/avail1 should be always TRUE
850 if (!avail0
&& !avail1
) {
853 if (!avail1
|| start0
> start1
) {
854 result
.time
= start0
;
858 result
.time
= start1
;
867 // Find a transition within the historic transitions
870 tzt
= (Transition
*)fHistoricTransitions
->elementAt(idx
);
872 if (tt
< base
|| (inclusive
&& tt
== base
)) {
882 // For now, this implementation ignore transitions with only zone name changes.
883 if (result
.from
->getRawOffset() == result
.to
->getRawOffset()
884 && result
.from
->getDSTSavings() == result
.to
->getDSTSavings()) {
885 // No offset changes. Try next one if not final
886 return findPrev(result
.time
, FALSE
/* always exclusive */,
887 transitionTime
, fromRule
, toRule
);
889 transitionTime
= result
.time
;
890 fromRule
= result
.from
;
898 RuleBasedTimeZone::getTransitionTime(Transition
* transition
, UBool local
,
899 int32_t NonExistingTimeOpt
, int32_t DuplicatedTimeOpt
) const {
900 UDate time
= transition
->time
;
902 time
+= getLocalDelta(transition
->from
->getRawOffset(), transition
->from
->getDSTSavings(),
903 transition
->to
->getRawOffset(), transition
->to
->getDSTSavings(),
904 NonExistingTimeOpt
, DuplicatedTimeOpt
);
910 RuleBasedTimeZone::getLocalDelta(int32_t rawBefore
, int32_t dstBefore
, int32_t rawAfter
, int32_t dstAfter
,
911 int32_t NonExistingTimeOpt
, int32_t DuplicatedTimeOpt
) const {
914 int32_t offsetBefore
= rawBefore
+ dstBefore
;
915 int32_t offsetAfter
= rawAfter
+ dstAfter
;
917 UBool dstToStd
= (dstBefore
!= 0) && (dstAfter
== 0);
918 UBool stdToDst
= (dstBefore
== 0) && (dstAfter
!= 0);
920 if (offsetAfter
- offsetBefore
>= 0) {
921 // Positive transition, which makes a non-existing local time range
922 if (((NonExistingTimeOpt
& kStdDstMask
) == kStandard
&& dstToStd
)
923 || ((NonExistingTimeOpt
& kStdDstMask
) == kDaylight
&& stdToDst
)) {
924 delta
= offsetBefore
;
925 } else if (((NonExistingTimeOpt
& kStdDstMask
) == kStandard
&& stdToDst
)
926 || ((NonExistingTimeOpt
& kStdDstMask
) == kDaylight
&& dstToStd
)) {
928 } else if ((NonExistingTimeOpt
& kFormerLatterMask
) == kLatter
) {
929 delta
= offsetBefore
;
931 // Interprets the time with rule before the transition,
932 // default for non-existing time range
936 // Negative transition, which makes a duplicated local time range
937 if (((DuplicatedTimeOpt
& kStdDstMask
) == kStandard
&& dstToStd
)
938 || ((DuplicatedTimeOpt
& kStdDstMask
) == kDaylight
&& stdToDst
)) {
940 } else if (((DuplicatedTimeOpt
& kStdDstMask
) == kStandard
&& stdToDst
)
941 || ((DuplicatedTimeOpt
& kStdDstMask
) == kDaylight
&& dstToStd
)) {
942 delta
= offsetBefore
;
943 } else if ((DuplicatedTimeOpt
& kFormerLatterMask
) == kFormer
) {
944 delta
= offsetBefore
;
946 // Interprets the time with rule after the transition,
947 // default for duplicated local time range
956 #endif /* #if !UCONFIG_NO_FORMATTING */