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