Browse Source

Various bug fixes in CronExpression

This commit makes various bug fixes in CronExpression and related files.

Closes gh-27136
pull/27151/head
Arjen Poutsma 3 years ago
parent
commit
76b1c0f1fc
  1. 3
      spring-context/src/main/java/org/springframework/scheduling/support/CronExpression.java
  2. 9
      spring-context/src/main/java/org/springframework/scheduling/support/CronField.java
  3. 3
      spring-context/src/main/java/org/springframework/scheduling/support/QuartzCronField.java
  4. 15
      spring-context/src/test/java/org/springframework/scheduling/support/CronExpressionTests.java

3
spring-context/src/main/java/org/springframework/scheduling/support/CronExpression.java

@ -66,8 +66,9 @@ public final class CronExpression { @@ -66,8 +66,9 @@ public final class CronExpression {
CronField daysOfWeek,
String expression) {
// reverse order, to make big changes first
// to make sure we end up at 0 nanos, we add an extra field
this.fields = new CronField[]{CronField.zeroNanos(), seconds, minutes, hours, daysOfMonth, months, daysOfWeek};
this.fields = new CronField[]{daysOfWeek, months, daysOfMonth, hours, minutes, seconds, CronField.zeroNanos()};
this.expression = expression;
}

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

@ -230,9 +230,7 @@ abstract class CronField { @@ -230,9 +230,7 @@ abstract class CronField {
* Elapse the given temporal for the difference between the current
* value of this field and the goal value. Typically, the returned
* temporal will have the given goal as the current value for this type,
* but this is not the case for {@link #DAY_OF_MONTH}. For instance,
* if {@code goal} is 31, and {@code temporal} is April 16th,
* this method returns May 1st, because April 31st does not exist.
* but this is not the case for {@link #DAY_OF_MONTH}.
* @param temporal the temporal to elapse
* @param goal the goal value
* @param <T> the type of temporal
@ -247,8 +245,9 @@ abstract class CronField { @@ -247,8 +245,9 @@ abstract class CronField {
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);
// goal is invalid, eg. 29th Feb, so roll forward
long amount = range.getMaximum() - current + 1;
return this.field.getBaseUnit().addTo(temporal, amount);
}
}
else {

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

@ -334,6 +334,9 @@ final class QuartzCronField extends CronField { @@ -334,6 +334,9 @@ final class QuartzCronField extends CronField {
// We ended up before the start, roll forward and try again
temporal = this.rollForwardType.rollForward(temporal);
result = adjust(temporal);
if (result != null) {
result = type().reset(result);
}
}
}
return result;

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

@ -1319,6 +1319,21 @@ class CronExpressionTests { @@ -1319,6 +1319,21 @@ class CronExpressionTests {
assertThat(actual).isEqualTo(expected);
}
@Test
public void various() {
CronExpression cronExpression = CronExpression.parse("3-57 13-28 17,18 1,15 3-12 6#1");
LocalDateTime last = LocalDateTime.of(2022, 9, 15, 17, 44, 11);
LocalDateTime expected = LocalDateTime.of(2022, 10, 1, 17, 13, 3);
LocalDateTime actual = cronExpression.next(last);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
cronExpression = CronExpression.parse("*/28 56 22 */6 * *");
last = LocalDateTime.of(2022, 2, 27, 8, 0, 42);
expected = LocalDateTime.of(2022, 3, 1, 22, 56, 0);
actual = cronExpression.next(last);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
}
}

Loading…
Cancel
Save