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