• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 * Copyright (C) 2007-2010, International Business Machines Corporation and         *
4 * others. All Rights Reserved.                                                *
5 *******************************************************************************
6 */
7 #include "unicode/utypes.h"
8 
9 #if !UCONFIG_NO_FORMATTING
10 
11 #include "tzoffloc.h"
12 
13 #include "unicode/ucal.h"
14 #include "unicode/timezone.h"
15 #include "unicode/calendar.h"
16 #include "unicode/dtrule.h"
17 #include "unicode/tzrule.h"
18 #include "unicode/rbtz.h"
19 #include "unicode/simpletz.h"
20 #include "unicode/tzrule.h"
21 #include "unicode/smpdtfmt.h"
22 #include "unicode/gregocal.h"
23 
24 void
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)25 TimeZoneOffsetLocalTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
26 {
27     if (exec) {
28         logln("TestSuite TimeZoneOffsetLocalTest");
29     }
30     switch (index) {
31         TESTCASE(0, TestGetOffsetAroundTransition);
32         default: name = ""; break;
33     }
34 }
35 
36 /*
37  * Testing getOffset APIs around rule transition by local standard/wall time.
38  */
39 void
TestGetOffsetAroundTransition()40 TimeZoneOffsetLocalTest::TestGetOffsetAroundTransition() {
41     const int32_t NUM_DATES = 10;
42     const int32_t NUM_TIMEZONES = 3;
43 
44     const int32_t HOUR = 60*60*1000;
45     const int32_t MINUTE = 60*1000;
46 
47     const int32_t DATES[NUM_DATES][6] = {
48         {2006, UCAL_APRIL, 2, 1, 30, 1*HOUR+30*MINUTE},
49         {2006, UCAL_APRIL, 2, 2, 00, 2*HOUR},
50         {2006, UCAL_APRIL, 2, 2, 30, 2*HOUR+30*MINUTE},
51         {2006, UCAL_APRIL, 2, 3, 00, 3*HOUR},
52         {2006, UCAL_APRIL, 2, 3, 30, 3*HOUR+30*MINUTE},
53         {2006, UCAL_OCTOBER, 29, 0, 30, 0*HOUR+30*MINUTE},
54         {2006, UCAL_OCTOBER, 29, 1, 00, 1*HOUR},
55         {2006, UCAL_OCTOBER, 29, 1, 30, 1*HOUR+30*MINUTE},
56         {2006, UCAL_OCTOBER, 29, 2, 00, 2*HOUR},
57         {2006, UCAL_OCTOBER, 29, 2, 30, 2*HOUR+30*MINUTE},
58     };
59 
60     // Expected offsets by int32_t getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
61     // uint8_t dayOfWeek, int32_t millis, UErrorCode& status)
62     const int32_t OFFSETS1[NUM_DATES] = {
63         // April 2, 2006
64         -8*HOUR,
65         -7*HOUR,
66         -7*HOUR,
67         -7*HOUR,
68         -7*HOUR,
69 
70         // October 29, 2006
71         -7*HOUR,
72         -8*HOUR,
73         -8*HOUR,
74         -8*HOUR,
75         -8*HOUR,
76     };
77 
78     // Expected offsets by void getOffset(UDate date, UBool local, int32_t& rawOffset,
79     // int32_t& dstOffset, UErrorCode& ec) with local=TRUE
80     // or void getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
81     // int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) with
82     // nonExistingTimeOpt=kStandard/duplicatedTimeOpt=kStandard
83     const int32_t OFFSETS2[NUM_DATES][2] = {
84         // April 2, 2006
85         {-8*HOUR, 0},
86         {-8*HOUR, 0},
87         {-8*HOUR, 0},
88         {-8*HOUR, 1*HOUR},
89         {-8*HOUR, 1*HOUR},
90 
91         // Oct 29, 2006
92         {-8*HOUR, 1*HOUR},
93         {-8*HOUR, 0},
94         {-8*HOUR, 0},
95         {-8*HOUR, 0},
96         {-8*HOUR, 0},
97     };
98 
99     // Expected offsets by void getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt,
100     // int32_t duplicatedTimeOpt, int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) with
101     // nonExistingTimeOpt=kDaylight/duplicatedTimeOpt=kDaylight
102     const int32_t OFFSETS3[][2] = {
103         // April 2, 2006
104         {-8*HOUR, 0},
105         {-8*HOUR, 1*HOUR},
106         {-8*HOUR, 1*HOUR},
107         {-8*HOUR, 1*HOUR},
108         {-8*HOUR, 1*HOUR},
109 
110         // October 29, 2006
111         {-8*HOUR, 1*HOUR},
112         {-8*HOUR, 1*HOUR},
113         {-8*HOUR, 1*HOUR},
114         {-8*HOUR, 0},
115         {-8*HOUR, 0},
116     };
117 
118     UErrorCode status = U_ZERO_ERROR;
119 
120     int32_t rawOffset, dstOffset;
121     TimeZone* utc = TimeZone::createTimeZone("UTC");
122     Calendar* cal = Calendar::createInstance(*utc, status);
123     if (U_FAILURE(status)) {
124         dataerrln("Calendar::createInstance failed: %s", u_errorName(status));
125         return;
126     }
127     cal->clear();
128 
129     // Set up TimeZone objects - OlsonTimeZone, SimpleTimeZone and RuleBasedTimeZone
130     BasicTimeZone *TESTZONES[NUM_TIMEZONES];
131 
132     TESTZONES[0] = (BasicTimeZone*)TimeZone::createTimeZone("America/Los_Angeles");
133     TESTZONES[1] = new SimpleTimeZone(-8*HOUR, "Simple Pacific Time",
134                                         UCAL_APRIL, 1, UCAL_SUNDAY, 2*HOUR,
135                                         UCAL_OCTOBER, -1, UCAL_SUNDAY, 2*HOUR, status);
136     if (U_FAILURE(status)) {
137         errln("SimpleTimeZone constructor failed");
138         return;
139     }
140 
141     InitialTimeZoneRule *ir = new InitialTimeZoneRule(
142             "Pacific Standard Time", // Initial time Name
143             -8*HOUR,        // Raw offset
144             0*HOUR);        // DST saving amount
145 
146     RuleBasedTimeZone *rbPT = new RuleBasedTimeZone("Rule based Pacific Time", ir);
147 
148     DateTimeRule *dtr;
149     AnnualTimeZoneRule *atzr;
150     const int32_t STARTYEAR = 2000;
151 
152     dtr = new DateTimeRule(UCAL_APRIL, 1, UCAL_SUNDAY,
153                         2*HOUR, DateTimeRule::WALL_TIME); // 1st Sunday in April, at 2AM wall time
154     atzr = new AnnualTimeZoneRule("Pacific Daylight Time",
155             -8*HOUR /* rawOffset */, 1*HOUR /* dstSavings */, dtr,
156             STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
157     rbPT->addTransitionRule(atzr, status);
158     if (U_FAILURE(status)) {
159         errln("Could not add DST start rule to the RuleBasedTimeZone rbPT");
160         return;
161     }
162 
163     dtr = new DateTimeRule(UCAL_OCTOBER, -1, UCAL_SUNDAY,
164                         2*HOUR, DateTimeRule::WALL_TIME); // last Sunday in October, at 2AM wall time
165     atzr = new AnnualTimeZoneRule("Pacific Standard Time",
166             -8*HOUR /* rawOffset */, 0 /* dstSavings */, dtr,
167             STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
168     rbPT->addTransitionRule(atzr, status);
169     if (U_FAILURE(status)) {
170         errln("Could not add STD start rule to the RuleBasedTimeZone rbPT");
171         return;
172     }
173 
174     rbPT->complete(status);
175     if (U_FAILURE(status)) {
176         errln("complete() failed for RuleBasedTimeZone rbPT");
177         return;
178     }
179 
180     TESTZONES[2] = rbPT;
181 
182     // Calculate millis
183     UDate MILLIS[NUM_DATES];
184     for (int32_t i = 0; i < NUM_DATES; i++) {
185         cal->clear();
186         cal->set(DATES[i][0], DATES[i][1], DATES[i][2], DATES[i][3], DATES[i][4]);
187         MILLIS[i] = cal->getTime(status);
188         if (U_FAILURE(status)) {
189             errln("cal->getTime failed");
190             return;
191         }
192     }
193 
194     SimpleDateFormat df(UnicodeString("yyyy-MM-dd HH:mm:ss"), status);
195     if (U_FAILURE(status)) {
196         errcheckln(status, "Failed to initialize a SimpleDateFormat - %s", u_errorName(status));
197     }
198     df.setTimeZone(*utc);
199     UnicodeString dateStr;
200 
201     // Test getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
202     // uint8_t dayOfWeek, int32_t millis, UErrorCode& status)
203     for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
204         for (int32_t d = 0; d < NUM_DATES; d++) {
205             status = U_ZERO_ERROR;
206             int32_t offset = TESTZONES[i]->getOffset(GregorianCalendar::AD, DATES[d][0], DATES[d][1], DATES[d][2],
207                                                 UCAL_SUNDAY, DATES[d][5], status);
208             if (U_FAILURE(status)) {
209                 errln((UnicodeString)"getOffset(era,year,month,day,dayOfWeek,millis,status) failed for TESTZONES[" + i + "]");
210             } else if (offset != OFFSETS1[d]) {
211                 dateStr.remove();
212                 df.format(MILLIS[d], dateStr);
213                 dataerrln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
214                         + dateStr + "(standard) - Got: " + offset + " Expected: " + OFFSETS1[d]);
215             }
216         }
217     }
218 
219     // Test getOffset(UDate date, UBool local, int32_t& rawOffset,
220     // int32_t& dstOffset, UErrorCode& ec) with local = TRUE
221     for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
222         for (int32_t m = 0; m < NUM_DATES; m++) {
223             status = U_ZERO_ERROR;
224             TESTZONES[i]->getOffset(MILLIS[m], TRUE, rawOffset, dstOffset, status);
225             if (U_FAILURE(status)) {
226                 errln((UnicodeString)"getOffset(date,local,rawOfset,dstOffset,ec) failed for TESTZONES[" + i + "]");
227             } else if (rawOffset != OFFSETS2[m][0] || dstOffset != OFFSETS2[m][1]) {
228                 dateStr.remove();
229                 df.format(MILLIS[m], dateStr);
230                 dataerrln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
231                         + dateStr + "(wall) - Got: "
232                         + rawOffset + "/" + dstOffset
233                         + " Expected: " + OFFSETS2[m][0] + "/" + OFFSETS2[m][1]);
234             }
235         }
236     }
237 
238     // Test getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
239     // int32_t& rawOffset, int32_t& dstOffset, UErroCode& status)
240     // with nonExistingTimeOpt=kStandard/duplicatedTimeOpt=kStandard
241     for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
242         for (int m = 0; m < NUM_DATES; m++) {
243             status = U_ZERO_ERROR;
244             TESTZONES[i]->getOffsetFromLocal(MILLIS[m], BasicTimeZone::kStandard, BasicTimeZone::kStandard,
245                 rawOffset, dstOffset, status);
246             if (U_FAILURE(status)) {
247                 errln((UnicodeString)"getOffsetFromLocal with kStandard/kStandard failed for TESTZONES[" + i + "]");
248             } else if (rawOffset != OFFSETS2[m][0] || dstOffset != OFFSETS2[m][1]) {
249                 dateStr.remove();
250                 df.format(MILLIS[m], dateStr);
251                 dataerrln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
252                         + dateStr + "(wall/kStandard/kStandard) - Got: "
253                         + rawOffset + "/" + dstOffset
254                         + " Expected: " + OFFSETS2[m][0] + "/" + OFFSETS2[m][1]);
255             }
256         }
257     }
258 
259     // Test getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
260     // int32_t& rawOffset, int32_t& dstOffset, UErroCode& status)
261     // with nonExistingTimeOpt=kDaylight/duplicatedTimeOpt=kDaylight
262     for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
263         for (int m = 0; m < NUM_DATES; m++) {
264             status = U_ZERO_ERROR;
265             TESTZONES[i]->getOffsetFromLocal(MILLIS[m], BasicTimeZone::kDaylight, BasicTimeZone::kDaylight,
266                 rawOffset, dstOffset, status);
267             if (U_FAILURE(status)) {
268                 errln((UnicodeString)"getOffsetFromLocal with kDaylight/kDaylight failed for TESTZONES[" + i + "]");
269             } else if (rawOffset != OFFSETS3[m][0] || dstOffset != OFFSETS3[m][1]) {
270                 dateStr.remove();
271                 df.format(MILLIS[m], dateStr);
272                 dataerrln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
273                         + dateStr + "(wall/kDaylight/kDaylight) - Got: "
274                         + rawOffset + "/" + dstOffset
275                         + " Expected: " + OFFSETS3[m][0] + "/" + OFFSETS3[m][1]);
276             }
277         }
278     }
279 
280     // Test getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
281     // int32_t& rawOffset, int32_t& dstOffset, UErroCode& status)
282     // with nonExistingTimeOpt=kFormer/duplicatedTimeOpt=kLatter
283     for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
284         for (int m = 0; m < NUM_DATES; m++) {
285             status = U_ZERO_ERROR;
286             TESTZONES[i]->getOffsetFromLocal(MILLIS[m], BasicTimeZone::kFormer, BasicTimeZone::kLatter,
287                 rawOffset, dstOffset, status);
288             if (U_FAILURE(status)) {
289                 errln((UnicodeString)"getOffsetFromLocal with kFormer/kLatter failed for TESTZONES[" + i + "]");
290             } else if (rawOffset != OFFSETS2[m][0] || dstOffset != OFFSETS2[m][1]) {
291                 dateStr.remove();
292                 df.format(MILLIS[m], dateStr);
293                 dataerrln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
294                         + dateStr + "(wall/kFormer/kLatter) - Got: "
295                         + rawOffset + "/" + dstOffset
296                         + " Expected: " + OFFSETS2[m][0] + "/" + OFFSETS2[m][1]);
297             }
298         }
299     }
300 
301     // Test getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
302     // int32_t& rawOffset, int32_t& dstOffset, UErroCode& status)
303     // with nonExistingTimeOpt=kLatter/duplicatedTimeOpt=kFormer
304     for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
305         for (int m = 0; m < NUM_DATES; m++) {
306             status = U_ZERO_ERROR;
307             TESTZONES[i]->getOffsetFromLocal(MILLIS[m], BasicTimeZone::kLatter, BasicTimeZone::kFormer,
308                 rawOffset, dstOffset, status);
309             if (U_FAILURE(status)) {
310                 errln((UnicodeString)"getOffsetFromLocal with kLatter/kFormer failed for TESTZONES[" + i + "]");
311             } else if (rawOffset != OFFSETS3[m][0] || dstOffset != OFFSETS3[m][1]) {
312                 dateStr.remove();
313                 df.format(MILLIS[m], dateStr);
314                 dataerrln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
315                         + dateStr + "(wall/kLatter/kFormer) - Got: "
316                         + rawOffset + "/" + dstOffset
317                         + " Expected: " + OFFSETS3[m][0] + "/" + OFFSETS3[m][1]);
318             }
319         }
320     }
321 
322     for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
323         delete TESTZONES[i];
324     }
325     delete utc;
326     delete cal;
327 }
328 
329 #endif /* #if !UCONFIG_NO_FORMATTING */
330