1 /* 2 * Copyright (C) 2017 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 com.android.server.notification; 18 19 import static junit.framework.Assert.assertFalse; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertTrue; 23 24 import android.service.notification.ScheduleCalendar; 25 import android.service.notification.ZenModeConfig; 26 import android.test.suitebuilder.annotation.SmallTest; 27 28 import androidx.test.filters.FlakyTest; 29 import androidx.test.runner.AndroidJUnit4; 30 31 import com.android.server.UiServiceTestCase; 32 33 import org.junit.Before; 34 import org.junit.Test; 35 import org.junit.runner.RunWith; 36 37 import java.util.Calendar; 38 import java.util.GregorianCalendar; 39 import java.util.TimeZone; 40 41 @SmallTest 42 @RunWith(AndroidJUnit4.class) 43 public class ScheduleCalendarTest extends UiServiceTestCase { 44 45 private ScheduleCalendar mScheduleCalendar; 46 private ZenModeConfig.ScheduleInfo mScheduleInfo; 47 48 @Before setUp()49 public void setUp() throws Exception { 50 mScheduleCalendar = new ScheduleCalendar(); 51 mScheduleInfo = new ZenModeConfig.ScheduleInfo(); 52 mScheduleInfo.days = new int[] {1, 2, 3, 4, 5}; 53 } 54 55 @Test testNullScheduleInfo()56 public void testNullScheduleInfo() throws Exception { 57 mScheduleCalendar.setSchedule(null); 58 59 mScheduleCalendar.maybeSetNextAlarm(1000, 1999); 60 assertEquals(0, mScheduleCalendar.getNextChangeTime(1000)); 61 assertFalse(mScheduleCalendar.isInSchedule(100)); 62 assertFalse(mScheduleCalendar.shouldExitForAlarm(100)); 63 } 64 65 @Test testGetNextChangeTime_startToday()66 public void testGetNextChangeTime_startToday() throws Exception { 67 Calendar cal = new GregorianCalendar(); 68 cal.set(Calendar.HOUR_OF_DAY, 1); 69 cal.set(Calendar.MINUTE, 15); 70 cal.set(Calendar.SECOND, 0); 71 cal.set(Calendar.MILLISECOND, 0); 72 mScheduleInfo.days = new int[] {getTodayDay()}; 73 mScheduleInfo.startHour = cal.get(Calendar.HOUR_OF_DAY) + 1; 74 mScheduleInfo.endHour = cal.get(Calendar.HOUR_OF_DAY) + 3; 75 mScheduleInfo.startMinute = 15; 76 mScheduleInfo.endMinute = 15; 77 mScheduleInfo.exitAtAlarm = false; 78 mScheduleCalendar.setSchedule(mScheduleInfo); 79 80 Calendar expected = new GregorianCalendar(); 81 expected.setTimeInMillis(cal.getTimeInMillis()); 82 expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.startHour); 83 84 long actualMs = mScheduleCalendar.getNextChangeTime(cal.getTimeInMillis()); 85 GregorianCalendar actual = new GregorianCalendar(); 86 actual.setTimeInMillis(actualMs); 87 assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(), 88 actualMs); 89 } 90 91 @Test testGetNextChangeTime_endToday()92 public void testGetNextChangeTime_endToday() throws Exception { 93 Calendar cal = new GregorianCalendar(); 94 cal.set(Calendar.HOUR_OF_DAY, 2); 95 cal.set(Calendar.MINUTE, 15); 96 cal.set(Calendar.SECOND, 0); 97 cal.set(Calendar.MILLISECOND, 0); 98 mScheduleInfo.days = new int[] {getTodayDay()}; 99 mScheduleInfo.startHour = cal.get(Calendar.HOUR_OF_DAY) - 1; 100 mScheduleInfo.endHour = cal.get(Calendar.HOUR_OF_DAY) + 3; 101 mScheduleInfo.startMinute = 15; 102 mScheduleInfo.endMinute = 15; 103 mScheduleInfo.exitAtAlarm = false; 104 mScheduleCalendar.setSchedule(mScheduleInfo); 105 106 Calendar expected = new GregorianCalendar(); 107 expected.setTimeInMillis(cal.getTimeInMillis()); 108 expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.endHour); 109 expected.set(Calendar.MINUTE, mScheduleInfo.endMinute); 110 111 long actualMs = mScheduleCalendar.getNextChangeTime(cal.getTimeInMillis()); 112 GregorianCalendar actual = new GregorianCalendar(); 113 actual.setTimeInMillis(actualMs); 114 assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(), 115 actualMs); 116 } 117 118 @Test testGetNextChangeTime_startTomorrow()119 public void testGetNextChangeTime_startTomorrow() throws Exception { 120 Calendar cal = new GregorianCalendar(); 121 cal.set(Calendar.HOUR_OF_DAY, 23); 122 cal.set(Calendar.MINUTE, 15); 123 cal.set(Calendar.SECOND, 0); 124 cal.set(Calendar.MILLISECOND, 0); 125 mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)}; 126 mScheduleInfo.startHour = 1; 127 mScheduleInfo.endHour = 3; 128 mScheduleInfo.startMinute = 15; 129 mScheduleInfo.endMinute = 15; 130 mScheduleInfo.exitAtAlarm = false; 131 mScheduleCalendar.setSchedule(mScheduleInfo); 132 133 Calendar expected = new GregorianCalendar(); 134 expected.setTimeInMillis(cal.getTimeInMillis()); 135 expected.add(Calendar.DATE, 1); 136 expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.startHour); 137 expected.set(Calendar.MINUTE, mScheduleInfo.startMinute); 138 139 long actualMs = mScheduleCalendar.getNextChangeTime(cal.getTimeInMillis()); 140 GregorianCalendar actual = new GregorianCalendar(); 141 actual.setTimeInMillis(actualMs); 142 assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(), 143 actualMs); 144 } 145 146 @Test testGetNextChangeTime_endTomorrow()147 public void testGetNextChangeTime_endTomorrow() throws Exception { 148 Calendar cal = new GregorianCalendar(); 149 cal.set(Calendar.HOUR_OF_DAY, 23); 150 cal.set(Calendar.MINUTE, 15); 151 cal.set(Calendar.SECOND, 0); 152 cal.set(Calendar.MILLISECOND, 0); 153 mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)}; 154 mScheduleInfo.startHour = 22; 155 mScheduleInfo.endHour = 3; 156 mScheduleInfo.startMinute = 15; 157 mScheduleInfo.endMinute = 15; 158 mScheduleInfo.exitAtAlarm = false; 159 mScheduleCalendar.setSchedule(mScheduleInfo); 160 161 Calendar expected = new GregorianCalendar(); 162 expected.setTimeInMillis(cal.getTimeInMillis()); 163 expected.add(Calendar.DATE, 1); 164 expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.endHour); 165 expected.set(Calendar.MINUTE, mScheduleInfo.endMinute); 166 167 long actualMs = mScheduleCalendar.getNextChangeTime(cal.getTimeInMillis()); 168 GregorianCalendar actual = new GregorianCalendar(); 169 actual.setTimeInMillis(actualMs); 170 assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(), 171 actualMs); 172 } 173 174 @Test testGetNextChangeTime_startTomorrowInDaylight()175 public void testGetNextChangeTime_startTomorrowInDaylight() { 176 // Test that the correct thing happens when the next start time would be tomorrow, during 177 // a schedule start time that doesn't exist that day. Consistent with "start times" as 178 // implemented in isInSchedule, this should get adjusted to the closest actual time. 179 mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York")); 180 181 // "today" = the day before the skipped hour for daylight savings. 182 Calendar today = getDaylightSavingsForwardDay(); 183 today.set(Calendar.HOUR_OF_DAY, 23); 184 today.set(Calendar.MINUTE, 15); 185 Calendar tomorrow = getDaylightSavingsForwardDay(); 186 tomorrow.add(Calendar.DATE, 1); 187 mScheduleInfo.days = new int[] {today.get(Calendar.DAY_OF_WEEK), 188 tomorrow.get(Calendar.DAY_OF_WEEK)}; 189 mScheduleInfo.startHour = 2; 190 mScheduleInfo.endHour = 4; 191 mScheduleInfo.startMinute = 15; 192 mScheduleInfo.endMinute = 15; 193 mScheduleInfo.exitAtAlarm = false; 194 mScheduleCalendar.setSchedule(mScheduleInfo); 195 196 // The expected next change time should be tomorrow, 3AM as 2:15AM doesn't exist. 197 Calendar expected = new GregorianCalendar(TimeZone.getTimeZone("America/New_York")); 198 expected.setTimeInMillis(tomorrow.getTimeInMillis()); 199 expected.set(Calendar.HOUR_OF_DAY, 3); 200 expected.set(Calendar.MINUTE, 0); 201 expected.set(Calendar.SECOND, 0); 202 expected.set(Calendar.MILLISECOND, 0); 203 204 long actualMs = mScheduleCalendar.getNextChangeTime(today.getTimeInMillis()); 205 GregorianCalendar actual = new GregorianCalendar(TimeZone.getTimeZone("America/New_York")); 206 actual.setTimeInMillis(actualMs); 207 assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(), 208 actualMs); 209 } 210 211 @Test testGetNextChangeTime_startTomorrowWhenTodayIsDaylight()212 public void testGetNextChangeTime_startTomorrowWhenTodayIsDaylight() { 213 // Test that the correct thing happens when the next start time would be tomorrow, but 214 // today is the day when daylight time switches over (so the "schedule start time" today 215 // may not exist). 216 mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York")); 217 218 // "today" = the day with the skipped hour for daylight savings. 219 Calendar today = getDaylightSavingsForwardDay(); 220 today.add(Calendar.DATE, 1); 221 today.set(Calendar.HOUR_OF_DAY, 23); 222 today.set(Calendar.MINUTE, 15); 223 Calendar tomorrow = getDaylightSavingsForwardDay(); 224 tomorrow.add(Calendar.DATE, 2); 225 mScheduleInfo.days = new int[] {today.get(Calendar.DAY_OF_WEEK), 226 tomorrow.get(Calendar.DAY_OF_WEEK)}; 227 mScheduleInfo.startHour = 2; 228 mScheduleInfo.endHour = 4; 229 mScheduleInfo.startMinute = 15; 230 mScheduleInfo.endMinute = 15; 231 mScheduleInfo.exitAtAlarm = false; 232 mScheduleCalendar.setSchedule(mScheduleInfo); 233 234 // The expected next change time should be tomorrow, 2:15AM. 235 Calendar expected = new GregorianCalendar(TimeZone.getTimeZone("America/New_York")); 236 expected.setTimeInMillis(tomorrow.getTimeInMillis()); 237 expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.startHour); 238 expected.set(Calendar.MINUTE, mScheduleInfo.startMinute); 239 expected.set(Calendar.SECOND, 0); 240 expected.set(Calendar.MILLISECOND, 0); 241 242 long actualMs = mScheduleCalendar.getNextChangeTime(today.getTimeInMillis()); 243 GregorianCalendar actual = new GregorianCalendar(TimeZone.getTimeZone("America/New_York")); 244 actual.setTimeInMillis(actualMs); 245 assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(), 246 actualMs); 247 } 248 249 @Test testGetNextChangeTime_startTomorrowWhenTodayIsDaylightBackward()250 public void testGetNextChangeTime_startTomorrowWhenTodayIsDaylightBackward() { 251 // Test that the correct thing happens when the next start time would be tomorrow, but 252 // today is the day when clocks are adjusted backwards (so the "schedule start time" today 253 // exists twice). 254 mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York")); 255 256 // "today" = the day with the extra hour for daylight savings. 257 Calendar today = getDaylightSavingsBackwardDay(); 258 today.add(Calendar.DATE, 1); 259 today.set(Calendar.HOUR_OF_DAY, 23); 260 today.set(Calendar.MINUTE, 15); 261 Calendar tomorrow = getDaylightSavingsBackwardDay(); 262 tomorrow.add(Calendar.DATE, 2); 263 mScheduleInfo.days = new int[] {today.get(Calendar.DAY_OF_WEEK), 264 tomorrow.get(Calendar.DAY_OF_WEEK)}; 265 mScheduleInfo.startHour = 1; 266 mScheduleInfo.endHour = 4; 267 mScheduleInfo.startMinute = 15; 268 mScheduleInfo.endMinute = 15; 269 mScheduleInfo.exitAtAlarm = false; 270 mScheduleCalendar.setSchedule(mScheduleInfo); 271 272 // The expected next change time should be tomorrow, 1:15AM. 273 Calendar expected = new GregorianCalendar(TimeZone.getTimeZone("America/New_York")); 274 expected.setTimeInMillis(tomorrow.getTimeInMillis()); 275 expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.startHour); 276 expected.set(Calendar.MINUTE, mScheduleInfo.startMinute); 277 expected.set(Calendar.SECOND, 0); 278 expected.set(Calendar.MILLISECOND, 0); 279 280 long actualMs = mScheduleCalendar.getNextChangeTime(today.getTimeInMillis()); 281 GregorianCalendar actual = new GregorianCalendar(TimeZone.getTimeZone("America/New_York")); 282 actual.setTimeInMillis(actualMs); 283 assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(), 284 actualMs); 285 } 286 287 @Test testShouldExitForAlarm_settingOff()288 public void testShouldExitForAlarm_settingOff() { 289 mScheduleInfo.exitAtAlarm = false; 290 mScheduleInfo.nextAlarm = 1000; 291 mScheduleCalendar.setSchedule(mScheduleInfo); 292 293 assertFalse(mScheduleCalendar.shouldExitForAlarm(1000)); 294 } 295 296 @Test testShouldExitForAlarm_beforeAlarm()297 public void testShouldExitForAlarm_beforeAlarm() { 298 mScheduleInfo.exitAtAlarm = true; 299 mScheduleInfo.nextAlarm = 1000; 300 mScheduleCalendar.setSchedule(mScheduleInfo); 301 302 assertFalse(mScheduleCalendar.shouldExitForAlarm(999)); 303 } 304 305 @Test testShouldExitForAlarm_noAlarm()306 public void testShouldExitForAlarm_noAlarm() { 307 mScheduleInfo.exitAtAlarm = true; 308 mScheduleInfo.nextAlarm = 0; 309 mScheduleCalendar.setSchedule(mScheduleInfo); 310 311 assertFalse(mScheduleCalendar.shouldExitForAlarm(999)); 312 } 313 314 @Test testShouldExitForAlarm()315 public void testShouldExitForAlarm() { 316 mScheduleInfo.exitAtAlarm = true; 317 mScheduleInfo.nextAlarm = 1000; 318 mScheduleCalendar.setSchedule(mScheduleInfo); 319 320 assertTrue(mScheduleCalendar.shouldExitForAlarm(1000)); 321 } 322 323 @Test testShouldExitForAlarm_oldAlarm()324 public void testShouldExitForAlarm_oldAlarm() { 325 // Cal: today 2:15pm 326 Calendar now = new GregorianCalendar(); 327 now.set(Calendar.HOUR_OF_DAY, 14); 328 now.set(Calendar.MINUTE, 15); 329 now.set(Calendar.SECOND, 0); 330 now.set(Calendar.MILLISECOND, 0); 331 332 // ScheduleInfo: today 12:16pm - today 3:15pm 333 mScheduleInfo.days = new int[] {getTodayDay()}; 334 mScheduleInfo.startHour = 12; 335 mScheduleInfo.endHour = 3; 336 mScheduleInfo.startMinute = 16; 337 mScheduleInfo.endMinute = 15; 338 mScheduleInfo.exitAtAlarm = true; 339 mScheduleInfo.nextAlarm = 1000; // very old alarm 340 341 mScheduleCalendar.setSchedule(mScheduleInfo); 342 assertTrue(mScheduleCalendar.isInSchedule(now.getTimeInMillis())); 343 344 // don't exit for an alarm if it's an old alarm 345 assertFalse(mScheduleCalendar.shouldExitForAlarm(now.getTimeInMillis())); 346 } 347 348 @Test testShouldExitForAlarm_oldAlarmInSchedule()349 public void testShouldExitForAlarm_oldAlarmInSchedule() { 350 // calNow: day 2 at 9pm 351 Calendar calNow = new GregorianCalendar(); 352 calNow.set(Calendar.HOUR_OF_DAY, 21); 353 calNow.set(Calendar.MINUTE, 0); 354 calNow.set(Calendar.SECOND, 0); 355 calNow.set(Calendar.MILLISECOND, 0); 356 calNow.add(Calendar.DATE, 1); // add a day 357 358 // calAlarm: day 2 at 5am 359 Calendar calAlarm = new GregorianCalendar(); 360 calAlarm.set(Calendar.HOUR_OF_DAY, 5); 361 calAlarm.set(Calendar.MINUTE, 0); 362 calAlarm.set(Calendar.SECOND, 0); 363 calAlarm.set(Calendar.MILLISECOND, 0); 364 calAlarm.add(Calendar.DATE, 1); // add a day 365 366 // ScheduleInfo: day 1, day 2: 9pm-7am 367 mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)}; 368 mScheduleInfo.startHour = 21; 369 mScheduleInfo.endHour = 7; 370 mScheduleInfo.startMinute = 0; 371 mScheduleInfo.endMinute = 0; 372 mScheduleInfo.exitAtAlarm = true; 373 mScheduleInfo.nextAlarm = calAlarm.getTimeInMillis(); // old alarm (5am day 2) 374 375 mScheduleCalendar.setSchedule(mScheduleInfo); 376 assertTrue(mScheduleCalendar.isInSchedule(calNow.getTimeInMillis())); 377 assertTrue(mScheduleCalendar.isInSchedule(calAlarm.getTimeInMillis())); 378 379 // don't exit for an alarm if it's an old alarm 380 assertFalse(mScheduleCalendar.shouldExitForAlarm(calNow.getTimeInMillis())); 381 } 382 383 @Test testMaybeSetNextAlarm_settingOff()384 public void testMaybeSetNextAlarm_settingOff() { 385 mScheduleInfo.exitAtAlarm = false; 386 mScheduleInfo.nextAlarm = 0; 387 mScheduleCalendar.setSchedule(mScheduleInfo); 388 389 mScheduleCalendar.maybeSetNextAlarm(1000, 2000); 390 391 assertEquals(0, mScheduleInfo.nextAlarm); 392 } 393 394 @Test testMaybeSetNextAlarm_settingOn()395 public void testMaybeSetNextAlarm_settingOn() { 396 mScheduleInfo.exitAtAlarm = true; 397 mScheduleInfo.nextAlarm = 0; 398 mScheduleCalendar.setSchedule(mScheduleInfo); 399 400 mScheduleCalendar.maybeSetNextAlarm(1000, 2000); 401 402 assertEquals(2000, mScheduleInfo.nextAlarm); 403 } 404 405 @Test testMaybeSetNextAlarm_alarmCanceled()406 public void testMaybeSetNextAlarm_alarmCanceled() { 407 mScheduleInfo.exitAtAlarm = true; 408 mScheduleInfo.nextAlarm = 10000; 409 mScheduleCalendar.setSchedule(mScheduleInfo); 410 411 mScheduleCalendar.maybeSetNextAlarm(1000, 0); 412 413 assertEquals(0, mScheduleInfo.nextAlarm); 414 } 415 416 @Test testMaybeSetNextAlarm_earlierAlarm()417 public void testMaybeSetNextAlarm_earlierAlarm() { 418 mScheduleInfo.exitAtAlarm = true; 419 mScheduleInfo.nextAlarm = 2000; 420 mScheduleCalendar.setSchedule(mScheduleInfo); 421 422 mScheduleCalendar.maybeSetNextAlarm(1000, 1500); 423 424 assertEquals(1500, mScheduleInfo.nextAlarm); 425 } 426 427 @Test testMaybeSetNextAlarm_laterAlarm()428 public void testMaybeSetNextAlarm_laterAlarm() { 429 mScheduleInfo.exitAtAlarm = true; 430 mScheduleCalendar.setSchedule(mScheduleInfo); 431 mScheduleInfo.nextAlarm = 2000; 432 433 // next alarm updated to 3000 (alarm for 2000 was changed to 3000) 434 mScheduleCalendar.maybeSetNextAlarm(1000, 3000); 435 436 assertEquals(3000, mScheduleInfo.nextAlarm); 437 } 438 439 @Test testMaybeSetNextAlarm_expiredAlarm()440 public void testMaybeSetNextAlarm_expiredAlarm() { 441 mScheduleInfo.exitAtAlarm = true; 442 mScheduleInfo.nextAlarm = 998; 443 mScheduleCalendar.setSchedule(mScheduleInfo); 444 445 mScheduleCalendar.maybeSetNextAlarm(1000, 999); 446 447 assertEquals(0, mScheduleInfo.nextAlarm); 448 } 449 450 @Test testMaybeSetNextAlarm_expiredOldAlarm()451 public void testMaybeSetNextAlarm_expiredOldAlarm() { 452 mScheduleInfo.exitAtAlarm = true; 453 mScheduleInfo.nextAlarm = 998; 454 mScheduleCalendar.setSchedule(mScheduleInfo); 455 456 mScheduleCalendar.maybeSetNextAlarm(1000, 1001); 457 458 assertEquals(1001, mScheduleInfo.nextAlarm); 459 } 460 461 @Test 462 @FlakyTest testIsInSchedule_inScheduleOvernight()463 public void testIsInSchedule_inScheduleOvernight() { 464 Calendar cal = new GregorianCalendar(); 465 cal.set(Calendar.HOUR_OF_DAY, 23); 466 cal.set(Calendar.MINUTE, 15); 467 cal.set(Calendar.SECOND, 0); 468 cal.set(Calendar.MILLISECOND, 0); 469 mScheduleInfo.days = new int[] {getTodayDay()}; 470 mScheduleInfo.startHour = 22; 471 mScheduleInfo.endHour = 3; 472 mScheduleInfo.startMinute = 15; 473 mScheduleInfo.endMinute = 15; 474 mScheduleCalendar.setSchedule(mScheduleInfo); 475 476 assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis())); 477 } 478 479 @Test 480 @FlakyTest testIsInSchedule_inScheduleSingleDay()481 public void testIsInSchedule_inScheduleSingleDay() { 482 Calendar cal = new GregorianCalendar(); 483 cal.set(Calendar.HOUR_OF_DAY, 14); 484 cal.set(Calendar.MINUTE, 15); 485 cal.set(Calendar.SECOND, 0); 486 cal.set(Calendar.MILLISECOND, 0); 487 mScheduleInfo.days = new int[] {getTodayDay()}; 488 mScheduleInfo.startHour = 12; 489 mScheduleInfo.endHour = 3; 490 mScheduleInfo.startMinute = 16; 491 mScheduleInfo.endMinute = 15; 492 mScheduleCalendar.setSchedule(mScheduleInfo); 493 494 assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis())); 495 } 496 497 @Test testIsInSchedule_notToday()498 public void testIsInSchedule_notToday() { 499 Calendar cal = new GregorianCalendar(); 500 cal.set(Calendar.HOUR_OF_DAY, 14); 501 cal.set(Calendar.MINUTE, 15); 502 cal.set(Calendar.SECOND, 0); 503 cal.set(Calendar.MILLISECOND, 0); 504 cal.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY); 505 mScheduleInfo.days = new int[] {Calendar.FRIDAY, Calendar.SUNDAY}; 506 mScheduleInfo.startHour = 12; 507 mScheduleInfo.startMinute = 16; 508 mScheduleInfo.endHour = 15; 509 mScheduleInfo.endMinute = 15; 510 mScheduleCalendar.setSchedule(mScheduleInfo); 511 512 assertFalse(mScheduleCalendar.isInSchedule(cal.getTimeInMillis())); 513 } 514 515 @Test testIsInSchedule_startingSoon()516 public void testIsInSchedule_startingSoon() { 517 Calendar cal = new GregorianCalendar(); 518 cal.set(Calendar.HOUR_OF_DAY, 14); 519 cal.set(Calendar.MINUTE, 15); 520 cal.set(Calendar.SECOND, 59); 521 cal.set(Calendar.MILLISECOND, 0); 522 mScheduleInfo.days = new int[] {getTodayDay()}; 523 mScheduleInfo.startHour = 14; 524 mScheduleInfo.endHour = 3; 525 mScheduleInfo.startMinute = 16; 526 mScheduleInfo.endMinute = 15; 527 mScheduleCalendar.setSchedule(mScheduleInfo); 528 529 assertFalse(mScheduleCalendar.isInSchedule(cal.getTimeInMillis())); 530 } 531 532 @Test testIsInSchedule_daylightSavingsForward_startDuringChange()533 public void testIsInSchedule_daylightSavingsForward_startDuringChange() { 534 // Test that if the start time of a ScheduleCalendar is during the nonexistent 535 // hour of daylight savings forward time, the evaluation of whether a time is in the 536 // schedule still works. 537 538 // Set timezone to make sure we're evaluating the correct days. 539 mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York")); 540 541 // Set up schedule for 2:30AM - 4:00AM. 542 final Calendar dstYesterday = getDaylightSavingsForwardDay(); 543 final Calendar dstToday = getDaylightSavingsForwardDay(); 544 dstToday.add(Calendar.DATE, 1); 545 mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK), 546 dstToday.get(Calendar.DAY_OF_WEEK)}; 547 mScheduleInfo.startHour = 2; 548 mScheduleInfo.startMinute = 30; 549 mScheduleInfo.endHour = 4; 550 mScheduleCalendar.setSchedule(mScheduleInfo); 551 552 // Test cases: there are 2 "on" periods. These cover: before the first schedule 553 // (1AM previous day), during the first schedule (2:30AM), two between the two schedules 554 // (one on each calendar day), during the second (3:30AM), and after the second (4:30AM) 555 Calendar out1 = getDaylightSavingsForwardDay(); 556 out1.set(Calendar.HOUR_OF_DAY, 1); 557 out1.set(Calendar.MINUTE, 00); 558 out1.set(Calendar.SECOND, 0); 559 out1.set(Calendar.MILLISECOND, 0); 560 561 Calendar in1 = getDaylightSavingsForwardDay(); 562 in1.set(Calendar.HOUR_OF_DAY, 2); 563 in1.set(Calendar.MINUTE, 45); 564 in1.set(Calendar.SECOND, 0); 565 in1.set(Calendar.MILLISECOND, 0); 566 567 Calendar midOut1 = getDaylightSavingsForwardDay(); 568 midOut1.set(Calendar.HOUR_OF_DAY, 7); 569 midOut1.set(Calendar.MINUTE, 30); 570 midOut1.set(Calendar.SECOND, 0); 571 midOut1.set(Calendar.MILLISECOND, 0); 572 573 Calendar midOut2 = getDaylightSavingsForwardDay(); 574 midOut2.add(Calendar.DATE, 1); 575 midOut2.set(Calendar.HOUR_OF_DAY, 1); 576 midOut2.set(Calendar.MINUTE, 30); 577 midOut2.set(Calendar.SECOND, 0); 578 midOut2.set(Calendar.MILLISECOND, 0); 579 580 // Question: should 3:15AM be in the 2:30-4 schedule on a day when 2:30-3 doesn't exist? 581 Calendar in2 = getDaylightSavingsForwardDay(); 582 in2.add(Calendar.DATE, 1); 583 in2.set(Calendar.HOUR_OF_DAY, 3); 584 in2.set(Calendar.MINUTE, 30); 585 in2.set(Calendar.SECOND, 0); 586 in2.set(Calendar.MILLISECOND, 0); 587 588 Calendar out2 = getDaylightSavingsForwardDay(); 589 out2.add(Calendar.DATE, 1); 590 out2.set(Calendar.HOUR_OF_DAY, 4); 591 out2.set(Calendar.MINUTE, 30); 592 out2.set(Calendar.SECOND, 0); 593 out2.set(Calendar.MILLISECOND, 0); 594 595 assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis())); 596 assertTrue(mScheduleCalendar.isInSchedule(in1.getTimeInMillis())); 597 assertFalse(mScheduleCalendar.isInSchedule(midOut1.getTimeInMillis())); 598 assertFalse(mScheduleCalendar.isInSchedule(midOut2.getTimeInMillis())); 599 assertTrue(mScheduleCalendar.isInSchedule(in2.getTimeInMillis())); 600 assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis())); 601 } 602 603 @Test testIsInSchedule_daylightSavingsForward_endDuringChange()604 public void testIsInSchedule_daylightSavingsForward_endDuringChange() { 605 // Test that if the end time of a ScheduleCalendar is during the nonexistent 606 // hour of daylight savings forward time, the evaluation of whether a time is in the 607 // schedule still works. 608 609 // Set timezone to make sure we're evaluating the correct days. 610 mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York")); 611 612 // Set up schedule for 11:00PM - 2:30AM. On the day when 2AM doesn't exist, this should 613 // effectively finish at 3:30AM(?) 614 final Calendar dstYesterday = getDaylightSavingsForwardDay(); 615 final Calendar dstToday = getDaylightSavingsForwardDay(); 616 dstToday.add(Calendar.DATE, 1); 617 mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK), 618 dstToday.get(Calendar.DAY_OF_WEEK)}; 619 mScheduleInfo.startHour = 23; 620 mScheduleInfo.endHour = 2; 621 mScheduleInfo.endMinute = 30; 622 mScheduleCalendar.setSchedule(mScheduleInfo); 623 624 // Test cases: before the time period on the previous day; during the time period when 625 // the calendar day is still the previous day; during the time period when the calendar 626 // day is the change day; afterwards. 627 Calendar out1 = getDaylightSavingsForwardDay(); 628 out1.set(Calendar.HOUR_OF_DAY, 22); 629 out1.set(Calendar.MINUTE, 00); 630 out1.set(Calendar.SECOND, 0); 631 out1.set(Calendar.MILLISECOND, 0); 632 633 Calendar in1 = getDaylightSavingsForwardDay(); 634 in1.set(Calendar.HOUR_OF_DAY, 23); 635 in1.set(Calendar.MINUTE, 30); 636 in1.set(Calendar.SECOND, 0); 637 in1.set(Calendar.MILLISECOND, 0); 638 639 Calendar in2 = getDaylightSavingsForwardDay(); 640 in2.add(Calendar.DATE, 1); 641 in2.set(Calendar.HOUR_OF_DAY, 1); 642 in2.set(Calendar.MINUTE, 30); 643 in2.set(Calendar.SECOND, 0); 644 in2.set(Calendar.MILLISECOND, 0); 645 646 // Question: Should 3:15AM be out of the schedule on a day when 2-3 doesn't exist? 647 Calendar out2 = getDaylightSavingsForwardDay(); 648 out2.add(Calendar.DATE, 1); 649 out2.set(Calendar.HOUR_OF_DAY, 3); 650 out2.set(Calendar.MINUTE, 45); 651 out2.set(Calendar.SECOND, 0); 652 out2.set(Calendar.MILLISECOND, 0); 653 654 assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis())); 655 assertTrue(mScheduleCalendar.isInSchedule(in1.getTimeInMillis())); 656 assertTrue(mScheduleCalendar.isInSchedule(in2.getTimeInMillis())); 657 assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis())); 658 } 659 660 @Test testIsInSchedule_daylightSavingsBackward_startDuringChange()661 public void testIsInSchedule_daylightSavingsBackward_startDuringChange() { 662 // Test that if the start time of a ScheduleCalendar is during the duplicated 663 // hour of daylight savings backward time, the evaluation of whether a time is in the 664 // schedule still works. It's not clear what correct behavior is during the duplicated 665 // 1:00->1:59->1:00->1:59 time period, but times outside that should still work. 666 667 // Set timezone to make sure we're evaluating the correct days. 668 mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York")); 669 670 // Set up schedule for 1:15AM - 4:00AM. 671 final Calendar dstYesterday = getDaylightSavingsBackwardDay(); 672 final Calendar dstToday = getDaylightSavingsBackwardDay(); 673 dstToday.add(Calendar.DATE, 1); 674 mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK), 675 dstToday.get(Calendar.DAY_OF_WEEK)}; 676 mScheduleInfo.startHour = 1; 677 mScheduleInfo.startMinute = 15; 678 mScheduleInfo.endHour = 4; 679 mScheduleCalendar.setSchedule(mScheduleInfo); 680 681 // Test cases: there are 2 "on" periods. These cover: before the first schedule 682 // (1AM previous day), during the first schedule (2:30AM), two between the two schedules 683 // (one on each calendar day), during the second (2:30AM), and after the second (4:30AM) 684 Calendar out1 = getDaylightSavingsBackwardDay(); 685 out1.set(Calendar.HOUR_OF_DAY, 1); 686 out1.set(Calendar.MINUTE, 00); 687 out1.set(Calendar.SECOND, 0); 688 out1.set(Calendar.MILLISECOND, 0); 689 690 Calendar in1 = getDaylightSavingsBackwardDay(); 691 in1.set(Calendar.HOUR_OF_DAY, 2); 692 in1.set(Calendar.MINUTE, 30); 693 in1.set(Calendar.SECOND, 0); 694 in1.set(Calendar.MILLISECOND, 0); 695 696 Calendar midOut1 = getDaylightSavingsBackwardDay(); 697 midOut1.set(Calendar.HOUR_OF_DAY, 7); 698 midOut1.set(Calendar.MINUTE, 30); 699 midOut1.set(Calendar.SECOND, 0); 700 midOut1.set(Calendar.MILLISECOND, 0); 701 702 Calendar midOut2 = getDaylightSavingsBackwardDay(); 703 midOut2.add(Calendar.DATE, 1); 704 midOut2.set(Calendar.HOUR_OF_DAY, 0); 705 midOut2.set(Calendar.MINUTE, 30); 706 midOut2.set(Calendar.SECOND, 0); 707 midOut2.set(Calendar.MILLISECOND, 0); 708 709 Calendar in2 = getDaylightSavingsBackwardDay(); 710 in2.add(Calendar.DATE, 1); 711 in2.set(Calendar.HOUR_OF_DAY, 2); 712 in2.set(Calendar.MINUTE, 30); 713 in2.set(Calendar.SECOND, 0); 714 in2.set(Calendar.MILLISECOND, 0); 715 716 Calendar out2 = getDaylightSavingsBackwardDay(); 717 out2.add(Calendar.DATE, 1); 718 out2.set(Calendar.HOUR_OF_DAY, 4); 719 out2.set(Calendar.MINUTE, 30); 720 out2.set(Calendar.SECOND, 0); 721 out2.set(Calendar.MILLISECOND, 0); 722 723 assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis())); 724 assertTrue(mScheduleCalendar.isInSchedule(in1.getTimeInMillis())); 725 assertFalse(mScheduleCalendar.isInSchedule(midOut1.getTimeInMillis())); 726 assertFalse(mScheduleCalendar.isInSchedule(midOut2.getTimeInMillis())); 727 assertTrue(mScheduleCalendar.isInSchedule(in2.getTimeInMillis())); 728 assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis())); 729 } 730 731 @Test testIsInSchedule_daylightSavings_flippedSchedule()732 public void testIsInSchedule_daylightSavings_flippedSchedule() { 733 // This test is for the unlikely edge case where the skipped hour due to daylight savings 734 // causes the evaluated start time to be "later" than the schedule's end time on that day, 735 // for instance if the schedule is 2:30AM-3:15AM; 2:30AM may evaluate to 3:30AM on the day 736 // of daylight change. 737 mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York")); 738 739 // Set up schedule for 2:30AM - 3:15AM. 740 final Calendar dstYesterday = getDaylightSavingsForwardDay(); 741 final Calendar dstToday = getDaylightSavingsForwardDay(); 742 dstToday.add(Calendar.DATE, 1); 743 mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK), 744 dstToday.get(Calendar.DAY_OF_WEEK)}; 745 mScheduleInfo.startHour = 2; 746 mScheduleInfo.startMinute = 30; 747 mScheduleInfo.endHour = 3; 748 mScheduleInfo.endMinute = 15; 749 mScheduleCalendar.setSchedule(mScheduleInfo); 750 751 // It may not be well-defined what times around the 2-3AM range one might expect to be 752 // included or not included on the weird day when 2AM doesn't exist, but other unrelated 753 // times of day (here, 3PM) should definitely be out. 754 Calendar out1 = getDaylightSavingsForwardDay(); 755 out1.set(Calendar.HOUR_OF_DAY, 15); 756 out1.set(Calendar.MINUTE, 0); 757 out1.set(Calendar.SECOND, 0); 758 out1.set(Calendar.MILLISECOND, 0); 759 760 Calendar out2 = getDaylightSavingsForwardDay(); 761 out2.add(Calendar.DATE, 1); 762 out2.set(Calendar.HOUR_OF_DAY, 15); 763 out2.set(Calendar.MINUTE, 0); 764 out2.set(Calendar.SECOND, 0); 765 out2.set(Calendar.MILLISECOND, 0); 766 767 assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis())); 768 assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis())); 769 } 770 771 @Test testIsAlarmInSchedule_alarmAndNowInSchedule_sameScheduleTrigger_daylightSavings()772 public void testIsAlarmInSchedule_alarmAndNowInSchedule_sameScheduleTrigger_daylightSavings() { 773 // Need to set the time zone explicitly to a US one so that the daylight savings time day is 774 // correct. 775 mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York")); 776 Calendar alarm = getDaylightSavingsForwardDay(); 777 alarm.set(Calendar.HOUR_OF_DAY, 23); 778 alarm.set(Calendar.MINUTE, 15); 779 alarm.set(Calendar.SECOND, 0); 780 alarm.set(Calendar.MILLISECOND, 0); 781 782 Calendar now = getDaylightSavingsForwardDay(); 783 now.set(Calendar.HOUR_OF_DAY, 2); 784 now.set(Calendar.MINUTE, 10); 785 now.set(Calendar.SECOND, 0); 786 now.set(Calendar.MILLISECOND, 0); 787 now.add(Calendar.DATE, 1); // add a day, on daylight savings this becomes 3:10am 788 789 final Calendar tempToday = getDaylightSavingsForwardDay(); 790 final Calendar tempTomorrow = getDaylightSavingsForwardDay(); 791 tempTomorrow.add(Calendar.DATE, 1); 792 mScheduleInfo.days = new int[] {tempToday.get(Calendar.DAY_OF_WEEK), 793 tempTomorrow.get(Calendar.DAY_OF_WEEK)}; 794 795 mScheduleInfo.startHour = 22; 796 mScheduleInfo.startMinute = 15; 797 mScheduleInfo.endHour = 3; 798 mScheduleInfo.endMinute = 15; 799 mScheduleCalendar.setSchedule(mScheduleInfo); 800 801 assertTrue(mScheduleCalendar.isInSchedule(alarm.getTimeInMillis())); 802 assertTrue(mScheduleCalendar.isInSchedule(now.getTimeInMillis())); 803 assertTrue(mScheduleCalendar.isAlarmInSchedule(alarm.getTimeInMillis(), 804 now.getTimeInMillis())); 805 } 806 807 @Test testIsAlarmInSchedule_alarmAndNowInSchedule_sameScheduleTrigger()808 public void testIsAlarmInSchedule_alarmAndNowInSchedule_sameScheduleTrigger() { 809 Calendar alarm = new GregorianCalendar(); 810 alarm.set(Calendar.HOUR_OF_DAY, 23); 811 alarm.set(Calendar.MINUTE, 15); 812 alarm.set(Calendar.SECOND, 0); 813 alarm.set(Calendar.MILLISECOND, 0); 814 815 Calendar now = new GregorianCalendar(); 816 now.set(Calendar.HOUR_OF_DAY, 2); 817 now.set(Calendar.MINUTE, 10); 818 now.set(Calendar.SECOND, 0); 819 now.set(Calendar.MILLISECOND, 0); 820 now.add(Calendar.DATE, 1); // add a day, on daylight savings this becomes 3:10am 821 822 mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)}; 823 mScheduleInfo.startHour = 22; 824 mScheduleInfo.startMinute = 15; 825 mScheduleInfo.endHour = 3; 826 mScheduleInfo.endMinute = 15; 827 mScheduleCalendar.setSchedule(mScheduleInfo); 828 829 assertTrue(mScheduleCalendar.isInSchedule(alarm.getTimeInMillis())); 830 assertTrue(mScheduleCalendar.isInSchedule(now.getTimeInMillis())); 831 assertTrue(mScheduleCalendar.isAlarmInSchedule(alarm.getTimeInMillis(), 832 now.getTimeInMillis())); 833 } 834 835 @Test testIsAlarmInSchedule_alarmAndNowInSchedule_differentScheduleTrigger()836 public void testIsAlarmInSchedule_alarmAndNowInSchedule_differentScheduleTrigger() { 837 Calendar alarm = new GregorianCalendar(); 838 alarm.set(Calendar.HOUR_OF_DAY, 23); 839 alarm.set(Calendar.MINUTE, 15); 840 alarm.set(Calendar.SECOND, 0); 841 alarm.set(Calendar.MILLISECOND, 0); 842 843 Calendar now = new GregorianCalendar(); 844 now.set(Calendar.HOUR_OF_DAY, 23); 845 now.set(Calendar.MINUTE, 15); 846 now.set(Calendar.SECOND, 0); 847 now.set(Calendar.MILLISECOND, 0); 848 now.add(Calendar.DATE, 1); // add a day 849 850 mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)}; 851 mScheduleInfo.startHour = 22; 852 mScheduleInfo.startMinute = 15; 853 mScheduleInfo.endHour = 3; 854 mScheduleInfo.endMinute = 15; 855 mScheduleCalendar.setSchedule(mScheduleInfo); 856 857 // even though both alarm and now are in schedule, they are not in the same part of 858 // the schedule (alarm is in schedule for the previous day's schedule compared to now) 859 assertTrue(mScheduleCalendar.isInSchedule(alarm.getTimeInMillis())); 860 assertTrue(mScheduleCalendar.isInSchedule(now.getTimeInMillis())); 861 assertFalse(mScheduleCalendar.isAlarmInSchedule(alarm.getTimeInMillis(), 862 now.getTimeInMillis())); 863 } 864 865 @Test testClosestActualTime_regularTimesAndSkippedTime()866 public void testClosestActualTime_regularTimesAndSkippedTime() { 867 // Make sure we're operating in the relevant time zone for the assumed Daylight Savings day 868 mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York")); 869 Calendar day = getDaylightSavingsForwardDay(); 870 day.set(Calendar.HOUR_OF_DAY, 15); 871 day.set(Calendar.MINUTE, 25); 872 day.set(Calendar.SECOND, 0); 873 day.set(Calendar.MILLISECOND, 0); 874 assertEquals(day.getTimeInMillis(), 875 mScheduleCalendar.getClosestActualTime(day.getTimeInMillis(), 15, 25)); 876 877 // Check a skipped time 878 day.add(Calendar.DATE, 1); 879 day.set(Calendar.HOUR_OF_DAY, 3); 880 day.set(Calendar.MINUTE, 0); 881 day.set(Calendar.SECOND, 0); 882 day.set(Calendar.MILLISECOND, 0); 883 assertEquals(day.getTimeInMillis(), 884 mScheduleCalendar.getClosestActualTime(day.getTimeInMillis(), 2, 15)); 885 886 // Check a non-skipped time after the clocks have moved forward 887 day.set(Calendar.HOUR_OF_DAY, 15); 888 day.set(Calendar.MINUTE, 25); 889 day.set(Calendar.SECOND, 0); 890 day.set(Calendar.MILLISECOND, 0); 891 assertEquals(day.getTimeInMillis(), 892 mScheduleCalendar.getClosestActualTime(day.getTimeInMillis(), 15, 25)); 893 } 894 895 @Test testClosestActualTime_otherTimeZones()896 public void testClosestActualTime_otherTimeZones() { 897 // Make sure this doesn't only work for US/Eastern time. 898 mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("Europe/London")); 899 Calendar ukDstDay = new GregorianCalendar(TimeZone.getTimeZone("Europe/London")); 900 ukDstDay.set(2021, Calendar.MARCH, 28); 901 902 // Check a skipped time, which is 01:xx on that day in the UK 903 ukDstDay.set(Calendar.HOUR_OF_DAY, 2); 904 ukDstDay.set(Calendar.MINUTE, 0); 905 ukDstDay.set(Calendar.SECOND, 0); 906 ukDstDay.set(Calendar.MILLISECOND, 0); 907 assertEquals(ukDstDay.getTimeInMillis(), 908 mScheduleCalendar.getClosestActualTime(ukDstDay.getTimeInMillis(), 1, 25)); 909 910 // Check a non-skipped time 911 ukDstDay.set(Calendar.HOUR_OF_DAY, 11); 912 ukDstDay.set(Calendar.MINUTE, 23); 913 ukDstDay.set(Calendar.SECOND, 0); 914 ukDstDay.set(Calendar.MILLISECOND, 0); 915 assertEquals(ukDstDay.getTimeInMillis(), 916 mScheduleCalendar.getClosestActualTime(ukDstDay.getTimeInMillis(), 11, 23)); 917 918 mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("Europe/Paris")); 919 Calendar frDstDay = new GregorianCalendar(TimeZone.getTimeZone("Europe/Paris")); 920 frDstDay.set(2021, Calendar.MARCH, 28); 921 922 // Check a skipped time, which is 02:xx on that day in France 923 frDstDay.set(Calendar.HOUR_OF_DAY, 3); 924 frDstDay.set(Calendar.MINUTE, 0); 925 frDstDay.set(Calendar.SECOND, 0); 926 frDstDay.set(Calendar.MILLISECOND, 0); 927 assertEquals(frDstDay.getTimeInMillis(), 928 mScheduleCalendar.getClosestActualTime(frDstDay.getTimeInMillis(), 2, 25)); 929 930 // Check a regular time 931 frDstDay.set(Calendar.HOUR_OF_DAY, 14); 932 frDstDay.set(Calendar.MINUTE, 59); 933 frDstDay.set(Calendar.SECOND, 0); 934 frDstDay.set(Calendar.MILLISECOND, 0); 935 assertEquals(frDstDay.getTimeInMillis(), 936 mScheduleCalendar.getClosestActualTime(frDstDay.getTimeInMillis(), 14, 59)); 937 } 938 getTodayDay()939 private int getTodayDay() { 940 return new GregorianCalendar().get(Calendar.DAY_OF_WEEK); 941 } 942 getTodayDay(int offset)943 private int getTodayDay(int offset) { 944 Calendar cal = new GregorianCalendar(); 945 cal.add(Calendar.DATE, offset); 946 return cal.get(Calendar.DAY_OF_WEEK); 947 } 948 949 getDaylightSavingsForwardDay()950 private Calendar getDaylightSavingsForwardDay() { 951 // the day before daylight savings rolls forward in the US - March 9, 2019 952 // 2AM March 10, 2019 does not exist -- goes straight from 1:59 to 3:00 953 // Specifically set to US/Eastern time zone rather than relying on a default time zone 954 // to make sure the date is the correct one, since DST changes vary by region. 955 Calendar daylightSavingsDay = new GregorianCalendar( 956 TimeZone.getTimeZone("America/New_York")); 957 daylightSavingsDay.set(2019, Calendar.MARCH, 9); 958 return daylightSavingsDay; 959 } 960 getDaylightSavingsBackwardDay()961 private Calendar getDaylightSavingsBackwardDay() { 962 // the day before daylight savings rolls backward in the US - November 2, 2019 963 // In this instance, 1AM November 3 2019 is repeated twice; 1:00->1:59->1:00->1:59->2:00 964 Calendar daylightSavingsDay = new GregorianCalendar( 965 TimeZone.getTimeZone("America/New_York")); 966 daylightSavingsDay.set(2019, Calendar.NOVEMBER, 2); 967 return daylightSavingsDay; 968 } 969 } 970