1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2007-2012, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
10 #include "utypeinfo.h" // for 'typeid' to work
12 #include "unicode/utypes.h"
14 #if !UCONFIG_NO_FORMATTING
16 #include "unicode/tzrule.h"
17 #include "unicode/ucal.h"
23 // UComparator function for sorting start times
24 static int32_t U_CALLCONV
25 compareDates(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);
35 TimeZoneRule::TimeZoneRule(const UnicodeString
& name
, int32_t rawOffset
, int32_t dstSavings
)
36 : UObject(), fName(name
), fRawOffset(rawOffset
), fDSTSavings(dstSavings
) {
39 TimeZoneRule::TimeZoneRule(const TimeZoneRule
& source
)
40 : UObject(source
), fName(source
.fName
), fRawOffset(source
.fRawOffset
), fDSTSavings(source
.fDSTSavings
) {
43 TimeZoneRule::~TimeZoneRule() {
47 TimeZoneRule::operator=(const TimeZoneRule
& right
) {
50 fRawOffset
= right
.fRawOffset
;
51 fDSTSavings
= right
.fDSTSavings
;
57 TimeZoneRule::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
));
66 TimeZoneRule::operator!=(const TimeZoneRule
& that
) const {
67 return !operator==(that
);
71 TimeZoneRule::getName(UnicodeString
& name
) const {
77 TimeZoneRule::getRawOffset(void) const {
82 TimeZoneRule::getDSTSavings(void) const {
87 TimeZoneRule::isEquivalentTo(const TimeZoneRule
& other
) const {
88 return ((this == &other
) ||
89 (typeid(*this) == typeid(other
) &&
90 fRawOffset
== other
.fRawOffset
&&
91 fDSTSavings
== other
.fDSTSavings
));
95 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(InitialTimeZoneRule
)
97 InitialTimeZoneRule::InitialTimeZoneRule(const UnicodeString
& name
,
100 : TimeZoneRule(name
, rawOffset
, dstSavings
) {
103 InitialTimeZoneRule::InitialTimeZoneRule(const InitialTimeZoneRule
& source
)
104 : TimeZoneRule(source
) {
107 InitialTimeZoneRule::~InitialTimeZoneRule() {
111 InitialTimeZoneRule::clone(void) const {
112 return new InitialTimeZoneRule(*this);
116 InitialTimeZoneRule::operator=(const InitialTimeZoneRule
& right
) {
117 if (this != &right
) {
118 TimeZoneRule::operator=(right
);
124 InitialTimeZoneRule::operator==(const TimeZoneRule
& that
) const {
125 return ((this == &that
) ||
126 (typeid(*this) == typeid(that
) &&
127 TimeZoneRule::operator==(that
)));
131 InitialTimeZoneRule::operator!=(const TimeZoneRule
& that
) const {
132 return !operator==(that
);
136 InitialTimeZoneRule::isEquivalentTo(const TimeZoneRule
& other
) const {
137 if (this == &other
) {
140 if (typeid(*this) != typeid(other
) || TimeZoneRule::isEquivalentTo(other
) == FALSE
) {
147 InitialTimeZoneRule::getFirstStart(int32_t /*prevRawOffset*/,
148 int32_t /*prevDSTSavings*/,
149 UDate
& /*result*/) const {
154 InitialTimeZoneRule::getFinalStart(int32_t /*prevRawOffset*/,
155 int32_t /*prevDSTSavings*/,
156 UDate
& /*result*/) const {
161 InitialTimeZoneRule::getNextStart(UDate
/*base*/,
162 int32_t /*prevRawOffset*/,
163 int32_t /*prevDSTSavings*/,
165 UDate
& /*result*/) const {
170 InitialTimeZoneRule::getPreviousStart(UDate
/*base*/,
171 int32_t /*prevRawOffset*/,
172 int32_t /*prevDSTSavings*/,
174 UDate
& /*result*/) const {
179 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AnnualTimeZoneRule
)
181 const int32_t AnnualTimeZoneRule::MAX_YEAR
= 0x7FFFFFFF; /* max signed int32 */
183 AnnualTimeZoneRule::AnnualTimeZoneRule(const UnicodeString
& name
,
186 const DateTimeRule
& dateTimeRule
,
189 : TimeZoneRule(name
, rawOffset
, dstSavings
), fDateTimeRule(new DateTimeRule(dateTimeRule
)),
190 fStartYear(startYear
), fEndYear(endYear
) {
193 AnnualTimeZoneRule::AnnualTimeZoneRule(const UnicodeString
& name
,
196 DateTimeRule
* dateTimeRule
,
199 : TimeZoneRule(name
, rawOffset
, dstSavings
), fDateTimeRule(dateTimeRule
),
200 fStartYear(startYear
), fEndYear(endYear
) {
203 AnnualTimeZoneRule::AnnualTimeZoneRule(const AnnualTimeZoneRule
& source
)
204 : TimeZoneRule(source
), fDateTimeRule(new DateTimeRule(*(source
.fDateTimeRule
))),
205 fStartYear(source
.fStartYear
), fEndYear(source
.fEndYear
) {
208 AnnualTimeZoneRule::~AnnualTimeZoneRule() {
209 delete fDateTimeRule
;
213 AnnualTimeZoneRule::clone(void) const {
214 return new AnnualTimeZoneRule(*this);
218 AnnualTimeZoneRule::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
;
230 AnnualTimeZoneRule::operator==(const TimeZoneRule
& that
) const {
234 if (typeid(*this) != typeid(that
)) {
237 AnnualTimeZoneRule
*atzr
= (AnnualTimeZoneRule
*)&that
;
238 return (*fDateTimeRule
== *(atzr
->fDateTimeRule
) &&
239 fStartYear
== atzr
->fStartYear
&&
240 fEndYear
== atzr
->fEndYear
);
244 AnnualTimeZoneRule::operator!=(const TimeZoneRule
& that
) const {
245 return !operator==(that
);
249 AnnualTimeZoneRule::getRule() const {
250 return fDateTimeRule
;
254 AnnualTimeZoneRule::getStartYear() const {
259 AnnualTimeZoneRule::getEndYear() const {
264 AnnualTimeZoneRule::getStartInYear(int32_t year
,
265 int32_t prevRawOffset
,
266 int32_t prevDSTSavings
,
267 UDate
&result
) const {
268 if (year
< fStartYear
|| year
> fEndYear
) {
272 DateTimeRule::DateRuleType type
= fDateTimeRule
->getDateRuleType();
273 if (type
== DateTimeRule::DOM
) {
274 ruleDay
= Grego::fieldsToDay(year
, fDateTimeRule
->getRuleMonth(), fDateTimeRule
->getRuleDayOfMonth());
277 if (type
== DateTimeRule::DOW
) {
278 // Normalize DOW rule into DOW_GEQ_DOM or DOW_LEQ_DOM
279 int32_t weeks
= fDateTimeRule
->getRuleWeekInMonth();
281 ruleDay
= Grego::fieldsToDay(year
, fDateTimeRule
->getRuleMonth(), 1);
282 ruleDay
+= 7 * (weeks
- 1);
285 ruleDay
= Grego::fieldsToDay(year
, fDateTimeRule
->getRuleMonth(),
286 Grego::monthLength(year
, fDateTimeRule
->getRuleMonth()));
287 ruleDay
+= 7 * (weeks
+ 1);
290 int32_t month
= fDateTimeRule
->getRuleMonth();
291 int32_t dom
= fDateTimeRule
->getRuleDayOfMonth();
292 if (type
== DateTimeRule::DOW_LEQ_DOM
) {
295 if (month
== UCAL_FEBRUARY
&& dom
== 29 && !Grego::isLeapYear(year
)) {
299 ruleDay
= Grego::fieldsToDay(year
, month
, dom
);
301 int32_t dow
= Grego::dayOfWeek(ruleDay
);
302 int32_t delta
= fDateTimeRule
->getRuleDayOfWeek() - dow
;
304 delta
= delta
< 0 ? delta
+ 7 : delta
;
306 delta
= delta
> 0 ? delta
- 7 : delta
;
311 result
= ruleDay
*U_MILLIS_PER_DAY
+ fDateTimeRule
->getRuleMillisInDay();
312 if (fDateTimeRule
->getTimeRuleType() != DateTimeRule::UTC_TIME
) {
313 result
-= prevRawOffset
;
315 if (fDateTimeRule
->getTimeRuleType() == DateTimeRule::WALL_TIME
) {
316 result
-= prevDSTSavings
;
322 AnnualTimeZoneRule::isEquivalentTo(const TimeZoneRule
& other
) const {
323 if (this == &other
) {
326 if (typeid(*this) != typeid(other
) || TimeZoneRule::isEquivalentTo(other
) == FALSE
) {
329 AnnualTimeZoneRule
* that
= (AnnualTimeZoneRule
*)&other
;
330 return (*fDateTimeRule
== *(that
->fDateTimeRule
) &&
331 fStartYear
== that
->fStartYear
&&
332 fEndYear
== that
->fEndYear
);
336 AnnualTimeZoneRule::getFirstStart(int32_t prevRawOffset
,
337 int32_t prevDSTSavings
,
338 UDate
& result
) const {
339 return getStartInYear(fStartYear
, prevRawOffset
, prevDSTSavings
, result
);
343 AnnualTimeZoneRule::getFinalStart(int32_t prevRawOffset
,
344 int32_t prevDSTSavings
,
345 UDate
& result
) const {
346 if (fEndYear
== MAX_YEAR
) {
349 return getStartInYear(fEndYear
, prevRawOffset
, prevDSTSavings
, result
);
353 AnnualTimeZoneRule::getNextStart(UDate base
,
354 int32_t prevRawOffset
,
355 int32_t prevDSTSavings
,
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
);
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
);
377 AnnualTimeZoneRule::getPreviousStart(UDate base
,
378 int32_t prevRawOffset
,
379 int32_t prevDSTSavings
,
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
);
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
);
400 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeArrayTimeZoneRule
)
402 TimeArrayTimeZoneRule::TimeArrayTimeZoneRule(const UnicodeString
& name
,
405 const UDate
* startTimes
,
406 int32_t numStartTimes
,
407 DateTimeRule::TimeRuleType timeRuleType
)
408 : TimeZoneRule(name
, rawOffset
, dstSavings
), fTimeRuleType(timeRuleType
),
410 UErrorCode status
= U_ZERO_ERROR
;
411 initStartTimes(startTimes
, numStartTimes
, status
);
416 TimeArrayTimeZoneRule::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
);
424 TimeArrayTimeZoneRule::~TimeArrayTimeZoneRule() {
425 if (fStartTimes
!= NULL
&& fStartTimes
!= fLocalStartTimes
) {
426 uprv_free(fStartTimes
);
430 TimeArrayTimeZoneRule
*
431 TimeArrayTimeZoneRule::clone(void) const {
432 return new TimeArrayTimeZoneRule(*this);
436 TimeArrayTimeZoneRule
&
437 TimeArrayTimeZoneRule::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
);
443 fTimeRuleType
= right
.fTimeRuleType
;
449 TimeArrayTimeZoneRule::operator==(const TimeZoneRule
& that
) const {
453 if (typeid(*this) != typeid(that
) || TimeZoneRule::operator==(that
) == FALSE
) {
456 TimeArrayTimeZoneRule
*tatzr
= (TimeArrayTimeZoneRule
*)&that
;
457 if (fTimeRuleType
!= tatzr
->fTimeRuleType
||
458 fNumStartTimes
!= tatzr
->fNumStartTimes
) {
461 // Compare start times
463 for (int32_t i
= 0; i
< fNumStartTimes
; i
++) {
464 if (fStartTimes
[i
] != tatzr
->fStartTimes
[i
]) {
473 TimeArrayTimeZoneRule::operator!=(const TimeZoneRule
& that
) const {
474 return !operator==(that
);
477 DateTimeRule::TimeRuleType
478 TimeArrayTimeZoneRule::getTimeType(void) const {
479 return fTimeRuleType
;
483 TimeArrayTimeZoneRule::getStartTimeAt(int32_t index
, UDate
& result
) const {
484 if (index
>= fNumStartTimes
|| index
< 0) {
487 result
= fStartTimes
[index
];
492 TimeArrayTimeZoneRule::countStartTimes(void) const {
493 return fNumStartTimes
;
497 TimeArrayTimeZoneRule::isEquivalentTo(const TimeZoneRule
& other
) const {
498 if (this == &other
) {
501 if (typeid(*this) != typeid(other
) || TimeZoneRule::isEquivalentTo(other
) == FALSE
) {
504 TimeArrayTimeZoneRule
* that
= (TimeArrayTimeZoneRule
*)&other
;
505 if (fTimeRuleType
!= that
->fTimeRuleType
||
506 fNumStartTimes
!= that
->fNumStartTimes
) {
509 // Compare start times
511 for (int32_t i
= 0; i
< fNumStartTimes
; i
++) {
512 if (fStartTimes
[i
] != that
->fStartTimes
[i
]) {
521 TimeArrayTimeZoneRule::getFirstStart(int32_t prevRawOffset
,
522 int32_t prevDSTSavings
,
523 UDate
& result
) const {
524 if (fNumStartTimes
<= 0 || fStartTimes
== NULL
) {
527 result
= getUTC(fStartTimes
[0], prevRawOffset
, prevDSTSavings
);
532 TimeArrayTimeZoneRule::getFinalStart(int32_t prevRawOffset
,
533 int32_t prevDSTSavings
,
534 UDate
& result
) const {
535 if (fNumStartTimes
<= 0 || fStartTimes
== NULL
) {
538 result
= getUTC(fStartTimes
[fNumStartTimes
- 1], prevRawOffset
, prevDSTSavings
);
543 TimeArrayTimeZoneRule::getNextStart(UDate base
,
544 int32_t prevRawOffset
,
545 int32_t prevDSTSavings
,
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
)) {
556 if (i
== fNumStartTimes
- 1) {
563 TimeArrayTimeZoneRule::getPreviousStart(UDate base
,
564 int32_t prevRawOffset
,
565 int32_t prevDSTSavings
,
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
)) {
580 // ---- private methods ------
583 TimeArrayTimeZoneRule::initStartTimes(const UDate source
[], int32_t size
, UErrorCode
& status
) {
585 if (fStartTimes
!= NULL
&& fStartTimes
!= fLocalStartTimes
) {
586 uprv_free(fStartTimes
);
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
;
597 fStartTimes
= (UDate
*)fLocalStartTimes
;
599 uprv_memcpy(fStartTimes
, source
, sizeof(UDate
)*size
);
600 fNumStartTimes
= size
;
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
);
614 TimeArrayTimeZoneRule::getUTC(UDate time
, int32_t raw
, int32_t dst
) const {
615 if (fTimeRuleType
!= DateTimeRule::UTC_TIME
) {
618 if (fTimeRuleType
== DateTimeRule::WALL_TIME
) {
626 #endif /* #if !UCONFIG_NO_FORMATTING */