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-2013, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
8 */
9
10 #include "unicode/utypes.h"
11
12 #if !UCONFIG_NO_FORMATTING
13
14 #include "unicode/basictz.h"
15 #include "gregoimp.h"
16 #include "uvector.h"
17 #include "cmemory.h"
18
19 U_NAMESPACE_BEGIN
20
21 #define MILLIS_PER_YEAR (365*24*60*60*1000.0)
22
BasicTimeZone()23 BasicTimeZone::BasicTimeZone()
24 : TimeZone() {
25 }
26
BasicTimeZone(const UnicodeString & id)27 BasicTimeZone::BasicTimeZone(const UnicodeString &id)
28 : TimeZone(id) {
29 }
30
BasicTimeZone(const BasicTimeZone & source)31 BasicTimeZone::BasicTimeZone(const BasicTimeZone& source)
32 : TimeZone(source) {
33 }
34
~BasicTimeZone()35 BasicTimeZone::~BasicTimeZone() {
36 }
37
38 UBool
hasEquivalentTransitions(const BasicTimeZone & tz,UDate start,UDate end,UBool ignoreDstAmount,UErrorCode & status) const39 BasicTimeZone::hasEquivalentTransitions(const BasicTimeZone& tz, UDate start, UDate end,
40 UBool ignoreDstAmount, UErrorCode& status) const {
41 if (U_FAILURE(status)) {
42 return false;
43 }
44 if (hasSameRules(tz)) {
45 return true;
46 }
47 // Check the offsets at the start time
48 int32_t raw1, raw2, dst1, dst2;
49 getOffset(start, false, raw1, dst1, status);
50 if (U_FAILURE(status)) {
51 return false;
52 }
53 tz.getOffset(start, false, raw2, dst2, status);
54 if (U_FAILURE(status)) {
55 return false;
56 }
57 if (ignoreDstAmount) {
58 if ((raw1 + dst1 != raw2 + dst2)
59 || (dst1 != 0 && dst2 == 0)
60 || (dst1 == 0 && dst2 != 0)) {
61 return false;
62 }
63 } else {
64 if (raw1 != raw2 || dst1 != dst2) {
65 return false;
66 }
67 }
68 // Check transitions in the range
69 UDate time = start;
70 TimeZoneTransition tr1, tr2;
71 while (true) {
72 UBool avail1 = getNextTransition(time, false, tr1);
73 UBool avail2 = tz.getNextTransition(time, false, tr2);
74
75 if (ignoreDstAmount) {
76 // Skip a transition which only differ the amount of DST savings
77 while (true) {
78 if (avail1
79 && tr1.getTime() <= end
80 && (tr1.getFrom()->getRawOffset() + tr1.getFrom()->getDSTSavings()
81 == tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings())
82 && (tr1.getFrom()->getDSTSavings() != 0 && tr1.getTo()->getDSTSavings() != 0)) {
83 getNextTransition(tr1.getTime(), false, tr1);
84 } else {
85 break;
86 }
87 }
88 while (true) {
89 if (avail2
90 && tr2.getTime() <= end
91 && (tr2.getFrom()->getRawOffset() + tr2.getFrom()->getDSTSavings()
92 == tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings())
93 && (tr2.getFrom()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() != 0)) {
94 tz.getNextTransition(tr2.getTime(), false, tr2);
95 } else {
96 break;
97 }
98 }
99 }
100
101 UBool inRange1 = (avail1 && tr1.getTime() <= end);
102 UBool inRange2 = (avail2 && tr2.getTime() <= end);
103 if (!inRange1 && !inRange2) {
104 // No more transition in the range
105 break;
106 }
107 if (!inRange1 || !inRange2) {
108 return false;
109 }
110 if (tr1.getTime() != tr2.getTime()) {
111 return false;
112 }
113 if (ignoreDstAmount) {
114 if (tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings()
115 != tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings()
116 || (tr1.getTo()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() == 0)
117 || (tr1.getTo()->getDSTSavings() == 0 && tr2.getTo()->getDSTSavings() != 0)) {
118 return false;
119 }
120 } else {
121 if (tr1.getTo()->getRawOffset() != tr2.getTo()->getRawOffset() ||
122 tr1.getTo()->getDSTSavings() != tr2.getTo()->getDSTSavings()) {
123 return false;
124 }
125 }
126 time = tr1.getTime();
127 }
128 return true;
129 }
130
131 void
getSimpleRulesNear(UDate date,InitialTimeZoneRule * & initial,AnnualTimeZoneRule * & std,AnnualTimeZoneRule * & dst,UErrorCode & status) const132 BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial,
133 AnnualTimeZoneRule*& std, AnnualTimeZoneRule*& dst, UErrorCode& status) const {
134 initial = nullptr;
135 std = nullptr;
136 dst = nullptr;
137 if (U_FAILURE(status)) {
138 return;
139 }
140 int32_t initialRaw, initialDst;
141 UnicodeString initialName;
142
143 AnnualTimeZoneRule *ar1 = nullptr;
144 AnnualTimeZoneRule *ar2 = nullptr;
145 UnicodeString name;
146
147 UBool avail;
148 TimeZoneTransition tr;
149 // Get the next transition
150 avail = getNextTransition(date, false, tr);
151 if (avail) {
152 tr.getFrom()->getName(initialName);
153 initialRaw = tr.getFrom()->getRawOffset();
154 initialDst = tr.getFrom()->getDSTSavings();
155
156 // Check if the next transition is either DST->STD or STD->DST and
157 // within roughly 1 year from the specified date
158 UDate nextTransitionTime = tr.getTime();
159 if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
160 || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0))
161 && (date + MILLIS_PER_YEAR > nextTransitionTime)) {
162
163 int32_t year, month, dom, dow, doy, mid;
164 UDate d;
165
166 // Get local wall time for the next transition time
167 Grego::timeToFields(nextTransitionTime + initialRaw + initialDst,
168 year, month, dom, dow, doy, mid, status);
169 if (U_FAILURE(status)) return;
170 int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
171 // Create DOW rule
172 DateTimeRule *dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
173 tr.getTo()->getName(name);
174
175 // Note: SimpleTimeZone does not support raw offset change.
176 // So we always use raw offset of the given time for the rule,
177 // even raw offset is changed. This will result that the result
178 // zone to return wrong offset after the transition.
179 // When we encounter such case, we do not inspect next next
180 // transition for another rule.
181 ar1 = new AnnualTimeZoneRule(name, initialRaw, tr.getTo()->getDSTSavings(),
182 dtr, year, AnnualTimeZoneRule::MAX_YEAR);
183
184 if (tr.getTo()->getRawOffset() == initialRaw) {
185 // Get the next next transition
186 avail = getNextTransition(nextTransitionTime, false, tr);
187 if (avail) {
188 // Check if the next next transition is either DST->STD or STD->DST
189 // and within roughly 1 year from the next transition
190 if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
191 || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0))
192 && nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) {
193
194 // Get local wall time for the next transition time
195 Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
196 year, month, dom, dow, doy, mid, status);
197 if (U_FAILURE(status)) return;
198 weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
199 // Generate another DOW rule
200 dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
201 tr.getTo()->getName(name);
202 ar2 = new AnnualTimeZoneRule(name, tr.getTo()->getRawOffset(), tr.getTo()->getDSTSavings(),
203 dtr, year - 1, AnnualTimeZoneRule::MAX_YEAR);
204
205 // Make sure this rule can be applied to the specified date
206 avail = ar2->getPreviousStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), true, d);
207 if (!avail || d > date
208 || initialRaw != tr.getTo()->getRawOffset()
209 || initialDst != tr.getTo()->getDSTSavings()) {
210 // We cannot use this rule as the second transition rule
211 delete ar2;
212 ar2 = nullptr;
213 }
214 }
215 }
216 }
217 if (ar2 == nullptr) {
218 // Try previous transition
219 avail = getPreviousTransition(date, true, tr);
220 if (avail) {
221 // Check if the previous transition is either DST->STD or STD->DST.
222 // The actual transition time does not matter here.
223 if ((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
224 || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) {
225
226 // Generate another DOW rule
227 Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
228 year, month, dom, dow, doy, mid, status);
229 if (U_FAILURE(status)) return;
230 weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
231 dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
232 tr.getTo()->getName(name);
233
234 // second rule raw/dst offsets should match raw/dst offsets
235 // at the given time
236 ar2 = new AnnualTimeZoneRule(name, initialRaw, initialDst,
237 dtr, ar1->getStartYear() - 1, AnnualTimeZoneRule::MAX_YEAR);
238
239 // Check if this rule start after the first rule after the specified date
240 avail = ar2->getNextStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), false, d);
241 if (!avail || d <= nextTransitionTime) {
242 // We cannot use this rule as the second transition rule
243 delete ar2;
244 ar2 = nullptr;
245 }
246 }
247 }
248 }
249 if (ar2 == nullptr) {
250 // Cannot find a good pair of AnnualTimeZoneRule
251 delete ar1;
252 ar1 = nullptr;
253 } else {
254 // The initial rule should represent the rule before the previous transition
255 ar1->getName(initialName);
256 initialRaw = ar1->getRawOffset();
257 initialDst = ar1->getDSTSavings();
258 }
259 }
260 }
261 else {
262 // Try the previous one
263 avail = getPreviousTransition(date, true, tr);
264 if (avail) {
265 tr.getTo()->getName(initialName);
266 initialRaw = tr.getTo()->getRawOffset();
267 initialDst = tr.getTo()->getDSTSavings();
268 } else {
269 // No transitions in the past. Just use the current offsets
270 getOffset(date, false, initialRaw, initialDst, status);
271 if (U_FAILURE(status)) {
272 return;
273 }
274 }
275 }
276 // Set the initial rule
277 initial = new InitialTimeZoneRule(initialName, initialRaw, initialDst);
278
279 // Set the standard and daylight saving rules
280 if (ar1 != nullptr && ar2 != nullptr) {
281 if (ar1->getDSTSavings() != 0) {
282 dst = ar1;
283 std = ar2;
284 } else {
285 std = ar1;
286 dst = ar2;
287 }
288 }
289 }
290
291 void
getTimeZoneRulesAfter(UDate start,InitialTimeZoneRule * & initial,UVector * & transitionRules,UErrorCode & status) const292 BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial,
293 UVector*& transitionRules, UErrorCode& status) const {
294 if (U_FAILURE(status)) {
295 return;
296 }
297
298 const InitialTimeZoneRule *orgini;
299 TimeZoneTransition tzt;
300 bool avail;
301 int32_t ruleCount;
302 TimeZoneRule *r = nullptr;
303 UnicodeString name;
304 int32_t i;
305 UDate time, t;
306 UDate firstStart;
307 UBool bFinalStd = false, bFinalDst = false;
308
309 initial = nullptr;
310 transitionRules = nullptr;
311
312 // Original transition rules
313 ruleCount = countTransitionRules(status);
314 if (U_FAILURE(status)) {
315 return;
316 }
317 LocalPointer<UVector> orgRules(
318 new UVector(uprv_deleteUObject, nullptr, ruleCount, status), status);
319 if (U_FAILURE(status)) {
320 return;
321 }
322 LocalMemory<const TimeZoneRule *> orgtrs(
323 static_cast<const TimeZoneRule **>(uprv_malloc(sizeof(TimeZoneRule*)*ruleCount)));
324 if (orgtrs.isNull()) {
325 status = U_MEMORY_ALLOCATION_ERROR;
326 return;
327 }
328 getTimeZoneRules(orgini, &orgtrs[0], ruleCount, status);
329 if (U_FAILURE(status)) {
330 return;
331 }
332 for (i = 0; i < ruleCount; i++) {
333 LocalPointer<TimeZoneRule> lpRule(orgtrs[i]->clone(), status);
334 orgRules->adoptElement(lpRule.orphan(), status);
335 if (U_FAILURE(status)) {
336 return;
337 }
338 }
339
340 avail = getPreviousTransition(start, true, tzt);
341 if (!avail) {
342 // No need to filter out rules only applicable to time before the start
343 initial = orgini->clone();
344 if (initial == nullptr) {
345 status = U_MEMORY_ALLOCATION_ERROR;
346 return;
347 }
348 transitionRules = orgRules.orphan();
349 return;
350 }
351
352 LocalMemory<bool> done(static_cast<bool *>(uprv_malloc(sizeof(bool)*ruleCount)));
353 if (done.isNull()) {
354 status = U_MEMORY_ALLOCATION_ERROR;
355 return;
356 }
357 LocalPointer<UVector> filteredRules(
358 new UVector(uprv_deleteUObject, nullptr, status), status);
359 if (U_FAILURE(status)) {
360 return;
361 }
362
363 // Create initial rule
364 tzt.getTo()->getName(name);
365 LocalPointer<InitialTimeZoneRule> res_initial(
366 new InitialTimeZoneRule(name, tzt.getTo()->getRawOffset(), tzt.getTo()->getDSTSavings()), status);
367 if (U_FAILURE(status)) {
368 return;
369 }
370
371 // Mark rules which does not need to be processed
372 for (i = 0; i < ruleCount; i++) {
373 r = static_cast<TimeZoneRule*>(orgRules->elementAt(i));
374 avail = r->getNextStart(start, res_initial->getRawOffset(), res_initial->getDSTSavings(), false, time);
375 done[i] = !avail;
376 }
377
378 time = start;
379 while (!bFinalStd || !bFinalDst) {
380 avail = getNextTransition(time, false, tzt);
381 if (!avail) {
382 break;
383 }
384 UDate updatedTime = tzt.getTime();
385 if (updatedTime == time) {
386 // Can get here if rules for start & end of daylight time have exactly
387 // the same time.
388 // TODO: fix getNextTransition() to prevent it?
389 status = U_INVALID_STATE_ERROR;
390 return;
391 }
392 time = updatedTime;
393
394 const TimeZoneRule *toRule = tzt.getTo();
395 for (i = 0; i < ruleCount; i++) {
396 r = static_cast<TimeZoneRule*>(orgRules->elementAt(i));
397 if (*r == *toRule) {
398 break;
399 }
400 }
401 if (i >= ruleCount) {
402 // This case should never happen
403 status = U_INVALID_STATE_ERROR;
404 return;
405 }
406 if (done[i]) {
407 continue;
408 }
409 const TimeArrayTimeZoneRule *tar = dynamic_cast<const TimeArrayTimeZoneRule *>(toRule);
410 const AnnualTimeZoneRule *ar;
411 if (tar != nullptr) {
412 // Get the previous raw offset and DST savings before the very first start time
413 TimeZoneTransition tzt0;
414 t = start;
415 while (true) {
416 avail = getNextTransition(t, false, tzt0);
417 if (!avail) {
418 break;
419 }
420 if (*(tzt0.getTo()) == *tar) {
421 break;
422 }
423 t = tzt0.getTime();
424 }
425 if (avail) {
426 // Check if the entire start times to be added
427 tar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
428 if (firstStart > start) {
429 // Just add the rule as is
430 LocalPointer<TimeArrayTimeZoneRule> lpTar(tar->clone(), status);
431 filteredRules->adoptElement(lpTar.orphan(), status);
432 if (U_FAILURE(status)) {
433 return;
434 }
435 } else {
436 // Collect transitions after the start time
437 int32_t startTimes;
438 DateTimeRule::TimeRuleType timeType;
439 int32_t idx;
440
441 startTimes = tar->countStartTimes();
442 timeType = tar->getTimeType();
443 for (idx = 0; idx < startTimes; idx++) {
444 tar->getStartTimeAt(idx, t);
445 if (timeType == DateTimeRule::STANDARD_TIME) {
446 t -= tzt.getFrom()->getRawOffset();
447 }
448 if (timeType == DateTimeRule::WALL_TIME) {
449 t -= tzt.getFrom()->getDSTSavings();
450 }
451 if (t > start) {
452 break;
453 }
454 }
455 if (U_FAILURE(status)) {
456 return;
457 }
458 int32_t asize = startTimes - idx;
459 if (asize > 0) {
460 LocalMemory<UDate> newTimes(static_cast<UDate *>(uprv_malloc(sizeof(UDate) * asize)));
461 if (newTimes.isNull()) {
462 status = U_MEMORY_ALLOCATION_ERROR;
463 return;
464 }
465 for (int32_t newidx = 0; newidx < asize; newidx++) {
466 tar->getStartTimeAt(idx + newidx, newTimes[newidx]);
467 }
468 tar->getName(name);
469 LocalPointer<TimeArrayTimeZoneRule> newTar(new TimeArrayTimeZoneRule(
470 name, tar->getRawOffset(), tar->getDSTSavings(), &newTimes[0], asize, timeType), status);
471 filteredRules->adoptElement(newTar.orphan(), status);
472 if (U_FAILURE(status)) {
473 return;
474 }
475 }
476 }
477 }
478 } else if ((ar = dynamic_cast<const AnnualTimeZoneRule *>(toRule)) != nullptr) {
479 ar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
480 if (firstStart == tzt.getTime()) {
481 // Just add the rule as is
482 LocalPointer<AnnualTimeZoneRule> arClone(ar->clone(), status);
483 filteredRules->adoptElement(arClone.orphan(), status);
484 if (U_FAILURE(status)) {
485 return;
486 }
487 } else {
488 // Calculate the transition year
489 int32_t year, month, dom, dow, doy, mid;
490 Grego::timeToFields(tzt.getTime(), year, month, dom, dow, doy, mid, status);
491 if (U_FAILURE(status)) {
492 return;
493 }
494 // Re-create the rule
495 ar->getName(name);
496 LocalPointer<AnnualTimeZoneRule> newAr(new AnnualTimeZoneRule(name, ar->getRawOffset(), ar->getDSTSavings(),
497 *(ar->getRule()), year, ar->getEndYear()), status);
498 filteredRules->adoptElement(newAr.orphan(), status);
499 if (U_FAILURE(status)) {
500 return;
501 }
502 }
503 // check if this is a final rule
504 if (ar->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) {
505 // After bot final standard and dst rules are processed,
506 // exit this while loop.
507 if (ar->getDSTSavings() == 0) {
508 bFinalStd = true;
509 } else {
510 bFinalDst = true;
511 }
512 }
513 }
514 done[i] = true;
515 }
516
517 // Set the results
518 initial = res_initial.orphan();
519 transitionRules = filteredRules.orphan();
520 }
521
522 void
getOffsetFromLocal(UDate,UTimeZoneLocalOption,UTimeZoneLocalOption,int32_t &,int32_t &,UErrorCode & status) const523 BasicTimeZone::getOffsetFromLocal(UDate /*date*/, UTimeZoneLocalOption /*nonExistingTimeOpt*/,
524 UTimeZoneLocalOption /*duplicatedTimeOpt*/,
525 int32_t& /*rawOffset*/, int32_t& /*dstOffset*/,
526 UErrorCode& status) const {
527 if (U_FAILURE(status)) {
528 return;
529 }
530 status = U_UNSUPPORTED_ERROR;
531 }
532
getOffsetFromLocal(UDate date,int32_t nonExistingTimeOpt,int32_t duplicatedTimeOpt,int32_t & rawOffset,int32_t & dstOffset,UErrorCode & status) const533 void BasicTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
534 int32_t& rawOffset, int32_t& dstOffset,
535 UErrorCode& status) const {
536 getOffsetFromLocal(date, static_cast<UTimeZoneLocalOption>(nonExistingTimeOpt),
537 static_cast<UTimeZoneLocalOption>(duplicatedTimeOpt), rawOffset, dstOffset, status);
538 }
539
540 U_NAMESPACE_END
541
542 #endif /* #if !UCONFIG_NO_FORMATTING */
543
544 //eof
545