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