1// © 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() 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