• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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