Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/main/java/com/cronutils/model/time/SingleExecutionTime.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import com.cronutils.utils.VisibleForTesting;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalField;
Expand All @@ -52,7 +51,6 @@
* Calculates execution time given a cron pattern.
*/
public class SingleExecutionTime implements ExecutionTime {
private static DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
private static final int MAX_ITERATIONS = 100_000;

private static final LocalTime MAX_SECONDS = LocalTime.MAX.truncatedTo(SECONDS);
Expand Down Expand Up @@ -107,8 +105,15 @@ public Optional<ZonedDateTime> nextExecution(final ZonedDateTime date) {
if (nextMatch.equals(date)) {
nextMatch = nextClosestMatch(date.plusSeconds(1));

if(nextMatch.format(DATE_TIME_FORMATTER).equals(date.format(DATE_TIME_FORMATTER))){ // daylight saving case: issue #446
nextMatch = nextClosestMatch(date.plusSeconds(1).plusHours(1));
if (nextMatch.getOffset().compareTo(date.getOffset()) > 0) {
// daylight saving time overlap case: issue #446
ZonedDateTime nextNextExecution = nextClosestMatch(nextMatch.plusSeconds(1));

boolean lessFrequentThan1Hour = (Duration.between(nextMatch, nextNextExecution).toHours() > 1);
if (lessFrequentThan1Hour) {
// Avoid duplicate execution during DST overlap
nextMatch = nextClosestMatch(date.plusSeconds(1).plusHours(1));
}
}
}
return Optional.of(nextMatch);
Expand Down
20 changes: 10 additions & 10 deletions src/test/java/com/cronutils/Issue446Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.model.time.ExecutionTime;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.*;
import java.util.Optional;
Expand All @@ -21,6 +22,7 @@


public class Issue446Test {
private static final Logger LOGGER = LoggerFactory.getLogger(Issue446Test.class);
private static final CronDefinition definition = CronDefinitionBuilder.defineCron()
.withMinutes().and()
.withHours().and()
Expand All @@ -38,7 +40,7 @@ public void testWrongIntervalsForEvery6Months() {
LocalDateTime dayOfApril = LocalDateTime.of(2020, 4, 25, 0, 0);
Clock clock = Clock.fixed(dayOfApril.toInstant(ZoneOffset.UTC), ZoneId.systemDefault());
ZonedDateTime dayOfAprilInLocalTimezone = ZonedDateTime.now(clock);
System.out.println("now: " + dayOfAprilInLocalTimezone);
LOGGER.info("now: " + dayOfAprilInLocalTimezone);
Cron cron = getEveryMonthFromNow(dayOfAprilInLocalTimezone, 6).instance();

ZonedDateTime nextRun = nextRun(cron, dayOfAprilInLocalTimezone); // first run
Expand Down Expand Up @@ -69,7 +71,7 @@ public void testDaylightSavingOverlapMinuteNextRun() {
LocalDateTime daylightSaving2020 = LocalDateTime.of(2020, 10, 25, 1, 10);
Clock clock = Clock.fixed(daylightSaving2020.toInstant(ZoneOffset.ofHours(2)),ZoneId.of("Europe/Rome"));
ZonedDateTime daylightSaving2020InLocalTimezone = ZonedDateTime.now(clock);
System.out.println("\nnow: " + daylightSaving2020InLocalTimezone);
LOGGER.info("\nnow: " + daylightSaving2020InLocalTimezone);
Cron cron = getEvery30Minute(daylightSaving2020InLocalTimezone).instance();

ZonedDateTime nextRun = nextRun(cron, daylightSaving2020InLocalTimezone); // first run
Expand Down Expand Up @@ -101,13 +103,11 @@ public void testDaylightSavingOverlapMinuteNextRun() {
}

@Test
@Ignore
public void testDaylightSavingOverlapHourNextRun() {

LocalDateTime startDay = LocalDateTime.of(2020, 10, 24, 1, 0); // Day before Daylight saving
Clock clock = Clock.fixed(startDay.toInstant(ZoneOffset.ofHours(2)),ZoneId.of("Europe/Rome"));
Clock clock = Clock.fixed(startDay.toInstant(ZoneOffset.ofHours(2)), ZoneId.of("Europe/Rome"));
ZonedDateTime daylightSaving2020InLocalTimezone = ZonedDateTime.now(clock);
System.out.println("\nnow: " + daylightSaving2020InLocalTimezone);
LOGGER.info("\nnow: " + daylightSaving2020InLocalTimezone);
Cron cron = getEveryHour(daylightSaving2020InLocalTimezone).instance();

ZonedDateTime nextRun = nextRun(cron, daylightSaving2020InLocalTimezone);
Expand All @@ -120,9 +120,9 @@ public void testDaylightSavingOverlapHourNextRun() {
Assert.assertEquals(ZoneOffset.ofHours(2), nextRun.getOffset());

startDay = LocalDateTime.of(2020, 10, 25, 1, 0); // Daylight saving
clock = Clock.fixed(startDay.toInstant(ZoneOffset.ofHours(2)),ZoneId.of("Europe/Rome"));
clock = Clock.fixed(startDay.toInstant(ZoneOffset.ofHours(2)), ZoneId.of("Europe/Rome"));
daylightSaving2020InLocalTimezone = ZonedDateTime.now(clock);
System.out.println("\nnow: " + daylightSaving2020InLocalTimezone);
LOGGER.info("\nnow: " + daylightSaving2020InLocalTimezone);
cron = getEveryHour(daylightSaving2020InLocalTimezone).instance();

nextRun = nextRun(cron, daylightSaving2020InLocalTimezone); // first run
Expand Down Expand Up @@ -167,7 +167,7 @@ private static ZonedDateTime nextRun(Cron cron, ZonedDateTime when) {
if (!next.isPresent()) {
fail();
}
System.out.println("Calculated next run at " + next.get());
LOGGER.info("Calculated next run at " + next.get());
return next.get();
}
}