1 /*
2 *******************************************************************************
3 * Copyright (C) 2007-2012, 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
20 U_NAMESPACE_BEGIN
21
22 /**
23 * A struct representing a time zone transition
24 */
25 struct Transition {
26 UDate time;
27 TimeZoneRule* from;
28 TimeZoneRule* to;
29 };
30
compareRules(UVector * rules1,UVector * rules2)31 static 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
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedTimeZone)51 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedTimeZone)
52
53 RuleBasedTimeZone::RuleBasedTimeZone(const UnicodeString& id, InitialTimeZoneRule* initialRule)
54 : BasicTimeZone(id), fInitialRule(initialRule), fHistoricRules(NULL), fFinalRules(NULL),
55 fHistoricTransitions(NULL), fUpToDate(FALSE) {
56 }
57
RuleBasedTimeZone(const RuleBasedTimeZone & source)58 RuleBasedTimeZone::RuleBasedTimeZone(const RuleBasedTimeZone& source)
59 : BasicTimeZone(source), fInitialRule(source.fInitialRule->clone()),
60 fHistoricTransitions(NULL), fUpToDate(FALSE) {
61 fHistoricRules = copyRules(source.fHistoricRules);
62 fFinalRules = copyRules(source.fFinalRules);
63 if (source.fUpToDate) {
64 UErrorCode status = U_ZERO_ERROR;
65 complete(status);
66 }
67 }
68
~RuleBasedTimeZone()69 RuleBasedTimeZone::~RuleBasedTimeZone() {
70 deleteTransitions();
71 deleteRules();
72 }
73
74 RuleBasedTimeZone&
operator =(const RuleBasedTimeZone & right)75 RuleBasedTimeZone::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
88 UBool
operator ==(const TimeZone & that) const89 RuleBasedTimeZone::operator==(const TimeZone& that) const {
90 if (this == &that) {
91 return TRUE;
92 }
93 if (typeid(*this) != typeid(that)
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
108 UBool
operator !=(const TimeZone & that) const109 RuleBasedTimeZone::operator!=(const TimeZone& that) const {
110 return !operator==(that);
111 }
112
113 void
addTransitionRule(TimeZoneRule * rule,UErrorCode & status)114 RuleBasedTimeZone::addTransitionRule(TimeZoneRule* rule, UErrorCode& status) {
115 if (U_FAILURE(status)) {
116 return;
117 }
118 AnnualTimeZoneRule* atzrule = dynamic_cast<AnnualTimeZoneRule*>(rule);
119 if (atzrule != NULL && atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) {
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
146 void
complete(UErrorCode & status)147 RuleBasedTimeZone::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
333 cleanup:
334 deleteTransitions();
335 if (done != NULL) {
336 uprv_free(done);
337 }
338 fUpToDate = FALSE;
339 }
340
341 TimeZone*
clone(void) const342 RuleBasedTimeZone::clone(void) const {
343 return new RuleBasedTimeZone(*this);
344 }
345
346 int32_t
getOffset(uint8_t era,int32_t year,int32_t month,int32_t day,uint8_t dayOfWeek,int32_t millis,UErrorCode & status) const347 RuleBasedTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
348 uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const {
349 if (U_FAILURE(status)) {
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
361 int32_t
getOffset(uint8_t era,int32_t year,int32_t month,int32_t day,uint8_t,int32_t millis,int32_t,UErrorCode & status) const362 RuleBasedTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
363 uint8_t /*dayOfWeek*/, int32_t millis,
364 int32_t /*monthLength*/, UErrorCode& status) const {
365 // dayOfWeek and monthLength are unused
366 if (U_FAILURE(status)) {
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
382 void
getOffset(UDate date,UBool local,int32_t & rawOffset,int32_t & dstOffset,UErrorCode & status) const383 RuleBasedTimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
384 int32_t& dstOffset, UErrorCode& status) const {
385 getOffsetInternal(date, local, kFormer, kLatter, rawOffset, dstOffset, status);
386 }
387
388 void
getOffsetFromLocal(UDate date,int32_t nonExistingTimeOpt,int32_t duplicatedTimeOpt,int32_t & rawOffset,int32_t & dstOffset,UErrorCode & status)389 RuleBasedTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
390 int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) /*const*/ {
391 getOffsetInternal(date, TRUE, nonExistingTimeOpt, duplicatedTimeOpt, rawOffset, dstOffset, status);
392 }
393
394
395 /*
396 * The internal getOffset implementation
397 */
398 void
getOffsetInternal(UDate date,UBool local,int32_t NonExistingTimeOpt,int32_t DuplicatedTimeOpt,int32_t & rawOffset,int32_t & dstOffset,UErrorCode & status) const399 RuleBasedTimeZone::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 }
432 if (rule == NULL) {
433 // no final rules or the given time is before the first transition
434 // specified by the final rules -> use the last rule
435 rule = ((Transition*)fHistoricTransitions->elementAt(idx))->to;
436 }
437 } else {
438 // Find a historical transition
439 while (idx >= 0) {
440 if (date >= getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx),
441 local, NonExistingTimeOpt, DuplicatedTimeOpt)) {
442 break;
443 }
444 idx--;
445 }
446 rule = ((Transition*)fHistoricTransitions->elementAt(idx))->to;
447 }
448 }
449 }
450 if (rule != NULL) {
451 rawOffset = rule->getRawOffset();
452 dstOffset = rule->getDSTSavings();
453 }
454 }
455
456 void
setRawOffset(int32_t)457 RuleBasedTimeZone::setRawOffset(int32_t /*offsetMillis*/) {
458 // We don't support this operation at this moment.
459 // Nothing to do!
460 }
461
462 int32_t
getRawOffset(void) const463 RuleBasedTimeZone::getRawOffset(void) const {
464 // Note: This implementation returns standard GMT offset
465 // as of current time.
466 UErrorCode status = U_ZERO_ERROR;
467 int32_t raw, dst;
468 getOffset(uprv_getUTCtime() * U_MILLIS_PER_SECOND,
469 FALSE, raw, dst, status);
470 return raw;
471 }
472
473 UBool
useDaylightTime(void) const474 RuleBasedTimeZone::useDaylightTime(void) const {
475 // Note: This implementation returns true when
476 // daylight saving time is used as of now or
477 // after the next transition.
478 UErrorCode status = U_ZERO_ERROR;
479 UDate now = uprv_getUTCtime() * U_MILLIS_PER_SECOND;
480 int32_t raw, dst;
481 getOffset(now, FALSE, raw, dst, status);
482 if (dst != 0) {
483 return TRUE;
484 }
485 // If DST is not used now, check if DST is used after the next transition
486 UDate time;
487 TimeZoneRule *from, *to;
488 UBool avail = findNext(now, FALSE, time, from, to);
489 if (avail && to->getDSTSavings() != 0) {
490 return TRUE;
491 }
492 return FALSE;
493 }
494
495 UBool
inDaylightTime(UDate date,UErrorCode & status) const496 RuleBasedTimeZone::inDaylightTime(UDate date, UErrorCode& status) const {
497 if (U_FAILURE(status)) {
498 return FALSE;
499 }
500 int32_t raw, dst;
501 getOffset(date, FALSE, raw, dst, status);
502 if (dst != 0) {
503 return TRUE;
504 }
505 return FALSE;
506 }
507
508 UBool
hasSameRules(const TimeZone & other) const509 RuleBasedTimeZone::hasSameRules(const TimeZone& other) const {
510 if (this == &other) {
511 return TRUE;
512 }
513 if (typeid(*this) != typeid(other)) {
514 return FALSE;
515 }
516 const RuleBasedTimeZone& that = (const RuleBasedTimeZone&)other;
517 if (*fInitialRule != *(that.fInitialRule)) {
518 return FALSE;
519 }
520 if (compareRules(fHistoricRules, that.fHistoricRules)
521 && compareRules(fFinalRules, that.fFinalRules)) {
522 return TRUE;
523 }
524 return FALSE;
525 }
526
527 UBool
getNextTransition(UDate base,UBool inclusive,TimeZoneTransition & result)528 RuleBasedTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) /*const*/ {
529 UErrorCode status = U_ZERO_ERROR;
530 complete(status);
531 if (U_FAILURE(status)) {
532 return FALSE;
533 }
534 UDate transitionTime;
535 TimeZoneRule *fromRule, *toRule;
536 UBool found = findNext(base, inclusive, transitionTime, fromRule, toRule);
537 if (found) {
538 result.setTime(transitionTime);
539 result.setFrom((const TimeZoneRule&)*fromRule);
540 result.setTo((const TimeZoneRule&)*toRule);
541 return TRUE;
542 }
543 return FALSE;
544 }
545
546 UBool
getPreviousTransition(UDate base,UBool inclusive,TimeZoneTransition & result)547 RuleBasedTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) /*const*/ {
548 UErrorCode status = U_ZERO_ERROR;
549 complete(status);
550 if (U_FAILURE(status)) {
551 return FALSE;
552 }
553 UDate transitionTime;
554 TimeZoneRule *fromRule, *toRule;
555 UBool found = findPrev(base, inclusive, transitionTime, fromRule, toRule);
556 if (found) {
557 result.setTime(transitionTime);
558 result.setFrom((const TimeZoneRule&)*fromRule);
559 result.setTo((const TimeZoneRule&)*toRule);
560 return TRUE;
561 }
562 return FALSE;
563 }
564
565 int32_t
countTransitionRules(UErrorCode &)566 RuleBasedTimeZone::countTransitionRules(UErrorCode& /*status*/) /*const*/ {
567 int32_t count = 0;
568 if (fHistoricRules != NULL) {
569 count += fHistoricRules->size();
570 }
571 if (fFinalRules != NULL) {
572 count += fFinalRules->size();
573 }
574 return count;
575 }
576
577 void
getTimeZoneRules(const InitialTimeZoneRule * & initial,const TimeZoneRule * trsrules[],int32_t & trscount,UErrorCode & status)578 RuleBasedTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
579 const TimeZoneRule* trsrules[],
580 int32_t& trscount,
581 UErrorCode& status) /*const*/ {
582 if (U_FAILURE(status)) {
583 return;
584 }
585 // Initial rule
586 initial = fInitialRule;
587
588 // Transition rules
589 int32_t cnt = 0;
590 int32_t idx;
591 if (fHistoricRules != NULL && cnt < trscount) {
592 int32_t historicCount = fHistoricRules->size();
593 idx = 0;
594 while (cnt < trscount && idx < historicCount) {
595 trsrules[cnt++] = (const TimeZoneRule*)fHistoricRules->elementAt(idx++);
596 }
597 }
598 if (fFinalRules != NULL && cnt < trscount) {
599 int32_t finalCount = fFinalRules->size();
600 idx = 0;
601 while (cnt < trscount && idx < finalCount) {
602 trsrules[cnt++] = (const TimeZoneRule*)fFinalRules->elementAt(idx++);
603 }
604 }
605 // Set the result length
606 trscount = cnt;
607 }
608
609 void
deleteRules(void)610 RuleBasedTimeZone::deleteRules(void) {
611 delete fInitialRule;
612 fInitialRule = NULL;
613 if (fHistoricRules != NULL) {
614 while (!fHistoricRules->isEmpty()) {
615 delete (TimeZoneRule*)(fHistoricRules->orphanElementAt(0));
616 }
617 delete fHistoricRules;
618 fHistoricRules = NULL;
619 }
620 if (fFinalRules != NULL) {
621 while (!fFinalRules->isEmpty()) {
622 delete (AnnualTimeZoneRule*)(fFinalRules->orphanElementAt(0));
623 }
624 delete fFinalRules;
625 fFinalRules = NULL;
626 }
627 }
628
629 void
deleteTransitions(void)630 RuleBasedTimeZone::deleteTransitions(void) {
631 if (fHistoricTransitions != NULL) {
632 while (!fHistoricTransitions->isEmpty()) {
633 Transition *trs = (Transition*)fHistoricTransitions->orphanElementAt(0);
634 uprv_free(trs);
635 }
636 delete fHistoricTransitions;
637 }
638 fHistoricTransitions = NULL;
639 }
640
641 UVector*
copyRules(UVector * source)642 RuleBasedTimeZone::copyRules(UVector* source) {
643 if (source == NULL) {
644 return NULL;
645 }
646 UErrorCode ec = U_ZERO_ERROR;
647 int32_t size = source->size();
648 UVector *rules = new UVector(size, ec);
649 if (U_FAILURE(ec)) {
650 return NULL;
651 }
652 int32_t i;
653 for (i = 0; i < size; i++) {
654 rules->addElement(((TimeZoneRule*)source->elementAt(i))->clone(), ec);
655 if (U_FAILURE(ec)) {
656 break;
657 }
658 }
659 if (U_FAILURE(ec)) {
660 // In case of error, clean up
661 for (i = 0; i < rules->size(); i++) {
662 TimeZoneRule *rule = (TimeZoneRule*)rules->orphanElementAt(i);
663 delete rule;
664 }
665 delete rules;
666 return NULL;
667 }
668 return rules;
669 }
670
671 TimeZoneRule*
findRuleInFinal(UDate date,UBool local,int32_t NonExistingTimeOpt,int32_t DuplicatedTimeOpt) const672 RuleBasedTimeZone::findRuleInFinal(UDate date, UBool local,
673 int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const {
674 if (fFinalRules == NULL) {
675 return NULL;
676 }
677
678 AnnualTimeZoneRule* fr0 = (AnnualTimeZoneRule*)fFinalRules->elementAt(0);
679 AnnualTimeZoneRule* fr1 = (AnnualTimeZoneRule*)fFinalRules->elementAt(1);
680 if (fr0 == NULL || fr1 == NULL) {
681 return NULL;
682 }
683
684 UDate start0, start1;
685 UDate base;
686 int32_t localDelta;
687
688 base = date;
689 if (local) {
690 localDelta = getLocalDelta(fr1->getRawOffset(), fr1->getDSTSavings(),
691 fr0->getRawOffset(), fr0->getDSTSavings(),
692 NonExistingTimeOpt, DuplicatedTimeOpt);
693 base -= localDelta;
694 }
695 UBool avail0 = fr0->getPreviousStart(base, fr1->getRawOffset(), fr1->getDSTSavings(), TRUE, start0);
696
697 base = date;
698 if (local) {
699 localDelta = getLocalDelta(fr0->getRawOffset(), fr0->getDSTSavings(),
700 fr1->getRawOffset(), fr1->getDSTSavings(),
701 NonExistingTimeOpt, DuplicatedTimeOpt);
702 base -= localDelta;
703 }
704 UBool avail1 = fr1->getPreviousStart(base, fr0->getRawOffset(), fr0->getDSTSavings(), TRUE, start1);
705
706 if (!avail0 || !avail1) {
707 if (avail0) {
708 return fr0;
709 } else if (avail1) {
710 return fr1;
711 }
712 // Both rules take effect after the given time
713 return NULL;
714 }
715
716 return (start0 > start1) ? fr0 : fr1;
717 }
718
719 UBool
findNext(UDate base,UBool inclusive,UDate & transitionTime,TimeZoneRule * & fromRule,TimeZoneRule * & toRule) const720 RuleBasedTimeZone::findNext(UDate base, UBool inclusive, UDate& transitionTime,
721 TimeZoneRule*& fromRule, TimeZoneRule*& toRule) const {
722 if (fHistoricTransitions == NULL) {
723 return FALSE;
724 }
725 UBool isFinal = FALSE;
726 UBool found = FALSE;
727 Transition result;
728 Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0);
729 UDate tt = tzt->time;
730 if (tt > base || (inclusive && tt == base)) {
731 result = *tzt;
732 found = TRUE;
733 } else {
734 int32_t idx = fHistoricTransitions->size() - 1;
735 tzt = (Transition*)fHistoricTransitions->elementAt(idx);
736 tt = tzt->time;
737 if (inclusive && tt == base) {
738 result = *tzt;
739 found = TRUE;
740 } else if (tt <= base) {
741 if (fFinalRules != NULL) {
742 // Find a transion time with finalRules
743 TimeZoneRule *r0 = (TimeZoneRule*)fFinalRules->elementAt(0);
744 TimeZoneRule *r1 = (TimeZoneRule*)fFinalRules->elementAt(1);
745 UDate start0, start1;
746 UBool avail0 = r0->getNextStart(base, r1->getRawOffset(), r1->getDSTSavings(), inclusive, start0);
747 UBool avail1 = r1->getNextStart(base, r0->getRawOffset(), r0->getDSTSavings(), inclusive, start1);
748 // avail0/avail1 should be always TRUE
749 if (!avail0 && !avail1) {
750 return FALSE;
751 }
752 if (!avail1 || start0 < start1) {
753 result.time = start0;
754 result.from = r1;
755 result.to = r0;
756 } else {
757 result.time = start1;
758 result.from = r0;
759 result.to = r1;
760 }
761 isFinal = TRUE;
762 found = TRUE;
763 }
764 } else {
765 // Find a transition within the historic transitions
766 idx--;
767 Transition *prev = tzt;
768 while (idx > 0) {
769 tzt = (Transition*)fHistoricTransitions->elementAt(idx);
770 tt = tzt->time;
771 if (tt < base || (!inclusive && tt == base)) {
772 break;
773 }
774 idx--;
775 prev = tzt;
776 }
777 result.time = prev->time;
778 result.from = prev->from;
779 result.to = prev->to;
780 found = TRUE;
781 }
782 }
783 if (found) {
784 // For now, this implementation ignore transitions with only zone name changes.
785 if (result.from->getRawOffset() == result.to->getRawOffset()
786 && result.from->getDSTSavings() == result.to->getDSTSavings()) {
787 if (isFinal) {
788 return FALSE;
789 } else {
790 // No offset changes. Try next one if not final
791 return findNext(result.time, FALSE /* always exclusive */,
792 transitionTime, fromRule, toRule);
793 }
794 }
795 transitionTime = result.time;
796 fromRule = result.from;
797 toRule = result.to;
798 return TRUE;
799 }
800 return FALSE;
801 }
802
803 UBool
findPrev(UDate base,UBool inclusive,UDate & transitionTime,TimeZoneRule * & fromRule,TimeZoneRule * & toRule) const804 RuleBasedTimeZone::findPrev(UDate base, UBool inclusive, UDate& transitionTime,
805 TimeZoneRule*& fromRule, TimeZoneRule*& toRule) const {
806 if (fHistoricTransitions == NULL) {
807 return FALSE;
808 }
809 UBool found = FALSE;
810 Transition result;
811 Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0);
812 UDate tt = tzt->time;
813 if (inclusive && tt == base) {
814 result = *tzt;
815 found = TRUE;
816 } else if (tt < base) {
817 int32_t idx = fHistoricTransitions->size() - 1;
818 tzt = (Transition*)fHistoricTransitions->elementAt(idx);
819 tt = tzt->time;
820 if (inclusive && tt == base) {
821 result = *tzt;
822 found = TRUE;
823 } else if (tt < base) {
824 if (fFinalRules != NULL) {
825 // Find a transion time with finalRules
826 TimeZoneRule *r0 = (TimeZoneRule*)fFinalRules->elementAt(0);
827 TimeZoneRule *r1 = (TimeZoneRule*)fFinalRules->elementAt(1);
828 UDate start0, start1;
829 UBool avail0 = r0->getPreviousStart(base, r1->getRawOffset(), r1->getDSTSavings(), inclusive, start0);
830 UBool avail1 = r1->getPreviousStart(base, r0->getRawOffset(), r0->getDSTSavings(), inclusive, start1);
831 // avail0/avail1 should be always TRUE
832 if (!avail0 && !avail1) {
833 return FALSE;
834 }
835 if (!avail1 || start0 > start1) {
836 result.time = start0;
837 result.from = r1;
838 result.to = r0;
839 } else {
840 result.time = start1;
841 result.from = r0;
842 result.to = r1;
843 }
844 } else {
845 result = *tzt;
846 }
847 found = TRUE;
848 } else {
849 // Find a transition within the historic transitions
850 idx--;
851 while (idx >= 0) {
852 tzt = (Transition*)fHistoricTransitions->elementAt(idx);
853 tt = tzt->time;
854 if (tt < base || (inclusive && tt == base)) {
855 break;
856 }
857 idx--;
858 }
859 result = *tzt;
860 found = TRUE;
861 }
862 }
863 if (found) {
864 // For now, this implementation ignore transitions with only zone name changes.
865 if (result.from->getRawOffset() == result.to->getRawOffset()
866 && result.from->getDSTSavings() == result.to->getDSTSavings()) {
867 // No offset changes. Try next one if not final
868 return findPrev(result.time, FALSE /* always exclusive */,
869 transitionTime, fromRule, toRule);
870 }
871 transitionTime = result.time;
872 fromRule = result.from;
873 toRule = result.to;
874 return TRUE;
875 }
876 return FALSE;
877 }
878
879 UDate
getTransitionTime(Transition * transition,UBool local,int32_t NonExistingTimeOpt,int32_t DuplicatedTimeOpt) const880 RuleBasedTimeZone::getTransitionTime(Transition* transition, UBool local,
881 int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const {
882 UDate time = transition->time;
883 if (local) {
884 time += getLocalDelta(transition->from->getRawOffset(), transition->from->getDSTSavings(),
885 transition->to->getRawOffset(), transition->to->getDSTSavings(),
886 NonExistingTimeOpt, DuplicatedTimeOpt);
887 }
888 return time;
889 }
890
891 int32_t
getLocalDelta(int32_t rawBefore,int32_t dstBefore,int32_t rawAfter,int32_t dstAfter,int32_t NonExistingTimeOpt,int32_t DuplicatedTimeOpt) const892 RuleBasedTimeZone::getLocalDelta(int32_t rawBefore, int32_t dstBefore, int32_t rawAfter, int32_t dstAfter,
893 int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const {
894 int32_t delta = 0;
895
896 int32_t offsetBefore = rawBefore + dstBefore;
897 int32_t offsetAfter = rawAfter + dstAfter;
898
899 UBool dstToStd = (dstBefore != 0) && (dstAfter == 0);
900 UBool stdToDst = (dstBefore == 0) && (dstAfter != 0);
901
902 if (offsetAfter - offsetBefore >= 0) {
903 // Positive transition, which makes a non-existing local time range
904 if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd)
905 || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
906 delta = offsetBefore;
907 } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst)
908 || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
909 delta = offsetAfter;
910 } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) {
911 delta = offsetBefore;
912 } else {
913 // Interprets the time with rule before the transition,
914 // default for non-existing time range
915 delta = offsetAfter;
916 }
917 } else {
918 // Negative transition, which makes a duplicated local time range
919 if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd)
920 || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
921 delta = offsetAfter;
922 } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst)
923 || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
924 delta = offsetBefore;
925 } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) {
926 delta = offsetBefore;
927 } else {
928 // Interprets the time with rule after the transition,
929 // default for duplicated local time range
930 delta = offsetAfter;
931 }
932 }
933 return delta;
934 }
935
936 U_NAMESPACE_END
937
938 #endif /* #if !UCONFIG_NO_FORMATTING */
939
940 //eof
941
942