]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/basictz.cpp
ICU-400.42.tar.gz
[apple/icu.git] / icuSources / i18n / basictz.cpp
CommitLineData
46f4442e
A
1/*
2*******************************************************************************
3* Copyright (C) 2007-2008, 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
17U_NAMESPACE_BEGIN
18
19#define MILLIS_PER_YEAR (365*24*60*60*1000.0)
20
21BasicTimeZone::BasicTimeZone()
22: TimeZone() {
23}
24
25BasicTimeZone::BasicTimeZone(const UnicodeString &id)
26: TimeZone(id) {
27}
28
29BasicTimeZone::BasicTimeZone(const BasicTimeZone& source)
30: TimeZone(source) {
31}
32
33BasicTimeZone::~BasicTimeZone() {
34}
35
36UBool
37BasicTimeZone::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
119void
120BasicTimeZone::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 }
234 else {
235 // Try the previous one
236 avail = getPreviousTransition(date, TRUE, tr);
237 if (avail) {
238 tr.getTo()->getName(initialName);
239 initialRaw = tr.getTo()->getRawOffset();
240 initialDst = tr.getTo()->getDSTSavings();
241 } else {
242 // No transitions in the past. Just use the current offsets
243 getOffset(date, FALSE, initialRaw, initialDst, status);
244 if (U_FAILURE(status)) {
245 return;
246 }
247 }
248 }
249 // Set the initial rule
250 initial = new InitialTimeZoneRule(initialName, initialRaw, initialDst);
251
252 // Set the standard and daylight saving rules
253 if (ar1 != NULL && ar2 != NULL) {
254 if (ar1->getDSTSavings() != 0) {
255 dst = ar1;
256 std = ar2;
257 } else {
258 std = ar1;
259 dst = ar2;
260 }
261 }
262}
263
264void
265BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial,
266 UVector*& transitionRules, UErrorCode& status) /*const*/ {
267 if (U_FAILURE(status)) {
268 return;
269 }
270
271 const InitialTimeZoneRule *orgini;
272 const TimeZoneRule **orgtrs = NULL;
273 TimeZoneTransition tzt;
274 UBool avail;
275 UVector *orgRules = NULL;
276 int32_t ruleCount;
277 TimeZoneRule *r = NULL;
278 UBool *done = NULL;
279 InitialTimeZoneRule *res_initial = NULL;
280 UVector *filteredRules = NULL;
281 UnicodeString name;
282 int32_t i;
283 UDate time, t;
284 UDate *newTimes = NULL;
285 UDate firstStart;
286 UBool bFinalStd = FALSE, bFinalDst = FALSE;
287
288 // Original transition rules
289 ruleCount = countTransitionRules(status);
290 if (U_FAILURE(status)) {
291 return;
292 }
293 orgRules = new UVector(ruleCount, status);
294 if (U_FAILURE(status)) {
295 return;
296 }
297 orgtrs = (const TimeZoneRule**)uprv_malloc(sizeof(TimeZoneRule*)*ruleCount);
298 if (orgtrs == NULL) {
299 status = U_MEMORY_ALLOCATION_ERROR;
300 goto error;
301 }
302 getTimeZoneRules(orgini, orgtrs, ruleCount, status);
303 if (U_FAILURE(status)) {
304 goto error;
305 }
306 for (i = 0; i < ruleCount; i++) {
307 orgRules->addElement(orgtrs[i]->clone(), status);
308 if (U_FAILURE(status)) {
309 goto error;
310 }
311 }
312 uprv_free(orgtrs);
313 orgtrs = NULL;
314
315 avail = getPreviousTransition(start, TRUE, tzt);
316 if (!avail) {
317 // No need to filter out rules only applicable to time before the start
318 initial = orgini->clone();
319 transitionRules = orgRules;
320 return;
321 }
322
323 done = (UBool*)uprv_malloc(sizeof(UBool)*ruleCount);
324 if (done == NULL) {
325 status = U_MEMORY_ALLOCATION_ERROR;
326 goto error;
327 }
328 filteredRules = new UVector(status);
329 if (U_FAILURE(status)) {
330 goto error;
331 }
332
333 // Create initial rule
334 tzt.getTo()->getName(name);
335 res_initial = new InitialTimeZoneRule(name, tzt.getTo()->getRawOffset(),
336 tzt.getTo()->getDSTSavings());
337
338 // Mark rules which does not need to be processed
339 for (i = 0; i < ruleCount; i++) {
340 r = (TimeZoneRule*)orgRules->elementAt(i);
341 avail = r->getNextStart(start, res_initial->getRawOffset(), res_initial->getDSTSavings(), FALSE, time);
342 done[i] = !avail;
343 }
344
345 time = start;
346 while (!bFinalStd || !bFinalDst) {
347 avail = getNextTransition(time, FALSE, tzt);
348 if (!avail) {
349 break;
350 }
351 time = tzt.getTime();
352
353 const TimeZoneRule *toRule = tzt.getTo();
354 for (i = 0; i < ruleCount; i++) {
355 r = (TimeZoneRule*)orgRules->elementAt(i);
356 if (*r == *toRule) {
357 break;
358 }
359 }
360 if (i >= ruleCount) {
361 // This case should never happen
362 status = U_INVALID_STATE_ERROR;
363 goto error;
364 }
365 if (done[i]) {
366 continue;
367 }
368 if (toRule->getDynamicClassID() == TimeArrayTimeZoneRule::getStaticClassID()) {
369 TimeArrayTimeZoneRule *tar = (TimeArrayTimeZoneRule*)toRule;
370
371 // Get the previous raw offset and DST savings before the very first start time
372 TimeZoneTransition tzt0;
373 t = start;
374 while (TRUE) {
375 avail = getNextTransition(t, FALSE, tzt0);
376 if (!avail) {
377 break;
378 }
379 if (*(tzt0.getTo()) == *tar) {
380 break;
381 }
382 t = tzt0.getTime();
383 }
384 if (avail) {
385 // Check if the entire start times to be added
386 tar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
387 if (firstStart > start) {
388 // Just add the rule as is
389 filteredRules->addElement(tar->clone(), status);
390 if (U_FAILURE(status)) {
391 goto error;
392 }
393 } else {
394 // Colllect transitions after the start time
395 int32_t startTimes;
396 DateTimeRule::TimeRuleType timeType;
397 int32_t idx;
398
399 startTimes = tar->countStartTimes();
400 timeType = tar->getTimeType();
401 for (idx = 0; idx < startTimes; idx++) {
402 tar->getStartTimeAt(idx, t);
403 if (timeType == DateTimeRule::STANDARD_TIME) {
404 t -= tzt.getFrom()->getRawOffset();
405 }
406 if (timeType == DateTimeRule::WALL_TIME) {
407 t -= tzt.getFrom()->getDSTSavings();
408 }
409 if (t > start) {
410 break;
411 }
412 }
413 int32_t asize = startTimes - idx;
414 if (asize > 0) {
415 newTimes = (UDate*)uprv_malloc(sizeof(UDate) * asize);
416 if (newTimes == NULL) {
417 status = U_MEMORY_ALLOCATION_ERROR;
418 goto error;
419 }
420 for (int32_t newidx = 0; newidx < asize; newidx++) {
421 tar->getStartTimeAt(idx + newidx, newTimes[newidx]);
422 if (U_FAILURE(status)) {
423 uprv_free(newTimes);
424 newTimes = NULL;
425 goto error;
426 }
427 }
428 tar->getName(name);
429 TimeArrayTimeZoneRule *newTar = new TimeArrayTimeZoneRule(name,
430 tar->getRawOffset(), tar->getDSTSavings(), newTimes, asize, timeType);
431 uprv_free(newTimes);
432 filteredRules->addElement(newTar, status);
433 if (U_FAILURE(status)) {
434 goto error;
435 }
436 }
437 }
438 }
439 } else if (toRule->getDynamicClassID() == AnnualTimeZoneRule::getStaticClassID()) {
440 AnnualTimeZoneRule *ar = (AnnualTimeZoneRule*)toRule;
441 ar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
442 if (firstStart == tzt.getTime()) {
443 // Just add the rule as is
444 filteredRules->addElement(ar->clone(), status);
445 if (U_FAILURE(status)) {
446 goto error;
447 }
448 } else {
449 // Calculate the transition year
450 int32_t year, month, dom, dow, doy, mid;
451 Grego::timeToFields(tzt.getTime(), year, month, dom, dow, doy, mid);
452 // Re-create the rule
453 ar->getName(name);
454 AnnualTimeZoneRule *newAr = new AnnualTimeZoneRule(name, ar->getRawOffset(), ar->getDSTSavings(),
455 *(ar->getRule()), year, ar->getEndYear());
456 filteredRules->addElement(newAr, status);
457 if (U_FAILURE(status)) {
458 goto error;
459 }
460 }
461 // check if this is a final rule
462 if (ar->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) {
463 // After bot final standard and dst rules are processed,
464 // exit this while loop.
465 if (ar->getDSTSavings() == 0) {
466 bFinalStd = TRUE;
467 } else {
468 bFinalDst = TRUE;
469 }
470 }
471 }
472 done[i] = TRUE;
473 }
474
475 // Set the results
476 if (orgRules != NULL) {
477 while (!orgRules->isEmpty()) {
478 r = (TimeZoneRule*)orgRules->orphanElementAt(0);
479 delete r;
480 }
481 delete orgRules;
482 }
483 if (done != NULL) {
484 uprv_free(done);
485 }
486
487 initial = res_initial;
488 transitionRules = filteredRules;
489 return;
490
491error:
492 if (orgtrs != NULL) {
493 uprv_free(orgtrs);
494 }
495 if (orgRules != NULL) {
496 while (!orgRules->isEmpty()) {
497 r = (TimeZoneRule*)orgRules->orphanElementAt(0);
498 delete r;
499 }
500 delete orgRules;
501 }
502 if (done != NULL) {
503 uprv_free(done);
504 }
505
506 initial = NULL;
507 transitionRules = NULL;
508}
509
510void
511BasicTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
512 int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) /*const*/ {
513 if (U_FAILURE(status)) {
514 return;
515 }
516 status = U_UNSUPPORTED_ERROR;
517}
518
519U_NAMESPACE_END
520
521#endif /* #if !UCONFIG_NO_FORMATTING */
522
523//eof