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 26void 27TimeZoneOffsetLocalTest::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 */ 41void 42TimeZoneOffsetLocalTest::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