I believe a found a bug in how CronExpression
handles Quartz weekday of month expressions.
The cron expression 0 0 0 1W * ?
to my understanding should match the weekday (Monday-Friday) that is closest to the first day of a month. For example 2021-08-01 is a Sunday, so #next
should to my understanding produce 2021-08-02 the first weekday on the month of August in the year 2021. However it produces 2021-09-01.
In addition the Quartz documentation says:
However if you specify “1W” as the value for day-of-month, and the 1st is a Saturday, the trigger will fire on Monday the 3rd, as it will not ‘jump’ over the boundary of a month’s days.
If we check this very specific scenario with 2022-01-01 which is a Saturday we would expect #next
to produce 2022-01-03 but it produces 2022-02-01, "jumping" over the boundary of a month’s days.
Here are two tests for these scenarios
class WeekdayOfMonthQuartzExpressionTests {
@Test
void firstWeekdayOfMonth_fromSaturday() {
CronExpression firstWeekdayOfMonth = CronExpression.parse("0 0 0 1W * ?");
LocalDate saturday = LocalDate.of(2022, 1, 1);
assertSame(DayOfWeek.SATURDAY, saturday.getDayOfWeek());
LocalDate monday = LocalDate.of(2022, 1, 3);
assertSame(DayOfWeek.MONDAY, monday.getDayOfWeek());
assertEquals(monday.atStartOfDay(), firstWeekdayOfMonth.next(saturday.atStartOfDay()));
}
@Test
void firstWeekdayOfMonth_fromSunday() {
CronExpression firstWeekdayOfMonth = CronExpression.parse("0 0 0 1W * ?");
LocalDate sunday = LocalDate.of(2021, 8, 1);
assertSame(DayOfWeek.SUNDAY, sunday.getDayOfWeek());
LocalDate monday = LocalDate.of(2021, 8, 2);
assertSame(DayOfWeek.MONDAY, monday.getDayOfWeek());
assertEquals(monday.atStartOfDay(), firstWeekdayOfMonth.next(sunday.atStartOfDay()));
}
}
I tested with Spring 5.3.15.
Comment From: marschall
Awesome, thanks a lot!