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
17U_NAMESPACE_BEGIN
18
19#define MILLIS_PER_YEAR (365*24*60*60*1000.0)
20
21BasicTimeZone::BasicTimeZone()
22: TimeZone() {
23}
24
25BasicTimeZone::BasicTimeZone(const UnicodeString &id)
26: TimeZone(id) {
27}
28
29BasicTimeZone::BasicTimeZone(const BasicTimeZone& source)
30: TimeZone(source) {
31}
32
33BasicTimeZone::~BasicTimeZone() {
34}
35
36UBool
37BasicTimeZone::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
129void
130BasicTimeZone::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
286void
287BasicTimeZone::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
520error:
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
547void
548BasicTimeZone::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
556U_NAMESPACE_END
557
558#endif /* #if !UCONFIG_NO_FORMATTING */
559
560//eof
561