1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2007-2013, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
10 #include "unicode/utypes.h"
12 #if !UCONFIG_NO_FORMATTING
14 #include "unicode/basictz.h"
21 #define MILLIS_PER_YEAR (365*24*60*60*1000.0)
23 BasicTimeZone::BasicTimeZone()
27 BasicTimeZone::BasicTimeZone(const UnicodeString &id)
31 BasicTimeZone::BasicTimeZone(const BasicTimeZone& source)
35 BasicTimeZone::~BasicTimeZone() {
39 BasicTimeZone::hasEquivalentTransitions(const BasicTimeZone& tz, UDate start, UDate end,
40 UBool ignoreDstAmount, UErrorCode& status) const {
41 if (U_FAILURE(status)) {
44 if (hasSameRules(tz)) {
47 // Check the offsets at the start time
48 int32_t raw1, raw2, dst1, dst2;
49 getOffset(start, FALSE, raw1, dst1, status);
50 if (U_FAILURE(status)) {
53 tz.getOffset(start, FALSE, raw2, dst2, status);
54 if (U_FAILURE(status)) {
57 if (ignoreDstAmount) {
58 if ((raw1 + dst1 != raw2 + dst2)
59 || (dst1 != 0 && dst2 == 0)
60 || (dst1 == 0 && dst2 != 0)) {
64 if (raw1 != raw2 || dst1 != dst2) {
68 // Check transitions in the range
70 TimeZoneTransition tr1, tr2;
72 UBool avail1 = getNextTransition(time, FALSE, tr1);
73 UBool avail2 = tz.getNextTransition(time, FALSE, tr2);
75 if (ignoreDstAmount) {
76 // Skip a transition which only differ the amount of DST savings
79 && tr1.getTime() <= end
80 && (tr1.getFrom()->getRawOffset() + tr1.getFrom()->getDSTSavings()
81 == tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings())
82 && (tr1.getFrom()->getDSTSavings() != 0 && tr1.getTo()->getDSTSavings() != 0)) {
83 getNextTransition(tr1.getTime(), FALSE, tr1);
90 && tr2.getTime() <= end
91 && (tr2.getFrom()->getRawOffset() + tr2.getFrom()->getDSTSavings()
92 == tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings())
93 && (tr2.getFrom()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() != 0)) {
94 tz.getNextTransition(tr2.getTime(), FALSE, tr2);
101 UBool inRange1 = (avail1 && tr1.getTime() <= end);
102 UBool inRange2 = (avail2 && tr2.getTime() <= end);
103 if (!inRange1 && !inRange2) {
104 // No more transition in the range
107 if (!inRange1 || !inRange2) {
110 if (tr1.getTime() != tr2.getTime()) {
113 if (ignoreDstAmount) {
114 if (tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings()
115 != tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings()
116 || (tr1.getTo()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() == 0)
117 || (tr1.getTo()->getDSTSavings() == 0 && tr2.getTo()->getDSTSavings() != 0)) {
121 if (tr1.getTo()->getRawOffset() != tr2.getTo()->getRawOffset() ||
122 tr1.getTo()->getDSTSavings() != tr2.getTo()->getDSTSavings()) {
126 time = tr1.getTime();
132 BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial,
133 AnnualTimeZoneRule*& std, AnnualTimeZoneRule*& dst, UErrorCode& status) const {
137 if (U_FAILURE(status)) {
140 int32_t initialRaw, initialDst;
141 UnicodeString initialName;
143 AnnualTimeZoneRule *ar1 = NULL;
144 AnnualTimeZoneRule *ar2 = NULL;
148 TimeZoneTransition tr;
149 // Get the next transition
150 avail = getNextTransition(date, FALSE, tr);
152 tr.getFrom()->getName(initialName);
153 initialRaw = tr.getFrom()->getRawOffset();
154 initialDst = tr.getFrom()->getDSTSavings();
156 // Check if the next transition is either DST->STD or STD->DST and
157 // within roughly 1 year from the specified date
158 UDate nextTransitionTime = tr.getTime();
159 if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
160 || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0))
161 && (date + MILLIS_PER_YEAR > nextTransitionTime)) {
163 int32_t year, month, dom, dow, doy, mid;
166 // Get local wall time for the next transition time
167 Grego::timeToFields(nextTransitionTime + initialRaw + initialDst,
168 year, month, dom, dow, doy, mid);
169 int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
171 DateTimeRule *dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
172 tr.getTo()->getName(name);
174 // Note: SimpleTimeZone does not support raw offset change.
175 // So we always use raw offset of the given time for the rule,
176 // even raw offset is changed. This will result that the result
177 // zone to return wrong offset after the transition.
178 // When we encounter such case, we do not inspect next next
179 // transition for another rule.
180 ar1 = new AnnualTimeZoneRule(name, initialRaw, tr.getTo()->getDSTSavings(),
181 dtr, year, AnnualTimeZoneRule::MAX_YEAR);
183 if (tr.getTo()->getRawOffset() == initialRaw) {
184 // Get the next next transition
185 avail = getNextTransition(nextTransitionTime, FALSE, tr);
187 // Check if the next next transition is either DST->STD or STD->DST
188 // and within roughly 1 year from the next transition
189 if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
190 || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0))
191 && nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) {
193 // Get local wall time for the next transition time
194 Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
195 year, month, dom, dow, doy, mid);
196 weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
197 // Generate another DOW rule
198 dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
199 tr.getTo()->getName(name);
200 ar2 = new AnnualTimeZoneRule(name, tr.getTo()->getRawOffset(), tr.getTo()->getDSTSavings(),
201 dtr, year - 1, AnnualTimeZoneRule::MAX_YEAR);
203 // Make sure this rule can be applied to the specified date
204 avail = ar2->getPreviousStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), TRUE, d);
205 if (!avail || d > date
206 || initialRaw != tr.getTo()->getRawOffset()
207 || initialDst != tr.getTo()->getDSTSavings()) {
208 // We cannot use this rule as the second transition rule
216 // Try previous transition
217 avail = getPreviousTransition(date, TRUE, tr);
219 // Check if the previous transition is either DST->STD or STD->DST.
220 // The actual transition time does not matter here.
221 if ((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
222 || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) {
224 // Generate another DOW rule
225 Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
226 year, month, dom, dow, doy, mid);
227 weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
228 dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
229 tr.getTo()->getName(name);
231 // second rule raw/dst offsets should match raw/dst offsets
233 ar2 = new AnnualTimeZoneRule(name, initialRaw, initialDst,
234 dtr, ar1->getStartYear() - 1, AnnualTimeZoneRule::MAX_YEAR);
236 // Check if this rule start after the first rule after the specified date
237 avail = ar2->getNextStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), FALSE, d);
238 if (!avail || d <= nextTransitionTime) {
239 // We cannot use this rule as the second transition rule
247 // Cannot find a good pair of AnnualTimeZoneRule
251 // The initial rule should represent the rule before the previous transition
252 ar1->getName(initialName);
253 initialRaw = ar1->getRawOffset();
254 initialDst = ar1->getDSTSavings();
259 // Try the previous one
260 avail = getPreviousTransition(date, TRUE, tr);
262 tr.getTo()->getName(initialName);
263 initialRaw = tr.getTo()->getRawOffset();
264 initialDst = tr.getTo()->getDSTSavings();
266 // No transitions in the past. Just use the current offsets
267 getOffset(date, FALSE, initialRaw, initialDst, status);
268 if (U_FAILURE(status)) {
273 // Set the initial rule
274 initial = new InitialTimeZoneRule(initialName, initialRaw, initialDst);
276 // Set the standard and daylight saving rules
277 if (ar1 != NULL && ar2 != NULL) {
278 if (ar1->getDSTSavings() != 0) {
289 BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial,
290 UVector*& transitionRules, UErrorCode& status) const {
291 if (U_FAILURE(status)) {
295 const InitialTimeZoneRule *orgini;
296 const TimeZoneRule **orgtrs = NULL;
297 TimeZoneTransition tzt;
299 UVector *orgRules = NULL;
301 TimeZoneRule *r = NULL;
303 InitialTimeZoneRule *res_initial = NULL;
304 UVector *filteredRules = NULL;
308 UDate *newTimes = NULL;
310 UBool bFinalStd = FALSE, bFinalDst = FALSE;
312 // Original transition rules
313 ruleCount = countTransitionRules(status);
314 if (U_FAILURE(status)) {
317 orgRules = new UVector(ruleCount, status);
318 if (U_FAILURE(status)) {
321 orgtrs = (const TimeZoneRule**)uprv_malloc(sizeof(TimeZoneRule*)*ruleCount);
322 if (orgtrs == NULL) {
323 status = U_MEMORY_ALLOCATION_ERROR;
326 getTimeZoneRules(orgini, orgtrs, ruleCount, status);
327 if (U_FAILURE(status)) {
330 for (i = 0; i < ruleCount; i++) {
331 orgRules->addElement(orgtrs[i]->clone(), status);
332 if (U_FAILURE(status)) {
339 avail = getPreviousTransition(start, TRUE, tzt);
341 // No need to filter out rules only applicable to time before the start
342 initial = orgini->clone();
343 transitionRules = orgRules;
347 done = (UBool*)uprv_malloc(sizeof(UBool)*ruleCount);
349 status = U_MEMORY_ALLOCATION_ERROR;
352 filteredRules = new UVector(status);
353 if (U_FAILURE(status)) {
357 // Create initial rule
358 tzt.getTo()->getName(name);
359 res_initial = new InitialTimeZoneRule(name, tzt.getTo()->getRawOffset(),
360 tzt.getTo()->getDSTSavings());
362 // Mark rules which does not need to be processed
363 for (i = 0; i < ruleCount; i++) {
364 r = (TimeZoneRule*)orgRules->elementAt(i);
365 avail = r->getNextStart(start, res_initial->getRawOffset(), res_initial->getDSTSavings(), FALSE, time);
370 while (!bFinalStd || !bFinalDst) {
371 avail = getNextTransition(time, FALSE, tzt);
375 UDate updatedTime = tzt.getTime();
376 if (updatedTime == time) {
377 // Can get here if rules for start & end of daylight time have exactly
379 // TODO: fix getNextTransition() to prevent it?
380 status = U_INVALID_STATE_ERROR;
385 const TimeZoneRule *toRule = tzt.getTo();
386 for (i = 0; i < ruleCount; i++) {
387 r = (TimeZoneRule*)orgRules->elementAt(i);
392 if (i >= ruleCount) {
393 // This case should never happen
394 status = U_INVALID_STATE_ERROR;
400 const TimeArrayTimeZoneRule *tar = dynamic_cast<const TimeArrayTimeZoneRule *>(toRule);
401 const AnnualTimeZoneRule *ar;
403 // Get the previous raw offset and DST savings before the very first start time
404 TimeZoneTransition tzt0;
407 avail = getNextTransition(t, FALSE, tzt0);
411 if (*(tzt0.getTo()) == *tar) {
417 // Check if the entire start times to be added
418 tar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
419 if (firstStart > start) {
420 // Just add the rule as is
421 filteredRules->addElement(tar->clone(), status);
422 if (U_FAILURE(status)) {
426 // Colllect transitions after the start time
428 DateTimeRule::TimeRuleType timeType;
431 startTimes = tar->countStartTimes();
432 timeType = tar->getTimeType();
433 for (idx = 0; idx < startTimes; idx++) {
434 tar->getStartTimeAt(idx, t);
435 if (timeType == DateTimeRule::STANDARD_TIME) {
436 t -= tzt.getFrom()->getRawOffset();
438 if (timeType == DateTimeRule::WALL_TIME) {
439 t -= tzt.getFrom()->getDSTSavings();
445 int32_t asize = startTimes - idx;
447 newTimes = (UDate*)uprv_malloc(sizeof(UDate) * asize);
448 if (newTimes == NULL) {
449 status = U_MEMORY_ALLOCATION_ERROR;
452 for (int32_t newidx = 0; newidx < asize; newidx++) {
453 tar->getStartTimeAt(idx + newidx, newTimes[newidx]);
454 if (U_FAILURE(status)) {
461 TimeArrayTimeZoneRule *newTar = new TimeArrayTimeZoneRule(name,
462 tar->getRawOffset(), tar->getDSTSavings(), newTimes, asize, timeType);
464 filteredRules->addElement(newTar, status);
465 if (U_FAILURE(status)) {
471 } else if ((ar = dynamic_cast<const AnnualTimeZoneRule *>(toRule)) != NULL) {
472 ar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
473 if (firstStart == tzt.getTime()) {
474 // Just add the rule as is
475 filteredRules->addElement(ar->clone(), status);
476 if (U_FAILURE(status)) {
480 // Calculate the transition year
481 int32_t year, month, dom, dow, doy, mid;
482 Grego::timeToFields(tzt.getTime(), year, month, dom, dow, doy, mid);
483 // Re-create the rule
485 AnnualTimeZoneRule *newAr = new AnnualTimeZoneRule(name, ar->getRawOffset(), ar->getDSTSavings(),
486 *(ar->getRule()), year, ar->getEndYear());
487 filteredRules->addElement(newAr, status);
488 if (U_FAILURE(status)) {
492 // check if this is a final rule
493 if (ar->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) {
494 // After bot final standard and dst rules are processed,
495 // exit this while loop.
496 if (ar->getDSTSavings() == 0) {
507 if (orgRules != NULL) {
508 while (!orgRules->isEmpty()) {
509 r = (TimeZoneRule*)orgRules->orphanElementAt(0);
518 initial = res_initial;
519 transitionRules = filteredRules;
523 if (orgtrs != NULL) {
526 if (orgRules != NULL) {
527 while (!orgRules->isEmpty()) {
528 r = (TimeZoneRule*)orgRules->orphanElementAt(0);
534 if (filteredRules != NULL) {
535 while (!filteredRules->isEmpty()) {
536 r = (TimeZoneRule*)filteredRules->orphanElementAt(0);
539 delete filteredRules;
546 transitionRules = NULL;
550 BasicTimeZone::getOffsetFromLocal(UDate /*date*/, int32_t /*nonExistingTimeOpt*/, int32_t /*duplicatedTimeOpt*/,
551 int32_t& /*rawOffset*/, int32_t& /*dstOffset*/, UErrorCode& status) const {
552 if (U_FAILURE(status)) {
555 status = U_UNSUPPORTED_ERROR;
560 #endif /* #if !UCONFIG_NO_FORMATTING */