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 {
return this.type; 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, * Represents the type of cron field, i.e. seconds, minutes, hours,
@ -236,16 +241,17 @@ abstract class CronField {
*/ */
public <T extends Temporal & Comparable<? super T>> T elapseUntil(T temporal, int goal) { public <T extends Temporal & Comparable<? super T>> T elapseUntil(T temporal, int goal) {
int current = get(temporal); int current = get(temporal);
ValueRange range = temporal.range(this.field);
if (current < goal) { if (current < goal) {
T result = this.field.getBaseUnit().addTo(temporal, goal - current); if (range.isValidIntValue(goal)) {
current = get(result); return cast(temporal.with(this.field, goal));
if (current > goal) { // can occur due to daylight saving, see gh-26744 }
result = this.field.getBaseUnit().addTo(result, goal - current); 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 { else {
ValueRange range = temporal.range(this.field);
long amount = goal + range.getMaximum() - current + 1 - range.getMinimum(); long amount = goal + range.getMaximum() - current + 1 - range.getMinimum();
return this.field.getBaseUnit().addTo(temporal, amount); return this.field.getBaseUnit().addTo(temporal, amount);
} }

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

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -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 @Override
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) { public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
T result = adjust(temporal); T result = adjust(temporal);

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

@ -1276,6 +1276,14 @@ class CronExpressionTests {
actual = cronExpression.next(last); actual = cronExpression.next(last);
assertThat(actual).isNotNull(); assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected); 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 {
// 2:00 AM on March 31, 2013: start of Daylight Saving Time for CET in 2013. // 2:00 AM on March 31, 2013: start of Daylight Saving Time for CET in 2013.
// Setting up last completion: // Setting up last completion:
// - PST: Sun Mar 31 10:10:54 CEST 2013 // - PST: Sun Mar 31 10:09:54 CEST 2013
// - CET: Sun Mar 31 01:10:54 CET 2013 // - CET: Sun Mar 31 01:09:54 CET 2013
this.calendar.set(Calendar.DAY_OF_MONTH, 31); this.calendar.set(Calendar.DAY_OF_MONTH, 31);
this.calendar.set(Calendar.MONTH, Calendar.MARCH); this.calendar.set(Calendar.MONTH, Calendar.MARCH);
this.calendar.set(Calendar.YEAR, 2013); this.calendar.set(Calendar.YEAR, 2013);
this.calendar.set(Calendar.HOUR_OF_DAY, 1); 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); this.calendar.set(Calendar.SECOND, 54);
Date lastCompletionTime = this.calendar.getTime(); Date lastCompletionTime = this.calendar.getTime();

Loading…
Cancel
Save