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