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