• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 * Copyright (C) 2007-2010, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
6 */
7 
8 #include <typeinfo>  // 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                 } 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 
454 void
setRawOffset(int32_t)455 RuleBasedTimeZone::setRawOffset(int32_t /*offsetMillis*/) {
456     // We don't support this operation at this moment.
457     // Nothing to do!
458 }
459 
460 int32_t
getRawOffset(void) const461 RuleBasedTimeZone::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 
471 UBool
useDaylightTime(void) const472 RuleBasedTimeZone::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 
493 UBool
inDaylightTime(UDate date,UErrorCode & status) const494 RuleBasedTimeZone::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 
506 UBool
hasSameRules(const TimeZone & other) const507 RuleBasedTimeZone::hasSameRules(const TimeZone& other) const {
508     if (this == &other) {
509         return TRUE;
510     }
511     if (typeid(*this) != typeid(other)) {
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 
525 UBool
getNextTransition(UDate base,UBool inclusive,TimeZoneTransition & result)526 RuleBasedTimeZone::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 
544 UBool
getPreviousTransition(UDate base,UBool inclusive,TimeZoneTransition & result)545 RuleBasedTimeZone::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 
563 int32_t
countTransitionRules(UErrorCode &)564 RuleBasedTimeZone::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 
575 void
getTimeZoneRules(const InitialTimeZoneRule * & initial,const TimeZoneRule * trsrules[],int32_t & trscount,UErrorCode & status)576 RuleBasedTimeZone::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 
607 void
deleteRules(void)608 RuleBasedTimeZone::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 
627 void
deleteTransitions(void)628 RuleBasedTimeZone::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 
639 UVector*
copyRules(UVector * source)640 RuleBasedTimeZone::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 
669 TimeZoneRule*
findRuleInFinal(UDate date,UBool local,int32_t NonExistingTimeOpt,int32_t DuplicatedTimeOpt) const670 RuleBasedTimeZone::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 
712 UBool
findNext(UDate base,UBool inclusive,UDate & transitionTime,TimeZoneRule * & fromRule,TimeZoneRule * & toRule) const713 RuleBasedTimeZone::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 
796 UBool
findPrev(UDate base,UBool inclusive,UDate & transitionTime,TimeZoneRule * & fromRule,TimeZoneRule * & toRule) const797 RuleBasedTimeZone::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 
872 UDate
getTransitionTime(Transition * transition,UBool local,int32_t NonExistingTimeOpt,int32_t DuplicatedTimeOpt) const873 RuleBasedTimeZone::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 
884 int32_t
getLocalDelta(int32_t rawBefore,int32_t dstBefore,int32_t rawAfter,int32_t dstAfter,int32_t NonExistingTimeOpt,int32_t DuplicatedTimeOpt) const885 RuleBasedTimeZone::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 
929 U_NAMESPACE_END
930 
931 #endif /* #if !UCONFIG_NO_FORMATTING */
932 
933 //eof
934 
935