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