Closed
Description
Cron expressions with a quartz specific dayOfWeek part like 0 30 17 ? * 2#3
are not parsed correctly by Spring's CronExpression
:
2#3
means 'third Monday every month' but CronExpression
parses this as "third tuesday in month".
It looks like the parsing logic in QuartzCronField#parseDayOfWeek
is not correct:
private static DayOfWeek parseDayOfWeek(String value) {
int dayOfWeek = Integer.parseInt(value);
if (dayOfWeek == 0) {
dayOfWeek = 7; // cron is 0 based; java.time 1 based
}
try {
return DayOfWeek.of(dayOfWeek);
}...
}
This methods transforms the input-value (cron/quartz dayOfWeek) into a java.time dayOfWeek index, then returning the corresponding java.time.DayOfWeek
The problem is, that cron, quartz and java.time have different dayOfTime
indices. cron and quartz use same indices for su - fr, but Saturday differs. java.time is 1 based, beginning with Monday:
cron quartz java.time
sa 0 7 6
su 1 1 7
mo 2 2 1
tu 3 3 2
...
So the input-value has to be decremented by 1 and the overflow handled, like this:
// Offset cron/quartz => java.time:
dayOfWeek -= 1;
if (dayOfWeek < 1) {
dayOfWeek += 7;
}
Tests:
@Test
public void testDayOfWeek_7() {
// every third saturday in month.
QuartzCronField quartzCronField = QuartzCronField.parseDaysOfWeek("7#3");
LocalDateTime next = quartzCronField.nextOrSame(LocalDateTime.parse("2024-01-01T17:00:00"));
assertThat(next.getDayOfMonth()).isEqualTo(20); // expected: 2024-10-20, saturday
}
@Test
public void testDayOfWeek_0(){
// 0 is not a valid dayOfWeek for quartz, but for cron. To be tolerant for cron dayOfWeek, we handle 0 as saturday.
QuartzCronField quartzCronField = QuartzCronField.parseDaysOfWeek("0#3");
LocalDateTime next = quartzCronField.nextOrSame(LocalDateTime.parse("2024-01-01T17:00:00"));
assertThat(next.getDayOfMonth()).isEqualTo(20); // expected: 2024-10-20, saturday
}
@Test
public void testDayOfWeek_1(){
// every third sunday in month.
QuartzCronField quartzCronField = QuartzCronField.parseDaysOfWeek("1#3");
LocalDateTime next = quartzCronField.nextOrSame(LocalDateTime.parse("2024-01-01T17:00:00"));
assertThat(next.getDayOfMonth()).isEqualTo(21); // expected: 2024-10-21, sunday
}
@Test
public void testDayOfWeek_2(){
// every third monday in month.
QuartzCronField quartzCronField = QuartzCronField.parseDaysOfWeek("2#3");
LocalDateTime next = quartzCronField.nextOrSame(LocalDateTime.parse("2024-01-01T17:00:00"));
assertThat(next.getDayOfMonth()).isEqualTo(15); // expected: 2024-10-15, monday
}