Browse Source

Fix daylight saving issue in CronExpression

Closes gh-26830
pull/26846/head
Arjen Poutsma 4 years ago
parent
commit
27bfcbbc82
  1. 18
      spring-context/src/main/java/org/springframework/scheduling/support/CronField.java
  2. 8
      spring-context/src/main/java/org/springframework/scheduling/support/QuartzCronField.java
  3. 8
      spring-context/src/test/java/org/springframework/scheduling/support/CronExpressionTests.java
  4. 6
      spring-context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java

18
spring-context/src/main/java/org/springframework/scheduling/support/CronField.java

@ -157,6 +157,11 @@ abstract class CronField { @@ -157,6 +157,11 @@ abstract class CronField {
return this.type;
}
@SuppressWarnings("unchecked")
protected static <T extends Temporal & Comparable<? super T>> T cast(Temporal temporal) {
return (T) temporal;
}
/**
* Represents the type of cron field, i.e. seconds, minutes, hours,
@ -236,16 +241,17 @@ abstract class CronField { @@ -236,16 +241,17 @@ abstract class CronField {
*/
public <T extends Temporal & Comparable<? super T>> T elapseUntil(T temporal, int goal) {
int current = get(temporal);
ValueRange range = temporal.range(this.field);
if (current < goal) {
T result = this.field.getBaseUnit().addTo(temporal, goal - current);
current = get(result);
if (current > goal) { // can occur due to daylight saving, see gh-26744
result = this.field.getBaseUnit().addTo(result, goal - current);
if (range.isValidIntValue(goal)) {
return cast(temporal.with(this.field, goal));
}
else {
// goal is invalid, eg. 29th Feb, lets try to get as close as possible
return this.field.getBaseUnit().addTo(temporal, goal - current);
}
return result;
}
else {
ValueRange range = temporal.range(this.field);
long amount = goal + range.getMaximum() - current + 1 - range.getMinimum();
return this.field.getBaseUnit().addTo(temporal, amount);
}

8
spring-context/src/main/java/org/springframework/scheduling/support/QuartzCronField.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -326,12 +326,6 @@ final class QuartzCronField extends CronField { @@ -326,12 +326,6 @@ final class QuartzCronField extends CronField {
}
}
@SuppressWarnings("unchecked")
private static <T extends Temporal & Comparable<? super T>> T cast(Temporal temporal) {
return (T) temporal;
}
@Override
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
T result = adjust(temporal);

8
spring-context/src/test/java/org/springframework/scheduling/support/CronExpressionTests.java

@ -1276,6 +1276,14 @@ class CronExpressionTests { @@ -1276,6 +1276,14 @@ class CronExpressionTests {
actual = cronExpression.next(last);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
cronExpression = CronExpression.parse("0 10 2 * * *");
last = ZonedDateTime.parse("2013-03-31T01:09:00+01:00[Europe/Amsterdam]");
expected = ZonedDateTime.parse("2013-04-01T02:10:00+02:00[Europe/Amsterdam]");
actual = cronExpression.next(last);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
}

6
spring-context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java

@ -822,13 +822,13 @@ class CronTriggerTests { @@ -822,13 +822,13 @@ class CronTriggerTests {
// 2:00 AM on March 31, 2013: start of Daylight Saving Time for CET in 2013.
// Setting up last completion:
// - PST: Sun Mar 31 10:10:54 CEST 2013
// - CET: Sun Mar 31 01:10:54 CET 2013
// - PST: Sun Mar 31 10:09:54 CEST 2013
// - CET: Sun Mar 31 01:09:54 CET 2013
this.calendar.set(Calendar.DAY_OF_MONTH, 31);
this.calendar.set(Calendar.MONTH, Calendar.MARCH);
this.calendar.set(Calendar.YEAR, 2013);
this.calendar.set(Calendar.HOUR_OF_DAY, 1);
this.calendar.set(Calendar.MINUTE, 10); // changing to any minute from 0-9 causes the test to fail for CET.
this.calendar.set(Calendar.MINUTE, 9);
this.calendar.set(Calendar.SECOND, 54);
Date lastCompletionTime = this.calendar.getTime();

Loading…
Cancel
Save