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 "unicode/utypes.h"
11 :
12 : #if !UCONFIG_NO_FORMATTING
13 :
14 : #include "unicode/basictz.h"
15 : #include "gregoimp.h"
16 : #include "uvector.h"
17 : #include "cmemory.h"
18 :
19 : U_NAMESPACE_BEGIN
20 :
21 : #define MILLIS_PER_YEAR (365*24*60*60*1000.0)
22 :
23 0 : BasicTimeZone::BasicTimeZone()
24 0 : : TimeZone() {
25 0 : }
26 :
27 0 : BasicTimeZone::BasicTimeZone(const UnicodeString &id)
28 0 : : TimeZone(id) {
29 0 : }
30 :
31 0 : BasicTimeZone::BasicTimeZone(const BasicTimeZone& source)
32 0 : : TimeZone(source) {
33 0 : }
34 :
35 0 : BasicTimeZone::~BasicTimeZone() {
36 0 : }
37 :
38 : UBool
39 0 : BasicTimeZone::hasEquivalentTransitions(const BasicTimeZone& tz, UDate start, UDate end,
40 : UBool ignoreDstAmount, UErrorCode& status) const {
41 0 : if (U_FAILURE(status)) {
42 0 : return FALSE;
43 : }
44 0 : if (hasSameRules(tz)) {
45 0 : return TRUE;
46 : }
47 : // Check the offsets at the start time
48 : int32_t raw1, raw2, dst1, dst2;
49 0 : getOffset(start, FALSE, raw1, dst1, status);
50 0 : if (U_FAILURE(status)) {
51 0 : return FALSE;
52 : }
53 0 : tz.getOffset(start, FALSE, raw2, dst2, status);
54 0 : if (U_FAILURE(status)) {
55 0 : return FALSE;
56 : }
57 0 : if (ignoreDstAmount) {
58 0 : if ((raw1 + dst1 != raw2 + dst2)
59 0 : || (dst1 != 0 && dst2 == 0)
60 0 : || (dst1 == 0 && dst2 != 0)) {
61 0 : return FALSE;
62 : }
63 : } else {
64 0 : if (raw1 != raw2 || dst1 != dst2) {
65 0 : return FALSE;
66 : }
67 : }
68 : // Check transitions in the range
69 0 : UDate time = start;
70 0 : TimeZoneTransition tr1, tr2;
71 : while (TRUE) {
72 0 : UBool avail1 = getNextTransition(time, FALSE, tr1);
73 0 : UBool avail2 = tz.getNextTransition(time, FALSE, tr2);
74 :
75 0 : if (ignoreDstAmount) {
76 : // Skip a transition which only differ the amount of DST savings
77 : while (TRUE) {
78 0 : if (avail1
79 0 : && tr1.getTime() <= end
80 0 : && (tr1.getFrom()->getRawOffset() + tr1.getFrom()->getDSTSavings()
81 0 : == tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings())
82 0 : && (tr1.getFrom()->getDSTSavings() != 0 && tr1.getTo()->getDSTSavings() != 0)) {
83 0 : getNextTransition(tr1.getTime(), FALSE, tr1);
84 : } else {
85 0 : break;
86 : }
87 : }
88 : while (TRUE) {
89 0 : if (avail2
90 0 : && tr2.getTime() <= end
91 0 : && (tr2.getFrom()->getRawOffset() + tr2.getFrom()->getDSTSavings()
92 0 : == tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings())
93 0 : && (tr2.getFrom()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() != 0)) {
94 0 : tz.getNextTransition(tr2.getTime(), FALSE, tr2);
95 : } else {
96 0 : break;
97 : }
98 : }
99 : }
100 :
101 0 : UBool inRange1 = (avail1 && tr1.getTime() <= end);
102 0 : UBool inRange2 = (avail2 && tr2.getTime() <= end);
103 0 : if (!inRange1 && !inRange2) {
104 : // No more transition in the range
105 0 : break;
106 : }
107 0 : if (!inRange1 || !inRange2) {
108 0 : return FALSE;
109 : }
110 0 : if (tr1.getTime() != tr2.getTime()) {
111 0 : return FALSE;
112 : }
113 0 : if (ignoreDstAmount) {
114 0 : if (tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings()
115 0 : != tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings()
116 0 : || (tr1.getTo()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() == 0)
117 0 : || (tr1.getTo()->getDSTSavings() == 0 && tr2.getTo()->getDSTSavings() != 0)) {
118 0 : return FALSE;
119 : }
120 : } else {
121 0 : if (tr1.getTo()->getRawOffset() != tr2.getTo()->getRawOffset() ||
122 0 : tr1.getTo()->getDSTSavings() != tr2.getTo()->getDSTSavings()) {
123 0 : return FALSE;
124 : }
125 : }
126 0 : time = tr1.getTime();
127 0 : }
128 0 : return TRUE;
129 : }
130 :
131 : void
132 0 : BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial,
133 : AnnualTimeZoneRule*& std, AnnualTimeZoneRule*& dst, UErrorCode& status) const {
134 0 : initial = NULL;
135 0 : std = NULL;
136 0 : dst = NULL;
137 0 : if (U_FAILURE(status)) {
138 0 : return;
139 : }
140 : int32_t initialRaw, initialDst;
141 0 : UnicodeString initialName;
142 :
143 0 : AnnualTimeZoneRule *ar1 = NULL;
144 0 : AnnualTimeZoneRule *ar2 = NULL;
145 0 : UnicodeString name;
146 :
147 : UBool avail;
148 0 : TimeZoneTransition tr;
149 : // Get the next transition
150 0 : avail = getNextTransition(date, FALSE, tr);
151 0 : if (avail) {
152 0 : tr.getFrom()->getName(initialName);
153 0 : initialRaw = tr.getFrom()->getRawOffset();
154 0 : initialDst = tr.getFrom()->getDSTSavings();
155 :
156 : // Check if the next transition is either DST->STD or STD->DST and
157 : // within roughly 1 year from the specified date
158 0 : UDate nextTransitionTime = tr.getTime();
159 0 : if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
160 0 : || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0))
161 0 : && (date + MILLIS_PER_YEAR > nextTransitionTime)) {
162 :
163 : int32_t year, month, dom, dow, doy, mid;
164 : UDate d;
165 :
166 : // Get local wall time for the next transition time
167 0 : Grego::timeToFields(nextTransitionTime + initialRaw + initialDst,
168 0 : year, month, dom, dow, doy, mid);
169 0 : int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
170 : // Create DOW rule
171 0 : DateTimeRule *dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
172 0 : tr.getTo()->getName(name);
173 :
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 0 : ar1 = new AnnualTimeZoneRule(name, initialRaw, tr.getTo()->getDSTSavings(),
181 0 : dtr, year, AnnualTimeZoneRule::MAX_YEAR);
182 :
183 0 : if (tr.getTo()->getRawOffset() == initialRaw) {
184 : // Get the next next transition
185 0 : avail = getNextTransition(nextTransitionTime, FALSE, tr);
186 0 : if (avail) {
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 0 : if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
190 0 : || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0))
191 0 : && nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) {
192 :
193 : // Get local wall time for the next transition time
194 0 : Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
195 0 : year, month, dom, dow, doy, mid);
196 0 : weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
197 : // Generate another DOW rule
198 0 : dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
199 0 : tr.getTo()->getName(name);
200 0 : ar2 = new AnnualTimeZoneRule(name, tr.getTo()->getRawOffset(), tr.getTo()->getDSTSavings(),
201 0 : dtr, year - 1, AnnualTimeZoneRule::MAX_YEAR);
202 :
203 : // Make sure this rule can be applied to the specified date
204 0 : avail = ar2->getPreviousStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), TRUE, d);
205 0 : if (!avail || d > date
206 0 : || initialRaw != tr.getTo()->getRawOffset()
207 0 : || initialDst != tr.getTo()->getDSTSavings()) {
208 : // We cannot use this rule as the second transition rule
209 0 : delete ar2;
210 0 : ar2 = NULL;
211 : }
212 : }
213 : }
214 : }
215 0 : if (ar2 == NULL) {
216 : // Try previous transition
217 0 : avail = getPreviousTransition(date, TRUE, tr);
218 0 : if (avail) {
219 : // Check if the previous transition is either DST->STD or STD->DST.
220 : // The actual transition time does not matter here.
221 0 : if ((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
222 0 : || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) {
223 :
224 : // Generate another DOW rule
225 0 : Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
226 0 : year, month, dom, dow, doy, mid);
227 0 : weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
228 0 : dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
229 0 : tr.getTo()->getName(name);
230 :
231 : // second rule raw/dst offsets should match raw/dst offsets
232 : // at the given time
233 0 : ar2 = new AnnualTimeZoneRule(name, initialRaw, initialDst,
234 0 : dtr, ar1->getStartYear() - 1, AnnualTimeZoneRule::MAX_YEAR);
235 :
236 : // Check if this rule start after the first rule after the specified date
237 0 : avail = ar2->getNextStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), FALSE, d);
238 0 : if (!avail || d <= nextTransitionTime) {
239 : // We cannot use this rule as the second transition rule
240 0 : delete ar2;
241 0 : ar2 = NULL;
242 : }
243 : }
244 : }
245 : }
246 0 : if (ar2 == NULL) {
247 : // Cannot find a good pair of AnnualTimeZoneRule
248 0 : delete ar1;
249 0 : ar1 = NULL;
250 : } else {
251 : // The initial rule should represent the rule before the previous transition
252 0 : ar1->getName(initialName);
253 0 : initialRaw = ar1->getRawOffset();
254 0 : initialDst = ar1->getDSTSavings();
255 : }
256 : }
257 : }
258 : else {
259 : // Try the previous one
260 0 : avail = getPreviousTransition(date, TRUE, tr);
261 0 : if (avail) {
262 0 : tr.getTo()->getName(initialName);
263 0 : initialRaw = tr.getTo()->getRawOffset();
264 0 : initialDst = tr.getTo()->getDSTSavings();
265 : } else {
266 : // No transitions in the past. Just use the current offsets
267 0 : getOffset(date, FALSE, initialRaw, initialDst, status);
268 0 : if (U_FAILURE(status)) {
269 0 : return;
270 : }
271 : }
272 : }
273 : // Set the initial rule
274 0 : initial = new InitialTimeZoneRule(initialName, initialRaw, initialDst);
275 :
276 : // Set the standard and daylight saving rules
277 0 : if (ar1 != NULL && ar2 != NULL) {
278 0 : if (ar1->getDSTSavings() != 0) {
279 0 : dst = ar1;
280 0 : std = ar2;
281 : } else {
282 0 : std = ar1;
283 0 : dst = ar2;
284 : }
285 : }
286 : }
287 :
288 : void
289 0 : BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial,
290 : UVector*& transitionRules, UErrorCode& status) const {
291 0 : if (U_FAILURE(status)) {
292 0 : return;
293 : }
294 :
295 : const InitialTimeZoneRule *orgini;
296 0 : const TimeZoneRule **orgtrs = NULL;
297 0 : TimeZoneTransition tzt;
298 : UBool avail;
299 0 : UVector *orgRules = NULL;
300 : int32_t ruleCount;
301 0 : TimeZoneRule *r = NULL;
302 0 : UBool *done = NULL;
303 0 : InitialTimeZoneRule *res_initial = NULL;
304 0 : UVector *filteredRules = NULL;
305 0 : UnicodeString name;
306 : int32_t i;
307 : UDate time, t;
308 0 : UDate *newTimes = NULL;
309 : UDate firstStart;
310 0 : UBool bFinalStd = FALSE, bFinalDst = FALSE;
311 :
312 : // Original transition rules
313 0 : ruleCount = countTransitionRules(status);
314 0 : if (U_FAILURE(status)) {
315 0 : return;
316 : }
317 0 : orgRules = new UVector(ruleCount, status);
318 0 : if (U_FAILURE(status)) {
319 0 : return;
320 : }
321 0 : orgtrs = (const TimeZoneRule**)uprv_malloc(sizeof(TimeZoneRule*)*ruleCount);
322 0 : if (orgtrs == NULL) {
323 0 : status = U_MEMORY_ALLOCATION_ERROR;
324 0 : goto error;
325 : }
326 0 : getTimeZoneRules(orgini, orgtrs, ruleCount, status);
327 0 : if (U_FAILURE(status)) {
328 0 : goto error;
329 : }
330 0 : for (i = 0; i < ruleCount; i++) {
331 0 : orgRules->addElement(orgtrs[i]->clone(), status);
332 0 : if (U_FAILURE(status)) {
333 0 : goto error;
334 : }
335 : }
336 0 : uprv_free(orgtrs);
337 0 : orgtrs = NULL;
338 :
339 0 : avail = getPreviousTransition(start, TRUE, tzt);
340 0 : if (!avail) {
341 : // No need to filter out rules only applicable to time before the start
342 0 : initial = orgini->clone();
343 0 : transitionRules = orgRules;
344 0 : return;
345 : }
346 :
347 0 : done = (UBool*)uprv_malloc(sizeof(UBool)*ruleCount);
348 0 : if (done == NULL) {
349 0 : status = U_MEMORY_ALLOCATION_ERROR;
350 0 : goto error;
351 : }
352 0 : filteredRules = new UVector(status);
353 0 : if (U_FAILURE(status)) {
354 0 : goto error;
355 : }
356 :
357 : // Create initial rule
358 0 : tzt.getTo()->getName(name);
359 0 : res_initial = new InitialTimeZoneRule(name, tzt.getTo()->getRawOffset(),
360 0 : tzt.getTo()->getDSTSavings());
361 :
362 : // Mark rules which does not need to be processed
363 0 : for (i = 0; i < ruleCount; i++) {
364 0 : r = (TimeZoneRule*)orgRules->elementAt(i);
365 0 : avail = r->getNextStart(start, res_initial->getRawOffset(), res_initial->getDSTSavings(), FALSE, time);
366 0 : done[i] = !avail;
367 : }
368 :
369 0 : time = start;
370 0 : while (!bFinalStd || !bFinalDst) {
371 0 : avail = getNextTransition(time, FALSE, tzt);
372 0 : if (!avail) {
373 0 : break;
374 : }
375 0 : UDate updatedTime = tzt.getTime();
376 0 : if (updatedTime == time) {
377 : // Can get here if rules for start & end of daylight time have exactly
378 : // the same time.
379 : // TODO: fix getNextTransition() to prevent it?
380 0 : status = U_INVALID_STATE_ERROR;
381 0 : goto error;
382 : }
383 0 : time = updatedTime;
384 :
385 0 : const TimeZoneRule *toRule = tzt.getTo();
386 0 : for (i = 0; i < ruleCount; i++) {
387 0 : r = (TimeZoneRule*)orgRules->elementAt(i);
388 0 : if (*r == *toRule) {
389 0 : break;
390 : }
391 : }
392 0 : if (i >= ruleCount) {
393 : // This case should never happen
394 0 : status = U_INVALID_STATE_ERROR;
395 0 : goto error;
396 : }
397 0 : if (done[i]) {
398 0 : continue;
399 : }
400 0 : const TimeArrayTimeZoneRule *tar = dynamic_cast<const TimeArrayTimeZoneRule *>(toRule);
401 : const AnnualTimeZoneRule *ar;
402 0 : if (tar != NULL) {
403 : // Get the previous raw offset and DST savings before the very first start time
404 0 : TimeZoneTransition tzt0;
405 0 : t = start;
406 : while (TRUE) {
407 0 : avail = getNextTransition(t, FALSE, tzt0);
408 0 : if (!avail) {
409 0 : break;
410 : }
411 0 : if (*(tzt0.getTo()) == *tar) {
412 0 : break;
413 : }
414 0 : t = tzt0.getTime();
415 : }
416 0 : if (avail) {
417 : // Check if the entire start times to be added
418 0 : tar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
419 0 : if (firstStart > start) {
420 : // Just add the rule as is
421 0 : filteredRules->addElement(tar->clone(), status);
422 0 : if (U_FAILURE(status)) {
423 0 : goto error;
424 : }
425 : } else {
426 : // Colllect transitions after the start time
427 : int32_t startTimes;
428 : DateTimeRule::TimeRuleType timeType;
429 : int32_t idx;
430 :
431 0 : startTimes = tar->countStartTimes();
432 0 : timeType = tar->getTimeType();
433 0 : for (idx = 0; idx < startTimes; idx++) {
434 0 : tar->getStartTimeAt(idx, t);
435 0 : if (timeType == DateTimeRule::STANDARD_TIME) {
436 0 : t -= tzt.getFrom()->getRawOffset();
437 : }
438 0 : if (timeType == DateTimeRule::WALL_TIME) {
439 0 : t -= tzt.getFrom()->getDSTSavings();
440 : }
441 0 : if (t > start) {
442 0 : break;
443 : }
444 : }
445 0 : int32_t asize = startTimes - idx;
446 0 : if (asize > 0) {
447 0 : newTimes = (UDate*)uprv_malloc(sizeof(UDate) * asize);
448 0 : if (newTimes == NULL) {
449 0 : status = U_MEMORY_ALLOCATION_ERROR;
450 0 : goto error;
451 : }
452 0 : for (int32_t newidx = 0; newidx < asize; newidx++) {
453 0 : tar->getStartTimeAt(idx + newidx, newTimes[newidx]);
454 0 : if (U_FAILURE(status)) {
455 0 : uprv_free(newTimes);
456 0 : newTimes = NULL;
457 0 : goto error;
458 : }
459 : }
460 0 : tar->getName(name);
461 : TimeArrayTimeZoneRule *newTar = new TimeArrayTimeZoneRule(name,
462 0 : tar->getRawOffset(), tar->getDSTSavings(), newTimes, asize, timeType);
463 0 : uprv_free(newTimes);
464 0 : filteredRules->addElement(newTar, status);
465 0 : if (U_FAILURE(status)) {
466 0 : goto error;
467 : }
468 : }
469 : }
470 : }
471 0 : } else if ((ar = dynamic_cast<const AnnualTimeZoneRule *>(toRule)) != NULL) {
472 0 : ar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
473 0 : if (firstStart == tzt.getTime()) {
474 : // Just add the rule as is
475 0 : filteredRules->addElement(ar->clone(), status);
476 0 : if (U_FAILURE(status)) {
477 0 : goto error;
478 : }
479 : } else {
480 : // Calculate the transition year
481 : int32_t year, month, dom, dow, doy, mid;
482 0 : Grego::timeToFields(tzt.getTime(), year, month, dom, dow, doy, mid);
483 : // Re-create the rule
484 0 : ar->getName(name);
485 0 : AnnualTimeZoneRule *newAr = new AnnualTimeZoneRule(name, ar->getRawOffset(), ar->getDSTSavings(),
486 0 : *(ar->getRule()), year, ar->getEndYear());
487 0 : filteredRules->addElement(newAr, status);
488 0 : if (U_FAILURE(status)) {
489 0 : goto error;
490 : }
491 : }
492 : // check if this is a final rule
493 0 : if (ar->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) {
494 : // After bot final standard and dst rules are processed,
495 : // exit this while loop.
496 0 : if (ar->getDSTSavings() == 0) {
497 0 : bFinalStd = TRUE;
498 : } else {
499 0 : bFinalDst = TRUE;
500 : }
501 : }
502 : }
503 0 : done[i] = TRUE;
504 : }
505 :
506 : // Set the results
507 0 : if (orgRules != NULL) {
508 0 : while (!orgRules->isEmpty()) {
509 0 : r = (TimeZoneRule*)orgRules->orphanElementAt(0);
510 0 : delete r;
511 : }
512 0 : delete orgRules;
513 : }
514 0 : if (done != NULL) {
515 0 : uprv_free(done);
516 : }
517 :
518 0 : initial = res_initial;
519 0 : transitionRules = filteredRules;
520 0 : return;
521 :
522 : error:
523 0 : if (orgtrs != NULL) {
524 0 : uprv_free(orgtrs);
525 : }
526 0 : if (orgRules != NULL) {
527 0 : while (!orgRules->isEmpty()) {
528 0 : r = (TimeZoneRule*)orgRules->orphanElementAt(0);
529 0 : delete r;
530 : }
531 0 : delete orgRules;
532 : }
533 0 : if (done != NULL) {
534 0 : if (filteredRules != NULL) {
535 0 : while (!filteredRules->isEmpty()) {
536 0 : r = (TimeZoneRule*)filteredRules->orphanElementAt(0);
537 0 : delete r;
538 : }
539 0 : delete filteredRules;
540 : }
541 0 : delete res_initial;
542 0 : uprv_free(done);
543 : }
544 :
545 0 : initial = NULL;
546 0 : transitionRules = NULL;
547 : }
548 :
549 : void
550 0 : BasicTimeZone::getOffsetFromLocal(UDate /*date*/, int32_t /*nonExistingTimeOpt*/, int32_t /*duplicatedTimeOpt*/,
551 : int32_t& /*rawOffset*/, int32_t& /*dstOffset*/, UErrorCode& status) const {
552 0 : if (U_FAILURE(status)) {
553 0 : return;
554 : }
555 0 : status = U_UNSUPPORTED_ERROR;
556 : }
557 :
558 : U_NAMESPACE_END
559 :
560 : #endif /* #if !UCONFIG_NO_FORMATTING */
561 :
562 : //eof
|