• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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