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