From dbc25076f7673ad07648b8528b511880e7ed980d Mon Sep 17 00:00:00 2001 From: Nathan Rauh Date: Mon, 24 Apr 2023 09:18:27 -0500 Subject: [PATCH] Issue #274 ArrayIndexOutOfBoundsException from DST handling code Signed-off-by: Nathan Rauh --- .../enterprise/concurrent/CronTrigger.java | 19 +++-- .../concurrent/CronTriggerTest.java | 84 ++++++++++++++++++- 2 files changed, 95 insertions(+), 8 deletions(-) diff --git a/api/src/main/java/jakarta/enterprise/concurrent/CronTrigger.java b/api/src/main/java/jakarta/enterprise/concurrent/CronTrigger.java index ae00c93e..c7621a49 100644 --- a/api/src/main/java/jakarta/enterprise/concurrent/CronTrigger.java +++ b/api/src/main/java/jakarta/enterprise/concurrent/CronTrigger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -501,12 +501,17 @@ private ZonedDateTime nextDayOfMonth(final int d, final int l, final int m, */ private ZonedDateTime nextHour(final int h, final int d, final int l, final int dayOfMonth, final int m, final int year, final ZonedDateTime time) { - ZonedDateTime dst = ZonedDateTime.of(year, months[m], dayOfMonth, - hours[h], minutes[0], seconds[0], 0, time.getZone()); - ZonedDateTime std = dst.plusHours(1); - if (dst.getHour() == std.getHour() && time.isAfter(dst) && time.isBefore(std)) { - return std; // Daylight Saving Time --> Standard Time - } else if (h + 1 < hours.length) { + // Determine if the same hour can be kept due to transition from Daylight Saving Time to Standard Time: + if (h >= 0) { + ZonedDateTime dst = ZonedDateTime.of(year, months[m], dayOfMonth, + hours[h], minutes[0], seconds[0], 0, time.getZone()); + ZonedDateTime std = dst.plusHours(1); + if (dst.getHour() == std.getHour() && time.isAfter(dst) && time.isBefore(std)) { + return std; // Daylight Saving Time --> Standard Time + } + } + + if (h + 1 < hours.length) { return ZonedDateTime.of(year, months[m], dayOfMonth, hours[h + 1], minutes[0], seconds[0], 0, time.getZone()); } else { diff --git a/api/src/test/java/jakarta/enterprise/concurrent/CronTriggerTest.java b/api/src/test/java/jakarta/enterprise/concurrent/CronTriggerTest.java index 484aa598..fc14673f 100644 --- a/api/src/test/java/jakarta/enterprise/concurrent/CronTriggerTest.java +++ b/api/src/test/java/jakarta/enterprise/concurrent/CronTriggerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Contributors to the Eclipse Foundation + * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -22,6 +22,7 @@ import java.time.Month; import java.time.ZonedDateTime; import java.time.ZoneId; +import java.time.ZoneOffset; import org.junit.Test; @@ -316,6 +317,87 @@ public void testCronTriggerJavaDocExample() { assertEquals(ZonedDateTime.of(2021, 9, 30, 10, 0, 0, 0, zone), time); // last } + /** + * Verify that a cron trigger that runs daily at noon can be properly applied to all hours of the day. + */ + @Test + public void testDailyAtNoon() { + CronTrigger trigger = new CronTrigger("0 12 * * *", ZoneOffset.UTC); + ZonedDateTime next; + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 0, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 22, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 1, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 22, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 2, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 22, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 3, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 22, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 4, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 22, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 5, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 22, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 6, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 22, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 7, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 22, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 8, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 22, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 9, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 22, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 10, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 22, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 11, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 22, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 12, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 22, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 13, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 23, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 14, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 23, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 15, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 23, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 16, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 23, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 17, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 23, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 18, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 23, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 19, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 23, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 20, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 23, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 21, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 23, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 22, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 23, 12, 0, 0, 0, ZoneOffset.UTC), next); + + next = trigger.getNextRunTime(null, ZonedDateTime.of(2023, 4, 22, 23, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(ZonedDateTime.of(2023, 4, 23, 12, 0, 0, 0, ZoneOffset.UTC), next); + } + /** * Specify daysOfMonth as a cron expression. */