2 *******************************************************************************
3 * Copyright (C) 2007-2013, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
8 #include "utypeinfo.h" // for 'typeid' to work
10 #include "unicode/utypes.h"
12 #if !UCONFIG_NO_FORMATTING
14 #include "unicode/rbtz.h"
15 #include "unicode/gregocal.h"
24 * A struct representing a time zone transition
32 static UBool
compareRules(UVector
* rules1
, UVector
* rules2
) {
33 if (rules1
== NULL
&& rules2
== NULL
) {
35 } else if (rules1
== NULL
|| rules2
== NULL
) {
38 int32_t size
= rules1
->size();
39 if (size
!= rules2
->size()) {
42 for (int32_t i
= 0; i
< size
; i
++) {
43 TimeZoneRule
*r1
= (TimeZoneRule
*)rules1
->elementAt(i
);
44 TimeZoneRule
*r2
= (TimeZoneRule
*)rules2
->elementAt(i
);
52 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedTimeZone
)
54 RuleBasedTimeZone::RuleBasedTimeZone(const UnicodeString
& id
, InitialTimeZoneRule
* initialRule
)
55 : BasicTimeZone(id
), fInitialRule(initialRule
), fHistoricRules(NULL
), fFinalRules(NULL
),
56 fHistoricTransitions(NULL
), fUpToDate(FALSE
) {
59 RuleBasedTimeZone::RuleBasedTimeZone(const RuleBasedTimeZone
& source
)
60 : BasicTimeZone(source
), fInitialRule(source
.fInitialRule
->clone()),
61 fHistoricTransitions(NULL
), fUpToDate(FALSE
) {
62 fHistoricRules
= copyRules(source
.fHistoricRules
);
63 fFinalRules
= copyRules(source
.fFinalRules
);
64 if (source
.fUpToDate
) {
65 UErrorCode status
= U_ZERO_ERROR
;
70 RuleBasedTimeZone::~RuleBasedTimeZone() {
76 RuleBasedTimeZone::operator=(const RuleBasedTimeZone
& right
) {
78 BasicTimeZone::operator=(right
);
80 fInitialRule
= right
.fInitialRule
->clone();
81 fHistoricRules
= copyRules(right
.fHistoricRules
);
82 fFinalRules
= copyRules(right
.fFinalRules
);
90 RuleBasedTimeZone::operator==(const TimeZone
& that
) const {
94 if (typeid(*this) != typeid(that
)
95 || BasicTimeZone::operator==(that
) == FALSE
) {
98 RuleBasedTimeZone
*rbtz
= (RuleBasedTimeZone
*)&that
;
99 if (*fInitialRule
!= *(rbtz
->fInitialRule
)) {
102 if (compareRules(fHistoricRules
, rbtz
->fHistoricRules
)
103 && compareRules(fFinalRules
, rbtz
->fFinalRules
)) {
110 RuleBasedTimeZone::operator!=(const TimeZone
& that
) const {
111 return !operator==(that
);
115 RuleBasedTimeZone::addTransitionRule(TimeZoneRule
* rule
, UErrorCode
& status
) {
116 if (U_FAILURE(status
)) {
119 AnnualTimeZoneRule
* atzrule
= dynamic_cast<AnnualTimeZoneRule
*>(rule
);
120 if (atzrule
!= NULL
&& atzrule
->getEndYear() == AnnualTimeZoneRule::MAX_YEAR
) {
122 if (fFinalRules
== NULL
) {
123 fFinalRules
= new UVector(status
);
124 if (U_FAILURE(status
)) {
127 } else if (fFinalRules
->size() >= 2) {
128 // Cannot handle more than two final rules
129 status
= U_INVALID_STATE_ERROR
;
132 fFinalRules
->addElement((void*)rule
, status
);
135 if (fHistoricRules
== NULL
) {
136 fHistoricRules
= new UVector(status
);
137 if (U_FAILURE(status
)) {
141 fHistoricRules
->addElement((void*)rule
, status
);
143 // Mark dirty, so transitions are recalculated at next complete() call
147 static UMutex gLock
= U_MUTEX_INITIALIZER
;
150 RuleBasedTimeZone::completeConst(UErrorCode
& status
) const {
151 if (U_FAILURE(status
)) {
156 RuleBasedTimeZone
*ncThis
= const_cast<RuleBasedTimeZone
*>(this);
157 ncThis
->complete(status
);
163 RuleBasedTimeZone::complete(UErrorCode
& status
) {
164 if (U_FAILURE(status
)) {
170 // Make sure either no final rules or a pair of AnnualTimeZoneRules
172 if (fFinalRules
!= NULL
&& fFinalRules
->size() != 2) {
173 status
= U_INVALID_STATE_ERROR
;
178 // Create a TimezoneTransition and add to the list
179 if (fHistoricRules
!= NULL
|| fFinalRules
!= NULL
) {
180 TimeZoneRule
*curRule
= fInitialRule
;
181 UDate lastTransitionTime
= MIN_MILLIS
;
183 // Build the transition array which represents historical time zone
185 if (fHistoricRules
!= NULL
&& fHistoricRules
->size() > 0) {
187 int32_t historicCount
= fHistoricRules
->size();
188 done
= (UBool
*)uprv_malloc(sizeof(UBool
) * historicCount
);
190 status
= U_MEMORY_ALLOCATION_ERROR
;
193 for (i
= 0; i
< historicCount
; i
++) {
197 int32_t curStdOffset
= curRule
->getRawOffset();
198 int32_t curDstSavings
= curRule
->getDSTSavings();
199 UDate nextTransitionTime
= MAX_MILLIS
;
200 TimeZoneRule
*nextRule
= NULL
;
201 TimeZoneRule
*r
= NULL
;
204 UnicodeString curName
, name
;
205 curRule
->getName(curName
);
207 for (i
= 0; i
< historicCount
; i
++) {
211 r
= (TimeZoneRule
*)fHistoricRules
->elementAt(i
);
212 avail
= r
->getNextStart(lastTransitionTime
, curStdOffset
, curDstSavings
, false, tt
);
214 // No more transitions from this rule - skip this rule next time
218 if (*r
== *curRule
||
219 (name
== curName
&& r
->getRawOffset() == curRule
->getRawOffset()
220 && r
->getDSTSavings() == curRule
->getDSTSavings())) {
223 if (tt
< nextTransitionTime
) {
224 nextTransitionTime
= tt
;
230 if (nextRule
== NULL
) {
231 // Check if all historic rules are done
232 UBool bDoneAll
= TRUE
;
233 for (int32_t j
= 0; j
< historicCount
; j
++) {
244 if (fFinalRules
!= NULL
) {
245 // Check if one of final rules has earlier transition date
246 for (i
= 0; i
< 2 /* fFinalRules->size() */; i
++) {
247 TimeZoneRule
*fr
= (TimeZoneRule
*)fFinalRules
->elementAt(i
);
248 if (*fr
== *curRule
) {
251 r
= (TimeZoneRule
*)fFinalRules
->elementAt(i
);
252 avail
= r
->getNextStart(lastTransitionTime
, curStdOffset
, curDstSavings
, false, tt
);
254 if (tt
< nextTransitionTime
) {
255 nextTransitionTime
= tt
;
262 if (nextRule
== NULL
) {
267 if (fHistoricTransitions
== NULL
) {
268 fHistoricTransitions
= new UVector(status
);
269 if (U_FAILURE(status
)) {
273 Transition
*trst
= (Transition
*)uprv_malloc(sizeof(Transition
));
275 status
= U_MEMORY_ALLOCATION_ERROR
;
278 trst
->time
= nextTransitionTime
;
279 trst
->from
= curRule
;
281 fHistoricTransitions
->addElement(trst
, status
);
282 if (U_FAILURE(status
)) {
285 lastTransitionTime
= nextTransitionTime
;
289 if (fFinalRules
!= NULL
) {
290 if (fHistoricTransitions
== NULL
) {
291 fHistoricTransitions
= new UVector(status
);
292 if (U_FAILURE(status
)) {
296 // Append the first transition for each
297 TimeZoneRule
*rule0
= (TimeZoneRule
*)fFinalRules
->elementAt(0);
298 TimeZoneRule
*rule1
= (TimeZoneRule
*)fFinalRules
->elementAt(1);
300 UBool avail0
= rule0
->getNextStart(lastTransitionTime
, curRule
->getRawOffset(), curRule
->getDSTSavings(), false, tt0
);
301 UBool avail1
= rule1
->getNextStart(lastTransitionTime
, curRule
->getRawOffset(), curRule
->getDSTSavings(), false, tt1
);
302 if (!avail0
|| !avail1
) {
303 // Should not happen, because both rules are permanent
304 status
= U_INVALID_STATE_ERROR
;
307 Transition
*final0
= (Transition
*)uprv_malloc(sizeof(Transition
));
308 if (final0
== NULL
) {
309 status
= U_MEMORY_ALLOCATION_ERROR
;
312 Transition
*final1
= (Transition
*)uprv_malloc(sizeof(Transition
));
313 if (final1
== NULL
) {
315 status
= U_MEMORY_ALLOCATION_ERROR
;
320 final0
->from
= curRule
;
322 rule1
->getNextStart(tt0
, rule0
->getRawOffset(), rule0
->getDSTSavings(), false, final1
->time
);
323 final1
->from
= rule0
;
327 final0
->from
= curRule
;
329 rule0
->getNextStart(tt1
, rule1
->getRawOffset(), rule1
->getDSTSavings(), false, final1
->time
);
330 final1
->from
= rule1
;
333 fHistoricTransitions
->addElement(final0
, status
);
334 if (U_FAILURE(status
)) {
337 fHistoricTransitions
->addElement(final1
, status
);
338 if (U_FAILURE(status
)) {
358 RuleBasedTimeZone::clone(void) const {
359 return new RuleBasedTimeZone(*this);
363 RuleBasedTimeZone::getOffset(uint8_t era
, int32_t year
, int32_t month
, int32_t day
,
364 uint8_t dayOfWeek
, int32_t millis
, UErrorCode
& status
) const {
365 if (U_FAILURE(status
)) {
368 if (month
< UCAL_JANUARY
|| month
> UCAL_DECEMBER
) {
369 status
= U_ILLEGAL_ARGUMENT_ERROR
;
372 return getOffset(era
, year
, month
, day
, dayOfWeek
, millis
,
373 Grego::monthLength(year
, month
), status
);
378 RuleBasedTimeZone::getOffset(uint8_t era
, int32_t year
, int32_t month
, int32_t day
,
379 uint8_t /*dayOfWeek*/, int32_t millis
,
380 int32_t /*monthLength*/, UErrorCode
& status
) const {
381 // dayOfWeek and monthLength are unused
382 if (U_FAILURE(status
)) {
385 if (era
== GregorianCalendar::BC
) {
386 // Convert to extended year
389 int32_t rawOffset
, dstOffset
;
390 UDate time
= (UDate
)Grego::fieldsToDay(year
, month
, day
) * U_MILLIS_PER_DAY
+ millis
;
391 getOffsetInternal(time
, TRUE
, kDaylight
, kStandard
, rawOffset
, dstOffset
, status
);
392 if (U_FAILURE(status
)) {
395 return (rawOffset
+ dstOffset
);
399 RuleBasedTimeZone::getOffset(UDate date
, UBool local
, int32_t& rawOffset
,
400 int32_t& dstOffset
, UErrorCode
& status
) const {
401 getOffsetInternal(date
, local
, kFormer
, kLatter
, rawOffset
, dstOffset
, status
);
405 RuleBasedTimeZone::getOffsetFromLocal(UDate date
, int32_t nonExistingTimeOpt
, int32_t duplicatedTimeOpt
,
406 int32_t& rawOffset
, int32_t& dstOffset
, UErrorCode
& status
) const {
407 getOffsetInternal(date
, TRUE
, nonExistingTimeOpt
, duplicatedTimeOpt
, rawOffset
, dstOffset
, status
);
412 * The internal getOffset implementation
415 RuleBasedTimeZone::getOffsetInternal(UDate date
, UBool local
,
416 int32_t NonExistingTimeOpt
, int32_t DuplicatedTimeOpt
,
417 int32_t& rawOffset
, int32_t& dstOffset
,
418 UErrorCode
& status
) const {
422 if (U_FAILURE(status
)) {
426 // Transitions are not yet resolved. We cannot do it here
427 // because this method is const. Thus, do nothing and return
429 status
= U_INVALID_STATE_ERROR
;
432 const TimeZoneRule
*rule
= NULL
;
433 if (fHistoricTransitions
== NULL
) {
436 UDate tstart
= getTransitionTime((Transition
*)fHistoricTransitions
->elementAt(0),
437 local
, NonExistingTimeOpt
, DuplicatedTimeOpt
);
441 int32_t idx
= fHistoricTransitions
->size() - 1;
442 UDate tend
= getTransitionTime((Transition
*)fHistoricTransitions
->elementAt(idx
),
443 local
, NonExistingTimeOpt
, DuplicatedTimeOpt
);
445 if (fFinalRules
!= NULL
) {
446 rule
= findRuleInFinal(date
, local
, NonExistingTimeOpt
, DuplicatedTimeOpt
);
449 // no final rules or the given time is before the first transition
450 // specified by the final rules -> use the last rule
451 rule
= ((Transition
*)fHistoricTransitions
->elementAt(idx
))->to
;
454 // Find a historical transition
456 if (date
>= getTransitionTime((Transition
*)fHistoricTransitions
->elementAt(idx
),
457 local
, NonExistingTimeOpt
, DuplicatedTimeOpt
)) {
462 rule
= ((Transition
*)fHistoricTransitions
->elementAt(idx
))->to
;
467 rawOffset
= rule
->getRawOffset();
468 dstOffset
= rule
->getDSTSavings();
473 RuleBasedTimeZone::setRawOffset(int32_t /*offsetMillis*/) {
474 // We don't support this operation at this moment.
479 RuleBasedTimeZone::getRawOffset(void) const {
480 // Note: This implementation returns standard GMT offset
481 // as of current time.
482 UErrorCode status
= U_ZERO_ERROR
;
484 getOffset(uprv_getUTCtime() * U_MILLIS_PER_SECOND
,
485 FALSE
, raw
, dst
, status
);
490 RuleBasedTimeZone::useDaylightTime(void) const {
491 // Note: This implementation returns true when
492 // daylight saving time is used as of now or
493 // after the next transition.
494 UErrorCode status
= U_ZERO_ERROR
;
495 UDate now
= uprv_getUTCtime() * U_MILLIS_PER_SECOND
;
497 getOffset(now
, FALSE
, raw
, dst
, status
);
501 // If DST is not used now, check if DST is used after the next transition
503 TimeZoneRule
*from
, *to
;
504 UBool avail
= findNext(now
, FALSE
, time
, from
, to
);
505 if (avail
&& to
->getDSTSavings() != 0) {
512 RuleBasedTimeZone::inDaylightTime(UDate date
, UErrorCode
& status
) const {
513 if (U_FAILURE(status
)) {
517 getOffset(date
, FALSE
, raw
, dst
, status
);
525 RuleBasedTimeZone::hasSameRules(const TimeZone
& other
) const {
526 if (this == &other
) {
529 if (typeid(*this) != typeid(other
)) {
532 const RuleBasedTimeZone
& that
= (const RuleBasedTimeZone
&)other
;
533 if (*fInitialRule
!= *(that
.fInitialRule
)) {
536 if (compareRules(fHistoricRules
, that
.fHistoricRules
)
537 && compareRules(fFinalRules
, that
.fFinalRules
)) {
544 RuleBasedTimeZone::getNextTransition(UDate base
, UBool inclusive
, TimeZoneTransition
& result
) const {
545 UErrorCode status
= U_ZERO_ERROR
;
546 completeConst(status
);
547 if (U_FAILURE(status
)) {
550 UDate transitionTime
;
551 TimeZoneRule
*fromRule
, *toRule
;
552 UBool found
= findNext(base
, inclusive
, transitionTime
, fromRule
, toRule
);
554 result
.setTime(transitionTime
);
555 result
.setFrom((const TimeZoneRule
&)*fromRule
);
556 result
.setTo((const TimeZoneRule
&)*toRule
);
563 RuleBasedTimeZone::getPreviousTransition(UDate base
, UBool inclusive
, TimeZoneTransition
& result
) const {
564 UErrorCode status
= U_ZERO_ERROR
;
565 completeConst(status
);
566 if (U_FAILURE(status
)) {
569 UDate transitionTime
;
570 TimeZoneRule
*fromRule
, *toRule
;
571 UBool found
= findPrev(base
, inclusive
, transitionTime
, fromRule
, toRule
);
573 result
.setTime(transitionTime
);
574 result
.setFrom((const TimeZoneRule
&)*fromRule
);
575 result
.setTo((const TimeZoneRule
&)*toRule
);
582 RuleBasedTimeZone::countTransitionRules(UErrorCode
& /*status*/) const {
584 if (fHistoricRules
!= NULL
) {
585 count
+= fHistoricRules
->size();
587 if (fFinalRules
!= NULL
) {
588 count
+= fFinalRules
->size();
594 RuleBasedTimeZone::getTimeZoneRules(const InitialTimeZoneRule
*& initial
,
595 const TimeZoneRule
* trsrules
[],
597 UErrorCode
& status
) const {
598 if (U_FAILURE(status
)) {
602 initial
= fInitialRule
;
607 if (fHistoricRules
!= NULL
&& cnt
< trscount
) {
608 int32_t historicCount
= fHistoricRules
->size();
610 while (cnt
< trscount
&& idx
< historicCount
) {
611 trsrules
[cnt
++] = (const TimeZoneRule
*)fHistoricRules
->elementAt(idx
++);
614 if (fFinalRules
!= NULL
&& cnt
< trscount
) {
615 int32_t finalCount
= fFinalRules
->size();
617 while (cnt
< trscount
&& idx
< finalCount
) {
618 trsrules
[cnt
++] = (const TimeZoneRule
*)fFinalRules
->elementAt(idx
++);
621 // Set the result length
626 RuleBasedTimeZone::deleteRules(void) {
629 if (fHistoricRules
!= NULL
) {
630 while (!fHistoricRules
->isEmpty()) {
631 delete (TimeZoneRule
*)(fHistoricRules
->orphanElementAt(0));
633 delete fHistoricRules
;
634 fHistoricRules
= NULL
;
636 if (fFinalRules
!= NULL
) {
637 while (!fFinalRules
->isEmpty()) {
638 delete (AnnualTimeZoneRule
*)(fFinalRules
->orphanElementAt(0));
646 RuleBasedTimeZone::deleteTransitions(void) {
647 if (fHistoricTransitions
!= NULL
) {
648 while (!fHistoricTransitions
->isEmpty()) {
649 Transition
*trs
= (Transition
*)fHistoricTransitions
->orphanElementAt(0);
652 delete fHistoricTransitions
;
654 fHistoricTransitions
= NULL
;
658 RuleBasedTimeZone::copyRules(UVector
* source
) {
659 if (source
== NULL
) {
662 UErrorCode ec
= U_ZERO_ERROR
;
663 int32_t size
= source
->size();
664 UVector
*rules
= new UVector(size
, ec
);
669 for (i
= 0; i
< size
; i
++) {
670 rules
->addElement(((TimeZoneRule
*)source
->elementAt(i
))->clone(), ec
);
676 // In case of error, clean up
677 for (i
= 0; i
< rules
->size(); i
++) {
678 TimeZoneRule
*rule
= (TimeZoneRule
*)rules
->orphanElementAt(i
);
688 RuleBasedTimeZone::findRuleInFinal(UDate date
, UBool local
,
689 int32_t NonExistingTimeOpt
, int32_t DuplicatedTimeOpt
) const {
690 if (fFinalRules
== NULL
) {
694 AnnualTimeZoneRule
* fr0
= (AnnualTimeZoneRule
*)fFinalRules
->elementAt(0);
695 AnnualTimeZoneRule
* fr1
= (AnnualTimeZoneRule
*)fFinalRules
->elementAt(1);
696 if (fr0
== NULL
|| fr1
== NULL
) {
700 UDate start0
, start1
;
706 localDelta
= getLocalDelta(fr1
->getRawOffset(), fr1
->getDSTSavings(),
707 fr0
->getRawOffset(), fr0
->getDSTSavings(),
708 NonExistingTimeOpt
, DuplicatedTimeOpt
);
711 UBool avail0
= fr0
->getPreviousStart(base
, fr1
->getRawOffset(), fr1
->getDSTSavings(), TRUE
, start0
);
715 localDelta
= getLocalDelta(fr0
->getRawOffset(), fr0
->getDSTSavings(),
716 fr1
->getRawOffset(), fr1
->getDSTSavings(),
717 NonExistingTimeOpt
, DuplicatedTimeOpt
);
720 UBool avail1
= fr1
->getPreviousStart(base
, fr0
->getRawOffset(), fr0
->getDSTSavings(), TRUE
, start1
);
722 if (!avail0
|| !avail1
) {
728 // Both rules take effect after the given time
732 return (start0
> start1
) ? fr0
: fr1
;
736 RuleBasedTimeZone::findNext(UDate base
, UBool inclusive
, UDate
& transitionTime
,
737 TimeZoneRule
*& fromRule
, TimeZoneRule
*& toRule
) const {
738 if (fHistoricTransitions
== NULL
) {
741 UBool isFinal
= FALSE
;
744 Transition
*tzt
= (Transition
*)fHistoricTransitions
->elementAt(0);
745 UDate tt
= tzt
->time
;
746 if (tt
> base
|| (inclusive
&& tt
== base
)) {
750 int32_t idx
= fHistoricTransitions
->size() - 1;
751 tzt
= (Transition
*)fHistoricTransitions
->elementAt(idx
);
753 if (inclusive
&& tt
== base
) {
756 } else if (tt
<= base
) {
757 if (fFinalRules
!= NULL
) {
758 // Find a transion time with finalRules
759 TimeZoneRule
*r0
= (TimeZoneRule
*)fFinalRules
->elementAt(0);
760 TimeZoneRule
*r1
= (TimeZoneRule
*)fFinalRules
->elementAt(1);
761 UDate start0
, start1
;
762 UBool avail0
= r0
->getNextStart(base
, r1
->getRawOffset(), r1
->getDSTSavings(), inclusive
, start0
);
763 UBool avail1
= r1
->getNextStart(base
, r0
->getRawOffset(), r0
->getDSTSavings(), inclusive
, start1
);
764 // avail0/avail1 should be always TRUE
765 if (!avail0
&& !avail1
) {
768 if (!avail1
|| start0
< start1
) {
769 result
.time
= start0
;
773 result
.time
= start1
;
781 // Find a transition within the historic transitions
783 Transition
*prev
= tzt
;
785 tzt
= (Transition
*)fHistoricTransitions
->elementAt(idx
);
787 if (tt
< base
|| (!inclusive
&& tt
== base
)) {
793 result
.time
= prev
->time
;
794 result
.from
= prev
->from
;
795 result
.to
= prev
->to
;
800 // For now, this implementation ignore transitions with only zone name changes.
801 if (result
.from
->getRawOffset() == result
.to
->getRawOffset()
802 && result
.from
->getDSTSavings() == result
.to
->getDSTSavings()) {
806 // No offset changes. Try next one if not final
807 return findNext(result
.time
, FALSE
/* always exclusive */,
808 transitionTime
, fromRule
, toRule
);
811 transitionTime
= result
.time
;
812 fromRule
= result
.from
;
820 RuleBasedTimeZone::findPrev(UDate base
, UBool inclusive
, UDate
& transitionTime
,
821 TimeZoneRule
*& fromRule
, TimeZoneRule
*& toRule
) const {
822 if (fHistoricTransitions
== NULL
) {
827 Transition
*tzt
= (Transition
*)fHistoricTransitions
->elementAt(0);
828 UDate tt
= tzt
->time
;
829 if (inclusive
&& tt
== base
) {
832 } else if (tt
< base
) {
833 int32_t idx
= fHistoricTransitions
->size() - 1;
834 tzt
= (Transition
*)fHistoricTransitions
->elementAt(idx
);
836 if (inclusive
&& tt
== base
) {
839 } else if (tt
< base
) {
840 if (fFinalRules
!= NULL
) {
841 // Find a transion time with finalRules
842 TimeZoneRule
*r0
= (TimeZoneRule
*)fFinalRules
->elementAt(0);
843 TimeZoneRule
*r1
= (TimeZoneRule
*)fFinalRules
->elementAt(1);
844 UDate start0
, start1
;
845 UBool avail0
= r0
->getPreviousStart(base
, r1
->getRawOffset(), r1
->getDSTSavings(), inclusive
, start0
);
846 UBool avail1
= r1
->getPreviousStart(base
, r0
->getRawOffset(), r0
->getDSTSavings(), inclusive
, start1
);
847 // avail0/avail1 should be always TRUE
848 if (!avail0
&& !avail1
) {
851 if (!avail1
|| start0
> start1
) {
852 result
.time
= start0
;
856 result
.time
= start1
;
865 // Find a transition within the historic transitions
868 tzt
= (Transition
*)fHistoricTransitions
->elementAt(idx
);
870 if (tt
< base
|| (inclusive
&& tt
== base
)) {
880 // For now, this implementation ignore transitions with only zone name changes.
881 if (result
.from
->getRawOffset() == result
.to
->getRawOffset()
882 && result
.from
->getDSTSavings() == result
.to
->getDSTSavings()) {
883 // No offset changes. Try next one if not final
884 return findPrev(result
.time
, FALSE
/* always exclusive */,
885 transitionTime
, fromRule
, toRule
);
887 transitionTime
= result
.time
;
888 fromRule
= result
.from
;
896 RuleBasedTimeZone::getTransitionTime(Transition
* transition
, UBool local
,
897 int32_t NonExistingTimeOpt
, int32_t DuplicatedTimeOpt
) const {
898 UDate time
= transition
->time
;
900 time
+= getLocalDelta(transition
->from
->getRawOffset(), transition
->from
->getDSTSavings(),
901 transition
->to
->getRawOffset(), transition
->to
->getDSTSavings(),
902 NonExistingTimeOpt
, DuplicatedTimeOpt
);
908 RuleBasedTimeZone::getLocalDelta(int32_t rawBefore
, int32_t dstBefore
, int32_t rawAfter
, int32_t dstAfter
,
909 int32_t NonExistingTimeOpt
, int32_t DuplicatedTimeOpt
) const {
912 int32_t offsetBefore
= rawBefore
+ dstBefore
;
913 int32_t offsetAfter
= rawAfter
+ dstAfter
;
915 UBool dstToStd
= (dstBefore
!= 0) && (dstAfter
== 0);
916 UBool stdToDst
= (dstBefore
== 0) && (dstAfter
!= 0);
918 if (offsetAfter
- offsetBefore
>= 0) {
919 // Positive transition, which makes a non-existing local time range
920 if (((NonExistingTimeOpt
& kStdDstMask
) == kStandard
&& dstToStd
)
921 || ((NonExistingTimeOpt
& kStdDstMask
) == kDaylight
&& stdToDst
)) {
922 delta
= offsetBefore
;
923 } else if (((NonExistingTimeOpt
& kStdDstMask
) == kStandard
&& stdToDst
)
924 || ((NonExistingTimeOpt
& kStdDstMask
) == kDaylight
&& dstToStd
)) {
926 } else if ((NonExistingTimeOpt
& kFormerLatterMask
) == kLatter
) {
927 delta
= offsetBefore
;
929 // Interprets the time with rule before the transition,
930 // default for non-existing time range
934 // Negative transition, which makes a duplicated local time range
935 if (((DuplicatedTimeOpt
& kStdDstMask
) == kStandard
&& dstToStd
)
936 || ((DuplicatedTimeOpt
& kStdDstMask
) == kDaylight
&& stdToDst
)) {
938 } else if (((DuplicatedTimeOpt
& kStdDstMask
) == kStandard
&& stdToDst
)
939 || ((DuplicatedTimeOpt
& kStdDstMask
) == kDaylight
&& dstToStd
)) {
940 delta
= offsetBefore
;
941 } else if ((DuplicatedTimeOpt
& kFormerLatterMask
) == kFormer
) {
942 delta
= offsetBefore
;
944 // Interprets the time with rule after the transition,
945 // default for duplicated local time range
954 #endif /* #if !UCONFIG_NO_FORMATTING */