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