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
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)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
TestGetOffsetAroundTransition()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 */
332