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