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