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 {
CronField daysOfWeek, CronField daysOfWeek,
String expression) { String expression) {
// reverse order, to make big changes first
// to make sure we end up at 0 nanos, we add an extra field // 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; this.expression = expression;
} }

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

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

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

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

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

@ -1319,6 +1319,21 @@ class CronExpressionTests {
assertThat(actual).isEqualTo(expected); 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