1 /*
2 *******************************************************************************
3 * Copyright (C) 2007-2013, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
6 */
7 
8 #include "unicode/utypes.h"
9 
10 #if !UCONFIG_NO_FORMATTING
11 
12 #include "unicode/basictz.h"
13 #include "gregoimp.h"
14 #include "uvector.h"
15 #include "cmemory.h"
16 
17 U_NAMESPACE_BEGIN
18 
19 #define MILLIS_PER_YEAR (365*24*60*60*1000.0)
20 
BasicTimeZone()21 BasicTimeZone::BasicTimeZone()
22 : TimeZone() {
23 }
24 
BasicTimeZone(const UnicodeString & id)25 BasicTimeZone::BasicTimeZone(const UnicodeString &id)
26 : TimeZone(id) {
27 }
28 
BasicTimeZone(const BasicTimeZone & source)29 BasicTimeZone::BasicTimeZone(const BasicTimeZone& source)
30 : TimeZone(source) {
31 }
32 
~BasicTimeZone()33 BasicTimeZone::~BasicTimeZone() {
34 }
35 
36 UBool
hasEquivalentTransitions(const BasicTimeZone & tz,UDate start,UDate end,UBool ignoreDstAmount,UErrorCode & status) const37 BasicTimeZone::hasEquivalentTransitions(const BasicTimeZone& tz, UDate start, UDate end,
38                                         UBool ignoreDstAmount, UErrorCode& status) const {
39     if (U_FAILURE(status)) {
40         return FALSE;
41     }
42     if (hasSameRules(tz)) {
43         return TRUE;
44     }
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)) {
49         return FALSE;
50     }
51     tz.getOffset(start, FALSE, raw2, dst2, status);
52     if (U_FAILURE(status)) {
53         return FALSE;
54     }
55     if (ignoreDstAmount) {
56         if ((raw1 + dst1 != raw2 + dst2)
57             || (dst1 != 0 && dst2 == 0)
58             || (dst1 == 0 && dst2 != 0)) {
59             return FALSE;
60         }
61     } else {
62         if (raw1 != raw2 || dst1 != dst2) {
63             return FALSE;
64         }
65     }
66     // Check transitions in the range
67     UDate time = start;
68     TimeZoneTransition tr1, tr2;
69     while (TRUE) {
70         UBool avail1 = getNextTransition(time, FALSE, tr1);
71         UBool avail2 = tz.getNextTransition(time, FALSE, tr2);
72 
73         if (ignoreDstAmount) {
74             // Skip a transition which only differ the amount of DST savings
75             while (TRUE) {
76                 if (avail1
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);
82                 } else {
83                     break;
84                 }
85             }
86             while (TRUE) {
87                 if (avail2
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);
93                 } else {
94                     break;
95                 }
96             }
97         }
98 
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
103             break;
104         }
105         if (!inRange1 || !inRange2) {
106             return FALSE;
107         }
108         if (tr1.getTime() != tr2.getTime()) {
109             return FALSE;
110         }
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)) {
116                 return FALSE;
117             }
118         } else {
119             if (tr1.getTo()->getRawOffset() != tr2.getTo()->getRawOffset() ||
120                 tr1.getTo()->getDSTSavings() != tr2.getTo()->getDSTSavings()) {
121                 return FALSE;
122             }
123         }
124         time = tr1.getTime();
125     }
126     return TRUE;
127 }
128 
129 void
getSimpleRulesNear(UDate date,InitialTimeZoneRule * & initial,AnnualTimeZoneRule * & std,AnnualTimeZoneRule * & dst,UErrorCode & status) const130 BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial,
131         AnnualTimeZoneRule*& std, AnnualTimeZoneRule*& dst, UErrorCode& status) const {
132     initial = NULL;
133     std = NULL;
134     dst = NULL;
135     if (U_FAILURE(status)) {
136         return;
137     }
138     int32_t initialRaw, initialDst;
139     UnicodeString initialName;
140 
141     AnnualTimeZoneRule *ar1 = NULL;
142     AnnualTimeZoneRule *ar2 = NULL;
143     UnicodeString name;
144 
145     UBool avail;
146     TimeZoneTransition tr;
147     // Get the next transition
148     avail = getNextTransition(date, FALSE, tr);
149     if (avail) {
150         tr.getFrom()->getName(initialName);
151         initialRaw = tr.getFrom()->getRawOffset();
152         initialDst = tr.getFrom()->getDSTSavings();
153 
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)) {
160 
161             int32_t year, month, dom, dow, doy, mid;
162             UDate d;
163 
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);
168             // Create DOW rule
169             DateTimeRule *dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
170             tr.getTo()->getName(name);
171 
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);
180 
181             if (tr.getTo()->getRawOffset() == initialRaw) {
182                 // Get the next next transition
183                 avail = getNextTransition(nextTransitionTime, FALSE, tr);
184                 if (avail) {
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()) {
190 
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);
200 
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
207                             delete ar2;
208                             ar2 = NULL;
209                         }
210                     }
211                 }
212             }
213             if (ar2 == NULL) {
214                 // Try previous transition
215                 avail = getPreviousTransition(date, TRUE, tr);
216                 if (avail) {
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)) {
221 
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);
228 
229                         // second rule raw/dst offsets should match raw/dst offsets
230                         // at the given time
231                         ar2 = new AnnualTimeZoneRule(name, initialRaw, initialDst,
232                             dtr, ar1->getStartYear() - 1, AnnualTimeZoneRule::MAX_YEAR);
233 
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
238                             delete ar2;
239                             ar2 = NULL;
240                         }
241                     }
242                 }
243             }
244             if (ar2 == NULL) {
245                 // Cannot find a good pair of AnnualTimeZoneRule
246                 delete ar1;
247                 ar1 = NULL;
248             } else {
249                 // The initial rule should represent the rule before the previous transition
250                 ar1->getName(initialName);
251                 initialRaw = ar1->getRawOffset();
252                 initialDst = ar1->getDSTSavings();
253             }
254         }
255     }
256     else {
257         // Try the previous one
258         avail = getPreviousTransition(date, TRUE, tr);
259         if (avail) {
260             tr.getTo()->getName(initialName);
261             initialRaw = tr.getTo()->getRawOffset();
262             initialDst = tr.getTo()->getDSTSavings();
263         } else {
264             // No transitions in the past.  Just use the current offsets
265             getOffset(date, FALSE, initialRaw, initialDst, status);
266             if (U_FAILURE(status)) {
267                 return;
268             }
269         }
270     }
271     // Set the initial rule
272     initial = new InitialTimeZoneRule(initialName, initialRaw, initialDst);
273 
274     // Set the standard and daylight saving rules
275     if (ar1 != NULL && ar2 != NULL) {
276         if (ar1->getDSTSavings() != 0) {
277             dst = ar1;
278             std = ar2;
279         } else {
280             std = ar1;
281             dst = ar2;
282         }
283     }
284 }
285 
286 void
getTimeZoneRulesAfter(UDate start,InitialTimeZoneRule * & initial,UVector * & transitionRules,UErrorCode & status) const287 BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial,
288                                      UVector*& transitionRules, UErrorCode& status) const {
289     if (U_FAILURE(status)) {
290         return;
291     }
292 
293     const InitialTimeZoneRule *orgini;
294     const TimeZoneRule **orgtrs = NULL;
295     TimeZoneTransition tzt;
296     UBool avail;
297     UVector *orgRules = NULL;
298     int32_t ruleCount;
299     TimeZoneRule *r = NULL;
300     UBool *done = NULL;
301     InitialTimeZoneRule *res_initial = NULL;
302     UVector *filteredRules = NULL;
303     UnicodeString name;
304     int32_t i;
305     UDate time, t;
306     UDate *newTimes = NULL;
307     UDate firstStart;
308     UBool bFinalStd = FALSE, bFinalDst = FALSE;
309 
310     // Original transition rules
311     ruleCount = countTransitionRules(status);
312     if (U_FAILURE(status)) {
313         return;
314     }
315     orgRules = new UVector(ruleCount, status);
316     if (U_FAILURE(status)) {
317         return;
318     }
319     orgtrs = (const TimeZoneRule**)uprv_malloc(sizeof(TimeZoneRule*)*ruleCount);
320     if (orgtrs == NULL) {
321         status = U_MEMORY_ALLOCATION_ERROR;
322         goto error;
323     }
324     getTimeZoneRules(orgini, orgtrs, ruleCount, status);
325     if (U_FAILURE(status)) {
326         goto error;
327     }
328     for (i = 0; i < ruleCount; i++) {
329         orgRules->addElement(orgtrs[i]->clone(), status);
330         if (U_FAILURE(status)) {
331             goto error;
332         }
333     }
334     uprv_free(orgtrs);
335     orgtrs = NULL;
336 
337     avail = getPreviousTransition(start, TRUE, tzt);
338     if (!avail) {
339         // No need to filter out rules only applicable to time before the start
340         initial = orgini->clone();
341         transitionRules = orgRules;
342         return;
343     }
344 
345     done = (UBool*)uprv_malloc(sizeof(UBool)*ruleCount);
346     if (done == NULL) {
347         status = U_MEMORY_ALLOCATION_ERROR;
348         goto error;
349     }
350     filteredRules = new UVector(status);
351     if (U_FAILURE(status)) {
352         goto error;
353     }
354 
355     // Create initial rule
356     tzt.getTo()->getName(name);
357     res_initial = new InitialTimeZoneRule(name, tzt.getTo()->getRawOffset(),
358         tzt.getTo()->getDSTSavings());
359 
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);
364         done[i] = !avail;
365     }
366 
367     time = start;
368     while (!bFinalStd || !bFinalDst) {
369         avail = getNextTransition(time, FALSE, tzt);
370         if (!avail) {
371             break;
372         }
373         UDate updatedTime = tzt.getTime();
374         if (updatedTime == time) {
375             // Can get here if rules for start & end of daylight time have exactly
376             // the same time.
377             // TODO:  fix getNextTransition() to prevent it?
378             status = U_INVALID_STATE_ERROR;
379             goto error;
380         }
381         time = updatedTime;
382 
383         const TimeZoneRule *toRule = tzt.getTo();
384         for (i = 0; i < ruleCount; i++) {
385             r = (TimeZoneRule*)orgRules->elementAt(i);
386             if (*r == *toRule) {
387                 break;
388             }
389         }
390         if (i >= ruleCount) {
391             // This case should never happen
392             status = U_INVALID_STATE_ERROR;
393             goto error;
394         }
395         if (done[i]) {
396             continue;
397         }
398         const TimeArrayTimeZoneRule *tar = dynamic_cast<const TimeArrayTimeZoneRule *>(toRule);
399         const AnnualTimeZoneRule *ar;
400         if (tar != NULL) {
401             // Get the previous raw offset and DST savings before the very first start time
402             TimeZoneTransition tzt0;
403             t = start;
404             while (TRUE) {
405                 avail = getNextTransition(t, FALSE, tzt0);
406                 if (!avail) {
407                     break;
408                 }
409                 if (*(tzt0.getTo()) == *tar) {
410                     break;
411                 }
412                 t = tzt0.getTime();
413             }
414             if (avail) {
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)) {
421                         goto error;
422                     }
423                 } else {
424                     // Colllect transitions after the start time
425                     int32_t startTimes;
426                     DateTimeRule::TimeRuleType timeType;
427                     int32_t idx;
428 
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();
435                         }
436                         if (timeType == DateTimeRule::WALL_TIME) {
437                             t -= tzt.getFrom()->getDSTSavings();
438                         }
439                         if (t > start) {
440                             break;
441                         }
442                     }
443                     int32_t asize = startTimes - idx;
444                     if (asize > 0) {
445                         newTimes = (UDate*)uprv_malloc(sizeof(UDate) * asize);
446                         if (newTimes == NULL) {
447                             status = U_MEMORY_ALLOCATION_ERROR;
448                             goto error;
449                         }
450                         for (int32_t newidx = 0; newidx < asize; newidx++) {
451                             tar->getStartTimeAt(idx + newidx, newTimes[newidx]);
452                             if (U_FAILURE(status)) {
453                                 uprv_free(newTimes);
454                                 newTimes = NULL;
455                                 goto error;
456                             }
457                         }
458                         tar->getName(name);
459                         TimeArrayTimeZoneRule *newTar = new TimeArrayTimeZoneRule(name,
460                             tar->getRawOffset(), tar->getDSTSavings(), newTimes, asize, timeType);
461                         uprv_free(newTimes);
462                         filteredRules->addElement(newTar, status);
463                         if (U_FAILURE(status)) {
464                             goto error;
465                         }
466                     }
467                 }
468             }
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)) {
475                     goto error;
476                 }
477             } else {
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
482                 ar->getName(name);
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)) {
487                     goto error;
488                 }
489             }
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) {
495                     bFinalStd = TRUE;
496                 } else {
497                     bFinalDst = TRUE;
498                 }
499             }
500         }
501         done[i] = TRUE;
502     }
503 
504     // Set the results
505     if (orgRules != NULL) {
506         while (!orgRules->isEmpty()) {
507             r = (TimeZoneRule*)orgRules->orphanElementAt(0);
508             delete r;
509         }
510         delete orgRules;
511     }
512     if (done != NULL) {
513         uprv_free(done);
514     }
515 
516     initial = res_initial;
517     transitionRules = filteredRules;
518     return;
519 
520 error:
521     if (orgtrs != NULL) {
522         uprv_free(orgtrs);
523     }
524     if (orgRules != NULL) {
525         while (!orgRules->isEmpty()) {
526             r = (TimeZoneRule*)orgRules->orphanElementAt(0);
527             delete r;
528         }
529         delete orgRules;
530     }
531     if (done != NULL) {
532         if (filteredRules != NULL) {
533             while (!filteredRules->isEmpty()) {
534                 r = (TimeZoneRule*)filteredRules->orphanElementAt(0);
535                 delete r;
536             }
537             delete filteredRules;
538         }
539         delete res_initial;
540         uprv_free(done);
541     }
542 
543     initial = NULL;
544     transitionRules = NULL;
545 }
546 
547 void
getOffsetFromLocal(UDate,int32_t,int32_t,int32_t &,int32_t &,UErrorCode & status) const548 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)) {
551         return;
552     }
553     status = U_UNSUPPORTED_ERROR;
554 }
555 
556 U_NAMESPACE_END
557 
558 #endif /* #if !UCONFIG_NO_FORMATTING */
559 
560 //eof
561