2 *******************************************************************************
3 * Copyright (C) 2007-2010, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
8 #include "unicode/utypes.h"
10 #if !UCONFIG_NO_FORMATTING
12 #include "unicode/basictz.h"
19 #define MILLIS_PER_YEAR (365*24*60*60*1000.0)
21 BasicTimeZone::BasicTimeZone()
25 BasicTimeZone::BasicTimeZone(const UnicodeString &id)
29 BasicTimeZone::BasicTimeZone(const BasicTimeZone& source)
33 BasicTimeZone::~BasicTimeZone() {
37 BasicTimeZone::hasEquivalentTransitions(/*const*/ BasicTimeZone& tz, UDate start, UDate end,
38 UBool ignoreDstAmount, UErrorCode& status) /*const*/ {
39 if (U_FAILURE(status)) {
42 if (hasSameRules(tz)) {
45 // Check the offsets at the start time
46 int32_t raw1, raw2, dst1, dst2;
47 getOffset(start, FALSE, raw1, dst1, status);
48 if (U_FAILURE(status)) {
51 tz.getOffset(start, FALSE, raw2, dst2, status);
52 if (U_FAILURE(status)) {
55 if (ignoreDstAmount) {
56 if ((raw1 + dst1 != raw2 + dst2)
57 || (dst1 != 0 && dst2 == 0)
58 || (dst1 == 0 && dst2 != 0)) {
62 if (raw1 != raw2 || dst1 != dst2) {
66 // Check transitions in the range
68 TimeZoneTransition tr1, tr2;
70 UBool avail1 = getNextTransition(time, FALSE, tr1);
71 UBool avail2 = tz.getNextTransition(time, FALSE, tr2);
73 if (ignoreDstAmount) {
74 // Skip a transition which only differ the amount of DST savings
77 && tr1.getTime() <= end
78 && (tr1.getFrom()->getRawOffset() + tr1.getFrom()->getDSTSavings()
79 == tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings())
80 && (tr1.getFrom()->getDSTSavings() != 0 && tr1.getTo()->getDSTSavings() != 0)) {
81 getNextTransition(tr1.getTime(), FALSE, tr1);
88 && tr2.getTime() <= end
89 && (tr2.getFrom()->getRawOffset() + tr2.getFrom()->getDSTSavings()
90 == tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings())
91 && (tr2.getFrom()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() != 0)) {
92 tz.getNextTransition(tr2.getTime(), FALSE, tr2);
99 UBool inRange1 = (avail1 && tr1.getTime() <= end);
100 UBool inRange2 = (avail2 && tr2.getTime() <= end);
101 if (!inRange1 && !inRange2) {
102 // No more transition in the range
105 if (!inRange1 || !inRange2) {
108 if (tr1.getTime() != tr2.getTime()) {
111 if (ignoreDstAmount) {
112 if (tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings()
113 != tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings()
114 || (tr1.getTo()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() == 0)
115 || (tr1.getTo()->getDSTSavings() == 0 && tr2.getTo()->getDSTSavings() != 0)) {
119 if (tr1.getTo()->getRawOffset() != tr2.getTo()->getRawOffset() ||
120 tr1.getTo()->getDSTSavings() != tr2.getTo()->getDSTSavings()) {
124 time = tr1.getTime();
130 BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial,
131 AnnualTimeZoneRule*& std, AnnualTimeZoneRule*& dst, UErrorCode& status) /*const*/ {
135 if (U_FAILURE(status)) {
138 int32_t initialRaw, initialDst;
139 UnicodeString initialName;
141 AnnualTimeZoneRule *ar1 = NULL;
142 AnnualTimeZoneRule *ar2 = NULL;
146 TimeZoneTransition tr;
147 // Get the next transition
148 avail = getNextTransition(date, FALSE, tr);
150 tr.getFrom()->getName(initialName);
151 initialRaw = tr.getFrom()->getRawOffset();
152 initialDst = tr.getFrom()->getDSTSavings();
154 // Check if the next transition is either DST->STD or STD->DST and
155 // within roughly 1 year from the specified date
156 UDate nextTransitionTime = tr.getTime();
157 if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
158 || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0))
159 && (date + MILLIS_PER_YEAR > nextTransitionTime)) {
161 int32_t year, month, dom, dow, doy, mid;
164 // Get local wall time for the next transition time
165 Grego::timeToFields(nextTransitionTime + initialRaw + initialDst,
166 year, month, dom, dow, doy, mid);
167 int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
169 DateTimeRule *dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
170 tr.getTo()->getName(name);
172 // Note: SimpleTimeZone does not support raw offset change.
173 // So we always use raw offset of the given time for the rule,
174 // even raw offset is changed. This will result that the result
175 // zone to return wrong offset after the transition.
176 // When we encounter such case, we do not inspect next next
177 // transition for another rule.
178 ar1 = new AnnualTimeZoneRule(name, initialRaw, tr.getTo()->getDSTSavings(),
179 dtr, year, AnnualTimeZoneRule::MAX_YEAR);
181 if (tr.getTo()->getRawOffset() == initialRaw) {
182 // Get the next next transition
183 avail = getNextTransition(nextTransitionTime, FALSE, tr);
185 // Check if the next next transition is either DST->STD or STD->DST
186 // and within roughly 1 year from the next transition
187 if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
188 || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0))
189 && nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) {
191 // Get local wall time for the next transition time
192 Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
193 year, month, dom, dow, doy, mid);
194 weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
195 // Generate another DOW rule
196 dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
197 tr.getTo()->getName(name);
198 ar2 = new AnnualTimeZoneRule(name, tr.getTo()->getRawOffset(), tr.getTo()->getDSTSavings(),
199 dtr, year - 1, AnnualTimeZoneRule::MAX_YEAR);
201 // Make sure this rule can be applied to the specified date
202 avail = ar2->getPreviousStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), TRUE, d);
203 if (!avail || d > date
204 || initialRaw != tr.getTo()->getRawOffset()
205 || initialDst != tr.getTo()->getDSTSavings()) {
206 // We cannot use this rule as the second transition rule
214 // Try previous transition
215 avail = getPreviousTransition(date, TRUE, tr);
217 // Check if the previous transition is either DST->STD or STD->DST.
218 // The actual transition time does not matter here.
219 if ((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
220 || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) {
222 // Generate another DOW rule
223 Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
224 year, month, dom, dow, doy, mid);
225 weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
226 dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
227 tr.getTo()->getName(name);
229 // second rule raw/dst offsets should match raw/dst offsets
231 ar2 = new AnnualTimeZoneRule(name, initialRaw, initialDst,
232 dtr, ar1->getStartYear() - 1, AnnualTimeZoneRule::MAX_YEAR);
234 // Check if this rule start after the first rule after the specified date
235 avail = ar2->getNextStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), FALSE, d);
236 if (!avail || d <= nextTransitionTime) {
237 // We cannot use this rule as the second transition rule
245 // Cannot find a good pair of AnnualTimeZoneRule
249 // The initial rule should represent the rule before the previous transition
250 ar1->getName(initialName);
251 initialRaw = ar1->getRawOffset();
252 initialDst = ar1->getDSTSavings();
257 // Try the previous one
258 avail = getPreviousTransition(date, TRUE, tr);
260 tr.getTo()->getName(initialName);
261 initialRaw = tr.getTo()->getRawOffset();
262 initialDst = tr.getTo()->getDSTSavings();
264 // No transitions in the past. Just use the current offsets
265 getOffset(date, FALSE, initialRaw, initialDst, status);
266 if (U_FAILURE(status)) {
271 // Set the initial rule
272 initial = new InitialTimeZoneRule(initialName, initialRaw, initialDst);
274 // Set the standard and daylight saving rules
275 if (ar1 != NULL && ar2 != NULL) {
276 if (ar1->getDSTSavings() != 0) {
287 BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial,
288 UVector*& transitionRules, UErrorCode& status) /*const*/ {
289 if (U_FAILURE(status)) {
293 const InitialTimeZoneRule *orgini;
294 const TimeZoneRule **orgtrs = NULL;
295 TimeZoneTransition tzt;
297 UVector *orgRules = NULL;
299 TimeZoneRule *r = NULL;
301 InitialTimeZoneRule *res_initial = NULL;
302 UVector *filteredRules = NULL;
306 UDate *newTimes = NULL;
308 UBool bFinalStd = FALSE, bFinalDst = FALSE;
310 // Original transition rules
311 ruleCount = countTransitionRules(status);
312 if (U_FAILURE(status)) {
315 orgRules = new UVector(ruleCount, status);
316 if (U_FAILURE(status)) {
319 orgtrs = (const TimeZoneRule**)uprv_malloc(sizeof(TimeZoneRule*)*ruleCount);
320 if (orgtrs == NULL) {
321 status = U_MEMORY_ALLOCATION_ERROR;
324 getTimeZoneRules(orgini, orgtrs, ruleCount, status);
325 if (U_FAILURE(status)) {
328 for (i = 0; i < ruleCount; i++) {
329 orgRules->addElement(orgtrs[i]->clone(), status);
330 if (U_FAILURE(status)) {
337 avail = getPreviousTransition(start, TRUE, tzt);
339 // No need to filter out rules only applicable to time before the start
340 initial = orgini->clone();
341 transitionRules = orgRules;
345 done = (UBool*)uprv_malloc(sizeof(UBool)*ruleCount);
347 status = U_MEMORY_ALLOCATION_ERROR;
350 filteredRules = new UVector(status);
351 if (U_FAILURE(status)) {
355 // Create initial rule
356 tzt.getTo()->getName(name);
357 res_initial = new InitialTimeZoneRule(name, tzt.getTo()->getRawOffset(),
358 tzt.getTo()->getDSTSavings());
360 // Mark rules which does not need to be processed
361 for (i = 0; i < ruleCount; i++) {
362 r = (TimeZoneRule*)orgRules->elementAt(i);
363 avail = r->getNextStart(start, res_initial->getRawOffset(), res_initial->getDSTSavings(), FALSE, time);
368 while (!bFinalStd || !bFinalDst) {
369 avail = getNextTransition(time, FALSE, tzt);
373 UDate updatedTime = tzt.getTime();
374 if (updatedTime == time) {
375 // Can get here if rules for start & end of daylight time have exactly
377 // TODO: fix getNextTransition() to prevent it?
378 status = U_INVALID_STATE_ERROR;
383 const TimeZoneRule *toRule = tzt.getTo();
384 for (i = 0; i < ruleCount; i++) {
385 r = (TimeZoneRule*)orgRules->elementAt(i);
390 if (i >= ruleCount) {
391 // This case should never happen
392 status = U_INVALID_STATE_ERROR;
398 const TimeArrayTimeZoneRule *tar = dynamic_cast<const TimeArrayTimeZoneRule *>(toRule);
399 const AnnualTimeZoneRule *ar;
401 // Get the previous raw offset and DST savings before the very first start time
402 TimeZoneTransition tzt0;
405 avail = getNextTransition(t, FALSE, tzt0);
409 if (*(tzt0.getTo()) == *tar) {
415 // Check if the entire start times to be added
416 tar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
417 if (firstStart > start) {
418 // Just add the rule as is
419 filteredRules->addElement(tar->clone(), status);
420 if (U_FAILURE(status)) {
424 // Colllect transitions after the start time
426 DateTimeRule::TimeRuleType timeType;
429 startTimes = tar->countStartTimes();
430 timeType = tar->getTimeType();
431 for (idx = 0; idx < startTimes; idx++) {
432 tar->getStartTimeAt(idx, t);
433 if (timeType == DateTimeRule::STANDARD_TIME) {
434 t -= tzt.getFrom()->getRawOffset();
436 if (timeType == DateTimeRule::WALL_TIME) {
437 t -= tzt.getFrom()->getDSTSavings();
443 int32_t asize = startTimes - idx;
445 newTimes = (UDate*)uprv_malloc(sizeof(UDate) * asize);
446 if (newTimes == NULL) {
447 status = U_MEMORY_ALLOCATION_ERROR;
450 for (int32_t newidx = 0; newidx < asize; newidx++) {
451 tar->getStartTimeAt(idx + newidx, newTimes[newidx]);
452 if (U_FAILURE(status)) {
459 TimeArrayTimeZoneRule *newTar = new TimeArrayTimeZoneRule(name,
460 tar->getRawOffset(), tar->getDSTSavings(), newTimes, asize, timeType);
462 filteredRules->addElement(newTar, status);
463 if (U_FAILURE(status)) {
469 } else if ((ar = dynamic_cast<const AnnualTimeZoneRule *>(toRule)) != NULL) {
470 ar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
471 if (firstStart == tzt.getTime()) {
472 // Just add the rule as is
473 filteredRules->addElement(ar->clone(), status);
474 if (U_FAILURE(status)) {
478 // Calculate the transition year
479 int32_t year, month, dom, dow, doy, mid;
480 Grego::timeToFields(tzt.getTime(), year, month, dom, dow, doy, mid);
481 // Re-create the rule
483 AnnualTimeZoneRule *newAr = new AnnualTimeZoneRule(name, ar->getRawOffset(), ar->getDSTSavings(),
484 *(ar->getRule()), year, ar->getEndYear());
485 filteredRules->addElement(newAr, status);
486 if (U_FAILURE(status)) {
490 // check if this is a final rule
491 if (ar->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) {
492 // After bot final standard and dst rules are processed,
493 // exit this while loop.
494 if (ar->getDSTSavings() == 0) {
505 if (orgRules != NULL) {
506 while (!orgRules->isEmpty()) {
507 r = (TimeZoneRule*)orgRules->orphanElementAt(0);
516 initial = res_initial;
517 transitionRules = filteredRules;
521 if (orgtrs != NULL) {
524 if (orgRules != NULL) {
525 while (!orgRules->isEmpty()) {
526 r = (TimeZoneRule*)orgRules->orphanElementAt(0);
532 if (filteredRules != NULL) {
533 while (!filteredRules->isEmpty()) {
534 r = (TimeZoneRule*)filteredRules->orphanElementAt(0);
537 delete filteredRules;
544 transitionRules = NULL;
548 BasicTimeZone::getOffsetFromLocal(UDate /*date*/, int32_t /*nonExistingTimeOpt*/, int32_t /*duplicatedTimeOpt*/,
549 int32_t& /*rawOffset*/, int32_t& /*dstOffset*/, UErrorCode& status) /*const*/ {
550 if (U_FAILURE(status)) {
553 status = U_UNSUPPORTED_ERROR;
558 #endif /* #if !UCONFIG_NO_FORMATTING */