Browse Source

Consider current date in "1W" cron expressions

Prior to this commit, the QuartzCronField::weekdayNearestTo would elapse
until the next month before checking if the current day matched.

After this commit, the current day is checked before we elapse until
the next month.

Closes gh-27966
pull/28119/head
Arjen Poutsma 3 years ago
parent
commit
8f9a1cdc0c
  1. 39
      spring-context/src/main/java/org/springframework/scheduling/support/QuartzCronField.java
  2. 25
      spring-context/src/test/java/org/springframework/scheduling/support/CronExpressionTests.java

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

@ -251,43 +251,46 @@ final class QuartzCronField extends CronField {
private static TemporalAdjuster weekdayNearestTo(int dayOfMonth) { private static TemporalAdjuster weekdayNearestTo(int dayOfMonth) {
return temporal -> { return temporal -> {
int current = Type.DAY_OF_MONTH.get(temporal); int current = Type.DAY_OF_MONTH.get(temporal);
int dayOfWeek = temporal.get(ChronoField.DAY_OF_WEEK); DayOfWeek dayOfWeek = DayOfWeek.from(temporal);
if ((current == dayOfMonth && dayOfWeek < 6) || // dayOfMonth is a weekday if ((current == dayOfMonth && isWeekday(dayOfWeek)) || // dayOfMonth is a weekday
(dayOfWeek == 5 && current == dayOfMonth - 1) || // dayOfMonth is a Saturday, so Friday before (dayOfWeek == DayOfWeek.FRIDAY && current == dayOfMonth - 1) || // dayOfMonth is a Saturday, so Friday before
(dayOfWeek == 1 && current == dayOfMonth + 1) || // dayOfMonth is a Sunday, so Monday after (dayOfWeek == DayOfWeek.MONDAY && current == dayOfMonth + 1) || // dayOfMonth is a Sunday, so Monday after
(dayOfWeek == 1 && dayOfMonth == 1 && current == 3)) { // dayOfMonth is the 1st, so Monday 3rd (dayOfWeek == DayOfWeek.MONDAY && dayOfMonth == 1 && current == 3)) { // dayOfMonth is Saturday 1st, so Monday 3rd
return temporal; return temporal;
} }
int count = 0; int count = 0;
while (count++ < CronExpression.MAX_ATTEMPTS) { while (count++ < CronExpression.MAX_ATTEMPTS) {
temporal = Type.DAY_OF_MONTH.elapseUntil(cast(temporal), dayOfMonth);
temporal = atMidnight().adjustInto(temporal);
current = Type.DAY_OF_MONTH.get(temporal);
if (current == dayOfMonth) { if (current == dayOfMonth) {
dayOfWeek = temporal.get(ChronoField.DAY_OF_WEEK); dayOfWeek = DayOfWeek.from(temporal);
if (dayOfWeek == 6) { // Saturday if (dayOfWeek == DayOfWeek.SATURDAY) {
if (dayOfMonth != 1) { if (dayOfMonth != 1) {
return temporal.minus(1, ChronoUnit.DAYS); temporal = temporal.minus(1, ChronoUnit.DAYS);
} }
else { else {
// exception for "1W" fields: execute on nearest Monday // exception for "1W" fields: execute on next Monday
return temporal.plus(2, ChronoUnit.DAYS); temporal = temporal.plus(2, ChronoUnit.DAYS);
} }
} }
else if (dayOfWeek == 7) { // Sunday else if (dayOfWeek == DayOfWeek.SUNDAY) {
return temporal.plus(1, ChronoUnit.DAYS); temporal = temporal.plus(1, ChronoUnit.DAYS);
}
else {
return temporal;
} }
return atMidnight().adjustInto(temporal);
}
else {
temporal = Type.DAY_OF_MONTH.elapseUntil(cast(temporal), dayOfMonth);
current = Type.DAY_OF_MONTH.get(temporal);
} }
} }
return null; return null;
}; };
} }
private static boolean isWeekday(DayOfWeek dayOfWeek) {
return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY;
}
/** /**
* Return a temporal adjuster that finds the last of the given doy-of-week * Return a temporal adjuster that finds the last of the given doy-of-week
* in a month. * in a month.

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

@ -16,13 +16,13 @@
package org.springframework.scheduling.support; package org.springframework.scheduling.support;
import java.time.DayOfWeek;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.Year; import java.time.Year;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.temporal.ChronoField;
import java.time.temporal.Temporal; import java.time.temporal.Temporal;
import org.assertj.core.api.Condition; import org.assertj.core.api.Condition;
@ -30,6 +30,7 @@ import org.junit.jupiter.api.Test;
import static java.time.DayOfWeek.FRIDAY; import static java.time.DayOfWeek.FRIDAY;
import static java.time.DayOfWeek.MONDAY; import static java.time.DayOfWeek.MONDAY;
import static java.time.DayOfWeek.SATURDAY;
import static java.time.DayOfWeek.SUNDAY; import static java.time.DayOfWeek.SUNDAY;
import static java.time.DayOfWeek.THURSDAY; import static java.time.DayOfWeek.THURSDAY;
import static java.time.DayOfWeek.TUESDAY; import static java.time.DayOfWeek.TUESDAY;
@ -46,8 +47,8 @@ class CronExpressionTests {
@Override @Override
public boolean matches(Temporal value) { public boolean matches(Temporal value) {
int dayOfWeek = value.get(ChronoField.DAY_OF_WEEK); DayOfWeek dayOfWeek = DayOfWeek.from(value);
return dayOfWeek != 6 && dayOfWeek != 7; return dayOfWeek != SATURDAY && dayOfWeek != SUNDAY;
} }
}; };
@ -958,6 +959,24 @@ class CronExpressionTests {
assertThat(actual).isNotNull(); assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected); assertThat(actual).isEqualTo(expected);
assertThat(actual).is(weekday); assertThat(actual).is(weekday);
last = LocalDateTime.of(2022, 1, 1, 0, 0);
assertThat(last.getDayOfWeek()).isEqualTo(SATURDAY);
expected = LocalDateTime.of(2022, 1, 3, 0, 0);
assertThat(expected.getDayOfWeek()).isEqualTo(MONDAY);
actual = expression.next(last);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
assertThat(actual).is(weekday);
last = LocalDateTime.of(2021, 8, 1, 0,0);
assertThat(last.getDayOfWeek()).isEqualTo(SUNDAY);
expected = LocalDateTime.of(2021, 8, 2, 0, 0);
assertThat(expected.getDayOfWeek()).isEqualTo(MONDAY);
actual = expression.next(last);
assertThat(actual).isNotNull();
assertThat(actual).isEqualTo(expected);
assertThat(actual).is(weekday);
} }
@Test @Test

Loading…
Cancel
Save