1/*
2*******************************************************************************
3* Copyright (C) 2007-2012, International Business Machines Corporation and
4* others. All Rights Reserved.
5*******************************************************************************
6*/
7
8#include "utypeinfo.h"  // for 'typeid' to work
9
10#include "unicode/utypes.h"
11
12#if !UCONFIG_NO_FORMATTING
13
14#include "unicode/tzrule.h"
15#include "unicode/ucal.h"
16#include "gregoimp.h"
17#include "cmemory.h"
18#include "uarrsort.h"
19
20U_CDECL_BEGIN
21// UComparator function for sorting start times
22static int32_t U_CALLCONV
23compareDates(const void * /*context*/, const void *left, const void *right) {
24    UDate l = *((UDate*)left);
25    UDate r = *((UDate*)right);
26    int32_t res = l < r ? -1 : (l == r ? 0 : 1);
27    return res;
28}
29U_CDECL_END
30
31U_NAMESPACE_BEGIN
32
33TimeZoneRule::TimeZoneRule(const UnicodeString& name, int32_t rawOffset, int32_t dstSavings)
34: UObject(), fName(name), fRawOffset(rawOffset), fDSTSavings(dstSavings) {
35}
36
37TimeZoneRule::TimeZoneRule(const TimeZoneRule& source)
38: UObject(source), fName(source.fName), fRawOffset(source.fRawOffset), fDSTSavings(source.fDSTSavings) {
39}
40
41TimeZoneRule::~TimeZoneRule() {
42}
43
44TimeZoneRule&
45TimeZoneRule::operator=(const TimeZoneRule& right) {
46    if (this != &right) {
47        fName = right.fName;
48        fRawOffset = right.fRawOffset;
49        fDSTSavings = right.fDSTSavings;
50    }
51    return *this;
52}
53
54UBool
55TimeZoneRule::operator==(const TimeZoneRule& that) const {
56    return ((this == &that) ||
57            (typeid(*this) == typeid(that) &&
58            fName == that.fName &&
59            fRawOffset == that.fRawOffset &&
60            fDSTSavings == that.fDSTSavings));
61}
62
63UBool
64TimeZoneRule::operator!=(const TimeZoneRule& that) const {
65    return !operator==(that);
66}
67
68UnicodeString&
69TimeZoneRule::getName(UnicodeString& name) const {
70    name = fName;
71    return name;
72}
73
74int32_t
75TimeZoneRule::getRawOffset(void) const {
76    return fRawOffset;
77}
78
79int32_t
80TimeZoneRule::getDSTSavings(void) const {
81    return fDSTSavings;
82}
83
84UBool
85TimeZoneRule::isEquivalentTo(const TimeZoneRule& other) const {
86    return ((this == &other) ||
87            (typeid(*this) == typeid(other) &&
88            fRawOffset == other.fRawOffset &&
89            fDSTSavings == other.fDSTSavings));
90}
91
92
93UOBJECT_DEFINE_RTTI_IMPLEMENTATION(InitialTimeZoneRule)
94
95InitialTimeZoneRule::InitialTimeZoneRule(const UnicodeString& name,
96                                         int32_t rawOffset,
97                                         int32_t dstSavings)
98: TimeZoneRule(name, rawOffset, dstSavings) {
99}
100
101InitialTimeZoneRule::InitialTimeZoneRule(const InitialTimeZoneRule& source)
102: TimeZoneRule(source) {
103}
104
105InitialTimeZoneRule::~InitialTimeZoneRule() {
106}
107
108InitialTimeZoneRule*
109InitialTimeZoneRule::clone(void) const {
110    return new InitialTimeZoneRule(*this);
111}
112
113InitialTimeZoneRule&
114InitialTimeZoneRule::operator=(const InitialTimeZoneRule& right) {
115    if (this != &right) {
116        TimeZoneRule::operator=(right);
117    }
118    return *this;
119}
120
121UBool
122InitialTimeZoneRule::operator==(const TimeZoneRule& that) const {
123    return ((this == &that) ||
124            (typeid(*this) == typeid(that) &&
125            TimeZoneRule::operator==(that)));
126}
127
128UBool
129InitialTimeZoneRule::operator!=(const TimeZoneRule& that) const {
130    return !operator==(that);
131}
132
133UBool
134InitialTimeZoneRule::isEquivalentTo(const TimeZoneRule& other) const {
135    if (this == &other) {
136        return TRUE;
137    }
138    if (typeid(*this) != typeid(other) || TimeZoneRule::isEquivalentTo(other) == FALSE) {
139        return FALSE;
140    }
141    return TRUE;
142}
143
144UBool
145InitialTimeZoneRule::getFirstStart(int32_t /*prevRawOffset*/,
146                                  int32_t /*prevDSTSavings*/,
147                                  UDate& /*result*/) const {
148    return FALSE;
149}
150
151UBool
152InitialTimeZoneRule::getFinalStart(int32_t /*prevRawOffset*/,
153                                  int32_t /*prevDSTSavings*/,
154                                  UDate& /*result*/) const {
155    return FALSE;
156}
157
158UBool
159InitialTimeZoneRule::getNextStart(UDate /*base*/,
160                                 int32_t /*prevRawOffset*/,
161                                 int32_t /*prevDSTSavings*/,
162                                 UBool /*inclusive*/,
163                                 UDate& /*result*/) const {
164    return FALSE;
165}
166
167UBool
168InitialTimeZoneRule::getPreviousStart(UDate /*base*/,
169                                     int32_t /*prevRawOffset*/,
170                                     int32_t /*prevDSTSavings*/,
171                                     UBool /*inclusive*/,
172                                     UDate& /*result*/) const {
173    return FALSE;
174}
175
176
177UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AnnualTimeZoneRule)
178
179const int32_t AnnualTimeZoneRule::MAX_YEAR = 0x7FFFFFFF; /* max signed int32 */
180
181AnnualTimeZoneRule::AnnualTimeZoneRule(const UnicodeString& name,
182                                       int32_t rawOffset,
183                                       int32_t dstSavings,
184                                       const DateTimeRule& dateTimeRule,
185                                       int32_t startYear,
186                                       int32_t endYear)
187: TimeZoneRule(name, rawOffset, dstSavings), fDateTimeRule(new DateTimeRule(dateTimeRule)),
188  fStartYear(startYear), fEndYear(endYear) {
189}
190
191AnnualTimeZoneRule::AnnualTimeZoneRule(const UnicodeString& name,
192                                       int32_t rawOffset,
193                                       int32_t dstSavings,
194                                       DateTimeRule* dateTimeRule,
195                                       int32_t startYear,
196                                       int32_t endYear)
197: TimeZoneRule(name, rawOffset, dstSavings), fDateTimeRule(dateTimeRule),
198  fStartYear(startYear), fEndYear(endYear) {
199}
200
201AnnualTimeZoneRule::AnnualTimeZoneRule(const AnnualTimeZoneRule& source)
202: TimeZoneRule(source), fDateTimeRule(new DateTimeRule(*(source.fDateTimeRule))),
203  fStartYear(source.fStartYear), fEndYear(source.fEndYear) {
204}
205
206AnnualTimeZoneRule::~AnnualTimeZoneRule() {
207    delete fDateTimeRule;
208}
209
210AnnualTimeZoneRule*
211AnnualTimeZoneRule::clone(void) const {
212    return new AnnualTimeZoneRule(*this);
213}
214
215AnnualTimeZoneRule&
216AnnualTimeZoneRule::operator=(const AnnualTimeZoneRule& right) {
217    if (this != &right) {
218        TimeZoneRule::operator=(right);
219        delete fDateTimeRule;
220        fDateTimeRule = right.fDateTimeRule->clone();
221        fStartYear = right.fStartYear;
222        fEndYear = right.fEndYear;
223    }
224    return *this;
225}
226
227UBool
228AnnualTimeZoneRule::operator==(const TimeZoneRule& that) const {
229    if (this == &that) {
230        return TRUE;
231    }
232    if (typeid(*this) != typeid(that)) {
233        return FALSE;
234    }
235    AnnualTimeZoneRule *atzr = (AnnualTimeZoneRule*)&that;
236    return (*fDateTimeRule == *(atzr->fDateTimeRule) &&
237            fStartYear == atzr->fStartYear &&
238            fEndYear == atzr->fEndYear);
239}
240
241UBool
242AnnualTimeZoneRule::operator!=(const TimeZoneRule& that) const {
243    return !operator==(that);
244}
245
246const DateTimeRule*
247AnnualTimeZoneRule::getRule() const {
248    return fDateTimeRule;
249}
250
251int32_t
252AnnualTimeZoneRule::getStartYear() const {
253    return fStartYear;
254}
255
256int32_t
257AnnualTimeZoneRule::getEndYear() const {
258    return fEndYear;
259}
260
261UBool
262AnnualTimeZoneRule::getStartInYear(int32_t year,
263                                   int32_t prevRawOffset,
264                                   int32_t prevDSTSavings,
265                                   UDate &result) const {
266    if (year < fStartYear || year > fEndYear) {
267        return FALSE;
268    }
269    double ruleDay;
270    DateTimeRule::DateRuleType type = fDateTimeRule->getDateRuleType();
271    if (type == DateTimeRule::DOM) {
272        ruleDay = Grego::fieldsToDay(year, fDateTimeRule->getRuleMonth(), fDateTimeRule->getRuleDayOfMonth());
273    } else {
274        UBool after = TRUE;
275        if (type == DateTimeRule::DOW) {
276            // Normalize DOW rule into DOW_GEQ_DOM or DOW_LEQ_DOM
277            int32_t weeks = fDateTimeRule->getRuleWeekInMonth();
278            if (weeks > 0) {
279                ruleDay = Grego::fieldsToDay(year, fDateTimeRule->getRuleMonth(), 1);
280                ruleDay += 7 * (weeks - 1);
281            } else {
282                after = FALSE;
283                ruleDay = Grego::fieldsToDay(year, fDateTimeRule->getRuleMonth(),
284                    Grego::monthLength(year, fDateTimeRule->getRuleMonth()));
285                ruleDay += 7 * (weeks + 1);
286           }
287        } else {
288            int32_t month = fDateTimeRule->getRuleMonth();
289            int32_t dom = fDateTimeRule->getRuleDayOfMonth();
290            if (type == DateTimeRule::DOW_LEQ_DOM) {
291                after = FALSE;
292                // Handle Feb <=29
293                if (month == UCAL_FEBRUARY && dom == 29 && !Grego::isLeapYear(year)) {
294                    dom--;
295                }
296            }
297            ruleDay = Grego::fieldsToDay(year, month, dom);
298        }
299        int32_t dow = Grego::dayOfWeek(ruleDay);
300        int32_t delta = fDateTimeRule->getRuleDayOfWeek() - dow;
301        if (after) {
302            delta = delta < 0 ? delta + 7 : delta;
303        } else {
304            delta = delta > 0 ? delta - 7 : delta;
305        }
306        ruleDay += delta;
307    }
308
309    result = ruleDay*U_MILLIS_PER_DAY + fDateTimeRule->getRuleMillisInDay();
310    if (fDateTimeRule->getTimeRuleType() != DateTimeRule::UTC_TIME) {
311        result -= prevRawOffset;
312    }
313    if (fDateTimeRule->getTimeRuleType() == DateTimeRule::WALL_TIME) {
314        result -= prevDSTSavings;
315    }
316    return TRUE;
317}
318
319UBool
320AnnualTimeZoneRule::isEquivalentTo(const TimeZoneRule& other) const {
321    if (this == &other) {
322        return TRUE;
323    }
324    if (typeid(*this) != typeid(other) || TimeZoneRule::isEquivalentTo(other) == FALSE) {
325        return FALSE;
326    }
327    AnnualTimeZoneRule* that = (AnnualTimeZoneRule*)&other;
328    return (*fDateTimeRule == *(that->fDateTimeRule) &&
329            fStartYear == that->fStartYear &&
330            fEndYear == that->fEndYear);
331}
332
333UBool
334AnnualTimeZoneRule::getFirstStart(int32_t prevRawOffset,
335                                  int32_t prevDSTSavings,
336                                  UDate& result) const {
337    return getStartInYear(fStartYear, prevRawOffset, prevDSTSavings, result);
338}
339
340UBool
341AnnualTimeZoneRule::getFinalStart(int32_t prevRawOffset,
342                                  int32_t prevDSTSavings,
343                                  UDate& result) const {
344    if (fEndYear == MAX_YEAR) {
345        return FALSE;
346    }
347    return getStartInYear(fEndYear, prevRawOffset, prevDSTSavings, result);
348}
349
350UBool
351AnnualTimeZoneRule::getNextStart(UDate base,
352                                 int32_t prevRawOffset,
353                                 int32_t prevDSTSavings,
354                                 UBool inclusive,
355                                 UDate& result) const {
356    int32_t year, month, dom, dow, doy, mid;
357    Grego::timeToFields(base, year, month, dom, dow, doy, mid);
358    if (year < fStartYear) {
359        return getFirstStart(prevRawOffset, prevDSTSavings, result);
360    }
361    UDate tmp;
362    if (getStartInYear(year, prevRawOffset, prevDSTSavings, tmp)) {
363        if (tmp < base || (!inclusive && (tmp == base))) {
364            // Return the next one
365            return getStartInYear(year + 1, prevRawOffset, prevDSTSavings, result);
366        } else {
367            result = tmp;
368            return TRUE;
369        }
370    }
371    return FALSE;
372}
373
374UBool
375AnnualTimeZoneRule::getPreviousStart(UDate base,
376                                     int32_t prevRawOffset,
377                                     int32_t prevDSTSavings,
378                                     UBool inclusive,
379                                     UDate& result) const {
380    int32_t year, month, dom, dow, doy, mid;
381    Grego::timeToFields(base, year, month, dom, dow, doy, mid);
382    if (year > fEndYear) {
383        return getFinalStart(prevRawOffset, prevDSTSavings, result);
384    }
385    UDate tmp;
386    if (getStartInYear(year, prevRawOffset, prevDSTSavings, tmp)) {
387        if (tmp > base || (!inclusive && (tmp == base))) {
388            // Return the previous one
389            return getStartInYear(year - 1, prevRawOffset, prevDSTSavings, result);
390        } else {
391            result = tmp;
392            return TRUE;
393        }
394    }
395    return FALSE;
396}
397
398UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeArrayTimeZoneRule)
399
400TimeArrayTimeZoneRule::TimeArrayTimeZoneRule(const UnicodeString& name,
401                                             int32_t rawOffset,
402                                             int32_t dstSavings,
403                                             const UDate* startTimes,
404                                             int32_t numStartTimes,
405                                             DateTimeRule::TimeRuleType timeRuleType)
406: TimeZoneRule(name, rawOffset, dstSavings), fTimeRuleType(timeRuleType),
407  fStartTimes(NULL) {
408    UErrorCode status = U_ZERO_ERROR;
409    initStartTimes(startTimes, numStartTimes, status);
410    //TODO - status?
411}
412
413
414TimeArrayTimeZoneRule::TimeArrayTimeZoneRule(const TimeArrayTimeZoneRule& source)
415: TimeZoneRule(source), fTimeRuleType(source.fTimeRuleType), fStartTimes(NULL) {
416    UErrorCode status = U_ZERO_ERROR;
417    initStartTimes(source.fStartTimes, source.fNumStartTimes, status);
418    //TODO - status?
419}
420
421
422TimeArrayTimeZoneRule::~TimeArrayTimeZoneRule() {
423    if (fStartTimes != NULL && fStartTimes != fLocalStartTimes) {
424        uprv_free(fStartTimes);
425    }
426}
427
428TimeArrayTimeZoneRule*
429TimeArrayTimeZoneRule::clone(void) const {
430    return new TimeArrayTimeZoneRule(*this);
431}
432
433
434TimeArrayTimeZoneRule&
435TimeArrayTimeZoneRule::operator=(const TimeArrayTimeZoneRule& right) {
436    if (this != &right) {
437        TimeZoneRule::operator=(right);
438        UErrorCode status = U_ZERO_ERROR;
439        initStartTimes(right.fStartTimes, right.fNumStartTimes, status);
440        //TODO - status?
441        fTimeRuleType = right.fTimeRuleType;
442    }
443    return *this;
444}
445
446UBool
447TimeArrayTimeZoneRule::operator==(const TimeZoneRule& that) const {
448    if (this == &that) {
449        return TRUE;
450    }
451    if (typeid(*this) != typeid(that) || TimeZoneRule::operator==(that) == FALSE) {
452        return FALSE;
453    }
454    TimeArrayTimeZoneRule *tatzr = (TimeArrayTimeZoneRule*)&that;
455    if (fTimeRuleType != tatzr->fTimeRuleType ||
456        fNumStartTimes != tatzr->fNumStartTimes) {
457        return FALSE;
458    }
459    // Compare start times
460    UBool res = TRUE;
461    for (int32_t i = 0; i < fNumStartTimes; i++) {
462        if (fStartTimes[i] != tatzr->fStartTimes[i]) {
463            res = FALSE;
464            break;
465        }
466    }
467    return res;
468}
469
470UBool
471TimeArrayTimeZoneRule::operator!=(const TimeZoneRule& that) const {
472    return !operator==(that);
473}
474
475DateTimeRule::TimeRuleType
476TimeArrayTimeZoneRule::getTimeType(void) const {
477    return fTimeRuleType;
478}
479
480UBool
481TimeArrayTimeZoneRule::getStartTimeAt(int32_t index, UDate& result) const {
482    if (index >= fNumStartTimes || index < 0) {
483        return FALSE;
484    }
485    result = fStartTimes[index];
486    return TRUE;
487}
488
489int32_t
490TimeArrayTimeZoneRule::countStartTimes(void) const {
491    return fNumStartTimes;
492}
493
494UBool
495TimeArrayTimeZoneRule::isEquivalentTo(const TimeZoneRule& other) const {
496    if (this == &other) {
497        return TRUE;
498    }
499    if (typeid(*this) != typeid(other) || TimeZoneRule::isEquivalentTo(other) == FALSE) {
500        return FALSE;
501    }
502    TimeArrayTimeZoneRule* that = (TimeArrayTimeZoneRule*)&other;
503    if (fTimeRuleType != that->fTimeRuleType ||
504        fNumStartTimes != that->fNumStartTimes) {
505        return FALSE;
506    }
507    // Compare start times
508    UBool res = TRUE;
509    for (int32_t i = 0; i < fNumStartTimes; i++) {
510        if (fStartTimes[i] != that->fStartTimes[i]) {
511            res = FALSE;
512            break;
513        }
514    }
515    return res;
516}
517
518UBool
519TimeArrayTimeZoneRule::getFirstStart(int32_t prevRawOffset,
520                                             int32_t prevDSTSavings,
521                                             UDate& result) const {
522    if (fNumStartTimes <= 0 || fStartTimes == NULL) {
523        return FALSE;
524    }
525    result = getUTC(fStartTimes[0], prevRawOffset, prevDSTSavings);
526    return TRUE;
527}
528
529UBool
530TimeArrayTimeZoneRule::getFinalStart(int32_t prevRawOffset,
531                                     int32_t prevDSTSavings,
532                                     UDate& result) const {
533    if (fNumStartTimes <= 0 || fStartTimes == NULL) {
534        return FALSE;
535    }
536    result = getUTC(fStartTimes[fNumStartTimes - 1], prevRawOffset, prevDSTSavings);
537    return TRUE;
538}
539
540UBool
541TimeArrayTimeZoneRule::getNextStart(UDate base,
542                                    int32_t prevRawOffset,
543                                    int32_t prevDSTSavings,
544                                    UBool inclusive,
545                                    UDate& result) const {
546    int32_t i = fNumStartTimes - 1;
547    for (; i >= 0; i--) {
548        UDate time = getUTC(fStartTimes[i], prevRawOffset, prevDSTSavings);
549        if (time < base || (!inclusive && time == base)) {
550            break;
551        }
552        result = time;
553    }
554    if (i == fNumStartTimes - 1) {
555        return FALSE;
556    }
557    return TRUE;
558}
559
560UBool
561TimeArrayTimeZoneRule::getPreviousStart(UDate base,
562                                        int32_t prevRawOffset,
563                                        int32_t prevDSTSavings,
564                                        UBool inclusive,
565                                        UDate& result) const {
566    int32_t i = fNumStartTimes - 1;
567    for (; i >= 0; i--) {
568        UDate time = getUTC(fStartTimes[i], prevRawOffset, prevDSTSavings);
569        if (time < base || (inclusive && time == base)) {
570            result = time;
571            return TRUE;
572        }
573    }
574    return FALSE;
575}
576
577
578// ---- private methods ------
579
580UBool
581TimeArrayTimeZoneRule::initStartTimes(const UDate source[], int32_t size, UErrorCode& status) {
582    // Free old array
583    if (fStartTimes != NULL && fStartTimes != fLocalStartTimes) {
584        uprv_free(fStartTimes);
585    }
586    // Allocate new one if needed
587    if (size > TIMEARRAY_STACK_BUFFER_SIZE) {
588        fStartTimes = (UDate*)uprv_malloc(sizeof(UDate)*size);
589        if (fStartTimes == NULL) {
590            status = U_MEMORY_ALLOCATION_ERROR;
591            fNumStartTimes = 0;
592            return FALSE;
593        }
594    } else {
595        fStartTimes = (UDate*)fLocalStartTimes;
596    }
597    uprv_memcpy(fStartTimes, source, sizeof(UDate)*size);
598    fNumStartTimes = size;
599    // Sort dates
600    uprv_sortArray(fStartTimes, fNumStartTimes, (int32_t)sizeof(UDate), compareDates, NULL, TRUE, &status);
601    if (U_FAILURE(status)) {
602        if (fStartTimes != NULL && fStartTimes != fLocalStartTimes) {
603            uprv_free(fStartTimes);
604        }
605        fNumStartTimes = 0;
606        return FALSE;
607    }
608    return TRUE;
609}
610
611UDate
612TimeArrayTimeZoneRule::getUTC(UDate time, int32_t raw, int32_t dst) const {
613    if (fTimeRuleType != DateTimeRule::UTC_TIME) {
614        time -= raw;
615    }
616    if (fTimeRuleType == DateTimeRule::WALL_TIME) {
617        time -= dst;
618    }
619    return time;
620}
621
622U_NAMESPACE_END
623
624#endif /* #if !UCONFIG_NO_FORMATTING */
625
626//eof
627
628