1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.text.format.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertTrue; 22 import static org.junit.Assert.fail; 23 24 import android.content.res.Configuration; 25 import android.content.res.Resources; 26 import android.text.format.Time; 27 import android.util.Log; 28 import android.util.TimeFormatException; 29 30 import androidx.test.ext.junit.runners.AndroidJUnit4; 31 import androidx.test.filters.SmallTest; 32 33 import org.junit.After; 34 import org.junit.Before; 35 import org.junit.Test; 36 import org.junit.runner.RunWith; 37 38 import java.time.Duration; 39 import java.time.Instant; 40 import java.time.LocalDate; 41 import java.time.LocalDateTime; 42 import java.time.LocalTime; 43 import java.time.Month; 44 import java.time.ZoneId; 45 import java.time.ZoneOffset; 46 import java.time.temporal.ChronoUnit; 47 import java.time.temporal.JulianFields; 48 import java.time.zone.ZoneOffsetTransition; 49 import java.time.zone.ZoneRules; 50 import java.util.ArrayList; 51 import java.util.Arrays; 52 import java.util.Calendar; 53 import java.util.Date; 54 import java.util.GregorianCalendar; 55 import java.util.List; 56 import java.util.Locale; 57 import java.util.Objects; 58 import java.util.TimeZone; 59 60 @SmallTest 61 @RunWith(AndroidJUnit4.class) 62 public class TimeTest { 63 private static final String TAG = "TimeTest"; 64 private static List<Locale> sSystemLocales; 65 66 private Locale originalLocale; 67 68 @Before setup()69 public void setup() { 70 originalLocale = Locale.getDefault(); 71 72 maybeInitializeSystemLocales(); 73 } 74 75 @After teardown()76 public void teardown() { 77 // The Locale may be changed by tests. Revert to the original. 78 changeJavaAndAndroidLocale(originalLocale, true /* force */); 79 } 80 81 @Test testConstructor()82 public void testConstructor() { 83 Time time = new Time(); 84 new Time(Time.getCurrentTimezone()); 85 time.set(System.currentTimeMillis()); 86 Time anotherTime = new Time(time); 87 verifyTime(time, anotherTime); 88 } 89 90 @Test testNormalize()91 public void testNormalize() { 92 final int expectedMonth = 3; 93 final int expectedDate = 1; 94 Time time = new Time(); 95 // set date to March 32. 96 time.year = 2008; 97 time.month = 2; 98 time.monthDay = 32; 99 long timeMilliseconds = time.normalize(false); 100 Calendar cal = Calendar.getInstance(); 101 cal.setTimeInMillis(timeMilliseconds); 102 assertEquals(2008, cal.get(Calendar.YEAR)); 103 assertEquals(3, cal.get(Calendar.MONTH)); 104 assertEquals(1, cal.get(Calendar.DAY_OF_MONTH)); 105 assertEquals(expectedMonth, time.month); 106 assertEquals(expectedDate, time.monthDay); 107 108 // reset date to March 32. 109 time.month = 2; 110 time.monthDay = 32; 111 assertEquals(timeMilliseconds, time.normalize(true)); 112 assertEquals(expectedMonth, time.month); 113 assertEquals(expectedDate, time.monthDay); 114 } 115 116 @Test testSwitchTimezone()117 public void testSwitchTimezone() { 118 String timeZone = "US/Pacific"; 119 String anotherTimeZone = "Asia/Chongqing"; 120 Time time = new Time(timeZone); 121 assertEquals(timeZone, time.timezone); 122 time.switchTimezone(anotherTimeZone); 123 assertEquals(anotherTimeZone, time.timezone); 124 } 125 126 @Test testSet()127 public void testSet() { 128 final int year = 2008; 129 final int month = 5; 130 final int date = 10; 131 Time time = new Time(); 132 time.set(date, month, year); 133 assertEquals(year, time.year); 134 assertEquals(month, time.month); 135 assertEquals(date, time.monthDay); 136 137 Time anotherTime = new Time(); 138 anotherTime.set(time); 139 verifyTime(time, anotherTime); 140 } 141 verifyTime(Time time, Time anotherTime)142 private void verifyTime(Time time, Time anotherTime) { 143 assertEquals(time.timezone, anotherTime.timezone); 144 assertEquals(time.allDay, anotherTime.allDay); 145 assertEquals(time.second, anotherTime.second); 146 assertEquals(time.minute, anotherTime.minute); 147 assertEquals(time.hour, anotherTime.hour); 148 assertEquals(time.monthDay, anotherTime.monthDay); 149 assertEquals(time.month, anotherTime.month); 150 assertEquals(time.year, anotherTime.year); 151 assertEquals(time.weekDay, anotherTime.weekDay); 152 assertEquals(time.yearDay, anotherTime.yearDay); 153 assertEquals(time.isDst, anotherTime.isDst); 154 assertEquals(time.gmtoff, anotherTime.gmtoff); 155 } 156 157 @Test testGetWeekNumber()158 public void testGetWeekNumber() { 159 Time time = new Time(); 160 time.normalize(false); 161 time.set(12, 10, 2008); 162 assertEquals(45, time.getWeekNumber()); 163 164 assertEquals("20081112", time.format2445()); 165 166 assertEquals("2008-11-12", time.format3339(true)); 167 assertEquals("2008-11-12T00:00:00.000+00:00", time.format3339(false)); 168 169 Time anotherTime = new Time(); 170 assertTrue(anotherTime.parse3339("2008-11-12T00:00:00.000+00:00")); 171 assertEquals(time.year, anotherTime.year); 172 assertEquals(time.month, anotherTime.month); 173 assertEquals(time.monthDay, anotherTime.monthDay); 174 assertEquals(Time.TIMEZONE_UTC, anotherTime.timezone); 175 176 try { 177 anotherTime.parse3339("2008-111-12T00:00:00.000+00:00"); 178 fail("should throw exception"); 179 } catch (TimeFormatException e) { 180 // expected 181 } 182 } 183 184 @Test(expected=NullPointerException.class) testParseNull()185 public void testParseNull() { 186 Time t = new Time(); 187 t.parse(null); 188 } 189 190 @Test(expected=NullPointerException.class) testParse3339Null()191 public void testParse3339Null() { 192 Time t = new Time(); 193 t.parse3339(null); 194 } 195 196 // http://code.google.com/p/android/issues/detail?id=16002 197 // We'd leak one JNI global reference each time parsing failed. 198 // This would cause a crash when we filled the global reference table. 199 @Test testBug16002()200 public void testBug16002() { 201 Time t = new Time(); 202 for (int i = 0; i < 8192; ++i) { 203 try { 204 t.parse3339("xxx"); 205 fail(); 206 } catch (TimeFormatException expected) { 207 } 208 } 209 } 210 211 // http://code.google.com/p/android/issues/detail?id=22225 212 // We'd leak one JNI global reference each time parsing failed. 213 // This would cause a crash when we filled the global reference table. 214 @Test testBug22225()215 public void testBug22225() { 216 Time t = new Time(); 217 for (int i = 0; i < 8192; ++i) { 218 try { 219 t.parse("xxx"); 220 fail(); 221 } catch (TimeFormatException expected) { 222 } 223 } 224 } 225 226 @Test testIsEpoch()227 public void testIsEpoch() { 228 // Create a Time that uses UTC to provide a behavior baseline. 229 Time time = new Time(Time.TIMEZONE_UTC); 230 231 // Time is initialized to 1970-01-01 00:00:00 232 assertTrue(Time.isEpoch(time)); 233 234 // 1970-01-01 23:59:59 235 checkIsEpochResult(time, 1970, 0 /* Jan */, 1, 23, 59, 59, true); 236 237 // 1970-01-02 00:00:00 238 checkIsEpochResult(time, 1970, 0 /* Jan */, 2, 0, 0, 0, false); 239 240 // 1969-12-31 23:59:59 241 checkIsEpochResult(time, 1969, 11 /* Dec */, 31, 23, 59, 59, false); 242 243 // Now demonstrate that the isEpoch() method just checks against the Julian day 244 // calculated for UTC. America/Los_Angeles is UTC-8 so all times have to be adjusted 245 // by 8 hours. 246 time.timezone = "America/Los_Angeles"; 247 248 // 1969-12-31 15:59:59 == 1969-12-31 23:59:59 in UTC 249 checkIsEpochResult(time, 1969, 11 /* Dec */, 31, 15, 59, 59, false); 250 251 // 1969-12-31 16:00:00 == 1970-01-01 00:00:00 in UTC 252 checkIsEpochResult(time, 1969, 11 /* Dec */, 31, 16, 0, 0, true); 253 254 // 1970-01-01 15:59:59 == 1970-01-01 23:59:59 in UTC 255 checkIsEpochResult(time, 1970, 0 /* Jan */, 1, 15, 59, 59, true); 256 257 // 1970-01-01 16:00:00 == 1970-01-02 00:00:00 in UTC 258 checkIsEpochResult(time, 1970, 0 /* Jan */, 1, 16, 0, 0, false); 259 } 260 checkIsEpochResult(Time time, int year, int month, int monthDay, int hour, int minute, int second, boolean expectedIsEpoch)261 private void checkIsEpochResult(Time time, int year, int month, int monthDay, int hour, 262 int minute, int second, boolean expectedIsEpoch) { 263 time.set(second, minute, hour, monthDay, month, year); 264 time.normalize(false); 265 assertEquals(expectedIsEpoch, Time.isEpoch(time)); 266 } 267 268 @Test testAfterBefore()269 public void testAfterBefore() { 270 Time a = new Time(Time.TIMEZONE_UTC); 271 Time b = new Time("America/Los_Angeles"); 272 assertFalse(a.after(b)); 273 assertTrue(b.after(a)); 274 assertFalse(b.before(a)); 275 assertTrue(a.before(b)); 276 } 277 278 // Below are tests merged from Google 279 private static class DateTest { 280 public int year1; 281 public int month1; 282 public int day1; 283 public int hour1; 284 public int minute1; 285 public int dst1; 286 287 public int delta; 288 289 public int year2; 290 public int month2; 291 public int day2; 292 public int hour2; 293 public int minute2; 294 public int dst2; 295 DateTest(int year1, int month1, int day1, int hour1, int minute1, int dst1, int delta, int year2, int month2, int day2, int hour2, int minute2, int dst2)296 public DateTest(int year1, int month1, int day1, int hour1, int minute1, int dst1, 297 int delta, int year2, int month2, int day2, int hour2, int minute2, 298 int dst2) { 299 this.year1 = year1; 300 this.month1 = month1; 301 this.day1 = day1; 302 this.hour1 = hour1; 303 this.minute1 = minute1; 304 this.dst1 = dst1; 305 this.delta = delta; 306 this.year2 = year2; 307 this.month2 = month2; 308 this.day2 = day2; 309 this.hour2 = hour2; 310 this.minute2 = minute2; 311 this.dst2 = dst2; 312 } 313 DateTest(int year1, int month1, int day1, int hour1, int minute1, int delta, int year2, int month2, int day2, int hour2, int minute2)314 public DateTest(int year1, int month1, int day1, int hour1, int minute1, 315 int delta, int year2, int month2, int day2, int hour2, int minute2) { 316 this.year1 = year1; 317 this.month1 = month1; 318 this.day1 = day1; 319 this.hour1 = hour1; 320 this.minute1 = minute1; 321 this.dst1 = -1; 322 this.delta = delta; 323 this.year2 = year2; 324 this.month2 = month2; 325 this.day2 = day2; 326 this.hour2 = hour2; 327 this.minute2 = minute2; 328 this.dst2 = -1; 329 } 330 } 331 332 // These tests assume that DST changes on Nov 4, 2007 at 2am (to 1am). 333 334 // The "offset" field in "dayTests" represents days. 335 // Use normalize(true) with these tests to change the date by 1 day. 336 private DateTest[] dayTests = { 337 // The month numbers are 0-relative, so Jan=0, Feb=1,...Dec=11 338 339 // Nov 4, 12am + 0 day = Nov 4, 12am 340 // Nov 5, 12am + 0 day = Nov 5, 12am 341 new DateTest(2007, 10, 4, 0, 0, 0, 2007, 10, 4, 0, 0), 342 new DateTest(2007, 10, 5, 0, 0, 0, 2007, 10, 5, 0, 0), 343 344 // Nov 3, 12am + 1 day = Nov 4, 12am 345 // Nov 4, 12am + 1 day = Nov 5, 12am 346 // Nov 5, 12am + 1 day = Nov 6, 12am 347 new DateTest(2007, 10, 3, 0, 0, 1, 2007, 10, 4, 0, 0), 348 new DateTest(2007, 10, 4, 0, 0, 1, 2007, 10, 5, 0, 0), 349 new DateTest(2007, 10, 5, 0, 0, 1, 2007, 10, 6, 0, 0), 350 351 // Nov 3, 1am + 1 day = Nov 4, 1am 352 // Nov 4, 1am + 1 day = Nov 5, 1am 353 // Nov 5, 1am + 1 day = Nov 6, 1am 354 new DateTest(2007, 10, 3, 1, 0, 1, 2007, 10, 4, 1, 0), 355 new DateTest(2007, 10, 4, 1, 0, 1, 2007, 10, 5, 1, 0), 356 new DateTest(2007, 10, 5, 1, 0, 1, 2007, 10, 6, 1, 0), 357 358 // Nov 3, 2am + 1 day = Nov 4, 2am 359 // Nov 4, 2am + 1 day = Nov 5, 2am 360 // Nov 5, 2am + 1 day = Nov 6, 2am 361 new DateTest(2007, 10, 3, 2, 0, 1, 2007, 10, 4, 2, 0), 362 new DateTest(2007, 10, 4, 2, 0, 1, 2007, 10, 5, 2, 0), 363 new DateTest(2007, 10, 5, 2, 0, 1, 2007, 10, 6, 2, 0), 364 }; 365 366 // The "offset" field in "minuteTests" represents minutes. 367 // Use normalize(false) with these tests. 368 private DateTest[] minuteTests = { 369 // The month numbers are 0-relative, so Jan=0, Feb=1,...Dec=11 370 371 // Nov 4, 12am + 0 minutes = Nov 4, 12am 372 // Nov 5, 12am + 0 minutes = Nov 5, 12am 373 new DateTest(2007, 10, 4, 0, 0, 0, 2007, 10, 4, 0, 0), 374 new DateTest(2007, 10, 5, 0, 0, 0, 2007, 10, 5, 0, 0), 375 376 // Nov 3, 12am + 60 minutes = Nov 3, 1am 377 // Nov 4, 12am + 60 minutes = Nov 4, 1am 378 // Nov 5, 12am + 60 minutes = Nov 5, 1am 379 new DateTest(2007, 10, 3, 0, 0, 60, 2007, 10, 3, 1, 0), 380 new DateTest(2007, 10, 4, 0, 0, 60, 2007, 10, 4, 1, 0), 381 new DateTest(2007, 10, 5, 0, 0, 60, 2007, 10, 5, 1, 0), 382 383 // Nov 3, 1am + 60 minutes = Nov 3, 2am 384 // Nov 4, 1am (PDT) + 30 minutes = Nov 4, 1:30am (PDT) 385 // Nov 4, 1am (PDT) + 60 minutes = Nov 4, 1am (PST) 386 new DateTest(2007, 10, 3, 1, 0, 60, 2007, 10, 3, 2, 0), 387 new DateTest(2007, 10, 4, 1, 0, 1, 30, 2007, 10, 4, 1, 30, 1), 388 new DateTest(2007, 10, 4, 1, 0, 1, 60, 2007, 10, 4, 1, 0, 0), 389 390 // Nov 4, 1:30am (PDT) + 15 minutes = Nov 4, 1:45am (PDT) 391 // Nov 4, 1:30am (PDT) + 30 minutes = Nov 4, 1:00am (PST) 392 // Nov 4, 1:30am (PDT) + 60 minutes = Nov 4, 1:30am (PST) 393 new DateTest(2007, 10, 4, 1, 30, 1, 15, 2007, 10, 4, 1, 45, 1), 394 new DateTest(2007, 10, 4, 1, 30, 1, 30, 2007, 10, 4, 1, 0, 0), 395 new DateTest(2007, 10, 4, 1, 30, 1, 60, 2007, 10, 4, 1, 30, 0), 396 397 // Nov 4, 1:30am (PST) + 15 minutes = Nov 4, 1:45am (PST) 398 // Nov 4, 1:30am (PST) + 30 minutes = Nov 4, 2:00am (PST) 399 // Nov 5, 1am + 60 minutes = Nov 5, 2am 400 new DateTest(2007, 10, 4, 1, 30, 0, 15, 2007, 10, 4, 1, 45, 0), 401 new DateTest(2007, 10, 4, 1, 30, 0, 30, 2007, 10, 4, 2, 0, 0), 402 new DateTest(2007, 10, 5, 1, 0, 60, 2007, 10, 5, 2, 0), 403 404 // Nov 3, 2am + 60 minutes = Nov 3, 3am 405 // Nov 4, 2am + 30 minutes = Nov 4, 2:30am 406 // Nov 4, 2am + 60 minutes = Nov 4, 3am 407 // Nov 5, 2am + 60 minutes = Nov 5, 3am 408 new DateTest(2007, 10, 3, 2, 0, 60, 2007, 10, 3, 3, 0), 409 new DateTest(2007, 10, 4, 2, 0, 30, 2007, 10, 4, 2, 30), 410 new DateTest(2007, 10, 4, 2, 0, 60, 2007, 10, 4, 3, 0), 411 new DateTest(2007, 10, 5, 2, 0, 60, 2007, 10, 5, 3, 0), 412 }; 413 414 @Test testNormalize1()415 public void testNormalize1() { 416 String tz = "America/Los_Angeles"; 417 Time local = new Time(tz); 418 419 int len = dayTests.length; 420 for (int index = 0; index < len; index++) { 421 DateTest test = dayTests[index]; 422 local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1); 423 // call normalize() to make sure that isDst is set 424 local.normalize(false /* use isDst */); 425 local.monthDay += test.delta; 426 local.normalize(true /* ignore isDst */); 427 428 Time expected = new Time(tz); 429 Fields.setDateTime(expected, test.year2, test.month2, test.day2, test.hour2, 430 test.minute2, 0); 431 Fields.verifyTimeEquals("day test index " + index + ", normalize():", 432 Fields.MAIN_DATE_TIME, expected, local); 433 434 local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1); 435 // call normalize() to make sure that isDst is set 436 local.normalize(false /* use isDst */); 437 local.monthDay += test.delta; 438 long millis = local.toMillis(true /* ignore isDst */); 439 local.set(millis); 440 441 expected = new Time(tz); 442 Fields.setDateTime(expected, test.year2, test.month2, test.day2, test.hour2, 443 test.minute2, 0); 444 Fields.verifyTimeEquals("day test index " + index + ", toMillis():", 445 Fields.MAIN_DATE_TIME, expected, local); 446 } 447 448 len = minuteTests.length; 449 for (int index = 0; index < len; index++) { 450 DateTest test = minuteTests[index]; 451 local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1); 452 local.isDst = test.dst1; 453 // call normalize() to make sure that isDst is set 454 local.normalize(false /* use isDst */); 455 if (test.dst2 == -1) test.dst2 = local.isDst; 456 local.minute += test.delta; 457 local.normalize(false /* use isDst */); 458 459 Time expected = new Time(tz); 460 Fields.setDateTime(expected, test.year2, test.month2, test.day2, test.hour2, 461 test.minute2, 0); 462 Fields.setDst(expected, test.dst2 /* isDst */, PstPdt.getUtcOffsetSeconds(test.dst2)); 463 Fields.verifyTimeEquals("minute test index " + index + ", normalize():", 464 Fields.MAIN_DATE_TIME | Fields.DST_FIELDS, expected, local); 465 466 local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1); 467 local.isDst = test.dst1; 468 // call normalize() to make sure that isDst is set 469 local.normalize(false /* use isDst */); 470 if (test.dst2 == -1) test.dst2 = local.isDst; 471 local.minute += test.delta; 472 long millis = local.toMillis(false /* use isDst */); 473 local.set(millis); 474 475 expected = new Time(tz); 476 Fields.setDateTime(expected, test.year2, test.month2, test.day2, test.hour2, 477 test.minute2, 0); 478 Fields.setDst(expected, test.dst2 /* isDst */, PstPdt.getUtcOffsetSeconds(test.dst2)); 479 Fields.verifyTimeEquals("minute test index " + index + ", toMillis():", 480 Fields.MAIN_DATE_TIME | Fields.DST_FIELDS, expected, local); 481 } 482 } 483 484 @Test testSwitchTimezone_simpleUtc()485 public void testSwitchTimezone_simpleUtc() { 486 String originalTz = Time.TIMEZONE_UTC; 487 Time t = new Time(originalTz); 488 Fields.set(t, 2006, 9, 5, 12, 0, 0, -1 /* isDst */, 0, 0, 0); 489 490 String newTz = "America/Los_Angeles"; 491 t.switchTimezone(newTz); 492 493 Time expected1 = new Time(newTz); 494 Fields.set(expected1, 2006, 9, 5, 5, 0, 0, 1 /* isDst */, -25200, 277, 4); 495 Fields.verifyTimeEquals(expected1, t); 496 497 t.switchTimezone(originalTz); 498 499 Time expected2 = new Time(originalTz); 500 Fields.set(expected2, 2006, 9, 5, 12, 0, 0, 0 /* isDst */, 0, 277, 4); 501 Fields.verifyTimeEquals(expected2, t); 502 } 503 504 @Test testSwitchTimezone_standardToStandardTime()505 public void testSwitchTimezone_standardToStandardTime() { 506 String zone1 = "Europe/London"; 507 String zone2 = "America/Los_Angeles"; 508 509 // A time unambiguously in standard time in both zones. 510 Time t = new Time(zone1); 511 Fields.set(t, 2007, 2, 10, 12, 0, 0, -1 /* isDst */, 0, 0, 0); 512 513 t.switchTimezone(zone2); 514 515 Time expected1 = new Time(zone2); 516 Fields.set(expected1, 2007, 2, 10, 4, 0, 0, 0 /* isDst */, -28800, 68, 6); 517 Fields.verifyTimeEquals(expected1, t); 518 519 t.switchTimezone(zone1); 520 521 Time expected2 = new Time(zone1); 522 Fields.set(expected2, 2007, 2, 10, 12, 0, 0, 0 /* isDst */, 0, 68, 6); 523 Fields.verifyTimeEquals(expected2, t); 524 } 525 526 @Test testSwitchTimezone_dstToDstTime()527 public void testSwitchTimezone_dstToDstTime() { 528 String zone1 = "Europe/London"; 529 String zone2 = "America/Los_Angeles"; 530 531 // A time unambiguously in DST in both zones. 532 Time t = new Time(zone1); 533 Fields.set(t, 2007, 2, 26, 12, 0, 0, -1 /* isDst */, 0, 0, 0); 534 535 t.switchTimezone(zone2); 536 537 Time expected1 = new Time(zone2); 538 Fields.set(expected1, 2007, 2, 26, 4, 0, 0, 1 /* isDst */, -25200, 84, 1); 539 Fields.verifyTimeEquals(expected1, t); 540 541 t.switchTimezone(zone1); 542 543 Time expected2 = new Time(zone1); 544 Fields.set(expected2, 2007, 2, 26, 12, 0, 0, 1 /* isDst */, 3600, 84, 1); 545 Fields.verifyTimeEquals(expected2, t); 546 } 547 548 @Test testSwitchTimezone_standardToDstTime()549 public void testSwitchTimezone_standardToDstTime() { 550 String zone1 = "Europe/London"; 551 String zone2 = "America/Los_Angeles"; 552 553 // A time that is in standard time in zone1, but in DST in zone2. 554 Time t = new Time(zone1); 555 Fields.set(t, 2007, 2, 24, 12, 0, 0, -1 /* isDst */, 0, 0, 0); 556 557 t.switchTimezone(zone2); 558 559 Time expected1 = new Time(zone2); 560 Fields.set(expected1, 2007, 2, 24, 5, 0, 0, 1 /* isDst */, -25200, 82, 6); 561 Fields.verifyTimeEquals(expected1, t); 562 563 t.switchTimezone(zone1); 564 565 Time expected2 = new Time(zone1); 566 Fields.set(expected2, 2007, 2, 24, 12, 0, 0, 0 /* isDst */, 0, 82, 6); 567 Fields.verifyTimeEquals(expected2, t); 568 } 569 570 @Test testSwitchTimezone_sourceDateInvalid()571 public void testSwitchTimezone_sourceDateInvalid() { 572 String zone1 = "Europe/London"; 573 String zone2 = "America/Los_Angeles"; 574 575 // A source wall time known not to normalize because it doesn't "exist" locally. 576 Time t = new Time(zone1); 577 Fields.set(t, 2007, 2, 25, 1, 30, 0, -1 /* isDst */, 0, 0, 0); 578 assertEquals(-1, t.toMillis(false)); 579 580 t.switchTimezone(zone2); 581 582 // It is assumed a sad trombone noise is also emitted from the device at this point. 583 // This illustrates why using -1 to indicate a problem, when -1 is in range, is a poor idea. 584 Time expected1 = new Time(zone2); 585 Fields.set(expected1, 1969, 11, 31, 15, 59, 59, 0 /* isDst */, -28800, 364, 3); 586 Fields.verifyTimeEquals(expected1, t); 587 } 588 589 @Test testSwitchTimezone_dstToStandardTime()590 public void testSwitchTimezone_dstToStandardTime() { 591 String zone1 = "America/Los_Angeles"; 592 String zone2 = "Europe/London"; 593 594 // A time that is in DST in zone1, but in standard in zone2. 595 Time t = new Time(zone1); 596 Fields.set(t, 2007, 2, 12, 12, 0, 0, -1 /* isDst */, 0, 0, 0); 597 598 t.switchTimezone(zone2); 599 600 Time expected1 = new Time(zone2); 601 Fields.set(expected1, 2007, 2, 12, 19, 0, 0, 0 /* isDst */, 0, 70, 1); 602 Fields.verifyTimeEquals(expected1, t); 603 604 t.switchTimezone(zone1); 605 606 Time expected2 = new Time(zone1); 607 Fields.set(expected2, 2007, 2, 12, 12, 0, 0, 1 /* isDst */, -25200, 70, 1); 608 Fields.verifyTimeEquals(expected2, t); 609 } 610 611 @Test testCtor()612 public void testCtor() { 613 String tz = Time.TIMEZONE_UTC; 614 Time t = new Time(tz); 615 assertEquals(tz, t.timezone); 616 617 Time expected = new Time(tz); 618 Fields.set(expected, 1970, 0, 1, 0, 0, 0, -1 /* isDst */, 0, 0, 0); 619 Fields.verifyTimeEquals(expected, t); 620 } 621 622 @Test testGetActualMaximum()623 public void testGetActualMaximum() { 624 Time t = new Time(Time.TIMEZONE_UTC); 625 assertEquals(59, t.getActualMaximum(Time.SECOND)); 626 assertEquals(59, t.getActualMaximum(Time.MINUTE)); 627 assertEquals(23, t.getActualMaximum(Time.HOUR)); 628 assertEquals(11, t.getActualMaximum(Time.MONTH)); 629 assertEquals(2037, t.getActualMaximum(Time.YEAR)); 630 assertEquals(6, t.getActualMaximum(Time.WEEK_DAY)); 631 assertEquals(364, t.getActualMaximum(Time.YEAR_DAY)); 632 t.year = 2000; 633 assertEquals(365, t.getActualMaximum(Time.YEAR_DAY)); 634 635 try { 636 t.getActualMaximum(Time.WEEK_NUM); 637 fail("should throw runtime exception"); 638 } catch (Exception e) { 639 // expected 640 } 641 final int noExistField = -1; 642 try { 643 t.getActualMaximum(noExistField); 644 } catch (Exception e) { 645 // expected 646 } 647 648 t.year = 2001; 649 final int[] DAYS_PER_MONTH = { 650 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 651 }; 652 verifyMonth(t, DAYS_PER_MONTH); 653 654 t.year = 2000; 655 DAYS_PER_MONTH[1] = 29; 656 verifyMonth(t, DAYS_PER_MONTH); 657 } 658 verifyMonth(Time t, final int[] DAYS_PER_MONTH)659 private void verifyMonth(Time t, final int[] DAYS_PER_MONTH) { 660 for (int i = 0; i < t.getActualMaximum(Time.MONTH); i++) { 661 t.month = i; 662 assertEquals(DAYS_PER_MONTH[i], t.getActualMaximum(Time.MONTH_DAY)); 663 } 664 } 665 666 @Test testClear0()667 public void testClear0() { 668 Time t = new Time(Time.getCurrentTimezone()); 669 t.clear(Time.TIMEZONE_UTC); 670 assertEquals(Time.TIMEZONE_UTC, t.timezone); 671 assertFalse(t.allDay); 672 assertEquals(0, t.second); 673 assertEquals(0, t.minute); 674 assertEquals(0, t.hour); 675 assertEquals(0, t.monthDay); 676 assertEquals(0, t.month); 677 assertEquals(0, t.year); 678 assertEquals(0, t.weekDay); 679 assertEquals(0, t.yearDay); 680 assertEquals(0, t.gmtoff); 681 assertEquals(-1, t.isDst); 682 } 683 684 @Test testClear()685 public void testClear() { 686 Time t = new Time("America/Los_Angeles"); 687 Fields.set(t, 1, 2, 3, 4, 5, 6, 7 /* isDst */, 8, 9, 10); 688 689 t.clear(Time.TIMEZONE_UTC); 690 691 Time expected = new Time(Time.TIMEZONE_UTC); 692 Fields.set(expected, 0, 0, 0, 0, 0, 0, -1 /* isDst */, 0, 0, 0); 693 Fields.verifyTimeEquals(expected, t); 694 } 695 696 @Test testCompare()697 public void testCompare() { 698 String timezone = "America/New_York"; 699 int[] aDateTimeFields = new int[] { 2005, 2, 3, 4, 5, 6 }; 700 701 Time a = new Time(timezone); 702 Fields.setDateTime(a, aDateTimeFields); 703 Fields.setDst(a, 7, 8); 704 Fields.setDerivedDateTime(a, 9, 10); 705 706 int[] bDateTimeFields = new int[aDateTimeFields.length]; 707 System.arraycopy(aDateTimeFields, 0, bDateTimeFields, 0, aDateTimeFields.length); 708 Time b = new Time(timezone); 709 Fields.setDateTime(b, bDateTimeFields); 710 Fields.setDst(b, 7, 8); 711 Fields.setDerivedDateTime(b, 9, 10); 712 713 // Confirm timezone behavior: When timezones differ the result depends on the millis time. 714 // This means that all date/time and the isDst field can impact compare() when timezones 715 // are different. The result is always -1, 0 or 1. 716 717 // East of New York. Millis goes down for a given wall time. 718 b.timezone = "Europe/London"; 719 assertEquals(1, Time.compare(a, b)); 720 assertEquals(-1, Time.compare(b, a)); 721 722 // West of New York. Millis goes up for a given wall time. 723 b.timezone = "America/Los_Angeles"; 724 assertEquals(-1, Time.compare(a, b)); 725 assertEquals(1, Time.compare(b, a)); 726 727 b.timezone = timezone; 728 assertEquals(0, Time.compare(a, b)); 729 assertEquals(0, Time.compare(b, a)); 730 731 // Now confirm behavior when timezones are the same: Only the date/time fields are checked 732 // and the result depends on the difference between the fields and the order in which they 733 // are checked. 734 735 // Check date/time fields 736 for (int i = 0; i < aDateTimeFields.length; i++) { 737 bDateTimeFields[i] = 99999; 738 Fields.setDateTime(b, bDateTimeFields); 739 740 assertEquals(aDateTimeFields[i] - bDateTimeFields[i], Time.compare(a, b)); 741 assertEquals(bDateTimeFields[i] - aDateTimeFields[i], Time.compare(b, a)); 742 743 bDateTimeFields[i] = -99999; 744 Fields.setDateTime(b, bDateTimeFields); 745 746 assertEquals(aDateTimeFields[i] - bDateTimeFields[i], Time.compare(a, b)); 747 assertEquals(bDateTimeFields[i] - aDateTimeFields[i], Time.compare(b, a)); 748 749 bDateTimeFields[i] = aDateTimeFields[i]; 750 Fields.setDateTime(b, bDateTimeFields); 751 752 assertEquals(0, Time.compare(a, b)); 753 assertEquals(0, Time.compare(b, a)); 754 } 755 756 // Show that the derived fields have no effect on compare when timezones are the same. 757 Fields.setDst(b, 999, 999); 758 Fields.setDerivedDateTime(b, 999, 999); 759 assertEquals(0, Time.compare(a, b)); 760 assertEquals(0, Time.compare(b, a)); 761 } 762 763 @Test(expected=NullPointerException.class) testCompareNullSecond()764 public void testCompareNullSecond() { 765 Time a = new Time(Time.TIMEZONE_UTC); 766 Time.compare(a, null); 767 } 768 769 @Test(expected=NullPointerException.class) testCompareNullFirst()770 public void testCompareNullFirst() { 771 Time a = new Time(Time.TIMEZONE_UTC); 772 Time.compare(null, a); 773 } 774 775 @Test(expected=NullPointerException.class) testCompareNullBoth()776 public void testCompareNullBoth() { 777 Time.compare(null, null); 778 } 779 780 @Test testCompare_invalidDatesAreEqualIfTimezoneDiffers()781 public void testCompare_invalidDatesAreEqualIfTimezoneDiffers() { 782 String timezone = "America/New_York"; 783 // This date is outside of the valid set of dates that can be calculated so toMillis() 784 // returns -1. 785 int[] dateTimeFields = new int[] { 1, 2, 3, 4, 5, 6 }; 786 787 Time a = new Time(timezone); 788 Fields.setDateTime(a, dateTimeFields); 789 Fields.setDst(a, 7, 8); 790 Fields.setDerivedDateTime(a, 9, 10); 791 assertEquals(-1, a.toMillis(false)); 792 793 Time b = new Time(timezone); 794 Fields.setDateTime(b, dateTimeFields); 795 Fields.setDst(b, 11, 12); 796 Fields.setDerivedDateTime(b, 13, 14); 797 assertEquals(-1, b.toMillis(false)); 798 799 // DST and derived-date time fields are always ignored. 800 assertEquals(0, Time.compare(a, b)); 801 802 // Set a different invalid set of date fields. 803 Fields.setDateTime(b, new int[] { 6, 5, 4, 3, 2, 1 }); 804 assertEquals(-5, Time.compare(a, b)); 805 806 // Now change the timezone 807 b.timezone = Time.TIMEZONE_UTC; 808 809 // >:-( 810 assertEquals(0, Time.compare(a, b)); 811 } 812 813 @Test testFormat()814 public void testFormat() { 815 Time t = new Time(Time.TIMEZONE_UTC); 816 String r = t.format("%Y%m%dT%H%M%S"); 817 assertEquals("19700101T000000", r); 818 } 819 820 @Test testFormat_null()821 public void testFormat_null() { 822 Time t = new Time(Time.TIMEZONE_UTC); 823 assertEquals(t.format("%c"), t.format(null)); 824 } 825 826 @Test testFormat_badPatterns()827 public void testFormat_badPatterns() { 828 Time t = new Time(Time.TIMEZONE_UTC); 829 verifyFormatEquals(t, "%~Y", "~Y"); 830 verifyFormatEquals(t, "%", "%"); 831 } 832 833 @Test testFormat_doesNotNormalize()834 public void testFormat_doesNotNormalize() { 835 Time t = new Time(Time.TIMEZONE_UTC); 836 Fields.set(t, 2005, 13, 32, -1, -1, -1, -2, -2, -2, -2); 837 838 Time tCopy = new Time(t); 839 Fields.verifyTimeEquals(t, tCopy); 840 841 verifyFormatEquals(t, "%Y%m%dT%H%M%S", "20051432T-1-1-1"); 842 843 Fields.verifyTimeEquals(t, tCopy); 844 } 845 verifyFormatEquals(Time t, String formatArg, String expected)846 private static void verifyFormatEquals(Time t, String formatArg, String expected) { 847 assertEquals(expected, t.format(formatArg)); 848 } 849 850 @Test testFormat_tokensUkLocale()851 public void testFormat_tokensUkLocale() { 852 if (!changeJavaAndAndroidLocale(Locale.UK, false /* force */)) { 853 Log.w(TAG, "Skipping testFormat_tokensUkLocale: no assets found"); 854 return; 855 } 856 857 Time t = new Time("Europe/London"); 858 Fields.setDateTime(t, 2005, 5, 1, 12, 30, 15); 859 860 // Prove the un-normalized fields are used. 861 verifyFormatEquals(t, "%A", "Sunday"); 862 863 // Set fields like weekday. 864 t.normalize(true); 865 866 verifyFormatEquals(t, "%A", "Wednesday"); 867 verifyFormatEquals(t, "%a", "Wed"); 868 verifyFormatEquals(t, "%B", "June"); 869 verifyFormatEquals(t, "%b", "Jun"); 870 verifyFormatEquals(t, "%C", "20"); 871 verifyFormatEquals(t, "%c", "1 Jun 2005, 12:30:15"); 872 verifyFormatEquals(t, "%D", "06/01/05"); 873 verifyFormatEquals(t, "%d", "01"); 874 verifyFormatEquals(t, "%E", "E"); 875 verifyFormatEquals(t, "%e", " 1"); 876 verifyFormatEquals(t, "%F", "2005-06-01"); 877 verifyFormatEquals(t, "%G", "2005"); 878 verifyFormatEquals(t, "%g", "05"); 879 verifyFormatEquals(t, "%H", "12"); 880 verifyFormatEquals(t, "%h", "Jun"); 881 verifyFormatEquals(t, "%I", "12"); 882 verifyFormatEquals(t, "%j", "152"); 883 verifyFormatEquals(t, "%K", "K"); 884 verifyFormatEquals(t, "%k", "12"); 885 verifyFormatEquals(t, "%l", "12"); 886 verifyFormatEquals(t, "%M", "30"); 887 verifyFormatEquals(t, "%m", "06"); 888 verifyFormatEquals(t, "%n", "\n"); 889 verifyFormatEquals(t, "%O", "O"); 890 verifyFormatEquals(t, "%p", "pm"); 891 verifyFormatEquals(t, "%P", "pm"); 892 verifyFormatEquals(t, "%R", "12:30"); 893 verifyFormatEquals(t, "%r", "12:30:15 pm"); 894 verifyFormatEquals(t, "%S", "15"); 895 // The original C implementation uses the (native) system default TZ, not the timezone of 896 // the Time to calculate this and was therefore not stable. This changed to use the Time's 897 // timezone when the Time class was re-written in Java. 898 verifyFormatEquals(t, "%s", "1117625415"); 899 verifyFormatEquals(t, "%T", "12:30:15"); 900 verifyFormatEquals(t, "%t", "\t"); 901 verifyFormatEquals(t, "%U", "22"); 902 verifyFormatEquals(t, "%u", "3"); 903 verifyFormatEquals(t, "%V", "22"); 904 verifyFormatEquals(t, "%v", " 1-Jun-2005"); 905 verifyFormatEquals(t, "%W", "22"); 906 verifyFormatEquals(t, "%w", "3"); 907 verifyFormatEquals(t, "%X", "12:30:15"); 908 verifyFormatEquals(t, "%x", "1 June 2005"); 909 verifyFormatEquals(t, "%y", "05"); 910 verifyFormatEquals(t, "%Y", "2005"); 911 verifyFormatEquals(t, "%Z", "BST"); 912 verifyFormatEquals(t, "%z", "+0100"); 913 verifyFormatEquals(t, "%+", "Wed Jun 1 12:30:15 BST 2005"); 914 verifyFormatEquals(t, "%%", "%"); 915 916 // Modifiers 917 918 verifyFormatEquals(t, "%EC", "20"); 919 verifyFormatEquals(t, "%OC", "20"); 920 921 verifyFormatEquals(t, "%_+", "Wed Jun 1 12:30:15 BST 2005"); 922 verifyFormatEquals(t, "%-+", "Wed Jun 1 12:30:15 BST 2005"); 923 verifyFormatEquals(t, "%0+", "Wed Jun 1 12:30:15 BST 2005"); 924 verifyFormatEquals(t, "%^+", "Wed Jun 1 12:30:15 BST 2005"); 925 verifyFormatEquals(t, "%#+", "Wed Jun 1 12:30:15 BST 2005"); 926 927 verifyFormatEquals(t, "%_A", "Wednesday"); 928 verifyFormatEquals(t, "%-A", "Wednesday"); 929 verifyFormatEquals(t, "%0A", "Wednesday"); 930 verifyFormatEquals(t, "%^A", "WEDNESDAY"); 931 verifyFormatEquals(t, "%#A", "wEDNESDAY"); 932 933 verifyFormatEquals(t, "%_Y", "20 5"); 934 verifyFormatEquals(t, "%-Y", "205"); 935 verifyFormatEquals(t, "%0Y", "2005"); 936 verifyFormatEquals(t, "%^Y", "2005"); 937 verifyFormatEquals(t, "%#Y", "2005"); 938 939 verifyFormatEquals(t, "%_d", " 1"); 940 verifyFormatEquals(t, "%-d", "1"); 941 verifyFormatEquals(t, "%0d", "01"); 942 verifyFormatEquals(t, "%^d", "01"); 943 verifyFormatEquals(t, "%#d", "01"); 944 } 945 946 @Test testFormat_tokensUsLocale()947 public void testFormat_tokensUsLocale() { 948 if (!changeJavaAndAndroidLocale(Locale.US, false /* force */)) { 949 Log.w(TAG, "Skipping testFormat_tokensUSLocale: no assets found"); 950 return; 951 } 952 953 Time t = new Time("America/New_York"); 954 Fields.setDateTime(t, 2005, 5, 1, 12, 30, 15); 955 956 // Prove the un-normalized fields are used. 957 verifyFormatEquals(t, "%A", "Sunday"); 958 959 // Set fields like weekday. 960 t.normalize(true); 961 962 verifyFormatEquals(t, "%A", "Wednesday"); 963 verifyFormatEquals(t, "%a", "Wed"); 964 verifyFormatEquals(t, "%B", "June"); 965 verifyFormatEquals(t, "%b", "Jun"); 966 verifyFormatEquals(t, "%C", "20"); 967 verifyFormatEquals(t, "%c", "Jun 1, 2005, 12:30:15 PM"); 968 verifyFormatEquals(t, "%D", "06/01/05"); 969 verifyFormatEquals(t, "%d", "01"); 970 verifyFormatEquals(t, "%E", "E"); 971 verifyFormatEquals(t, "%e", " 1"); 972 verifyFormatEquals(t, "%F", "2005-06-01"); 973 verifyFormatEquals(t, "%G", "2005"); 974 verifyFormatEquals(t, "%g", "05"); 975 verifyFormatEquals(t, "%H", "12"); 976 verifyFormatEquals(t, "%h", "Jun"); 977 verifyFormatEquals(t, "%I", "12"); 978 verifyFormatEquals(t, "%j", "152"); 979 verifyFormatEquals(t, "%K", "K"); 980 verifyFormatEquals(t, "%k", "12"); 981 verifyFormatEquals(t, "%l", "12"); 982 verifyFormatEquals(t, "%M", "30"); 983 verifyFormatEquals(t, "%m", "06"); 984 verifyFormatEquals(t, "%n", "\n"); 985 verifyFormatEquals(t, "%O", "O"); 986 verifyFormatEquals(t, "%p", "PM"); 987 verifyFormatEquals(t, "%P", "pm"); 988 verifyFormatEquals(t, "%R", "12:30"); 989 verifyFormatEquals(t, "%r", "12:30:15 PM"); 990 verifyFormatEquals(t, "%S", "15"); 991 // The original C implementation uses the (native) system default TZ, not the timezone of 992 // the Time to calculate this and was therefore not stable. This changed to use the Time's 993 // timezone when the Time class was re-written in Java. 994 verifyFormatEquals(t, "%s", "1117643415"); 995 verifyFormatEquals(t, "%T", "12:30:15"); 996 verifyFormatEquals(t, "%t", "\t"); 997 verifyFormatEquals(t, "%U", "22"); 998 verifyFormatEquals(t, "%u", "3"); 999 verifyFormatEquals(t, "%V", "22"); 1000 verifyFormatEquals(t, "%v", " 1-Jun-2005"); 1001 verifyFormatEquals(t, "%W", "22"); 1002 verifyFormatEquals(t, "%w", "3"); 1003 verifyFormatEquals(t, "%X", "12:30:15 PM"); 1004 verifyFormatEquals(t, "%x", "June 1, 2005"); 1005 verifyFormatEquals(t, "%y", "05"); 1006 verifyFormatEquals(t, "%Y", "2005"); 1007 verifyFormatEquals(t, "%Z", "EDT"); 1008 verifyFormatEquals(t, "%z", "-0400"); 1009 verifyFormatEquals(t, "%+", "Wed Jun 1 12:30:15 EDT 2005"); 1010 verifyFormatEquals(t, "%%", "%"); 1011 1012 // Modifiers 1013 1014 verifyFormatEquals(t, "%EC", "20"); 1015 verifyFormatEquals(t, "%OC", "20"); 1016 1017 verifyFormatEquals(t, "%_+", "Wed Jun 1 12:30:15 EDT 2005"); 1018 verifyFormatEquals(t, "%-+", "Wed Jun 1 12:30:15 EDT 2005"); 1019 verifyFormatEquals(t, "%0+", "Wed Jun 1 12:30:15 EDT 2005"); 1020 verifyFormatEquals(t, "%^+", "Wed Jun 1 12:30:15 EDT 2005"); 1021 verifyFormatEquals(t, "%#+", "Wed Jun 1 12:30:15 EDT 2005"); 1022 1023 verifyFormatEquals(t, "%_A", "Wednesday"); 1024 verifyFormatEquals(t, "%-A", "Wednesday"); 1025 verifyFormatEquals(t, "%0A", "Wednesday"); 1026 verifyFormatEquals(t, "%^A", "WEDNESDAY"); 1027 verifyFormatEquals(t, "%#A", "wEDNESDAY"); 1028 1029 verifyFormatEquals(t, "%_Y", "20 5"); 1030 verifyFormatEquals(t, "%-Y", "205"); 1031 verifyFormatEquals(t, "%0Y", "2005"); 1032 verifyFormatEquals(t, "%^Y", "2005"); 1033 verifyFormatEquals(t, "%#Y", "2005"); 1034 1035 verifyFormatEquals(t, "%_d", " 1"); 1036 verifyFormatEquals(t, "%-d", "1"); 1037 verifyFormatEquals(t, "%0d", "01"); 1038 verifyFormatEquals(t, "%^d", "01"); 1039 verifyFormatEquals(t, "%#d", "01"); 1040 } 1041 1042 @Test testFormat_tokensFranceLocale()1043 public void testFormat_tokensFranceLocale() { 1044 if (!changeJavaAndAndroidLocale(Locale.FRANCE, false /* force */)) { 1045 Log.w(TAG, "Skipping testFormat_tokensFranceLocale: no assets found"); 1046 return; 1047 } 1048 1049 Time t = new Time("Europe/Paris"); 1050 Fields.setDateTime(t, 2005, 5, 1, 12, 30, 15); 1051 1052 // Prove the un-normalized fields are used. 1053 verifyFormatEquals(t, "%A", "dimanche"); 1054 1055 // Set fields like weekday. 1056 t.normalize(true); 1057 1058 verifyFormatEquals(t, "%A", "mercredi"); 1059 verifyFormatEquals(t, "%a", "mer."); 1060 verifyFormatEquals(t, "%B", "juin"); 1061 verifyFormatEquals(t, "%b", "juin"); 1062 verifyFormatEquals(t, "%C", "20"); 1063 verifyFormatEquals(t, "%c", "1 juin 2005 à 12:30:15"); 1064 verifyFormatEquals(t, "%D", "06/01/05"); 1065 verifyFormatEquals(t, "%d", "01"); 1066 verifyFormatEquals(t, "%E", "E"); 1067 verifyFormatEquals(t, "%e", " 1"); 1068 verifyFormatEquals(t, "%F", "2005-06-01"); 1069 verifyFormatEquals(t, "%G", "2005"); 1070 verifyFormatEquals(t, "%g", "05"); 1071 verifyFormatEquals(t, "%H", "12"); 1072 verifyFormatEquals(t, "%h", "juin"); 1073 verifyFormatEquals(t, "%I", "12"); 1074 verifyFormatEquals(t, "%j", "152"); 1075 verifyFormatEquals(t, "%K", "K"); 1076 verifyFormatEquals(t, "%k", "12"); 1077 verifyFormatEquals(t, "%l", "12"); 1078 verifyFormatEquals(t, "%M", "30"); 1079 verifyFormatEquals(t, "%m", "06"); 1080 verifyFormatEquals(t, "%n", "\n"); 1081 verifyFormatEquals(t, "%O", "O"); 1082 verifyFormatEquals(t, "%p", "PM"); 1083 verifyFormatEquals(t, "%P", "pm"); 1084 verifyFormatEquals(t, "%R", "12:30"); 1085 verifyFormatEquals(t, "%r", "12:30:15 PM"); 1086 verifyFormatEquals(t, "%S", "15"); 1087 // The original C implementation uses the (native) system default TZ, not the timezone of 1088 // the Time to calculate this and was therefore not stable. This changed to use the Time's 1089 // timezone when the Time class was re-written in Java. 1090 verifyFormatEquals(t, "%s", "1117621815"); 1091 verifyFormatEquals(t, "%T", "12:30:15"); 1092 verifyFormatEquals(t, "%t", "\t"); 1093 verifyFormatEquals(t, "%U", "22"); 1094 verifyFormatEquals(t, "%u", "3"); 1095 verifyFormatEquals(t, "%V", "22"); 1096 verifyFormatEquals(t, "%v", " 1-juin-2005"); 1097 verifyFormatEquals(t, "%W", "22"); 1098 verifyFormatEquals(t, "%w", "3"); 1099 verifyFormatEquals(t, "%X", "12:30:15"); 1100 verifyFormatEquals(t, "%x", "1 juin 2005"); 1101 verifyFormatEquals(t, "%y", "05"); 1102 verifyFormatEquals(t, "%Y", "2005"); 1103 verifyFormatEquals(t, "%Z", "GMT+02:00"); 1104 verifyFormatEquals(t, "%z", "+0200"); 1105 verifyFormatEquals(t, "%+", "mer. juin 1 12:30:15 GMT+02:00 2005"); 1106 verifyFormatEquals(t, "%%", "%"); 1107 1108 // Modifiers 1109 1110 verifyFormatEquals(t, "%EC", "20"); 1111 verifyFormatEquals(t, "%OC", "20"); 1112 1113 verifyFormatEquals(t, "%_+", "mer. juin 1 12:30:15 GMT+02:00 2005"); 1114 verifyFormatEquals(t, "%-+", "mer. juin 1 12:30:15 GMT+02:00 2005"); 1115 verifyFormatEquals(t, "%0+", "mer. juin 1 12:30:15 GMT+02:00 2005"); 1116 verifyFormatEquals(t, "%^+", "mer. juin 1 12:30:15 GMT+02:00 2005"); 1117 verifyFormatEquals(t, "%#+", "mer. juin 1 12:30:15 GMT+02:00 2005"); 1118 1119 verifyFormatEquals(t, "%_A", "mercredi"); 1120 verifyFormatEquals(t, "%-A", "mercredi"); 1121 verifyFormatEquals(t, "%0A", "mercredi"); 1122 verifyFormatEquals(t, "%^A", "MERCREDI"); 1123 verifyFormatEquals(t, "%#A", "MERCREDI"); 1124 1125 verifyFormatEquals(t, "%_Y", "20 5"); 1126 verifyFormatEquals(t, "%-Y", "205"); 1127 verifyFormatEquals(t, "%0Y", "2005"); 1128 verifyFormatEquals(t, "%^Y", "2005"); 1129 verifyFormatEquals(t, "%#Y", "2005"); 1130 1131 verifyFormatEquals(t, "%_d", " 1"); 1132 verifyFormatEquals(t, "%-d", "1"); 1133 verifyFormatEquals(t, "%0d", "01"); 1134 verifyFormatEquals(t, "%^d", "01"); 1135 verifyFormatEquals(t, "%#d", "01"); 1136 } 1137 1138 @Test testFormat_tokensJapanLocale()1139 public void testFormat_tokensJapanLocale() { 1140 if (!changeJavaAndAndroidLocale(Locale.JAPAN, false /* force */)) { 1141 Log.w(TAG, "Skipping testFormat_tokensJapanLocale: no assets found"); 1142 return; 1143 } 1144 1145 Time t = new Time("Asia/Tokyo"); 1146 Fields.setDateTime(t, 2005, 5, 1, 12, 30, 15); 1147 1148 // Prove the un-normalized fields are used. 1149 verifyFormatEquals(t, "%A", "日曜日"); 1150 1151 // Set fields like weekday. 1152 t.normalize(true); 1153 1154 verifyFormatEquals(t, "%A", "水曜日"); 1155 verifyFormatEquals(t, "%a", "水"); 1156 verifyFormatEquals(t, "%B", "6月"); 1157 verifyFormatEquals(t, "%b", "6月"); 1158 verifyFormatEquals(t, "%C", "20"); 1159 verifyFormatEquals(t, "%c", "2005/06/01 12:30:15"); 1160 verifyFormatEquals(t, "%D", "06/01/05"); 1161 verifyFormatEquals(t, "%d", "01"); 1162 verifyFormatEquals(t, "%E", "E"); 1163 verifyFormatEquals(t, "%e", " 1"); 1164 verifyFormatEquals(t, "%F", "2005-06-01"); 1165 verifyFormatEquals(t, "%G", "2005"); 1166 verifyFormatEquals(t, "%g", "05"); 1167 verifyFormatEquals(t, "%H", "12"); 1168 verifyFormatEquals(t, "%h", "6月"); 1169 verifyFormatEquals(t, "%I", "12"); 1170 verifyFormatEquals(t, "%j", "152"); 1171 verifyFormatEquals(t, "%k", "12"); 1172 verifyFormatEquals(t, "%l", "12"); 1173 verifyFormatEquals(t, "%M", "30"); 1174 verifyFormatEquals(t, "%m", "06"); 1175 verifyFormatEquals(t, "%n", "\n"); 1176 verifyFormatEquals(t, "%O", "O"); 1177 verifyFormatEquals(t, "%p", "午後"); 1178 verifyFormatEquals(t, "%P", "午後"); 1179 verifyFormatEquals(t, "%R", "12:30"); 1180 verifyFormatEquals(t, "%r", "12:30:15 午後"); 1181 verifyFormatEquals(t, "%S", "15"); 1182 // The original C implementation uses the (native) system default TZ, not the timezone of 1183 // the Time to calculate this and was therefore not stable. This changed to use the Time's 1184 // timezone when the Time class was re-written in Java. 1185 verifyFormatEquals(t, "%s", "1117596615"); 1186 verifyFormatEquals(t, "%T", "12:30:15"); 1187 verifyFormatEquals(t, "%t", "\t"); 1188 verifyFormatEquals(t, "%U", "22"); 1189 verifyFormatEquals(t, "%u", "3"); 1190 verifyFormatEquals(t, "%V", "22"); 1191 verifyFormatEquals(t, "%v", " 1-6月-2005"); 1192 verifyFormatEquals(t, "%W", "22"); 1193 verifyFormatEquals(t, "%w", "3"); 1194 verifyFormatEquals(t, "%X", "12:30:15"); 1195 verifyFormatEquals(t, "%x", "2005年6月1日"); 1196 verifyFormatEquals(t, "%y", "05"); 1197 verifyFormatEquals(t, "%Y", "2005"); 1198 verifyFormatEquals(t, "%Z", "JST"); 1199 verifyFormatEquals(t, "%z", "+0900"); 1200 verifyFormatEquals(t, "%+", "水 6月 1 12:30:15 JST 2005"); 1201 verifyFormatEquals(t, "%%", "%"); 1202 1203 // Modifiers 1204 1205 verifyFormatEquals(t, "%EC", "20"); 1206 verifyFormatEquals(t, "%OC", "20"); 1207 1208 verifyFormatEquals(t, "%_+", "水 6月 1 12:30:15 JST 2005"); 1209 verifyFormatEquals(t, "%-+", "水 6月 1 12:30:15 JST 2005"); 1210 verifyFormatEquals(t, "%0+", "水 6月 1 12:30:15 JST 2005"); 1211 verifyFormatEquals(t, "%^+", "水 6月 1 12:30:15 JST 2005"); 1212 verifyFormatEquals(t, "%#+", "水 6月 1 12:30:15 JST 2005"); 1213 1214 verifyFormatEquals(t, "%_A", "水曜日"); 1215 verifyFormatEquals(t, "%-A", "水曜日"); 1216 verifyFormatEquals(t, "%0A", "水曜日"); 1217 verifyFormatEquals(t, "%^A", "水曜日"); 1218 verifyFormatEquals(t, "%#A", "水曜日"); 1219 1220 verifyFormatEquals(t, "%_Y", "20 5"); 1221 verifyFormatEquals(t, "%-Y", "205"); 1222 verifyFormatEquals(t, "%0Y", "2005"); 1223 verifyFormatEquals(t, "%^Y", "2005"); 1224 verifyFormatEquals(t, "%#Y", "2005"); 1225 1226 verifyFormatEquals(t, "%_d", " 1"); 1227 verifyFormatEquals(t, "%-d", "1"); 1228 verifyFormatEquals(t, "%0d", "01"); 1229 verifyFormatEquals(t, "%^d", "01"); 1230 verifyFormatEquals(t, "%#d", "01"); 1231 } 1232 1233 @Test testFormat2445()1234 public void testFormat2445() { 1235 Time t = new Time(Time.TIMEZONE_UTC); 1236 Fields.setDateTime(t, 2005, 5, 1, 12, 30, 15); 1237 1238 // UTC behavior is to add a trailing Z. 1239 String expected = t.format("%Y%m%dT%H%M%SZ"); 1240 assertEquals(expected, t.format2445()); 1241 1242 // Only UTC includes the Z. 1243 t.timezone = "America/Los_Angeles"; 1244 expected = t.format("%Y%m%dT%H%M%S"); 1245 assertEquals(expected, t.format2445()); 1246 1247 // There is odd behavior around negative values. 1248 Fields.setDateTime(t, 2005, -1, -1, -1, -1, -1); 1249 assertEquals("2005000 T0 0 0 ", t.format2445()); 1250 } 1251 1252 @Test testFormat2445_doesNotNormalize()1253 public void testFormat2445_doesNotNormalize() { 1254 Time t = new Time(Time.TIMEZONE_UTC); 1255 Fields.set(t, 2005, 13, 32, 25, 61, 61, -2, -2, -2, -2); 1256 1257 Time tCopy = new Time(t); 1258 Fields.verifyTimeEquals(t, tCopy); 1259 1260 assertEquals("20051432T256161Z", t.format2445()); 1261 Fields.verifyTimeEquals(t, tCopy); 1262 1263 t.timezone = tCopy.timezone = "America/Los_Angeles"; 1264 assertEquals("20051432T256161", t.format2445()); 1265 Fields.verifyTimeEquals(t, tCopy); 1266 } 1267 1268 @Test testToString()1269 public void testToString() { 1270 Time t = new Time(Time.TIMEZONE_UTC); 1271 assertEquals("19700101T000000UTC(0,0,0,-1,0)", t.toString()); 1272 1273 t.timezone = "America/Los_Angeles"; 1274 assertEquals("19700101T000000America/Los_Angeles(0,0,0,-1,28800)", t.toString()); 1275 } 1276 1277 @Test testToString_doesNotNormalize()1278 public void testToString_doesNotNormalize() { 1279 Time t = new Time(Time.TIMEZONE_UTC); 1280 Fields.set(t, 2005, 13, 32, -1, -1, -1, -2, -2, -2, -2); 1281 1282 Time tCopy = new Time(t); 1283 Fields.verifyTimeEquals(t, tCopy); 1284 1285 String r = t.toString(); 1286 assertEquals("20051432T-1-1-1UTC(-2,-2,-2,-2,1141426739)", r); 1287 1288 Fields.verifyTimeEquals(t, tCopy); 1289 } 1290 1291 @Test testGetCurrentTimezone()1292 public void testGetCurrentTimezone() { 1293 String r = Time.getCurrentTimezone(); 1294 assertEquals(TimeZone.getDefault().getID(), r); 1295 } 1296 1297 @Test testSetToNow()1298 public void testSetToNow() { 1299 Time t = new Time(Time.TIMEZONE_UTC); 1300 1301 Instant lowerBound = Instant.now(); 1302 1303 t.setToNow(); 1304 1305 Instant upperBound = Instant.now(); 1306 1307 // Time.toMillis() only works in seconds so lower/upper/toMillis() are converted to seconds, 1308 // otherwise the greater accuracy from using milliseconds for lowerBound/upperBound can 1309 // cause the test to fail. 1310 long actualSeconds = 1311 Instant.ofEpochMilli(t.toMillis(true /* ignore isDst */)).getEpochSecond(); 1312 assertTrue(lowerBound.getEpochSecond() <= actualSeconds 1313 && actualSeconds <= upperBound.getEpochSecond()); 1314 } 1315 1316 @Test testToMillis_utc()1317 public void testToMillis_utc() { 1318 Time t = new Time(Time.TIMEZONE_UTC); 1319 1320 long winterTimeUtcMillis = 1167613323000L; 1321 1322 Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 1, 9, 9); 1323 long r = t.toMillis(true /* ignore isDst */); 1324 assertEquals(winterTimeUtcMillis, r); 1325 1326 Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 1, 9, 9); 1327 r = t.toMillis(true /* ignore isDst */); 1328 assertEquals(winterTimeUtcMillis, r); 1329 1330 Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 1, 9, 9); 1331 r = t.toMillis(true /* ignore isDst */); 1332 assertEquals(winterTimeUtcMillis, r); 1333 1334 Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 1, 9, 9); 1335 r = t.toMillis(false /* ignore isDst */); 1336 assertEquals(winterTimeUtcMillis, r); 1337 1338 Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 1, 9, 9); 1339 r = t.toMillis(false /* ignore isDst */); 1340 assertEquals(-1, r); 1341 1342 Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 1, 9, 9); 1343 r = t.toMillis(false /* ignore isDst */); 1344 assertEquals(winterTimeUtcMillis, r); 1345 1346 long summerTimeUtcMillis = 1180659723000L; 1347 1348 Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 1, 9, 9); 1349 r = t.toMillis(true /* ignore isDst */); 1350 assertEquals(summerTimeUtcMillis, r); 1351 1352 Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 1, 9, 9); 1353 r = t.toMillis(true /* ignore isDst */); 1354 assertEquals(summerTimeUtcMillis, r); 1355 1356 Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 1, 9, 9); 1357 r = t.toMillis(true /* ignore isDst */); 1358 assertEquals(summerTimeUtcMillis, r); 1359 1360 Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 1, 9, 9); 1361 r = t.toMillis(false /* ignore isDst */); 1362 assertEquals(summerTimeUtcMillis, r); 1363 1364 Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 1, 9, 9); 1365 r = t.toMillis(false /* ignore isDst */); 1366 assertEquals(-1, r); 1367 1368 Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 1, 9, 9); 1369 r = t.toMillis(false /* ignore isDst */); 1370 assertEquals(summerTimeUtcMillis, r); 1371 } 1372 1373 @Test testToMillis_dstTz()1374 public void testToMillis_dstTz() { 1375 Time t = new Time(PstPdt.ID); 1376 1377 // A STD time 1378 long stdTimeUtcMillis = 1167613323000L; 1379 long stdTimeMillis = stdTimeUtcMillis - PstPdt.getUtcOffsetMillis(false); 1380 1381 Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9); 1382 long r = t.toMillis(true /* ignore isDst */); 1383 assertEquals(stdTimeMillis, r); 1384 1385 Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9); 1386 verifyToMillisResult(true, t, stdTimeMillis); 1387 1388 Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9); 1389 verifyToMillisResult(true, t, stdTimeMillis); 1390 1391 long dstToStdCorrectionMillis = 1392 PstPdt.getUtcOffsetMillis(false) - PstPdt.getUtcOffsetMillis(true); 1393 1394 Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9); 1395 verifyToMillisResult(false, t, stdTimeMillis); 1396 1397 Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9); 1398 verifyToMillisResult(false, t, stdTimeMillis + dstToStdCorrectionMillis); 1399 1400 Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9); 1401 verifyToMillisResult(false, t, stdTimeMillis); 1402 1403 // A DST time 1404 long dstTimeUtcMillis = 1180659723000L; 1405 long dstTimeMillis = dstTimeUtcMillis - PstPdt.getUtcOffsetMillis(true); 1406 1407 Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9); 1408 verifyToMillisResult(true, t, dstTimeMillis); 1409 1410 Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9); 1411 verifyToMillisResult(true, t, dstTimeMillis); 1412 1413 Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9); 1414 verifyToMillisResult(true, t, dstTimeMillis); 1415 1416 long stdToDstCorrectionMillis = -dstToStdCorrectionMillis; 1417 1418 Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9); 1419 verifyToMillisResult(false, t, dstTimeMillis + stdToDstCorrectionMillis); 1420 1421 Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9); 1422 verifyToMillisResult(false, t, dstTimeMillis); 1423 1424 Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9); 1425 verifyToMillisResult(false, t, dstTimeMillis); 1426 } 1427 verifyToMillisResult(boolean toMillisArgument, Time t, long expectedResult)1428 private static void verifyToMillisResult(boolean toMillisArgument, Time t, 1429 long expectedResult) { 1430 long r = t.toMillis(toMillisArgument /* ignore isDst */); 1431 assertEquals(expectedResult, r); 1432 } 1433 1434 @Test testToMillis_doesNotNormalize()1435 public void testToMillis_doesNotNormalize() { 1436 Time t = new Time(Time.TIMEZONE_UTC); 1437 1438 Fields.set(t, 2007, 13, 32, 25, 60, 60, -2 /* isDst */, Integer.MAX_VALUE, 367, 7); 1439 1440 Time originalTime = new Time(t); 1441 Fields.verifyTimeEquals(t, originalTime); 1442 1443 t.toMillis(true); 1444 Fields.verifyTimeEquals(originalTime, t); 1445 1446 t.toMillis(false); 1447 Fields.verifyTimeEquals(originalTime, t); 1448 } 1449 1450 @Test testToMillis_skippedTime()1451 public void testToMillis_skippedTime() { 1452 // Tests behavior around a transition from STD to DST that introduces an hour of "skipped" 1453 // time from 01:00 to 01:59. 1454 String timezone = PstPdt.ID; 1455 long stdBaseTimeMillis = 1173607200000L; 1456 long dstBaseTimeMillis = 1173603600000L; 1457 1458 // Try each minute from one minute before the skipped hour until one after. 1459 for (int i = -1; i <= 60; i++) { 1460 int minutesInMillis = (int) Duration.ofMinutes(i).toMillis(); 1461 int[] timeFields = new int[] { 2007, 2, 11, 2, i, 0, -999 /* not used */, 9, 9, 9 }; 1462 1463 Time time = new Time(timezone); 1464 1465 // isDst = 0, toMillis(true) 1466 Fields.set(time, timeFields); 1467 time.isDst = 0; 1468 long expectedTimeMillis; 1469 if (i == -1) { 1470 expectedTimeMillis = stdBaseTimeMillis + minutesInMillis; 1471 } else if (i == 60) { 1472 expectedTimeMillis = dstBaseTimeMillis + minutesInMillis; 1473 } else { 1474 expectedTimeMillis = -1; 1475 } 1476 verifyToMillisResult(true, time, expectedTimeMillis); 1477 1478 // isDst = 0, toMillis(false) 1479 Fields.set(time, timeFields); 1480 time.isDst = 0; 1481 expectedTimeMillis = stdBaseTimeMillis + minutesInMillis; 1482 verifyToMillisResult(false, time, expectedTimeMillis); 1483 1484 // isDst = 1, toMillis(true) 1485 Fields.set(time, timeFields); 1486 time.isDst = 1; 1487 if (i == -1) { 1488 expectedTimeMillis = stdBaseTimeMillis + minutesInMillis; 1489 } else if (i == 60) { 1490 expectedTimeMillis = dstBaseTimeMillis + minutesInMillis; 1491 } else { 1492 expectedTimeMillis = -1; 1493 } 1494 verifyToMillisResult(true, time, expectedTimeMillis); 1495 1496 // isDst = 1, toMillis(false) 1497 Fields.set(time, timeFields); 1498 time.isDst = 1; 1499 expectedTimeMillis = dstBaseTimeMillis + minutesInMillis; 1500 verifyToMillisResult(false, time, expectedTimeMillis); 1501 1502 // isDst = -1, toMillis(true) 1503 Fields.set(time, timeFields); 1504 time.isDst = -1; 1505 1506 if (i == -1) { 1507 expectedTimeMillis = stdBaseTimeMillis + minutesInMillis; 1508 } else if (i == 60) { 1509 expectedTimeMillis = dstBaseTimeMillis + minutesInMillis; 1510 } else { 1511 expectedTimeMillis = -1; 1512 } 1513 verifyToMillisResult(false, time, expectedTimeMillis); 1514 1515 // isDst = -1, toMillis(false) 1516 Fields.set(time, timeFields); 1517 time.isDst = -1; 1518 verifyToMillisResult(false, time, expectedTimeMillis); 1519 } 1520 } 1521 1522 @Test testToMillis_duplicateWallTime()1523 public void testToMillis_duplicateWallTime() { 1524 // 1:00 in standard / 2:00 in DST 1525 long timeBaseMillis = 1194163200000L; 1526 long dstCorrectionMillis = 3600000; 1527 1528 // Try each minute from one minute before the duplicated hour until one after. 1529 for (int i = -1; i <= 60; i++) { 1530 int minutesInMillis = (int) Duration.ofMinutes(i).toMillis(); 1531 1532 Time time = new Time(PstPdt.ID); 1533 int[] timeFields = new int[] { 2007, 10, 4, 1, i, 0, -999 /* not used */, 9, 9, 9}; 1534 1535 // isDst = 0, toMillis(true) 1536 Fields.set(time, timeFields); 1537 time.isDst = 0; 1538 long timeMillis = time.toMillis(true); 1539 if (i == -1) { 1540 assertEquals("i = " + i, timeBaseMillis + minutesInMillis, timeMillis); 1541 } else if (i == 60) { 1542 assertEquals("i = " + i, timeBaseMillis + minutesInMillis + dstCorrectionMillis, 1543 timeMillis); 1544 } else { 1545 // When ignoreDst the choice between DST and STD is arbitrary when both are 1546 // possible. 1547 assertTrue("i = " + i, timeMillis == timeBaseMillis + minutesInMillis 1548 || timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis); 1549 } 1550 1551 // isDst = 0, toMillis(false) 1552 Fields.set(time, timeFields); 1553 time.isDst = 0; 1554 verifyToMillisResult(false, time, 1555 timeBaseMillis + minutesInMillis + dstCorrectionMillis); 1556 1557 // isDst = 1, toMillis(true) 1558 Fields.set(time, timeFields); 1559 time.isDst = 1; 1560 if (i == -1) { 1561 assertEquals("i = " + i, timeBaseMillis + minutesInMillis, timeMillis); 1562 } else if (i == 60) { 1563 assertEquals("i = " + i, timeBaseMillis + minutesInMillis + dstCorrectionMillis, 1564 timeMillis); 1565 } else { 1566 // When ignoreDst or isDst == -1 the choice between DST and STD is arbitrary when 1567 // both are possible. 1568 assertTrue("i = " + i, timeMillis == timeBaseMillis + minutesInMillis 1569 || timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis); 1570 } 1571 1572 // isDst = 1, toMillis(false) 1573 Fields.set(time, timeFields); 1574 time.isDst = 1; 1575 verifyToMillisResult(false, time, timeBaseMillis + minutesInMillis); 1576 1577 // isDst = -1, toMillis(true) 1578 Fields.set(time, timeFields); 1579 time.isDst = -1; 1580 timeMillis = time.toMillis(true); 1581 if (i == -1) { 1582 assertEquals("i = " + i, timeBaseMillis + minutesInMillis, timeMillis); 1583 } else if (i == 60) { 1584 assertEquals("i = " + i, timeBaseMillis + minutesInMillis + dstCorrectionMillis, 1585 timeMillis); 1586 } else { 1587 // When ignoreDst or isDst == -1 the choice between DST and STD is arbitrary when 1588 // both are possible. 1589 assertTrue("i = " + i, timeMillis == timeBaseMillis + minutesInMillis 1590 || timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis); 1591 } 1592 1593 // isDst = -1, toMillis(false) 1594 Fields.set(time, timeFields); 1595 time.isDst = -1; 1596 timeMillis = time.toMillis(false); 1597 if (i == -1) { 1598 assertEquals("i = " + i, timeBaseMillis + minutesInMillis, timeMillis); 1599 } else if (i == 60) { 1600 assertEquals("i = " + i, timeBaseMillis + minutesInMillis + dstCorrectionMillis, 1601 timeMillis); 1602 } else { 1603 // When ignoreDst or isDst == -1 the choice between DST and STD is arbitrary when 1604 // both are possible. 1605 assertTrue("i = " + i, timeMillis == timeBaseMillis + minutesInMillis 1606 || timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis); 1607 } 1608 } 1609 } 1610 1611 @Test testToMillis_before32BitSeconds()1612 public void testToMillis_before32BitSeconds() { 1613 int[] timeFields = new int[] { 1900, 0, 1, 2, 3, 4, -999 /* not used */, 9, 9, 9 }; 1614 verifyToMillisInvalid(timeFields, PstPdt.ID); 1615 verifyToMillisInvalid(timeFields, Time.TIMEZONE_UTC); 1616 } 1617 verifyToMillisInvalid(int[] timeFields, String timezone)1618 private static void verifyToMillisInvalid(int[] timeFields, String timezone) { 1619 Time time = new Time(timezone); 1620 1621 // isDst = 0, toMillis(true) 1622 Fields.set(time, timeFields); 1623 time.isDst = 0; 1624 verifyToMillisResult(true, time, -1); 1625 1626 // isDst = 0, toMillis(false) 1627 Fields.set(time, timeFields); 1628 time.isDst = 0; 1629 verifyToMillisResult(false, time, -1); 1630 1631 // isDst = 1, toMillis(true) 1632 Fields.set(time, timeFields); 1633 time.isDst = 1; 1634 verifyToMillisResult(true, time, -1); 1635 1636 // isDst = 1, toMillis(false) 1637 Fields.set(time, timeFields); 1638 time.isDst = 1; 1639 verifyToMillisResult(false, time, -1); 1640 1641 // isDst = -1, toMillis(true) 1642 Fields.set(time, timeFields); 1643 time.isDst = -1; 1644 verifyToMillisResult(true, time, -1); 1645 1646 // isDst = -1, toMillis(false) 1647 Fields.set(time, timeFields); 1648 time.isDst = -1; 1649 verifyToMillisResult(false, time, -1); 1650 } 1651 1652 @Test testToMillis_after32BitSeconds()1653 public void testToMillis_after32BitSeconds() { 1654 int[] timeFields = new int[] { 2039, 0, 1, 2, 3, 4, -999 /* not used */, 9, 9, 9 }; 1655 verifyToMillisInvalid(timeFields, PstPdt.ID); 1656 verifyToMillisInvalid(timeFields, Time.TIMEZONE_UTC); 1657 } 1658 1659 @Test testToMillis_invalid()1660 public void testToMillis_invalid() { 1661 int[] timeFields = new int[] { 0, 0, 0, 0, 0, 0, -999 /* not used */, 9, 9, 9 }; 1662 verifyToMillisInvalid(timeFields, PstPdt.ID); 1663 verifyToMillisInvalid(timeFields, Time.TIMEZONE_UTC); 1664 } 1665 1666 @Test testParse_date()1667 public void testParse_date() { 1668 String nonUtcTz = PstPdt.ID; 1669 Time t = new Time(nonUtcTz); 1670 assertFalse(t.parse("12345678")); 1671 Time expected = new Time(nonUtcTz); 1672 Fields.setAllDayDate(expected, 1234, 55, 78); 1673 Fields.setDst(expected, -1 /* isDst */, 0); 1674 Fields.setDerivedDateTime(expected, 0, 0); 1675 Fields.verifyTimeEquals(expected, t); 1676 } 1677 1678 @Test(expected=NullPointerException.class) testParse_null()1679 public void testParse_null() { 1680 Time t = new Time(Time.TIMEZONE_UTC); 1681 t.parse(null); 1682 } 1683 1684 @Test testParse()1685 public void testParse() { 1686 Time t = new Time(Time.TIMEZONE_UTC); 1687 t.parse("20061005T120000"); 1688 1689 Time expected = new Time(Time.TIMEZONE_UTC); 1690 Fields.set(expected, 2006, 9, 5, 12, 0, 0, -1 /* isDst */, 0, 0, 0); 1691 Fields.verifyTimeEquals(expected, t); 1692 } 1693 1694 @Test testParse_dateTime()1695 public void testParse_dateTime() { 1696 String nonUtcTz = PstPdt.ID; 1697 Time t = new Time(nonUtcTz); 1698 assertFalse(t.parse("12345678T901234")); 1699 Time expected = new Time(nonUtcTz); 1700 Fields.set(expected, 1234, 55, 78, 90, 12, 34, -1 /* isDst */, 0, 0, 0); 1701 Fields.verifyTimeEquals(expected, t); 1702 1703 Time t2 = new Time(nonUtcTz); 1704 assertTrue(t2.parse("12345678T901234Z")); 1705 Time utcExpected = new Time(Time.TIMEZONE_UTC); 1706 Fields.set(utcExpected, 1234, 55, 78, 90, 12, 34, -1 /* isDst */, 0, 0, 0); 1707 Fields.verifyTimeEquals(utcExpected, t2); 1708 } 1709 1710 @Test(expected=NullPointerException.class) testParse_pstPdtNull()1711 public void testParse_pstPdtNull() { 1712 Time t = new Time(PstPdt.ID); 1713 t.parse(null); 1714 } 1715 1716 @Test testParse_errors()1717 public void testParse_errors() { 1718 // Too short 1719 verifyParseError(""); 1720 verifyParseError("1"); 1721 verifyParseError("12"); 1722 verifyParseError("123"); 1723 verifyParseError("1234"); 1724 verifyParseError("12345"); 1725 verifyParseError("123456"); 1726 verifyParseError("1234567"); 1727 1728 // No "T" in the expected place 1729 verifyParseError("12345678S"); 1730 1731 // Invalid character in the first 8 characters. 1732 verifyParseError("12X45678"); 1733 1734 // Too short for a date/time (15 or 16 characters allowed) 1735 verifyParseError("12345678T"); 1736 verifyParseError("12345678T0"); 1737 verifyParseError("12345678T01"); 1738 verifyParseError("12345678T012"); 1739 verifyParseError("12345678T0123"); 1740 verifyParseError("12345678T01234"); 1741 1742 // Invalid character 1743 verifyParseError("12345678T0X2345"); 1744 } 1745 verifyParseError(String s)1746 private static void verifyParseError(String s) { 1747 Time t = new Time(Time.TIMEZONE_UTC); 1748 try { 1749 t.parse(s); 1750 fail(); 1751 } catch (TimeFormatException expected) { 1752 } 1753 } 1754 1755 @Test testParse3339()1756 public void testParse3339() { 1757 String tz = Time.TIMEZONE_UTC; 1758 Time expected = new Time(tz); 1759 Fields.setAllDayDate(expected, 1980, 4, 23); 1760 Fields.setDst(expected, -1 /* isDst */, 0); 1761 Fields.setDerivedDateTime(expected, 0, 0); 1762 verifyParse3339Succeeds(tz, "1980-05-23", expected); 1763 1764 Fields.setDateTime(expected, 1980, 4, 23, 9, 50, 50); 1765 Fields.setDst(expected, -1 /* isDst */, 0); 1766 Fields.setDerivedDateTime(expected, 0, 0); 1767 verifyParse3339Succeeds(tz, "1980-05-23T09:50:50", expected); 1768 1769 Fields.setDateTime(expected, 1980, 4, 23, 9, 50, 50); 1770 Fields.setDst(expected, -1 /* isDst */, 0); 1771 Fields.setDerivedDateTime(expected, 0, 0); 1772 verifyParse3339Succeeds(tz, "1980-05-23T09:50:50Z", expected); 1773 1774 Fields.setDateTime(expected, 1980, 4, 23, 9, 50, 50); 1775 Fields.setDst(expected, -1 /* isDst */, 0); 1776 Fields.setDerivedDateTime(expected, 0, 0); 1777 verifyParse3339Succeeds(tz, "1980-05-23T09:50:50.0Z", expected); 1778 1779 Fields.setDateTime(expected, 1980, 4, 23, 9, 50, 50); 1780 Fields.setDst(expected, -1 /* isDst */, 0); 1781 Fields.setDerivedDateTime(expected, 0, 0); 1782 verifyParse3339Succeeds(tz, "1980-05-23T09:50:50.12Z", expected); 1783 1784 Fields.setDateTime(expected, 1980, 4, 23, 9, 50, 50); 1785 Fields.setDst(expected, -1 /* isDst */, 0); 1786 Fields.setDerivedDateTime(expected, 0, 0); 1787 verifyParse3339Succeeds(tz, "1980-05-23T09:50:50.123Z", expected); 1788 1789 // The time should be normalized to UTC 1790 Fields.setDateTime(expected, 1980, 4, 23, 10, 55, 50); 1791 Fields.setDst(expected, -1 /* isDst */, 0); 1792 Fields.setDerivedDateTime(expected, 0, 0); 1793 verifyParse3339Succeeds(tz, "1980-05-23T09:50:50-01:05", expected); 1794 1795 // The time should be normalized to UTC 1796 Fields.setDateTime(expected, 1980, 4, 23, 10, 55, 50); 1797 Fields.setDst(expected, -1 /* isDst */, 0); 1798 Fields.setDerivedDateTime(expected, 0, 0); 1799 verifyParse3339Succeeds(tz, "1980-05-23T09:50:50.123-01:05", expected); 1800 } 1801 verifyParse3339Succeeds(String timeZone, String toParse, Time expected)1802 private static void verifyParse3339Succeeds(String timeZone, String toParse, Time expected) { 1803 Time t = new Time(timeZone); 1804 t.parse3339(toParse); 1805 Fields.verifyTimeEquals(expected, t); 1806 } 1807 1808 @Test testParse3339_parseErrors()1809 public void testParse3339_parseErrors() { 1810 // Too short 1811 verifyParse3339Error("1980"); 1812 1813 // Timezone too short 1814 verifyParse3339Error("1980-05-23T09:50:50.123+"); 1815 verifyParse3339Error("1980-05-23T09:50:50.123+05:0"); 1816 } 1817 1818 @Test(expected=NullPointerException.class) testParse3339_null()1819 public void testParse3339_null() { 1820 Time t = new Time(Time.TIMEZONE_UTC); 1821 t.parse3339(null); 1822 } 1823 verifyParse3339Error(String s)1824 private void verifyParse3339Error(String s) { 1825 String tz = Time.TIMEZONE_UTC; 1826 Time t = new Time(tz); 1827 try { 1828 t.parse3339(s); 1829 fail(); 1830 } catch (TimeFormatException expected) { 1831 } 1832 } 1833 1834 @Test testSetMillis_utc()1835 public void testSetMillis_utc() { 1836 Instant baseTime = Instant.ofEpochSecond(0); 1837 String tz = Time.TIMEZONE_UTC; 1838 Time t = new Time(tz); 1839 t.set(baseTime.plus(1, ChronoUnit.SECONDS).toEpochMilli()); 1840 1841 Time expected = new Time(tz); 1842 Fields.set(expected, 1970, 0, 1, 0, 0, 1, 0 /* isDst */, 0, 0, 4); 1843 Fields.verifyTimeEquals(expected, t); 1844 1845 t.set(baseTime.plus(2, ChronoUnit.SECONDS).toEpochMilli()); 1846 Fields.set(expected, 1970, 0, 1, 0, 0, 2, 0 /* isDst */, 0, 0, 4); 1847 Fields.verifyTimeEquals(expected, t); 1848 1849 t.set(baseTime.plus(1, ChronoUnit.MINUTES).toEpochMilli()); 1850 Fields.set(expected, 1970, 0, 1, 0, 1, 0, 0 /* isDst */, 0, 0, 4); 1851 Fields.verifyTimeEquals(expected, t); 1852 1853 t.set(baseTime.plus(1, ChronoUnit.DAYS).plus(1, ChronoUnit.SECONDS).toEpochMilli()); 1854 Fields.set(expected, 1970, 0, 2, 0, 0, 1, 0 /* isDst */, 0, 1, 5); 1855 Fields.verifyTimeEquals(expected, t); 1856 } 1857 1858 @Test testSetMillis_utc_edgeCases()1859 public void testSetMillis_utc_edgeCases() { 1860 String tz = Time.TIMEZONE_UTC; 1861 Time t = new Time(tz); 1862 t.set(Integer.MAX_VALUE + 1L); 1863 1864 Time expected = new Time(tz); 1865 // This a 32-bit int overflow bug. 1866 Fields.set(expected, 1970, 0, 25, 20, 31, 23, 0 /* isDst */, 0, 24, 0); 1867 Fields.verifyTimeEquals(expected, t); 1868 1869 t = new Time(tz); 1870 t.set(Integer.MIN_VALUE - 1L); 1871 // This a 32-bit int underflow bug. 1872 Fields.set(expected, 1969, 11, 7, 3, 28, 37, 0 /* isDst */, 0, 340, 0); 1873 Fields.verifyTimeEquals(expected, t); 1874 } 1875 1876 @Test testSetFields()1877 public void testSetFields() { 1878 String tz = Time.TIMEZONE_UTC; 1879 Time t = new Time(tz); 1880 Fields.set(t, 9, 9, 9, 9, 9, 9, 9 /* isDst */, 9, 9, 9); 1881 1882 t.set(1, 2, 3, 4, 5, 6); 1883 1884 Time expected = new Time(tz); 1885 Fields.set(expected, 6, 5, 4, 3, 2, 1, -1 /* isDst */, 0, 0, 0); 1886 Fields.verifyTimeEquals(expected, t); 1887 } 1888 1889 // Timezones that cover the world. Some GMT offsets occur more than 1890 // once in case some cities decide to change their GMT offset. 1891 private static final String[] mTimeZones = { 1892 "UTC", 1893 "Pacific/Kiritimati", 1894 "Pacific/Enderbury", 1895 "Pacific/Fiji", 1896 "Antarctica/South_Pole", 1897 "Pacific/Norfolk", 1898 "Pacific/Ponape", 1899 "Asia/Magadan", 1900 "Australia/Lord_Howe", 1901 "Australia/Sydney", 1902 "Australia/Adelaide", 1903 "Asia/Tokyo", 1904 "Asia/Seoul", 1905 "Asia/Taipei", 1906 "Asia/Singapore", 1907 "Asia/Hong_Kong", 1908 "Asia/Saigon", 1909 "Asia/Bangkok", 1910 "Indian/Cocos", 1911 "Asia/Rangoon", 1912 "Asia/Omsk", 1913 "Antarctica/Mawson", 1914 "Asia/Colombo", 1915 "Asia/Calcutta", 1916 "Asia/Oral", 1917 "Asia/Kabul", 1918 "Asia/Dubai", 1919 "Asia/Tehran", 1920 "Europe/Moscow", 1921 "Asia/Baghdad", 1922 "Africa/Mogadishu", 1923 "Europe/Athens", 1924 "Africa/Cairo", 1925 "Europe/Rome", 1926 "Europe/Berlin", 1927 "Europe/Amsterdam", 1928 "Africa/Tunis", 1929 "Europe/London", 1930 "Europe/Dublin", 1931 "Atlantic/St_Helena", 1932 "Africa/Monrovia", 1933 "Africa/Accra", 1934 "Atlantic/Azores", 1935 "Atlantic/South_Georgia", 1936 "America/Noronha", 1937 "America/Sao_Paulo", 1938 "America/Cayenne", 1939 "America/St_Johns", 1940 "America/Puerto_Rico", 1941 "America/Aruba", 1942 "America/New_York", 1943 "America/Chicago", 1944 "America/Denver", 1945 "America/Los_Angeles", 1946 "America/Anchorage", 1947 "Pacific/Marquesas", 1948 "America/Adak", 1949 "Pacific/Honolulu", 1950 "Pacific/Midway", 1951 }; 1952 1953 /** 1954 * This test uses java.time classes to construct test times so that it can test various years 1955 * including those outside of the int32 seconds range. 1956 */ 1957 @Test testGetJulianDay()1958 public void testGetJulianDay() { 1959 int[] years = { 2008, 1900, 1969, 2100 }; 1960 for (int year : years) { 1961 for (String timeZone : mTimeZones) { 1962 checkGetJulianDayForYearAndTimeZone(year, timeZone); 1963 } 1964 } 1965 } 1966 checkGetJulianDayForYearAndTimeZone(int year, String timeZone)1967 private static void checkGetJulianDayForYearAndTimeZone(int year, String timeZone) { 1968 final LocalTime midday = LocalTime.of(12, 0); 1969 1970 // For every 15th day of the year get the Julian day for 12pm and then check that if we 1971 // change the time we get the same Julian day. 1972 final LocalDate startDate = LocalDate.of(year, Month.JANUARY, 1); 1973 final LocalDate stopDate = startDate.plusYears(1); 1974 for (LocalDate testDate = startDate; testDate.isBefore(stopDate); 1975 testDate = testDate.plusDays(15)) { 1976 1977 LocalDateTime middayLocalDateTime = LocalDateTime.of(testDate, midday); 1978 ZoneOffset middayOffset = ZoneId.of(timeZone).getRules().getOffset(middayLocalDateTime); 1979 Instant middayInstant = middayLocalDateTime.toInstant(middayOffset); 1980 1981 // Record the Julian day for the date/time given. Since we want to know the local 1982 // calendar date we have to provide the time zone's UTC offset too. 1983 int middayJulianDay = 1984 Time.getJulianDay(middayInstant.toEpochMilli(), middayOffset.getTotalSeconds()); 1985 1986 // Check Time.getJulianDay() agrees with java.time's Julian day calculations. 1987 assertEquals(middayJulianDay, JulianFields.JULIAN_DAY.getFrom(middayLocalDateTime)); 1988 1989 checkGetJulianDayVariousTimes(timeZone, testDate); 1990 } 1991 } 1992 checkGetJulianDayVariousTimes(String timeZone, LocalDate testDate)1993 private static void checkGetJulianDayVariousTimes(String timeZone, LocalDate testDate) { 1994 long expectedJulianDay = JulianFields.JULIAN_DAY.getFrom(testDate); 1995 1996 // Change the time during the day and check that we get the same Julian day as the one 1997 // for midday. 1998 for (int hour = 0; hour < 24; hour++) { 1999 for (int minute = 0; minute < 60; minute += 15) { 2000 LocalTime localTime = LocalTime.of(hour, minute); 2001 LocalDateTime localDateTime = LocalDateTime.of(testDate, localTime); 2002 ZoneOffset localDateTimeOffset = 2003 ZoneId.of(timeZone).getRules().getOffset(localDateTime); 2004 Instant instant = localDateTime.toInstant(localDateTimeOffset); 2005 long millis = instant.toEpochMilli(); 2006 2007 // Find the Julian day for the date/time by supplying the offset. Since we want 2008 // to know the local calendar date we have to provide the UTC offset too. 2009 int julianDay = Time.getJulianDay(millis, localDateTimeOffset.getTotalSeconds()); 2010 2011 assertEquals("Julian day: " + julianDay + " at time " 2012 + hour + ":" + minute 2013 + " != today's Julian day: " + expectedJulianDay 2014 + " millis: " + millis 2015 + " localDatetime: " + localDateTime 2016 + " timeZone: " + timeZone, 2017 julianDay, expectedJulianDay); 2018 } 2019 } 2020 } 2021 2022 @Test testSetJulianDay()2023 public void testSetJulianDay() { 2024 Time time = new Time(); 2025 2026 // For each 20th day of the year in 2008, and for each timezone, 2027 // test that we can set the Julian day correctly. 2028 for (int monthDay = 1; monthDay <= 366; monthDay += 20) { 2029 for (int zoneIndex = 0; zoneIndex < mTimeZones.length; zoneIndex++) { 2030 // We leave the "month" as zero because we are changing the 2031 // "monthDay" from 1 to 366. The call to normalize() will 2032 // then change the "month" (but we don't really care). 2033 time.set(0, 0, 12, monthDay, 0, 2008); 2034 time.timezone = mTimeZones[zoneIndex]; 2035 long millis = time.normalize(true); 2036 if (zoneIndex == 0) { 2037 Log.i(TAG, time.format("%B %d, %Y")); 2038 } 2039 // This is the Julian day for 12pm for this day of the year 2040 int julianDay = Time.getJulianDay(millis, time.gmtoff); 2041 2042 time.setJulianDay(julianDay); 2043 2044 // Some places change daylight saving time at 12am and so there 2045 // is no 12am on some days in some timezones. In those cases, 2046 // the time is set to 1am. 2047 // Examples: Africa/Cairo on April 25, 2008 2048 // America/Sao_Paulo on October 12, 2008 2049 // Atlantic/Azores on March 30, 2008 2050 assertTrue(time.hour == 0 || time.hour == 1); 2051 assertEquals(0, time.minute); 2052 assertEquals(0, time.second); 2053 2054 millis = time.toMillis(false); 2055 if (millis == -1) { 2056 // millis == -1 means the wall time does not exist in the chosen 2057 // timezone due to a DST change. We cannot calculate a JulianDay for -1. 2058 continue; 2059 } 2060 int day = Time.getJulianDay(millis, time.gmtoff); 2061 assertEquals("Error: gmtoff " + time.gmtoff + " day " + julianDay 2062 + " millis " + millis+ " " + time.format("%B %d, %Y") 2063 + " " + time.timezone, 2064 day, julianDay); 2065 } 2066 } 2067 } 2068 2069 @Test testGetJulianMondayFromWeeksSinceEpoch()2070 public void testGetJulianMondayFromWeeksSinceEpoch() { 2071 final int mondayBeforeEpoch = Time.MONDAY_BEFORE_JULIAN_EPOCH; 2072 assertEquals(mondayBeforeEpoch, Time.getJulianMondayFromWeeksSinceEpoch(0)); 2073 assertEquals(mondayBeforeEpoch + 7, Time.getJulianMondayFromWeeksSinceEpoch(1)); 2074 assertEquals(mondayBeforeEpoch + 14, Time.getJulianMondayFromWeeksSinceEpoch(2)); 2075 assertEquals(mondayBeforeEpoch - 7, Time.getJulianMondayFromWeeksSinceEpoch(-1)); 2076 } 2077 2078 @Test testGetWeeksSinceEpochFromJulianDay()2079 public void testGetWeeksSinceEpochFromJulianDay() { 2080 final int epoch = Time.EPOCH_JULIAN_DAY; // a Thursday 2081 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.SUNDAY)); 2082 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.MONDAY)); 2083 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.TUESDAY)); 2084 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.WEDNESDAY)); 2085 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.THURSDAY)); 2086 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.FRIDAY)); 2087 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.SATURDAY)); 2088 2089 final int epochFriday = epoch + 1; 2090 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.SUNDAY)); 2091 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.MONDAY)); 2092 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.TUESDAY)); 2093 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.WEDNESDAY)); 2094 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.THURSDAY)); 2095 assertEquals(1, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.FRIDAY)); 2096 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.SATURDAY)); 2097 2098 final int epochSaturday = epoch + 2; 2099 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.SUNDAY)); 2100 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.MONDAY)); 2101 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.TUESDAY)); 2102 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.WEDNESDAY)); 2103 assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.THURSDAY)); 2104 assertEquals(1, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.FRIDAY)); 2105 assertEquals(1, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.SATURDAY)); 2106 2107 final int tenWeeksLater = epochSaturday + 10 * 7; 2108 assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.SUNDAY)); 2109 assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.MONDAY)); 2110 assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.TUESDAY)); 2111 assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.WEDNESDAY)); 2112 assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.THURSDAY)); 2113 assertEquals(11, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.FRIDAY)); 2114 assertEquals(11, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.SATURDAY)); 2115 } 2116 2117 @Test testNormalize_utc()2118 public void testNormalize_utc() { 2119 Time t = new Time(Time.TIMEZONE_UTC); 2120 Time expected = new Time(Time.TIMEZONE_UTC); 2121 2122 long winterTimeUtcMillis = 1167613323000L; 2123 2124 Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9); 2125 Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 0, 0, 1); 2126 verifyNormalizeResult(true, t, expected, winterTimeUtcMillis); 2127 2128 Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9); 2129 Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 0, 0, 1); 2130 verifyNormalizeResult(true, t, expected, winterTimeUtcMillis); 2131 2132 Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9); 2133 Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 0, 0, 1); 2134 verifyNormalizeResult(true, t, expected, winterTimeUtcMillis); 2135 2136 Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9); 2137 Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 0, 0, 1); 2138 verifyNormalizeResult(false, t, expected, winterTimeUtcMillis); 2139 2140 Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9); 2141 Fields.set(expected, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9); 2142 verifyNormalizeResult(false, t, expected, -1); 2143 2144 Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9); 2145 Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 0, 0, 1); 2146 verifyNormalizeResult(false, t, expected, winterTimeUtcMillis); 2147 2148 long summerTimeUtcMillis = 1180659723000L; 2149 2150 Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9); 2151 Fields.set(expected, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 0, 151, 5); 2152 verifyNormalizeResult(true, t, expected, summerTimeUtcMillis); 2153 2154 Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9); 2155 Fields.set(expected, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 0, 151, 5); 2156 verifyNormalizeResult(true, t, expected, summerTimeUtcMillis); 2157 2158 Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9); 2159 Fields.set(expected, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 0, 151, 5); 2160 verifyNormalizeResult(true, t, expected, summerTimeUtcMillis); 2161 2162 Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9); 2163 Fields.set(expected, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 0, 151, 5); 2164 verifyNormalizeResult(false, t, expected, summerTimeUtcMillis); 2165 2166 Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9); 2167 Fields.set(expected, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9); 2168 verifyNormalizeResult(false, t, expected, -1); 2169 2170 Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 1, 9, 9); 2171 Fields.set(expected, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 0, 151, 5); 2172 verifyNormalizeResult(false, t, expected, summerTimeUtcMillis); 2173 } 2174 2175 @Test testNormalize_dstTz()2176 public void testNormalize_dstTz() { 2177 Time t = new Time(PstPdt.ID); 2178 Time expected = new Time(PstPdt.ID); 2179 2180 // A STD time 2181 long stdTimeUtcMillis = 1167613323000L; 2182 long stdTimeMillis = stdTimeUtcMillis - PstPdt.getUtcOffsetMillis(false); 2183 2184 Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9); 2185 Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, -28800, 0, 1); 2186 verifyNormalizeResult(true, t, expected, stdTimeMillis); 2187 2188 Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9); 2189 Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, -28800, 0, 1); 2190 verifyNormalizeResult(true, t, expected, stdTimeMillis); 2191 2192 Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9); 2193 Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, -28800, 0, 1); 2194 verifyNormalizeResult(true, t, expected, stdTimeMillis); 2195 2196 long dstToStdCorrectionMillis = 2197 PstPdt.getUtcOffsetMillis(false) - PstPdt.getUtcOffsetMillis(true); 2198 2199 Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9); 2200 Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, -28800, 0, 1); 2201 verifyNormalizeResult(false, t, expected, stdTimeMillis); 2202 2203 Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9); 2204 Fields.set(expected, 2007, 0, 1, 0, 2, 3, 0 /* isDst */, -28800, 0, 1); 2205 verifyNormalizeResult(false, t, expected, stdTimeMillis + dstToStdCorrectionMillis); 2206 2207 Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9); 2208 Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, -28800, 0, 1); 2209 verifyNormalizeResult(false, t, expected, stdTimeMillis); 2210 2211 // A DST time 2212 long dstTimeUtcMillis = 1180659723000L; 2213 long dstTimeMillis = dstTimeUtcMillis - PstPdt.getUtcOffsetMillis(true); 2214 2215 Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9); 2216 Fields.set(expected, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, -25200, 151, 5); 2217 verifyNormalizeResult(true, t, expected, dstTimeMillis); 2218 2219 Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9); 2220 Fields.set(expected, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, -25200, 151, 5); 2221 verifyNormalizeResult(true, t, expected, dstTimeMillis); 2222 2223 Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9); 2224 Fields.set(expected, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, -25200, 151, 5); 2225 verifyNormalizeResult(true, t, expected, dstTimeMillis); 2226 2227 long stdToDstCorrectionMillis = -dstToStdCorrectionMillis; 2228 2229 Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9); 2230 Fields.set(expected, 2007, 5, 1, 2, 2, 3, 1 /* isDst */, -25200, 151, 5); 2231 verifyNormalizeResult(false, t, expected, dstTimeMillis + stdToDstCorrectionMillis); 2232 2233 Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, -25200, 151, 5); 2234 Fields.set(expected, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, -25200, 151, 5); 2235 verifyNormalizeResult(false, t, expected, dstTimeMillis); 2236 2237 Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, -25200, 151, 5); 2238 Fields.set(expected, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, -25200, 151, 5); 2239 verifyNormalizeResult(false, t, expected, dstTimeMillis); 2240 } 2241 2242 @Test testNormalize_skippedTime()2243 public void testNormalize_skippedTime() { 2244 // Tests behavior around a transition from STD to DST that introduces an hour of "skipped" 2245 // time from 01:00 to 01:59. 2246 String timezone = PstPdt.ID; 2247 long stdBaseTimeMillis = 1173607200000L; 2248 long dstBaseTimeMillis = 1173603600000L; 2249 2250 // Try each minute from one minute before the skipped hour until one after. 2251 for (int i = -1; i <= 60; i++) { 2252 int minutesInMillis = (int) Duration.ofMinutes(i).toMillis(); 2253 int[] dateTimeArgs = new int[] { 2007, 2, 11, 2, i, 0 }; 2254 2255 int[] normalizedAdjustedBackwardDateTimeArgs; 2256 int[] normalizedDateTimeArgs; 2257 int[] normalizedAdjustedForwardDateTimeArgs; 2258 if (i == -1) { 2259 normalizedAdjustedBackwardDateTimeArgs = new int[] { 2007, 2, 11, 0, 59, 0 }; 2260 normalizedDateTimeArgs = new int[] { 2007, 2, 11, 1, 59, 0 }; 2261 normalizedAdjustedForwardDateTimeArgs = null; 2262 } else if (i == 60) { 2263 normalizedAdjustedBackwardDateTimeArgs = null; 2264 normalizedDateTimeArgs = new int[] { 2007, 2, 11, 3, 0, 0 }; 2265 normalizedAdjustedForwardDateTimeArgs = new int[] { 2007, 2, 11, 4, 0, 0 }; 2266 } else { 2267 normalizedAdjustedBackwardDateTimeArgs = new int[] { 2007, 2, 11, 1, i, 0 }; 2268 normalizedDateTimeArgs = null; 2269 normalizedAdjustedForwardDateTimeArgs = new int[] { 2007, 2, 11, 3, i, 0 }; 2270 } 2271 2272 Time time = new Time(timezone); 2273 Time expected = new Time(timezone); 2274 2275 // isDst = 0, normalize(true) 2276 Fields.setDateTime(time, dateTimeArgs); 2277 Fields.setDst(time, 0 /* isDst */, 9); 2278 Fields.setDerivedDateTime(time, 9, 9); 2279 2280 long timeMillis = time.normalize(true); 2281 long expectedTimeMillis; 2282 if (i == -1) { 2283 expectedTimeMillis = stdBaseTimeMillis + minutesInMillis; 2284 Fields.setDateTime(expected, normalizedDateTimeArgs); 2285 Fields.setDst(expected, 0, PstPdt.getUtcOffsetSeconds(0)); 2286 Fields.setDerivedDateTime(expected, 69, 0); 2287 } else if (i == 60) { 2288 expectedTimeMillis = dstBaseTimeMillis + minutesInMillis; 2289 Fields.setDateTime(expected, normalizedDateTimeArgs); 2290 Fields.setDst(expected, 1, PstPdt.getUtcOffsetSeconds(1)); 2291 Fields.setDerivedDateTime(expected, 69, 0); 2292 } else { 2293 expectedTimeMillis = -1; 2294 Fields.setDateTime(expected, dateTimeArgs); 2295 Fields.setDst(expected, -1, 9); 2296 Fields.setDerivedDateTime(expected, 9, 9); 2297 } 2298 assertEquals("i = " + i, expectedTimeMillis, timeMillis); 2299 Fields.verifyTimeEquals("i = " + i, expected, time); 2300 2301 // isDst = 0, normalize(false) 2302 Fields.setDateTime(time, dateTimeArgs); 2303 Fields.setDst(time, 0 /* isDst */, 9); 2304 Fields.setDerivedDateTime(time, 9, 9); 2305 2306 timeMillis = time.normalize(false); 2307 expectedTimeMillis = stdBaseTimeMillis + minutesInMillis; 2308 assertEquals("i = " + i, expectedTimeMillis, timeMillis); 2309 if (i == -1) { 2310 Fields.setDateTime(expected, normalizedDateTimeArgs); 2311 Fields.setDst(expected, 0, PstPdt.getUtcOffsetSeconds(0)); 2312 } else { 2313 Fields.setDateTime(expected, normalizedAdjustedForwardDateTimeArgs); 2314 Fields.setDst(expected, 1, PstPdt.getUtcOffsetSeconds(1)); 2315 } 2316 Fields.setDerivedDateTime(expected, 69, 0); 2317 Fields.verifyTimeEquals("i = " + i, expected, time); 2318 2319 // isDst = 1, normalize(true) 2320 Fields.setDateTime(time, dateTimeArgs); 2321 Fields.setDst(time, 1 /* isDst */, 9); 2322 Fields.setDerivedDateTime(time, 9, 9); 2323 2324 timeMillis = time.normalize(true); 2325 if (i == -1) { 2326 expectedTimeMillis = stdBaseTimeMillis + minutesInMillis; 2327 Fields.setDateTime(expected, normalizedDateTimeArgs); 2328 Fields.setDst(expected, 0, PstPdt.getUtcOffsetSeconds(0)); 2329 Fields.setDerivedDateTime(expected, 69, 0); 2330 } else if (i == 60) { 2331 expectedTimeMillis = dstBaseTimeMillis + minutesInMillis; 2332 Fields.setDateTime(expected, normalizedDateTimeArgs); 2333 Fields.setDst(expected, 1, PstPdt.getUtcOffsetSeconds(1)); 2334 Fields.setDerivedDateTime(expected, 69, 0); 2335 } else { 2336 expectedTimeMillis = -1; 2337 Fields.setDateTime(expected, dateTimeArgs); 2338 Fields.setDst(expected, -1, 9); 2339 Fields.setDerivedDateTime(expected, 9, 9); 2340 } 2341 assertEquals("i = " + i, expectedTimeMillis, timeMillis); 2342 Fields.verifyTimeEquals("i = " + i, expected, time); 2343 2344 // isDst = 1, normalize(false) 2345 Fields.setDateTime(time, dateTimeArgs); 2346 Fields.setDst(time, 1 /* isDst */, 9); 2347 Fields.setDerivedDateTime(time, 9, 9); 2348 2349 timeMillis = time.normalize(false); 2350 expectedTimeMillis = dstBaseTimeMillis + minutesInMillis; 2351 assertEquals("i = " + i, expectedTimeMillis, timeMillis); 2352 if (i == 60) { 2353 Fields.setDateTime(expected, normalizedDateTimeArgs); 2354 Fields.setDst(expected, 1, PstPdt.getUtcOffsetSeconds(1)); 2355 } else { 2356 Fields.setDateTime(expected, normalizedAdjustedBackwardDateTimeArgs); 2357 Fields.setDst(expected, 0, PstPdt.getUtcOffsetSeconds(0)); 2358 } 2359 Fields.setDerivedDateTime(expected, 69, 0); 2360 Fields.verifyTimeEquals("i = " + i, expected, time); 2361 2362 // isDst = -1, normalize(true) 2363 Fields.setDateTime(time, dateTimeArgs); 2364 Fields.setDst(time, -1 /* isDst */, 9); 2365 Fields.setDerivedDateTime(time, 9, 9); 2366 2367 timeMillis = time.normalize(true); 2368 if (i == -1) { 2369 expectedTimeMillis = stdBaseTimeMillis + minutesInMillis; 2370 Fields.setDateTime(expected, normalizedDateTimeArgs); 2371 Fields.setDst(expected, 0, PstPdt.getUtcOffsetSeconds(0)); 2372 Fields.setDerivedDateTime(expected, 69, 0); 2373 } else if (i == 60) { 2374 expectedTimeMillis = dstBaseTimeMillis + minutesInMillis; 2375 Fields.setDateTime(expected, normalizedDateTimeArgs); 2376 Fields.setDst(expected, 1, PstPdt.getUtcOffsetSeconds(1)); 2377 Fields.setDerivedDateTime(expected, 69, 0); 2378 } else { 2379 expectedTimeMillis = -1; 2380 Fields.setDateTime(expected, dateTimeArgs); 2381 Fields.setDst(expected, -1, 9); 2382 Fields.setDerivedDateTime(expected, 9, 9); 2383 } 2384 assertEquals("i = " + i, expectedTimeMillis, timeMillis); 2385 Fields.verifyTimeEquals("i = " + i, expected, time); 2386 2387 // isDst = -1, normalize(false) 2388 Fields.setDateTime(time, dateTimeArgs); 2389 Fields.setDst(time, -1 /* isDst */, 9); 2390 Fields.setDerivedDateTime(time, 9, 9); 2391 2392 timeMillis = time.normalize(false); 2393 if (i == -1) { 2394 expectedTimeMillis = stdBaseTimeMillis + minutesInMillis; 2395 Fields.setDateTime(expected, normalizedDateTimeArgs); 2396 Fields.setDst(expected, 0, PstPdt.getUtcOffsetSeconds(0)); 2397 Fields.setDerivedDateTime(expected, 69, 0); 2398 } else if (i == 60) { 2399 expectedTimeMillis = dstBaseTimeMillis + minutesInMillis; 2400 Fields.setDateTime(expected, normalizedDateTimeArgs); 2401 Fields.setDst(expected, 1, PstPdt.getUtcOffsetSeconds(1)); 2402 Fields.setDerivedDateTime(expected, 69, 0); 2403 } else { 2404 expectedTimeMillis = -1; 2405 Fields.setDateTime(expected, dateTimeArgs); 2406 Fields.setDst(expected, -1, 9); 2407 Fields.setDerivedDateTime(expected, 9, 9); 2408 } 2409 assertEquals("i = " + i, expectedTimeMillis, timeMillis); 2410 Fields.verifyTimeEquals("i = " + i, expected, time); 2411 } 2412 } 2413 2414 @Test testNormalize_duplicateWallTime()2415 public void testNormalize_duplicateWallTime() { 2416 // 1:00 in standard / 2:00 in DST 2417 long timeBaseMillis = 1194163200000L; 2418 long dstCorrectionMillis = 3600000; 2419 2420 // Try each minute from one minute before the duplicated hour until one after. 2421 for (int i = -1; i <= 60; i++) { 2422 Time time = new Time(PstPdt.ID); 2423 Time expected = new Time(PstPdt.ID); 2424 2425 int[] dateTimeArgs = new int[] { 2007, 10, 4, 1, i, 0 }; 2426 int[] normalizedAdjustedBackwardDateTimeArgs; 2427 int[] normalizedDateTimeArgs; 2428 int[] normalizedAdjustedForwardDateTimeArgs; 2429 if (i == -1) { 2430 normalizedAdjustedBackwardDateTimeArgs = null; 2431 normalizedDateTimeArgs = new int[] { 2007, 10, 4, 0, 59, 0 }; 2432 normalizedAdjustedForwardDateTimeArgs = new int[] { 2007, 10, 4, 1, 59, 0 }; 2433 } else if (i == 60) { 2434 normalizedAdjustedBackwardDateTimeArgs = new int[] { 2007, 10, 4, 1, 0, 0 }; 2435 normalizedDateTimeArgs = new int[] { 2007, 10, 4, 2, 0, 0 }; 2436 normalizedAdjustedForwardDateTimeArgs = null; 2437 } else { 2438 normalizedAdjustedBackwardDateTimeArgs = null; 2439 normalizedDateTimeArgs = dateTimeArgs; 2440 normalizedAdjustedForwardDateTimeArgs = null; 2441 } 2442 2443 int minutesInMillis = (int) Duration.ofMinutes(i).toMillis(); 2444 2445 // isDst = 0, normalize(true) 2446 Fields.setDateTime(time, dateTimeArgs); 2447 Fields.setDst(time, 0 /* isDst */, 9); 2448 Fields.setDerivedDateTime(time, 9, 9); 2449 2450 long timeMillis = time.normalize(true); 2451 2452 Fields.setDateTime(expected, normalizedDateTimeArgs); 2453 // When ignoreDst == true the choice between DST and STD is arbitrary when both answers 2454 // are possible. 2455 if (timeMillis == timeBaseMillis + minutesInMillis) { 2456 // i == 60 is unambiguous 2457 assertTrue("i =" + i, i < 60); 2458 Fields.setDst(expected, 1 /* isDst */, PstPdt.getUtcOffsetSeconds(1)); 2459 } else if (timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis) { 2460 // i == -1 is unambiguous 2461 assertTrue("i =" + i, i > -1); 2462 Fields.setDst(expected, 0 /* isDst */, PstPdt.getUtcOffsetSeconds(0)); 2463 } else { 2464 fail("i =" + i); 2465 } 2466 Fields.setDerivedDateTime(expected, 307, 0); 2467 Fields.verifyTimeEquals("i = " + i, expected, time); 2468 2469 // isDst = 0, normalize(false) 2470 Fields.setDateTime(time, dateTimeArgs); 2471 Fields.setDst(time, 0 /* isDst */, 9); 2472 Fields.setDerivedDateTime(time, 9, 9); 2473 2474 timeMillis = time.normalize(false); 2475 long expectedTimeMillis = timeBaseMillis + minutesInMillis + dstCorrectionMillis; 2476 assertEquals("i = " + i, expectedTimeMillis, timeMillis); 2477 if (i == -1) { 2478 Fields.setDateTime(expected, normalizedAdjustedForwardDateTimeArgs); 2479 Fields.setDst(expected, 1 /* isDst */, PstPdt.getUtcOffsetSeconds(1)); 2480 } else { 2481 Fields.setDateTime(expected, normalizedDateTimeArgs); 2482 Fields.setDst(expected, 0 /* isDst */, PstPdt.getUtcOffsetSeconds(0)); 2483 } 2484 Fields.setDerivedDateTime(expected, 307, 0); 2485 Fields.verifyTimeEquals("i = " + i, expected, time); 2486 2487 // isDst = 1, normalize(true) 2488 Fields.setDateTime(time, dateTimeArgs); 2489 Fields.setDst(time, 1 /* isDst */, 9); 2490 Fields.setDerivedDateTime(time, 9, 9); 2491 2492 timeMillis = time.normalize(true); 2493 Fields.setDateTime(expected, normalizedDateTimeArgs); 2494 // When ignoreDst == true the choice between DST and STD is arbitrary when both answers 2495 // are possible. 2496 if (timeMillis == timeBaseMillis + minutesInMillis) { 2497 // i == 60 is unambiguous 2498 assertTrue("i =" + i, i < 60); 2499 Fields.setDst(expected, 1 /* isDst */, PstPdt.getUtcOffsetSeconds(1)); 2500 } else if (timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis) { 2501 // i == -1 is unambiguous 2502 assertTrue("i =" + i, i > -1); 2503 Fields.setDst(expected, 0 /* isDst */, PstPdt.getUtcOffsetSeconds(0)); 2504 } else { 2505 fail("i =" + i); 2506 } 2507 Fields.setDerivedDateTime(expected, 307, 0); 2508 Fields.verifyTimeEquals("i = " + i, expected, time); 2509 2510 // isDst = 1, normalize(false) 2511 Fields.setDateTime(time, dateTimeArgs); 2512 Fields.setDst(time, 1 /* isDst */, 9); 2513 Fields.setDerivedDateTime(time, 9, 9); 2514 2515 timeMillis = time.normalize(false); 2516 expectedTimeMillis = timeBaseMillis + minutesInMillis; 2517 if (i == 60) { 2518 Fields.setDateTime(expected, normalizedAdjustedBackwardDateTimeArgs); 2519 Fields.setDst(expected, 0 /* isDst */, PstPdt.getUtcOffsetSeconds(0)); 2520 } else { 2521 Fields.setDateTime(expected, normalizedDateTimeArgs); 2522 Fields.setDst(expected, 1 /* isDst */, PstPdt.getUtcOffsetSeconds(1)); 2523 } 2524 Fields.setDerivedDateTime(expected, 307, 0); 2525 assertEquals("i = " + i, expectedTimeMillis, timeMillis); 2526 Fields.verifyTimeEquals("i = " + i, expected, time); 2527 2528 // isDst = -1, normalize(true) 2529 Fields.setDateTime(time, dateTimeArgs); 2530 Fields.setDst(time, -1 /* isDst */, 9); 2531 Fields.setDerivedDateTime(time, 9, 9); 2532 2533 timeMillis = time.normalize(true); 2534 Fields.setDateTime(expected, normalizedDateTimeArgs); 2535 // When isDst == -1 the choice between DST and STD is arbitrary when both answers 2536 // are possible. 2537 if (timeMillis == timeBaseMillis + minutesInMillis) { 2538 // i == 60 is unambiguous 2539 assertTrue("i =" + i, i < 60); 2540 Fields.setDst(expected, 1 /* isDst */, PstPdt.getUtcOffsetSeconds(1)); 2541 } else if (timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis) { 2542 // i == -1 is unambiguous 2543 assertTrue("i =" + i, i > -1); 2544 Fields.setDst(expected, 0 /* isDst */, PstPdt.getUtcOffsetSeconds(0)); 2545 } else { 2546 fail("i =" + i); 2547 } 2548 Fields.setDerivedDateTime(expected, 307, 0); 2549 Fields.verifyTimeEquals("i = " + i, expected, time); 2550 2551 // isDst = -1, normalize(false) 2552 Fields.setDateTime(time, dateTimeArgs); 2553 Fields.setDst(time, -1 /* isDst */, 9); 2554 Fields.setDerivedDateTime(time, 9, 9); 2555 2556 timeMillis = time.normalize(false); 2557 // When isDst == -1 the choice between DST and STD is arbitrary when both answers 2558 // are possible. 2559 if (timeMillis == timeBaseMillis + minutesInMillis) { 2560 // i == 60 is unambiguous 2561 assertTrue("i =" + i, i < 60); 2562 Fields.setDst(expected, 1 /* isDst */, PstPdt.getUtcOffsetSeconds(1)); 2563 } else if (timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis) { 2564 // i == -1 is unambiguous 2565 assertTrue("i =" + i, i > -1); 2566 Fields.setDst(expected, 0 /* isDst */, PstPdt.getUtcOffsetSeconds(0)); 2567 } else { 2568 fail("i =" + i); 2569 } 2570 Fields.setDerivedDateTime(expected, 307, 0); 2571 Fields.verifyTimeEquals("i = " + i, expected, time); 2572 } 2573 } 2574 2575 @Test testNormalize_before32BitSeconds()2576 public void testNormalize_before32BitSeconds() { 2577 int[] timeFields = new int[] { 1900, 0, 1, 2, 3, 4, -999 /* not used */, 9, 9, 9 }; 2578 verifyNormalizeInvalid(timeFields, PstPdt.ID); 2579 verifyNormalizeInvalid(timeFields, Time.TIMEZONE_UTC); 2580 } 2581 verifyNormalizeInvalid(int[] timeFields, String timezone)2582 private static void verifyNormalizeInvalid(int[] timeFields, String timezone) { 2583 Time time = new Time(timezone); 2584 Time expected = new Time(timezone); 2585 2586 // isDst = 0, normalize(true) 2587 Fields.set(time, timeFields); 2588 time.isDst = 0; 2589 Fields.set(expected, timeFields); 2590 expected.isDst = -1; 2591 verifyNormalizeResult(true, time, expected, -1); 2592 2593 // isDst = 0, normalize(false) 2594 Fields.set(time, timeFields); 2595 time.isDst = 0; 2596 Fields.set(expected, timeFields); 2597 expected.isDst = 0; 2598 verifyNormalizeResult(false, time, expected, -1); 2599 2600 // isDst = 1, normalize(true) 2601 Fields.set(time, timeFields); 2602 time.isDst = 1; 2603 Fields.set(expected, timeFields); 2604 expected.isDst = -1; 2605 verifyNormalizeResult(true, time, expected, -1); 2606 2607 // isDst = 1, normalize(false) 2608 Fields.set(time, timeFields); 2609 time.isDst = 1; 2610 Fields.set(expected, timeFields); 2611 expected.isDst = 1; 2612 verifyNormalizeResult(false, time, expected, -1); 2613 2614 // isDst = -1, normalize(true) 2615 Fields.set(time, timeFields); 2616 time.isDst = -1; 2617 Fields.set(expected, timeFields); 2618 expected.isDst = -1; 2619 verifyNormalizeResult(true, time, expected, -1); 2620 2621 // isDst = -1, normalize(false) 2622 Fields.set(time, timeFields); 2623 time.isDst = -1; 2624 Fields.set(expected, timeFields); 2625 expected.isDst = -1; 2626 verifyNormalizeResult(false, time, expected, -1); 2627 } 2628 2629 @Test testNormalize_after32BitSeconds()2630 public void testNormalize_after32BitSeconds() { 2631 int[] timeFields = new int[] { 2039, 0, 1, 2, 3, 4, -999 /* not used */, 9, 9, 9 }; 2632 verifyNormalizeInvalid(timeFields, PstPdt.ID); 2633 verifyNormalizeInvalid(timeFields, Time.TIMEZONE_UTC); 2634 } 2635 2636 @Test testNormalize_invalid()2637 public void testNormalize_invalid() { 2638 int[] timeFields = new int[] { 0, 0, 0, 0, 0, 0, -999 /* not used */, 9, 9, 9 }; 2639 verifyNormalizeInvalid(timeFields, PstPdt.ID); 2640 verifyNormalizeInvalid(timeFields, Time.TIMEZONE_UTC); 2641 } 2642 2643 @Test testNormalize_dstToDstSkip()2644 public void testNormalize_dstToDstSkip() { 2645 // In London, 4th May 1941 02:00 - 03:00 was a skip from DST -> DST (+1 hour -> +2 hours) 2646 String timezone = "Europe/London"; 2647 Time t = new Time(timezone); 2648 Time expected = new Time(timezone); 2649 2650 // Demonstrate the data we expect either side of the skipped interval: 01:59 2651 Fields.set(t, 1941, 4, 4, 1, 59, 0, -1 /* isDst */, 9, 9, 9); 2652 Fields.set(expected, 1941, 4, 4, 1, 59, 0, 1 /* isDst */, 3600, 123, 0); 2653 verifyNormalizeResult(true, t, expected, -904518060000L); 2654 2655 // Demonstrate the data we expect either side of the skipped interval: 03:00 2656 Fields.set(t, 1941, 4, 4, 3, 0, 0, -1 /* isDst */, 9, 9, 9); 2657 Fields.set(expected, 1941, 4, 4, 3, 0, 0, 1 /* isDst */, 7200, 123, 0); 2658 verifyNormalizeResult(true, t, expected, -904518000000L); 2659 2660 // isDst = 1, normalize(false) 2661 Fields.set(t, 1941, 4, 4, 2, 30, 0, 1 /* isDst */, 9, 9, 9); 2662 Fields.set(expected, 1941, 4, 4, 2, 30, 0, 1 /* isDst */, 9, 9, 9); 2663 verifyNormalizeResult(false, t, expected, -1); 2664 2665 // isDst = -1, normalize(false) 2666 Fields.set(t, 1941, 4, 4, 2, 30, 0, -1 /* isDst */, 9, 9, 9); 2667 Fields.set(expected, 1941, 4, 4, 2, 30, 0, -1 /* isDst */, 9, 9, 9); 2668 verifyNormalizeResult(false, t, expected, -1); 2669 2670 // The results below are potentially arbitrary: 01:30 and 02:30 are not a valid standard 2671 // times so normalize() must apply one of the possible STD -> DST adjustments to arrive at a 2672 // date / time. 2673 2674 // isDst = 0, normalize(false) @ 01:30 2675 Fields.set(t, 1941, 4, 4, 1, 30, 0, 0 /* isDst */, 9, 9, 9); 2676 Fields.set(expected, 1941, 4, 4, 3, 30, 0, 1 /* isDst */, 7200, 123, 0); 2677 verifyNormalizeResult(false, t, expected, -904516200000L); 2678 2679 // isDst = 0, normalize(false) @ 02:30 2680 Fields.set(t, 1941, 4, 4, 2, 30, 0, 0 /* isDst */, 9, 9, 9); 2681 long timeMillis = t.normalize(false); 2682 2683 if (timeMillis == -904516200000L) { 2684 // The original C implementation chooses this one. 2685 Fields.set(expected, 1941, 4, 4, 3, 30, 0, 1 /* isDst */, 7200, 123, 0); 2686 } else if (timeMillis == -904512600000L) { 2687 Fields.set(expected, 1941, 4, 4, 4, 30, 0, 1 /* isDst */, 7200, 123, 0); 2688 } else { 2689 fail(); 2690 } 2691 Fields.verifyTimeEquals(expected, t); 2692 } 2693 2694 @Test testNormalize_dstToDstRepeat()2695 public void testNormalize_dstToDstRepeat() { 2696 // In London, 10th August 1941 02:00 - 03:00 was a repeat from DST -> DST 2697 // (+2 hour -> +1 hour) 2698 String timezone = "Europe/London"; 2699 Time t = new Time(timezone); 2700 Time expected = new Time(timezone); 2701 2702 // Demonstrate the data we expect during the repeated interval: 02:30 (first) 2703 t.set(-896052600000L); 2704 Fields.set(expected, 1941, 7, 10, 2, 30, 0, 1 /* isDst */, 7200, 221, 0); 2705 Fields.verifyTimeEquals(expected, t); 2706 2707 // Demonstrate the data we expect during the repeated interval: 02:30 (second) 2708 t.set(-896049000000L); 2709 Fields.set(expected, 1941, 7, 10, 2, 30, 0, 1 /* isDst */, 3600, 221, 0); 2710 Fields.verifyTimeEquals(expected, t); 2711 2712 // Now check times in the repeated hour with different isDst assertions... 2713 2714 // isDst = 1, normalize(false) @ 02:30 2715 Fields.set(t, 1941, 7, 10, 2, 30, 0, 1 /* isDst */, 9, 9, 9); 2716 Fields.set(expected, 1941, 7, 10, 2, 30, 0, 1 /* isDst */, 3600, 221, 0); 2717 verifyNormalizeResult(false, t, expected, -896049000000L); 2718 2719 // isDst = -1, normalize(false) @ 02:30 2720 Fields.set(t, 1941, 7, 10, 2, 30, 0, -1 /* isDst */, 9, 9, 9); 2721 Fields.set(expected, 1941, 7, 10, 2, 30, 0, 1 /* isDst */, 3600, 221, 0); 2722 verifyNormalizeResult(false, t, expected, -896049000000L); 2723 2724 // The results below are potentially arbitrary: 01:30 and 02:30 are not a valid standard 2725 // times so normalize() must apply one of the possible STD -> DST adjustments to arrive at a 2726 // date / time. 2727 2728 // isDst = 0, normalize(false) @ 01:30 2729 Fields.set(t, 1941, 7, 10, 1, 30, 0, 0 /* isDst */, 9, 9, 9); 2730 long timeMillis = t.normalize(false); 2731 if (timeMillis == -896052600000L) { 2732 // The original C implementation chooses this one. 2733 Fields.set(expected, 1941, 7, 10, 2, 30, 0, 1 /* isDst */, 7200, 221, 0); 2734 } else if (timeMillis == -896049000000L) { 2735 Fields.set(expected, 1941, 7, 10, 2, 30, 0, 1 /* isDst */, 3600, 221, 0); 2736 } else { 2737 fail(); 2738 } 2739 Fields.verifyTimeEquals(expected, t); 2740 2741 // isDst = 0, normalize(false) @ 02:30 2742 Fields.set(t, 1941, 7, 10, 2, 30, 0, 0 /* isDst */, 9, 9, 9); 2743 Fields.set(expected, 1941, 7, 10, 3, 30, 0, 1 /* isDst */, 3600, 221, 0); 2744 verifyNormalizeResult(false, t, expected, -896045400000L); 2745 } 2746 2747 @Test testNormalize_stdToStdRepeat()2748 public void testNormalize_stdToStdRepeat() { 2749 // In London, 31st October 1971 02:00 - 03:00 was a repeat from STD -> STD 2750 String timezone = "Europe/London"; 2751 Time t = new Time(timezone); 2752 Time expected = new Time(timezone); 2753 2754 // Demonstrate the data we expect during the repeated interval: 02:30 (first) 2755 t.set(57720600000L); 2756 Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 3600, 303, 0); 2757 Fields.verifyTimeEquals(expected, t); 2758 2759 // Demonstrate the data we expect during the repeated interval: 02:30 (second) 2760 t.set(57724200000L); 2761 Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 0, 303, 0); 2762 Fields.verifyTimeEquals(expected, t); 2763 2764 // isDst = 0, normalize(false) @ 02:30 2765 Fields.set(t, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 9, 9, 9); 2766 2767 long timeMillis = t.normalize(false); 2768 2769 // Either answer is valid: the choice is arbitrary. 2770 if (57720600000L == timeMillis) { 2771 // The original C implementation chooses this one. 2772 Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 3600, 303, 0); 2773 } else if (57724200000L == timeMillis) { 2774 Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 0, 303, 0); 2775 } else { 2776 fail(); 2777 } 2778 Fields.verifyTimeEquals(expected, t); 2779 2780 // isDst = -1, normalize(false) @ 02:30 2781 Fields.set(t, 1971, 9, 31, 2, 30, 0, -1 /* isDst */, 9, 9, 9); 2782 2783 timeMillis = t.normalize(false); 2784 2785 Fields.setDateTime(expected, 1971, 9, 31, 2, 30, 0); 2786 // Either answer is valid: the choice is arbitrary. 2787 if (57720600000L == timeMillis) { 2788 // The original C implementation chooses this one. 2789 Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 3600, 303, 0); 2790 } else if (57724200000L == timeMillis) { 2791 Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 0, 303, 0); 2792 } else { 2793 fail(); 2794 } 2795 Fields.verifyTimeEquals(expected, t); 2796 2797 // The results below are potentially arbitrary: 01:30 and 02:30 are not a valid DST 2798 // so normalize() must apply one of the possible STD -> DST adjustments to arrive at a 2799 // date / time. 2800 2801 // isDst = 1, normalize(false) @ 01:30 2802 Fields.set(t, 1971, 9, 31, 1, 30, 0, 1 /* isDst */, 9, 9, 9); 2803 2804 timeMillis = t.normalize(false); 2805 2806 if (timeMillis == 57713400000L) { 2807 // Original C implementation chooses this one. 2808 Fields.set(expected, 1971, 9, 31, 0, 30, 0, 0 /* isDst */, 3600, 303, 0); 2809 } else if (timeMillis == 57717000000L) { 2810 Fields.set(expected, 1971, 9, 31, 1, 30, 0, 0 /* isDst */, 3600, 303, 0); 2811 } else { 2812 fail(); 2813 } 2814 Fields.verifyTimeEquals(expected, t); 2815 2816 // isDst = 1, normalize(false) @ 02:30 2817 Fields.set(t, 1971, 9, 31, 2, 30, 0, 1 /* isDst */, 9, 9, 9); 2818 timeMillis = t.normalize(false); 2819 if (timeMillis == 57717000000L) { 2820 // The original C implementation chooses this one. 2821 Fields.set(expected, 1971, 9, 31, 1, 30, 0, 0 /* isDst */, 3600, 303, 0); 2822 } else if (timeMillis == 57720600000L) { 2823 Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 3600, 303, 0); 2824 } else { 2825 fail(); 2826 } 2827 Fields.verifyTimeEquals(expected, t); 2828 2829 // isDst = 1, normalize(false) @ 03:30 2830 Fields.set(t, 1971, 9, 31, 3, 30, 0, 1 /* isDst */, 9, 9, 9); 2831 Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 0, 303, 0); 2832 verifyNormalizeResult(false, t, expected, 57724200000L); 2833 } 2834 2835 @Test testNormalize_stdToStdSkip()2836 public void testNormalize_stdToStdSkip() { 2837 // In Kiritimati, 31st Dec 1994 was skipped entirely by going from UTC-10 to UTC+14 (was a 2838 // skip from STD -> STD, plus they do not observe DST). 2839 String timezone = "Pacific/Kiritimati"; 2840 Time t = new Time(timezone); 2841 Time expected = new Time(timezone); 2842 2843 // isDst = 0, normalize(false) 2844 Fields.set(t, 1994, 11, 31, 10, 20, 0, 0 /* isDst */, 9, 9, 9); 2845 Fields.set(expected, 1994, 11, 31, 10, 20, 0, 0 /* isDst */, 9, 9, 9); 2846 verifyNormalizeResult(false, t, expected, -1); 2847 2848 // isDst = 1, normalize(false) 2849 Fields.set(t, 1994, 11, 31, 10, 20, 0, 1 /* isDst */, 9, 9, 9); 2850 Fields.set(expected, 1994, 11, 31, 10, 20, 0, 1 /* isDst */, 9, 9, 9); 2851 verifyNormalizeResult(false, t, expected, -1); 2852 2853 // isDst = -1, normalize(false) 2854 Fields.set(t, 1994, 11, 31, 10, 20, 0, -1 /* isDst */, 9, 9, 9); 2855 Fields.set(expected, 1994, 11, 31, 10, 20, 0, -1 /* isDst */, 9, 9, 9); 2856 verifyNormalizeResult(false, t, expected, -1); 2857 } 2858 2859 @Test testNormalize_utcWithDst()2860 public void testNormalize_utcWithDst() { 2861 // In UTC (or other zone without DST), what happens when a DST time is specified and there 2862 // is no DST offset available in the timezone data. 2863 Time t = new Time(Time.TIMEZONE_UTC); 2864 Time expected = new Time(Time.TIMEZONE_UTC); 2865 2866 // isDst = 1, normalize(false) 2867 Fields.set(t, 2005, 6, 22, 1, 30, 0, 1 /* isDst */, 9, 9, 9); 2868 Fields.set(expected, 2005, 6, 22, 1, 30, 0, 1 /* isDst */, 9, 9, 9); 2869 verifyNormalizeResult(false, t, expected, -1); 2870 2871 // isDst = -1, normalize(false) 2872 Fields.set(t, 2005, 6, 22, 1, 30, 0, -1 /* isDst */, 9, 9, 9); 2873 Fields.set(expected, 2005, 6, 22, 1, 30, 0, 0 /* isDst */, 0, 202, 5); 2874 verifyNormalizeResult(false, t, expected, 1121995800000L); 2875 } 2876 2877 @Test testUnknownTz()2878 public void testUnknownTz() { 2879 // Historically the code used UTC if the timezone is unrecognized. 2880 2881 String unknownTimezoneId = "THIS_ID_IS_NOT_A_VALID_TZ"; 2882 Time t = new Time(unknownTimezoneId); 2883 assertEquals(unknownTimezoneId, t.timezone); 2884 Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9); 2885 2886 // We can't know for sure which time zone is being used, but we assume it is UTC if the date 2887 // normalizes to isDst == 0 and with an offset of 0 during the summer months. 2888 2889 long timeMillis = t.normalize(true); 2890 assertEquals(unknownTimezoneId, t.timezone); 2891 assertEquals(1180659723000L, timeMillis); 2892 2893 Time expected = new Time(unknownTimezoneId); 2894 Fields.set(expected, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 0, 151, 5); 2895 Fields.verifyTimeEquals(expected, t); 2896 } 2897 2898 @Test test_bug118835133_valuesCloseToInt32Saturation()2899 public void test_bug118835133_valuesCloseToInt32Saturation() { 2900 // With http://b/118835133 toMillis() returns -1. This is the original bug case. 2901 LocalDateTime localDateTime = LocalDateTime.of(2018, Month.OCTOBER, 30, 12, 48, 32); 2902 assertLocalTimeMillis("Asia/Singapore", localDateTime, 1540874912000L); 2903 2904 // The following tests check the upper limits of what Time supports. There's no guarantee 2905 // we can't deal with values higher that this but the exact value depends on the version of 2906 // zic being used as a transition at exactly Integer.MAX_VALUE can affect our ability to 2907 // deal with times. So, we just assert what we know we must be able to do to catch major 2908 // regressions. The following timezones are chosen because of their respective geographical 2909 // locations (positive / negative offset from UTC) plus whether they had transitions at 2910 // Integer.MAX_VALUE with zic > 2014b. The transitions could change in future but there's 2911 // nothing we can do about that. 2912 2913 // Positive offset, has MAX_VALUE transition with zic 2018e. 2914 checkUpperSupportedLimit("Asia/Singapore"); 2915 // Negative offset, has MAX_VALUE transition with zic 2018e. 2916 checkUpperSupportedLimit("America/Argentina/Buenos_Aires"); 2917 // Positive offset, has no MAX_VALUE transition with zic 2018e. 2918 checkUpperSupportedLimit("Asia/Shanghai"); 2919 // Negative offset, has no MAX_VALUE transition with zic 2018e. 2920 checkUpperSupportedLimit("America/Los_Angeles"); 2921 2922 // See comment above, but for Integer.MIN_VALUE / lower bound. Most zones do not have an 2923 // explicit MIN_VALUE transition with zic 2018e 64-bit data but some zones differ around 2924 // whether there are known transitions before Integer.MIN_VALUE. 2925 2926 // Positive offset zones with first transition before Integer.MIN_VALUE. 2927 checkLowerSupportedLimit("Asia/Singapore"); 2928 checkLowerSupportedLimit("Asia/Shanghai"); 2929 // Negative offset zones with first transition before Integer.MIN_VALUE. 2930 checkLowerSupportedLimit("America/Argentina/Buenos_Aires"); 2931 checkLowerSupportedLimit("America/Los_Angeles"); 2932 2933 // Positive offset zone with first transition after Integer.MIN_VALUE. 2934 checkLowerSupportedLimit("Asia/Kathmandu"); 2935 // Negative offset zone with first transition after Integer.MIN_VALUE. 2936 checkLowerSupportedLimit("America/Yellowknife"); 2937 } 2938 checkUpperSupportedLimit(String timeZoneId)2939 private static void checkUpperSupportedLimit(String timeZoneId) { 2940 // Integer.MAX_VALUE == Jan 19, 2038 03:14:07 GMT. 2941 LocalDateTime max32BitTime = LocalDateTime.of(2038, Month.JANUARY, 19, 3, 14, 7); 2942 2943 // We take off one extra day in all cases because we can't guarantee we can handle 2944 // exactly Integer.MAX_VALUE in all cases due to integer overflow when applying zone 2945 // offsets. It gets fiddly if we try to be exact and this is good enough. 2946 LocalDateTime testLocalTime = max32BitTime.minusDays(1); 2947 2948 long expectedMillis = calculateExpectedMillis(timeZoneId, testLocalTime); 2949 assertLocalTimeMillis(timeZoneId, testLocalTime, expectedMillis); 2950 } 2951 checkLowerSupportedLimit(String timeZoneId)2952 private static void checkLowerSupportedLimit(String timeZoneId) { 2953 // Integer.MIN_VALUE == 13 Dec 1901 20:45:52 GMT. 2954 LocalDateTime min32BitTime = LocalDateTime.of(1901, Month.DECEMBER, 13, 20, 45, 52); 2955 2956 // We add on one extra day in all cases because we can't guarantee we can handle 2957 // exactly Integer.MIN_VALUE in all cases due to integer underflow when applying 2958 // zone offsets. It gets fiddly if we try to be exact and this is good enough. 2959 LocalDateTime testLocalTime = min32BitTime.plusDays(1); 2960 2961 long expectedMillis = calculateExpectedMillis(timeZoneId, testLocalTime); 2962 assertLocalTimeMillis(timeZoneId, testLocalTime, expectedMillis); 2963 } 2964 calculateExpectedMillis(String timeZoneId, LocalDateTime localDateTime)2965 private static long calculateExpectedMillis(String timeZoneId, LocalDateTime localDateTime) { 2966 // We use java.util.TimeZone with Calendar because that's the same data as used by Time and 2967 // so they should generally agree. ICU, and java.time which is backed by ICU on Android, are 2968 // generally better because they handle things outside of the Integer range. 2969 TimeZone tz = TimeZone.getTimeZone(timeZoneId); 2970 Calendar calendar = new GregorianCalendar(tz); 2971 calendar.set( 2972 localDateTime.getYear(), 2973 localDateTime.getMonthValue() - 1 /* calendar uses [0-11] */, 2974 localDateTime.getDayOfMonth(), 2975 localDateTime.getHour(), 2976 localDateTime.getMinute(), 2977 localDateTime.getSecond() 2978 ); 2979 calendar.set(Calendar.MILLISECOND, 0); 2980 return calendar.getTimeInMillis(); 2981 } 2982 assertLocalTimeMillis(String timeZoneId, LocalDateTime testLocalTime, long expectedMillis)2983 private static void assertLocalTimeMillis(String timeZoneId, LocalDateTime testLocalTime, 2984 long expectedMillis) { 2985 2986 Time t = new Time(timeZoneId); 2987 Fields.set(t, 2988 testLocalTime.getYear(), 2989 testLocalTime.getMonthValue() - 1 /* Time class uses [0-11] */, 2990 testLocalTime.getDayOfMonth(), 2991 testLocalTime.getHour(), 2992 testLocalTime.getMinute(), 2993 testLocalTime.getSecond(), 2994 0 /* isDst */, 0, 0, 0); 2995 assertEquals(expectedMillis, t.toMillis(true /* ignoreDst */)); 2996 2997 // Check that Time agrees with native localtime / mktime. 2998 NativeTimeFunctions.assertNativeTimeResults(timeZoneId, testLocalTime, expectedMillis); 2999 } 3000 3001 /** 3002 * This is a test to ensure that Time generally works in the time period that apps will be 3003 * interested in. There are various specific tests that exist to catch known edge cases but this 3004 * test gives more general coverage looking for obvious issues in simple cases. Inspired by 3005 * b/118835133 which revealed there was an obvious problem with recent dates in some zones after 3006 * Android was upgraded to a newer zic tool. 3007 */ 3008 @Test test_toMillisAndNormalizeGenerallyWork()3009 public void test_toMillisAndNormalizeGenerallyWork() { 3010 String[] tzIds = TimeZone.getAvailableIDs(); 3011 for (String tzId : tzIds) { 3012 TimeZone timeZone = TimeZone.getTimeZone(tzId); 3013 Calendar calendar = new GregorianCalendar(timeZone); 3014 for (long timeInMillis : calculateToMillisTestTimes(tzId)) { 3015 // We use Calendar to obtain local time fields for timeInMillis. 3016 calendar.setTimeInMillis(timeInMillis); 3017 3018 Time time = new Time(tzId); 3019 Fields.setDateTime(time, calendar); 3020 3021 // Now we check that Time can work out the time in millis from the local time fields 3022 // we give it. 3023 assertEquals("toMillis() must match calendar in " + tzId + " at " 3024 + time, calendar.getTimeInMillis(), time.toMillis(true /* ignoreDst */)); 3025 3026 // Repeat with normalize(), which allows us to also check isDst / gmtoff. 3027 long normalizedMillis = time.normalize(true /* ignoreDst */); 3028 assertEquals("normalize() must match calendar in " + tzId + " at " 3029 + time, calendar.getTimeInMillis(), normalizedMillis); 3030 assertEquals("isDst failed to match calendar in " + tzId + " at " 3031 + time, timeZone.inDaylightTime(new Date(timeInMillis)), (time.isDst != 0)); 3032 assertEquals(timeZone.getOffset(timeInMillis), 3033 Duration.ofSeconds(time.gmtoff).toMillis()); 3034 3035 // Check that Time agrees with native localtime / mktime. 3036 NativeTimeFunctions.assertNativeTimeResults( 3037 tzId, createLocalDateTime(calendar), timeInMillis); 3038 } 3039 } 3040 } 3041 3042 /** 3043 * Creates a LocalDateTime from a Calendar by reading out the current date/time (to second 3044 * precision only!). 3045 */ createLocalDateTime(Calendar calendar)3046 private static LocalDateTime createLocalDateTime(Calendar calendar) { 3047 return LocalDateTime.of( 3048 calendar.get(Calendar.YEAR), 3049 calendar.get(Calendar.MONTH) + 1, 3050 calendar.get(Calendar.DAY_OF_MONTH), 3051 calendar.get(Calendar.HOUR_OF_DAY), 3052 calendar.get(Calendar.MINUTE), 3053 calendar.get(Calendar.SECOND)); 3054 } 3055 calculateToMillisTestTimes(String tzId)3056 private static List<Long> calculateToMillisTestTimes(String tzId) { 3057 List<Long> times = new ArrayList<>(); 3058 3059 // Some local times can be ambiguous (when there's a "fall back") or missing (when there's 3060 // a "spring forward"). Calendar and Time may reasonably do different things for these. We 3061 // try to avoid those cases by picking the middle of intervals with a given offset, or at 3062 // least a day away from a transition if we can't do that. 3063 3064 // java.time is used to calculate times below. java.time is built on ICU on Android 3065 // which is independently developed. It enables us to get to the transition / interval 3066 // information we need to generate mid-points using public APIs, something we can't 3067 // currently do any other way. 3068 3069 // Start generating times using the interval around 1970, then iterate through following 3070 // intervals. Stop when we get close to the top of the supported range for Time. 3071 final Instant startTime = Instant.ofEpochSecond(0); 3072 final Instant stopTime = Instant.ofEpochSecond(Integer.MAX_VALUE).minus(1, ChronoUnit.DAYS); 3073 3074 ZoneId zoneId = ZoneId.of(tzId); 3075 ZoneRules rules = zoneId.getRules(); 3076 ZoneOffsetTransition low = rules.previousTransition(startTime); 3077 if (low == null) { 3078 low = rules.nextTransition(startTime); 3079 if (low == null) { 3080 // Make sure we always test some times even for simple time zones without 3081 // transitions. 3082 return Arrays.asList(startTime.toEpochMilli(), stopTime.toEpochMilli()); 3083 } 3084 } 3085 3086 ZoneOffsetTransition high; 3087 while ((high = rules.nextTransition(low.getInstant())) != null) { 3088 Instant highTime = high.getInstant(); 3089 if (highTime.isAfter(stopTime)) { 3090 break; 3091 } 3092 3093 Instant lowTime = low.getInstant(); 3094 Instant midPointTime = calculateMidPoint(highTime, lowTime); 3095 if (midPointTime.isBefore(startTime)) { 3096 // Avoid the first-loop case where the mid point can be before the start time. 3097 // The next transition must be after start time so use a time before that. 3098 times.add(highTime.minus(1, ChronoUnit.DAYS).toEpochMilli()); 3099 } else { 3100 times.add(midPointTime.toEpochMilli()); 3101 } 3102 3103 low = high; 3104 } 3105 3106 // Add a time for the last interval we encountered. We either ran out of transitions or the 3107 // end point of the next transition is above the stop time. 3108 Instant finalTime = low.getInstant().plus(1, ChronoUnit.DAYS); 3109 if (finalTime.isAfter(startTime) && finalTime.isBefore(stopTime)) { 3110 times.add(finalTime.toEpochMilli()); 3111 } 3112 return times; 3113 } 3114 calculateMidPoint(Instant highStart, Instant lowStart)3115 private static Instant calculateMidPoint(Instant highStart, Instant lowStart) { 3116 // Use seconds so we don't end up with half seconds that Time can't deal with. 3117 long midPointSeconds = (lowStart.getEpochSecond() + highStart.getEpochSecond()) / 2; 3118 return Instant.ofEpochSecond(midPointSeconds); 3119 } 3120 3121 @Test test_bug118835133_fixEarliestRawOffsetValue()3122 public void test_bug118835133_fixEarliestRawOffsetValue() { 3123 // This test confirms the behavior between Integer.MIN_VALUE seconds and the next 3124 // transition. With zic <= 2014b there is no explicit transition at Integer.MIN_VALUE 3125 // seconds. Between 2014c and 2018e there is one, and it goes away again in 2018f. This test 3126 // should pass regardless of zic version used as the Android behavior is stable for this 3127 // zone as we have fixed the logic used to determine the offset before the first transition 3128 // AND the first transition for the zone is after Integer.MIN_VALUE. 3129 String tzId = "Africa/Abidjan"; 3130 Time t = new Time(tzId); 3131 // Jan 1, 1912 12:16:08 AM UTC / Jan 1, 1912 00:00:00 local time 3132 Instant oldEarliestTransition = Instant.ofEpochSecond(-1830383032); 3133 Instant beforeOldEarliestTransition = oldEarliestTransition.minus(Duration.ofDays(1)); 3134 t.set(beforeOldEarliestTransition.toEpochMilli()); 3135 3136 // The expected local time equivalent to the oldEarliestTransition time minus 1 day and 3137 // offset by -968 seconds. 3138 Time expected = new Time(tzId); 3139 Fields.set(expected, 1911, 11, 31, 0, 0, 0, 0 /* isDst */, -968, 364, 0); 3140 Fields.verifyTimeEquals(expected, t); 3141 3142 // Check that Time agrees with native localtime / mktime. 3143 NativeTimeFunctions.assertNativeTimeResults( 3144 tzId, LocalDateTime.of(1911, Month.DECEMBER, 31, 0, 0, 0), 3145 beforeOldEarliestTransition.toEpochMilli()); 3146 } 3147 verifyNormalizeResult(boolean normalizeArgument, Time toNormalize, Time expectedTime, long expectedTimeMillis)3148 private static void verifyNormalizeResult(boolean normalizeArgument, Time toNormalize, 3149 Time expectedTime, long expectedTimeMillis) { 3150 long actualTimeMillis = toNormalize.normalize(normalizeArgument /* ignore isDst */); 3151 assertEquals(expectedTimeMillis, actualTimeMillis); 3152 Fields.verifyTimeEquals(expectedTime, toNormalize); 3153 } 3154 3155 /** A helper class for manipulating / testing fields on Time objects. */ 3156 private static class Fields { 3157 final static int MAIN_DATE_TIME = 1; 3158 final static int DST_FIELDS = 2; 3159 final static int DERIVED_DATE_TIME = 4; 3160 3161 final static int ALL = MAIN_DATE_TIME | DST_FIELDS | DERIVED_DATE_TIME; 3162 verifyTimeEquals(Time expected, Time actual)3163 public static void verifyTimeEquals(Time expected, Time actual) { 3164 verifyTimeEquals("", ALL, expected, actual); 3165 } 3166 verifyTimeEquals(String message, Time expected, Time actual)3167 public static void verifyTimeEquals(String message, Time expected, Time actual) { 3168 verifyTimeEquals(message, Fields.ALL, expected, actual); 3169 } 3170 verifyTimeEquals(String message, int fields, Time expected, Time actual)3171 public static void verifyTimeEquals(String message, int fields, Time expected, 3172 Time actual) { 3173 boolean mainDateTimeOk = (fields & Fields.MAIN_DATE_TIME) == 0 3174 || (Objects.equals(expected.timezone, actual.timezone) 3175 && expected.year == actual.year 3176 && expected.month == actual.month 3177 && expected.monthDay == actual.monthDay 3178 && expected.hour == actual.hour 3179 && expected.minute == actual.minute 3180 && expected.second == actual.second 3181 && expected.allDay == actual.allDay); 3182 3183 boolean dstFieldsOk = (fields & Fields.DST_FIELDS) == 0 3184 || (expected.isDst == actual.isDst && expected.gmtoff == actual.gmtoff); 3185 3186 boolean derivedDateTimeOk = (fields & Fields.DERIVED_DATE_TIME) == 0 3187 || (expected.yearDay == actual.yearDay && expected.weekDay == actual.weekDay); 3188 3189 if (!mainDateTimeOk || !dstFieldsOk || !derivedDateTimeOk) { 3190 String expectedTime = timeToString(fields, expected); 3191 String actualTime = timeToString(fields, actual); 3192 fail(message + " [Time fields differed. Expected: " + expectedTime + "Actual: " 3193 + actualTime + "]"); 3194 } 3195 } 3196 timeToString(int fields, Time time)3197 private static String timeToString(int fields, Time time) { 3198 List<Object> values = new ArrayList<>(); 3199 StringBuilder format = new StringBuilder(); 3200 if ((fields & Fields.MAIN_DATE_TIME) > 0) { 3201 format.append("%d-%02d-%02d %02d:%02d:%02d allDay=%b timezone=%s "); 3202 values.addAll( 3203 Arrays.asList(time.year, time.month, time.monthDay, time.hour, time.minute, 3204 time.second, time.allDay, time.timezone)); 3205 } 3206 if ((fields & Fields.DST_FIELDS) > 0) { 3207 format.append("isDst=%d, gmtoff=%d "); 3208 values.add(time.isDst); 3209 values.add(time.gmtoff); 3210 } 3211 if ((fields & Fields.DERIVED_DATE_TIME) > 0) { 3212 format.append("yearDay=%d, weekDay=%d "); 3213 values.add(time.yearDay); 3214 values.add(time.weekDay); 3215 3216 } 3217 return String.format(format.toString(), values.toArray()); 3218 } 3219 3220 /** 3221 * Set date/time fields (only) on the supplied time. month is [0-11]. 3222 */ setDateTime(Time t, int year, int month, int monthDay, int hour, int minute, int second)3223 public static void setDateTime(Time t, int year, int month, int monthDay, int hour, 3224 int minute, int second) { 3225 t.year = year; 3226 t.month = month; 3227 t.monthDay = monthDay; 3228 t.hour = hour; 3229 t.minute = minute; 3230 t.second = second; 3231 t.allDay = false; 3232 } 3233 3234 /** 3235 * Set date/time fields (only) on the supplied time using the equivalent Calendar fields. 3236 */ setDateTime(Time t, Calendar calendar)3237 public static void setDateTime(Time t, Calendar calendar) { 3238 setDateTime(t, 3239 calendar.get(Calendar.YEAR), 3240 calendar.get(Calendar.MONTH), 3241 calendar.get(Calendar.DAY_OF_MONTH), 3242 calendar.get(Calendar.HOUR_OF_DAY), 3243 calendar.get(Calendar.MINUTE), 3244 calendar.get(Calendar.SECOND)); 3245 } 3246 3247 /** 3248 * See {@link #setDateTime(Time, int, int, int, int, int, int)} for array order. 3249 */ setDateTime(Time t, int[] args)3250 public static void setDateTime(Time t, int[] args) { 3251 assertEquals(6, args.length); 3252 setDateTime(t, args[0], args[1], args[2], args[3], args[4], args[5]); 3253 } 3254 setDst(Time t, int isDst, int gmtoff)3255 public static void setDst(Time t, int isDst, int gmtoff) { 3256 t.isDst = isDst; 3257 t.gmtoff = gmtoff; 3258 } 3259 setDerivedDateTime(Time t, int yearDay, int weekDay)3260 public static void setDerivedDateTime(Time t, int yearDay, int weekDay) { 3261 t.yearDay = yearDay; 3262 t.weekDay = weekDay; 3263 } 3264 3265 /** 3266 * Set fields on the supplied time. month is [0-11]. 3267 */ setAllDayDate(Time t, int year, int month, int monthDay)3268 public static void setAllDayDate(Time t, int year, int month, int monthDay) { 3269 t.year = year; 3270 t.month = month; 3271 t.monthDay = monthDay; 3272 t.allDay = true; 3273 } 3274 3275 /** 3276 * See {@link #set(Time, int, int, int, int, int, int, int, int, int, int)} for array order. 3277 */ set(Time t, int[] args)3278 public static void set(Time t, int[] args) { 3279 assertEquals(10, args.length); 3280 set(t, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 3281 args[9]); 3282 } 3283 3284 /** 3285 * Set fields on the supplied time. month is [0-11]. 3286 */ set(Time t, int year, int month, int monthDay, int hour, int minute, int second, int isDst, int gmtoff, int yearDay, int weekDay)3287 public static void set(Time t, int year, int month, int monthDay, int hour, int minute, 3288 int second, int isDst, int gmtoff, int yearDay, int weekDay) { 3289 setDateTime(t, year, month, monthDay, hour, minute, second); 3290 setDst(t, isDst, gmtoff); 3291 setDerivedDateTime(t, yearDay, weekDay); 3292 } 3293 } 3294 3295 private static class PstPdt { 3296 public static final String ID = "America/Los_Angeles"; 3297 getUtcOffsetSeconds(int isDst)3298 public static int getUtcOffsetSeconds(int isDst) { 3299 if (isDst == 0) { 3300 return -28800; 3301 } else if (isDst == 1) { 3302 return -25200; 3303 } 3304 throw new IllegalArgumentException(); 3305 } 3306 getUtcOffsetSeconds(boolean isDst)3307 public static int getUtcOffsetSeconds(boolean isDst) { 3308 return getUtcOffsetSeconds(isDst ? 1 : 0); 3309 } 3310 getUtcOffsetMillis(boolean isDst)3311 public static int getUtcOffsetMillis(boolean isDst) { 3312 return (int) Duration.ofSeconds(getUtcOffsetSeconds(isDst)).toMillis(); 3313 } 3314 } 3315 maybeInitializeSystemLocales()3316 private static void maybeInitializeSystemLocales() { 3317 if (sSystemLocales == null) { 3318 String[] locales = Resources.getSystem().getAssets().getLocales(); 3319 List<Locale> systemLocales = new ArrayList<Locale>(locales.length); 3320 for (String localeStr : locales) { 3321 systemLocales.add(Locale.forLanguageTag(localeStr)); 3322 } 3323 3324 sSystemLocales = systemLocales; 3325 } 3326 } 3327 changeJavaAndAndroidLocale(Locale locale, boolean force)3328 private static boolean changeJavaAndAndroidLocale(Locale locale, boolean force) { 3329 // The Time class uses the Android-managed locale for string resources containing format 3330 // patterns and the Java-managed locale for other things (e.g. month names, week days names) 3331 // that are placed into those patterns. For example the format "%c" expands to include 3332 // a pattern that includes month names. 3333 // Changing the Java-managed Locale does not affect the Android-managed locale. 3334 // Changing the Android-managed locale does not affect the Java-managed locale. 3335 // 3336 // In order to ensure consistent behavior in the tests the device Locale must not be 3337 // assumed. To simulate the most common behavior (i.e. when the Java and the Android-managed 3338 // locales agree), when the Java-managed locale is changed during this test the locale in 3339 // the runtime-local copy of the system resources is modified as well. 3340 3341 if (!force && !sSystemLocales.contains(locale)) { 3342 return false; 3343 } 3344 3345 // Change the Java-managed locale. 3346 Locale.setDefault(locale); 3347 3348 // Change the local copy of the Android system configuration: this simulates the device 3349 // being set to the locale and forces a reload of the string resources. 3350 Configuration configuration = Resources.getSystem().getConfiguration(); 3351 configuration.locale = locale; 3352 Resources.getSystem().updateConfiguration(configuration, null); 3353 return true; 3354 } 3355 } 3356