]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/rbtz.cpp
ICU-461.12.tar.gz
[apple/icu.git] / icuSources / i18n / rbtz.cpp
CommitLineData
46f4442e
A
1/*
2*******************************************************************************
729e4ab9
A
3* Copyright (C) 2007-2010, International Business Machines Corporation and
4* others. All Rights Reserved.
46f4442e
A
5*******************************************************************************
6*/
7
729e4ab9
A
8#include <typeinfo> // for 'typeid' to work
9
46f4442e
A
10#include "unicode/utypes.h"
11
12#if !UCONFIG_NO_FORMATTING
13
14#include "unicode/rbtz.h"
15#include "unicode/gregocal.h"
16#include "uvector.h"
17#include "gregoimp.h"
18#include "cmemory.h"
19
20U_NAMESPACE_BEGIN
21
22/**
23 * A struct representing a time zone transition
24 */
25struct Transition {
26 UDate time;
27 TimeZoneRule* from;
28 TimeZoneRule* to;
29};
30
31static UBool compareRules(UVector* rules1, UVector* rules2) {
32 if (rules1 == NULL && rules2 == NULL) {
33 return TRUE;
34 } else if (rules1 == NULL || rules2 == NULL) {
35 return FALSE;
36 }
37 int32_t size = rules1->size();
38 if (size != rules2->size()) {
39 return FALSE;
40 }
41 for (int32_t i = 0; i < size; i++) {
42 TimeZoneRule *r1 = (TimeZoneRule*)rules1->elementAt(i);
43 TimeZoneRule *r2 = (TimeZoneRule*)rules2->elementAt(i);
44 if (*r1 != *r2) {
45 return FALSE;
46 }
47 }
48 return TRUE;
49}
50
51UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedTimeZone)
52
53RuleBasedTimeZone::RuleBasedTimeZone(const UnicodeString& id, InitialTimeZoneRule* initialRule)
54: BasicTimeZone(id), fInitialRule(initialRule), fHistoricRules(NULL), fFinalRules(NULL),
55 fHistoricTransitions(NULL), fUpToDate(FALSE) {
56}
57
58RuleBasedTimeZone::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;
65 complete(status);
66 }
67}
68
69RuleBasedTimeZone::~RuleBasedTimeZone() {
70 deleteTransitions();
71 deleteRules();
72}
73
74RuleBasedTimeZone&
75RuleBasedTimeZone::operator=(const RuleBasedTimeZone& right) {
76 if (*this != right) {
77 BasicTimeZone::operator=(right);
78 deleteRules();
79 fInitialRule = right.fInitialRule->clone();
80 fHistoricRules = copyRules(right.fHistoricRules);
81 fFinalRules = copyRules(right.fFinalRules);
82 deleteTransitions();
83 fUpToDate = FALSE;
84 }
85 return *this;
86}
87
88UBool
89RuleBasedTimeZone::operator==(const TimeZone& that) const {
90 if (this == &that) {
91 return TRUE;
92 }
729e4ab9 93 if (typeid(*this) != typeid(that)
46f4442e
A
94 || BasicTimeZone::operator==(that) == FALSE) {
95 return FALSE;
96 }
97 RuleBasedTimeZone *rbtz = (RuleBasedTimeZone*)&that;
98 if (*fInitialRule != *(rbtz->fInitialRule)) {
99 return FALSE;
100 }
101 if (compareRules(fHistoricRules, rbtz->fHistoricRules)
102 && compareRules(fFinalRules, rbtz->fFinalRules)) {
103 return TRUE;
104 }
105 return FALSE;
106}
107
108UBool
109RuleBasedTimeZone::operator!=(const TimeZone& that) const {
110 return !operator==(that);
111}
112
113void
114RuleBasedTimeZone::addTransitionRule(TimeZoneRule* rule, UErrorCode& status) {
115 if (U_FAILURE(status)) {
116 return;
117 }
729e4ab9
A
118 AnnualTimeZoneRule* atzrule = dynamic_cast<AnnualTimeZoneRule*>(rule);
119 if (atzrule != NULL && atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) {
46f4442e
A
120 // A final rule
121 if (fFinalRules == NULL) {
122 fFinalRules = new UVector(status);
123 if (U_FAILURE(status)) {
124 return;
125 }
126 } else if (fFinalRules->size() >= 2) {
127 // Cannot handle more than two final rules
128 status = U_INVALID_STATE_ERROR;
129 return;
130 }
131 fFinalRules->addElement((void*)rule, status);
132 } else {
133 // Non-final rule
134 if (fHistoricRules == NULL) {
135 fHistoricRules = new UVector(status);
136 if (U_FAILURE(status)) {
137 return;
138 }
139 }
140 fHistoricRules->addElement((void*)rule, status);
141 }
142 // Mark dirty, so transitions are recalculated at next complete() call
143 fUpToDate = FALSE;
144}
145
146void
147RuleBasedTimeZone::complete(UErrorCode& status) {
148 if (U_FAILURE(status)) {
149 return;
150 }
151 if (fUpToDate) {
152 return;
153 }
154 // Make sure either no final rules or a pair of AnnualTimeZoneRules
155 // are available.
156 if (fFinalRules != NULL && fFinalRules->size() != 2) {
157 status = U_INVALID_STATE_ERROR;
158 return;
159 }
160
161 UBool *done = NULL;
162 // Create a TimezoneTransition and add to the list
163 if (fHistoricRules != NULL || fFinalRules != NULL) {
164 TimeZoneRule *curRule = fInitialRule;
165 UDate lastTransitionTime = MIN_MILLIS;
166
167 // Build the transition array which represents historical time zone
168 // transitions.
169 if (fHistoricRules != NULL && fHistoricRules->size() > 0) {
170 int32_t i;
171 int32_t historicCount = fHistoricRules->size();
172 done = (UBool*)uprv_malloc(sizeof(UBool) * historicCount);
173 if (done == NULL) {
174 status = U_MEMORY_ALLOCATION_ERROR;
175 goto cleanup;
176 }
177 for (i = 0; i < historicCount; i++) {
178 done[i] = FALSE;
179 }
180 while (TRUE) {
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;
186 UBool avail;
187 UDate tt;
188 UnicodeString curName, name;
189 curRule->getName(curName);
190
191 for (i = 0; i < historicCount; i++) {
192 if (done[i]) {
193 continue;
194 }
195 r = (TimeZoneRule*)fHistoricRules->elementAt(i);
196 avail = r->getNextStart(lastTransitionTime, curStdOffset, curDstSavings, false, tt);
197 if (!avail) {
198 // No more transitions from this rule - skip this rule next time
199 done[i] = TRUE;
200 } else {
201 r->getName(name);
202 if (*r == *curRule ||
203 (name == curName && r->getRawOffset() == curRule->getRawOffset()
204 && r->getDSTSavings() == curRule->getDSTSavings())) {
205 continue;
206 }
207 if (tt < nextTransitionTime) {
208 nextTransitionTime = tt;
209 nextRule = r;
210 }
211 }
212 }
213
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++) {
218 if (!done[j]) {
219 bDoneAll = FALSE;
220 break;
221 }
222 }
223 if (bDoneAll) {
224 break;
225 }
226 }
227
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) {
233 continue;
234 }
235 r = (TimeZoneRule*)fFinalRules->elementAt(i);
236 avail = r->getNextStart(lastTransitionTime, curStdOffset, curDstSavings, false, tt);
237 if (avail) {
238 if (tt < nextTransitionTime) {
239 nextTransitionTime = tt;
240 nextRule = r;
241 }
242 }
243 }
244 }
245
246 if (nextRule == NULL) {
247 // Nothing more
248 break;
249 }
250
251 if (fHistoricTransitions == NULL) {
252 fHistoricTransitions = new UVector(status);
253 if (U_FAILURE(status)) {
254 goto cleanup;
255 }
256 }
257 Transition *trst = (Transition*)uprv_malloc(sizeof(Transition));
258 if (trst == NULL) {
259 status = U_MEMORY_ALLOCATION_ERROR;
260 goto cleanup;
261 }
262 trst->time = nextTransitionTime;
263 trst->from = curRule;
264 trst->to = nextRule;
265 fHistoricTransitions->addElement(trst, status);
266 if (U_FAILURE(status)) {
267 goto cleanup;
268 }
269 lastTransitionTime = nextTransitionTime;
270 curRule = nextRule;
271 }
272 }
273 if (fFinalRules != NULL) {
274 if (fHistoricTransitions == NULL) {
275 fHistoricTransitions = new UVector(status);
276 if (U_FAILURE(status)) {
277 goto cleanup;
278 }
279 }
280 // Append the first transition for each
281 TimeZoneRule *rule0 = (TimeZoneRule*)fFinalRules->elementAt(0);
282 TimeZoneRule *rule1 = (TimeZoneRule*)fFinalRules->elementAt(1);
283 UDate tt0, tt1;
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;
289 goto cleanup;
290 }
291 Transition *final0 = (Transition*)uprv_malloc(sizeof(Transition));
292 if (final0 == NULL) {
293 status = U_MEMORY_ALLOCATION_ERROR;
294 goto cleanup;
295 }
296 Transition *final1 = (Transition*)uprv_malloc(sizeof(Transition));
297 if (final1 == NULL) {
298 uprv_free(final0);
299 status = U_MEMORY_ALLOCATION_ERROR;
300 goto cleanup;
301 }
302 if (tt0 < tt1) {
303 final0->time = tt0;
304 final0->from = curRule;
305 final0->to = rule0;
306 rule1->getNextStart(tt0, rule0->getRawOffset(), rule0->getDSTSavings(), false, final1->time);
307 final1->from = rule0;
308 final1->to = rule1;
309 } else {
310 final0->time = tt1;
311 final0->from = curRule;
312 final0->to = rule1;
313 rule0->getNextStart(tt1, rule1->getRawOffset(), rule1->getDSTSavings(), false, final1->time);
314 final1->from = rule1;
315 final1->to = rule0;
316 }
317 fHistoricTransitions->addElement(final0, status);
318 if (U_FAILURE(status)) {
319 goto cleanup;
320 }
321 fHistoricTransitions->addElement(final1, status);
322 if (U_FAILURE(status)) {
323 goto cleanup;
324 }
325 }
326 }
327 fUpToDate = TRUE;
328 if (done != NULL) {
329 uprv_free(done);
330 }
331 return;
332
333cleanup:
334 deleteTransitions();
335 if (done != NULL) {
336 uprv_free(done);
337 }
338 fUpToDate = FALSE;
339}
340
341TimeZone*
342RuleBasedTimeZone::clone(void) const {
343 return new RuleBasedTimeZone(*this);
344}
345
346int32_t
347RuleBasedTimeZone::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)) {
350 return 0;
351 }
352 if (month < UCAL_JANUARY || month > UCAL_DECEMBER) {
353 status = U_ILLEGAL_ARGUMENT_ERROR;
354 return 0;
355 } else {
356 return getOffset(era, year, month, day, dayOfWeek, millis,
357 Grego::monthLength(year, month), status);
358 }
359}
360
361int32_t
362RuleBasedTimeZone::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)) {
367 return 0;
368 }
369 if (era == GregorianCalendar::BC) {
370 // Convert to extended year
371 year = 1 - year;
372 }
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)) {
377 return 0;
378 }
379 return (rawOffset + dstOffset);
380}
381
382void
383RuleBasedTimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
384 int32_t& dstOffset, UErrorCode& status) const {
385 getOffsetInternal(date, local, kFormer, kLatter, rawOffset, dstOffset, status);
386}
387
388void
389RuleBasedTimeZone::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);
392}
393
394
395/*
396 * The internal getOffset implementation
397 */
398void
399RuleBasedTimeZone::getOffsetInternal(UDate date, UBool local,
400 int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt,
401 int32_t& rawOffset, int32_t& dstOffset,
402 UErrorCode& status) const {
403 rawOffset = 0;
404 dstOffset = 0;
405
406 if (U_FAILURE(status)) {
407 return;
408 }
409 if (!fUpToDate) {
410 // Transitions are not yet resolved. We cannot do it here
411 // because this method is const. Thus, do nothing and return
412 // error status.
413 status = U_INVALID_STATE_ERROR;
414 return;
415 }
416 const TimeZoneRule *rule = NULL;
417 if (fHistoricTransitions == NULL) {
418 rule = fInitialRule;
419 } else {
420 UDate tstart = getTransitionTime((Transition*)fHistoricTransitions->elementAt(0),
421 local, NonExistingTimeOpt, DuplicatedTimeOpt);
422 if (date < tstart) {
423 rule = fInitialRule;
424 } else {
425 int32_t idx = fHistoricTransitions->size() - 1;
426 UDate tend = getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx),
427 local, NonExistingTimeOpt, DuplicatedTimeOpt);
428 if (date > tend) {
429 if (fFinalRules != NULL) {
430 rule = findRuleInFinal(date, local, NonExistingTimeOpt, DuplicatedTimeOpt);
431 } else {
432 // no final rule, use the last rule
433 rule = ((Transition*)fHistoricTransitions->elementAt(idx))->to;
434 }
435 } else {
436 // Find a historical transition
437 while (idx >= 0) {
438 if (date >= getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx),
439 local, NonExistingTimeOpt, DuplicatedTimeOpt)) {
440 break;
441 }
442 idx--;
443 }
444 rule = ((Transition*)fHistoricTransitions->elementAt(idx))->to;
445 }
446 }
447 }
448 if (rule != NULL) {
449 rawOffset = rule->getRawOffset();
450 dstOffset = rule->getDSTSavings();
451 }
452}
453
454void
455RuleBasedTimeZone::setRawOffset(int32_t /*offsetMillis*/) {
456 // We don't support this operation at this moment.
457 // Nothing to do!
458}
459
460int32_t
461RuleBasedTimeZone::getRawOffset(void) const {
462 // Note: This implementation returns standard GMT offset
463 // as of current time.
464 UErrorCode status = U_ZERO_ERROR;
465 int32_t raw, dst;
466 getOffset(uprv_getUTCtime() * U_MILLIS_PER_SECOND,
467 FALSE, raw, dst, status);
468 return raw;
469}
470
471UBool
472RuleBasedTimeZone::useDaylightTime(void) const {
473 // Note: This implementation returns true when
474 // daylight saving time is used as of now or
475 // after the next transition.
476 UErrorCode status = U_ZERO_ERROR;
477 UDate now = uprv_getUTCtime() * U_MILLIS_PER_SECOND;
478 int32_t raw, dst;
479 getOffset(now, FALSE, raw, dst, status);
480 if (dst != 0) {
481 return TRUE;
482 }
483 // If DST is not used now, check if DST is used after the next transition
484 UDate time;
485 TimeZoneRule *from, *to;
486 UBool avail = findNext(now, FALSE, time, from, to);
487 if (avail && to->getDSTSavings() != 0) {
488 return TRUE;
489 }
490 return FALSE;
491}
492
493UBool
494RuleBasedTimeZone::inDaylightTime(UDate date, UErrorCode& status) const {
495 if (U_FAILURE(status)) {
496 return FALSE;
497 }
498 int32_t raw, dst;
499 getOffset(date, FALSE, raw, dst, status);
500 if (dst != 0) {
501 return TRUE;
502 }
503 return FALSE;
504}
505
506UBool
507RuleBasedTimeZone::hasSameRules(const TimeZone& other) const {
508 if (this == &other) {
509 return TRUE;
510 }
729e4ab9 511 if (typeid(*this) != typeid(other)) {
46f4442e
A
512 return FALSE;
513 }
514 const RuleBasedTimeZone& that = (const RuleBasedTimeZone&)other;
515 if (*fInitialRule != *(that.fInitialRule)) {
516 return FALSE;
517 }
518 if (compareRules(fHistoricRules, that.fHistoricRules)
519 && compareRules(fFinalRules, that.fFinalRules)) {
520 return TRUE;
521 }
522 return FALSE;
523}
524
525UBool
526RuleBasedTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) /*const*/ {
527 UErrorCode status = U_ZERO_ERROR;
528 complete(status);
529 if (U_FAILURE(status)) {
530 return FALSE;
531 }
532 UDate transitionTime;
533 TimeZoneRule *fromRule, *toRule;
534 UBool found = findNext(base, inclusive, transitionTime, fromRule, toRule);
535 if (found) {
536 result.setTime(transitionTime);
537 result.setFrom((const TimeZoneRule&)*fromRule);
538 result.setTo((const TimeZoneRule&)*toRule);
539 return TRUE;
540 }
541 return FALSE;
542}
543
544UBool
545RuleBasedTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) /*const*/ {
546 UErrorCode status = U_ZERO_ERROR;
547 complete(status);
548 if (U_FAILURE(status)) {
549 return FALSE;
550 }
551 UDate transitionTime;
552 TimeZoneRule *fromRule, *toRule;
553 UBool found = findPrev(base, inclusive, transitionTime, fromRule, toRule);
554 if (found) {
555 result.setTime(transitionTime);
556 result.setFrom((const TimeZoneRule&)*fromRule);
557 result.setTo((const TimeZoneRule&)*toRule);
558 return TRUE;
559 }
560 return FALSE;
561}
562
563int32_t
564RuleBasedTimeZone::countTransitionRules(UErrorCode& /*status*/) /*const*/ {
565 int32_t count = 0;
566 if (fHistoricRules != NULL) {
567 count += fHistoricRules->size();
568 }
569 if (fFinalRules != NULL) {
570 count += fFinalRules->size();
571 }
572 return count;
573}
574
575void
576RuleBasedTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
577 const TimeZoneRule* trsrules[],
578 int32_t& trscount,
579 UErrorCode& status) /*const*/ {
580 if (U_FAILURE(status)) {
581 return;
582 }
583 // Initial rule
584 initial = fInitialRule;
585
586 // Transition rules
587 int32_t cnt = 0;
588 int32_t idx;
589 if (fHistoricRules != NULL && cnt < trscount) {
590 int32_t historicCount = fHistoricRules->size();
591 idx = 0;
592 while (cnt < trscount && idx < historicCount) {
593 trsrules[cnt++] = (const TimeZoneRule*)fHistoricRules->elementAt(idx++);
594 }
595 }
596 if (fFinalRules != NULL && cnt < trscount) {
597 int32_t finalCount = fFinalRules->size();
598 idx = 0;
599 while (cnt < trscount && idx < finalCount) {
600 trsrules[cnt++] = (const TimeZoneRule*)fFinalRules->elementAt(idx++);
601 }
602 }
603 // Set the result length
604 trscount = cnt;
605}
606
607void
608RuleBasedTimeZone::deleteRules(void) {
609 delete fInitialRule;
610 fInitialRule = NULL;
611 if (fHistoricRules != NULL) {
612 while (!fHistoricRules->isEmpty()) {
613 delete (TimeZoneRule*)(fHistoricRules->orphanElementAt(0));
614 }
615 delete fHistoricRules;
616 fHistoricRules = NULL;
617 }
618 if (fFinalRules != NULL) {
619 while (!fFinalRules->isEmpty()) {
620 delete (AnnualTimeZoneRule*)(fFinalRules->orphanElementAt(0));
621 }
622 delete fFinalRules;
623 fFinalRules = NULL;
624 }
625}
626
627void
628RuleBasedTimeZone::deleteTransitions(void) {
629 if (fHistoricTransitions != NULL) {
630 while (!fHistoricTransitions->isEmpty()) {
631 Transition *trs = (Transition*)fHistoricTransitions->orphanElementAt(0);
632 uprv_free(trs);
633 }
634 delete fHistoricTransitions;
635 }
636 fHistoricTransitions = NULL;
637}
638
639UVector*
640RuleBasedTimeZone::copyRules(UVector* source) {
641 if (source == NULL) {
642 return NULL;
643 }
644 UErrorCode ec = U_ZERO_ERROR;
645 int32_t size = source->size();
646 UVector *rules = new UVector(size, ec);
647 if (U_FAILURE(ec)) {
648 return NULL;
649 }
650 int32_t i;
651 for (i = 0; i < size; i++) {
652 rules->addElement(((TimeZoneRule*)source->elementAt(i))->clone(), ec);
653 if (U_FAILURE(ec)) {
654 break;
655 }
656 }
657 if (U_FAILURE(ec)) {
658 // In case of error, clean up
659 for (i = 0; i < rules->size(); i++) {
660 TimeZoneRule *rule = (TimeZoneRule*)rules->orphanElementAt(i);
661 delete rule;
662 }
663 delete rules;
664 return NULL;
665 }
666 return rules;
667}
668
669TimeZoneRule*
670RuleBasedTimeZone::findRuleInFinal(UDate date, UBool local,
671 int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const {
672 if (fFinalRules == NULL) {
673 return NULL;
674 }
675
676 AnnualTimeZoneRule* fr0 = (AnnualTimeZoneRule*)fFinalRules->elementAt(0);
677 AnnualTimeZoneRule* fr1 = (AnnualTimeZoneRule*)fFinalRules->elementAt(1);
678 if (fr0 == NULL || fr1 == NULL) {
679 return NULL;
680 }
681
682 UDate start0, start1;
683 UDate base;
684 int32_t localDelta;
685
686 base = date;
687 if (local) {
688 localDelta = getLocalDelta(fr1->getRawOffset(), fr1->getDSTSavings(),
689 fr0->getRawOffset(), fr0->getDSTSavings(),
690 NonExistingTimeOpt, DuplicatedTimeOpt);
691 base -= localDelta;
692 }
693 UBool avail0 = fr0->getPreviousStart(base, fr1->getRawOffset(), fr1->getDSTSavings(), TRUE, start0);
694
695 base = date;
696 if (local) {
697 localDelta = getLocalDelta(fr0->getRawOffset(), fr0->getDSTSavings(),
698 fr1->getRawOffset(), fr1->getDSTSavings(),
699 NonExistingTimeOpt, DuplicatedTimeOpt);
700 base -= localDelta;
701 }
702 UBool avail1 = fr1->getPreviousStart(base, fr0->getRawOffset(), fr0->getDSTSavings(), TRUE, start1);
703
704 if (avail0 && (!avail1 || start0 > start1)) {
705 return fr0;
706 } else if (avail1) {
707 return fr1;
708 }
709 return NULL;
710}
711
712UBool
713RuleBasedTimeZone::findNext(UDate base, UBool inclusive, UDate& transitionTime,
714 TimeZoneRule*& fromRule, TimeZoneRule*& toRule) const {
715 if (fHistoricTransitions == NULL) {
716 return FALSE;
717 }
718 UBool isFinal = FALSE;
719 UBool found = FALSE;
720 Transition result;
721 Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0);
722 UDate tt = tzt->time;
723 if (tt > base || (inclusive && tt == base)) {
724 result = *tzt;
725 found = TRUE;
726 } else {
727 int32_t idx = fHistoricTransitions->size() - 1;
728 tzt = (Transition*)fHistoricTransitions->elementAt(idx);
729 tt = tzt->time;
730 if (inclusive && tt == base) {
731 result = *tzt;
732 found = TRUE;
733 } else if (tt <= base) {
734 if (fFinalRules != NULL) {
735 // Find a transion time with finalRules
736 TimeZoneRule *r0 = (TimeZoneRule*)fFinalRules->elementAt(0);
737 TimeZoneRule *r1 = (TimeZoneRule*)fFinalRules->elementAt(1);
738 UDate start0, start1;
739 UBool avail0 = r0->getNextStart(base, r1->getRawOffset(), r1->getDSTSavings(), inclusive, start0);
740 UBool avail1 = r1->getNextStart(base, r0->getRawOffset(), r0->getDSTSavings(), inclusive, start1);
741 // avail0/avail1 should be always TRUE
742 if (!avail0 && !avail1) {
743 return FALSE;
744 }
745 if (!avail1 || start0 < start1) {
746 result.time = start0;
747 result.from = r1;
748 result.to = r0;
749 } else {
750 result.time = start1;
751 result.from = r0;
752 result.to = r1;
753 }
754 isFinal = TRUE;
755 found = TRUE;
756 }
757 } else {
758 // Find a transition within the historic transitions
759 idx--;
760 Transition *prev = tzt;
761 while (idx > 0) {
762 tzt = (Transition*)fHistoricTransitions->elementAt(idx);
763 tt = tzt->time;
764 if (tt < base || (!inclusive && tt == base)) {
765 break;
766 }
767 idx--;
768 prev = tzt;
769 }
770 result.time = prev->time;
771 result.from = prev->from;
772 result.to = prev->to;
773 found = TRUE;
774 }
775 }
776 if (found) {
777 // For now, this implementation ignore transitions with only zone name changes.
778 if (result.from->getRawOffset() == result.to->getRawOffset()
779 && result.from->getDSTSavings() == result.to->getDSTSavings()) {
780 if (isFinal) {
781 return FALSE;
782 } else {
783 // No offset changes. Try next one if not final
784 return findNext(result.time, FALSE /* always exclusive */,
785 transitionTime, fromRule, toRule);
786 }
787 }
788 transitionTime = result.time;
789 fromRule = result.from;
790 toRule = result.to;
791 return TRUE;
792 }
793 return FALSE;
794}
795
796UBool
797RuleBasedTimeZone::findPrev(UDate base, UBool inclusive, UDate& transitionTime,
798 TimeZoneRule*& fromRule, TimeZoneRule*& toRule) const {
799 if (fHistoricTransitions == NULL) {
800 return FALSE;
801 }
802 UBool found = FALSE;
803 Transition result;
804 Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0);
805 UDate tt = tzt->time;
806 if (inclusive && tt == base) {
807 result = *tzt;
808 found = TRUE;
809 } else if (tt < base) {
810 int32_t idx = fHistoricTransitions->size() - 1;
811 tzt = (Transition*)fHistoricTransitions->elementAt(idx);
812 tt = tzt->time;
813 if (inclusive && tt == base) {
814 result = *tzt;
815 found = TRUE;
816 } else if (tt < base) {
817 if (fFinalRules != NULL) {
818 // Find a transion time with finalRules
819 TimeZoneRule *r0 = (TimeZoneRule*)fFinalRules->elementAt(0);
820 TimeZoneRule *r1 = (TimeZoneRule*)fFinalRules->elementAt(1);
821 UDate start0, start1;
822 UBool avail0 = r0->getPreviousStart(base, r1->getRawOffset(), r1->getDSTSavings(), inclusive, start0);
823 UBool avail1 = r1->getPreviousStart(base, r0->getRawOffset(), r0->getDSTSavings(), inclusive, start1);
824 // avail0/avail1 should be always TRUE
825 if (!avail0 && !avail1) {
826 return FALSE;
827 }
828 if (!avail1 || start0 > start1) {
829 result.time = start0;
830 result.from = r1;
831 result.to = r0;
832 } else {
833 result.time = start1;
834 result.from = r0;
835 result.to = r1;
836 }
837 } else {
838 result = *tzt;
839 }
840 found = TRUE;
841 } else {
842 // Find a transition within the historic transitions
843 idx--;
844 while (idx >= 0) {
845 tzt = (Transition*)fHistoricTransitions->elementAt(idx);
846 tt = tzt->time;
847 if (tt < base || (inclusive && tt == base)) {
848 break;
849 }
850 idx--;
851 }
852 result = *tzt;
853 found = TRUE;
854 }
855 }
856 if (found) {
857 // For now, this implementation ignore transitions with only zone name changes.
858 if (result.from->getRawOffset() == result.to->getRawOffset()
859 && result.from->getDSTSavings() == result.to->getDSTSavings()) {
860 // No offset changes. Try next one if not final
861 return findPrev(result.time, FALSE /* always exclusive */,
862 transitionTime, fromRule, toRule);
863 }
864 transitionTime = result.time;
865 fromRule = result.from;
866 toRule = result.to;
867 return TRUE;
868 }
869 return FALSE;
870}
871
872UDate
873RuleBasedTimeZone::getTransitionTime(Transition* transition, UBool local,
874 int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const {
875 UDate time = transition->time;
876 if (local) {
877 time += getLocalDelta(transition->from->getRawOffset(), transition->from->getDSTSavings(),
878 transition->to->getRawOffset(), transition->to->getDSTSavings(),
879 NonExistingTimeOpt, DuplicatedTimeOpt);
880 }
881 return time;
882}
883
884int32_t
885RuleBasedTimeZone::getLocalDelta(int32_t rawBefore, int32_t dstBefore, int32_t rawAfter, int32_t dstAfter,
886 int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const {
887 int32_t delta = 0;
888
889 int32_t offsetBefore = rawBefore + dstBefore;
890 int32_t offsetAfter = rawAfter + dstAfter;
891
892 UBool dstToStd = (dstBefore != 0) && (dstAfter == 0);
893 UBool stdToDst = (dstBefore == 0) && (dstAfter != 0);
894
895 if (offsetAfter - offsetBefore >= 0) {
896 // Positive transition, which makes a non-existing local time range
897 if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd)
898 || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
899 delta = offsetBefore;
900 } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst)
901 || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
902 delta = offsetAfter;
903 } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) {
904 delta = offsetBefore;
905 } else {
906 // Interprets the time with rule before the transition,
907 // default for non-existing time range
908 delta = offsetAfter;
909 }
910 } else {
911 // Negative transition, which makes a duplicated local time range
912 if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd)
913 || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
914 delta = offsetAfter;
915 } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst)
916 || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
917 delta = offsetBefore;
918 } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) {
919 delta = offsetBefore;
920 } else {
921 // Interprets the time with rule after the transition,
922 // default for duplicated local time range
923 delta = offsetAfter;
924 }
925 }
926 return delta;
927}
928
929U_NAMESPACE_END
930
931#endif /* #if !UCONFIG_NO_FORMATTING */
932
933//eof
934