Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix regression: "L" last day of month specifier with short months won't be triggered #1284

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions quartz/src/main/java/org/quartz/CronExpression.java
Original file line number Diff line number Diff line change
Expand Up @@ -1601,18 +1601,22 @@ private Optional<Integer> findSmallestDay(int day, int mon, int year, TreeSet<In

final int lastDay = getLastDayOfMonth(mon, year);
// For "L", "L-1", etc.
int smallestDay = Optional.ofNullable(set.ceiling(LAST_DAY_OFFSET_END - (lastDay - day)))
final int smallestDay = Optional.ofNullable(set.ceiling(LAST_DAY_OFFSET_END - (lastDay - day)))
.map(d -> d - LAST_DAY_OFFSET_START + 1)
.orElse(Integer.MAX_VALUE);

// For "1", "2", etc.
SortedSet<Integer> st = set.subSet(day, LAST_DAY_OFFSET_START);
// make sure we don't over-run a short month, such as february
if (!st.isEmpty() && st.first() < smallestDay && st.first() <= lastDay) {
smallestDay = st.first();
return Optional.of(st.first());
}

return smallestDay == Integer.MAX_VALUE ? Optional.empty() : Optional.of(smallestDay);
if (smallestDay == Integer.MAX_VALUE) {
return Optional.empty();
} else {
return Optional.of(smallestDay + lastDay - LAST_DAY_OFFSET_START + 1);
}
}

private void readObject(java.io.ObjectInputStream stream)
Expand Down
192 changes: 192 additions & 0 deletions quartz/src/test/java/org/quartz/CronExpressionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ void testIsSatisfiedBy() throws Exception {
cal.set(2005, Calendar.JUNE, 1, 10, 15, 0);
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(Calendar.DAY_OF_MONTH, 30);
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(Calendar.YEAR, 2006);
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

Expand All @@ -99,6 +102,147 @@ void testIsSatisfiedBy() throws Exception {
cal = Calendar.getInstance();
cal.set(2005, Calendar.JUNE, 1, 10, 14, 0);
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

// Test for March
cal = Calendar.getInstance();
cal.set(2005, Calendar.MARCH, 1, 10, 15, 0);
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(Calendar.DAY_OF_MONTH, 31);
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal = Calendar.getInstance();
cal.set(2005, Calendar.MARCH, 1, 10, 16, 0);
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal = Calendar.getInstance();
cal.set(2005, Calendar.MARCH, 1, 10, 14, 0);
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

// Test for February
cal = Calendar.getInstance();
cal.set(2005, Calendar.FEBRUARY, 1, 10, 15, 0);
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(Calendar.DAY_OF_MONTH, 28);
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal = Calendar.getInstance();
cal.set(2005, Calendar.FEBRUARY, 1, 10, 16, 0);
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal = Calendar.getInstance();
cal.set(2005, Calendar.FEBRUARY, 1, 10, 14, 0);
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

// Test specific day of month
cronExpression = new CronExpression("0 15 10 12 * ? 2005");
cal.set(2005, Calendar.DECEMBER, 12, 10, 15, 0);
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2005, Calendar.DECEMBER, 11, 10, 15, 0);
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2005, Calendar.DECEMBER, 13, 10, 15, 0);
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));
}

@Test
void testIsSatisfiedByLastDayOfMonth() throws Exception {
Calendar cal = Calendar.getInstance();

// Test months with 31 days
CronExpression cronExpression = new CronExpression("0 15 10 L * ? 2005");
cal.set(2005, Calendar.DECEMBER, 31, 10, 15, 0); // December has 31 days
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2005, Calendar.DECEMBER, 30, 10, 15, 0); // Not the last day
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

// Test months with 30 days
cronExpression = new CronExpression("0 15 10 L * ? 2005");
cal.set(2005, Calendar.SEPTEMBER, 30, 10, 15, 0); // September has 30 days
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2005, Calendar.SEPTEMBER, 29, 10, 15, 0); // Not the last day
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

// Test February (non-leap year)
cronExpression = new CronExpression("0 15 10 L 2 ? 2005");
cal.set(2005, Calendar.FEBRUARY, 28, 10, 15, 0);
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2005, Calendar.FEBRUARY, 27, 10, 15, 0); // Not the last day
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

// Test February (leap year)
cronExpression = new CronExpression("0 15 10 L 2 ? 2004");
cal.set(2004, Calendar.FEBRUARY, 29, 10, 15, 0);
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2004, Calendar.FEBRUARY, 28, 10, 15, 0); // Not the last day
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));
}

@Test
void testIsSatisfiedByLastDayOfMonthWithOffset() throws Exception {
Calendar cal = Calendar.getInstance();

// Test months with 31 days
CronExpression cronExpression = new CronExpression("0 15 10 L-2 * ? 2005");
cal.set(2005, Calendar.DECEMBER, 29, 10, 15, 0); // December has 31 days, L-2 = 29th
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2005, Calendar.DECEMBER, 28, 10, 15, 0); // Not L-2
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2005, Calendar.DECEMBER, 30, 10, 15, 0); // Not L-2
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2005, Calendar.DECEMBER, 31, 10, 15, 0); // Not L-2
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

// Test months with 30 days
cronExpression = new CronExpression("0 15 10 L-1 * ? 2005");
cal.set(2005, Calendar.SEPTEMBER, 29, 10, 15, 0); // September has 30 days, L-1 = 29th
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2005, Calendar.SEPTEMBER, 27, 10, 15, 0); // Not L-1
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2005, Calendar.SEPTEMBER, 28, 10, 15, 0); // Not L-1
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2005, Calendar.SEPTEMBER, 30, 10, 15, 0); // Not L-1
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

// Test February (non-leap year)
cronExpression = new CronExpression("0 15 10 L-3 2 ? 2005");
cal.set(2005, Calendar.FEBRUARY, 25, 10, 15, 0); // February has 28 days in 2005, L-3 = 25th
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2005, Calendar.FEBRUARY, 24, 10, 15, 0); // Not L-3
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2005, Calendar.FEBRUARY, 26, 10, 15, 0); // Not L-3
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2005, Calendar.FEBRUARY, 28, 10, 15, 0); // Not L-3
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

// Test February (leap year)
cronExpression = new CronExpression("0 15 10 L-3 2 ? 2000");
cal.set(2000, Calendar.FEBRUARY, 26, 10, 15, 0); // February has 29 days in 2000, L-3 = 26th
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2000, Calendar.FEBRUARY, 25, 10, 15, 0); // Not L-3
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2000, Calendar.FEBRUARY, 27, 10, 15, 0); // Not L-3
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2000, Calendar.FEBRUARY, 29, 10, 15, 0); // Not L-3
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));
}

@Test
Expand All @@ -113,11 +257,38 @@ void testLastDayOffset() throws Exception {
cal.set(2010, Calendar.OCTOBER, 28, 10, 15, 0);
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.FEBRUARY, 26, 10, 15, 0); // last day - 2 for February
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.FEBRUARY, 25, 10, 15, 0);
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.JUNE, 28, 10, 15, 0); // last day - 2 for June
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.JUNE, 27, 10, 15, 0);
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cronExpression = new CronExpression("0 15 10 L-5W * ? 2010");

cal.set(2010, Calendar.OCTOBER, 26, 10, 15, 0); // last day - 5
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.OCTOBER, 25, 10, 15, 0); // not last day - 5
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.OCTOBER, 27, 10, 15, 0); // not last day - 5
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.SEPTEMBER, 24, 10, 15, 0); // last day - 5 (September has 30 days)
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.SEPTEMBER, 23, 10, 15, 0); // not last day - 5
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.SEPTEMBER, 25, 10, 15, 0); // not last day - 5
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cronExpression = new CronExpression("0 15 10 L-1 * ? 2010");

cal.set(2010, Calendar.OCTOBER, 30, 10, 15, 0); // last day - 1
Expand All @@ -139,12 +310,28 @@ void testLastDayOffset() throws Exception {
cal.set(2010, Calendar.OCTOBER, 30, 10, 15, 0);
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.FEBRUARY, 1, 10, 15, 0);
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.FEBRUARY, 28, 10, 15, 0);
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.FEBRUARY, 27, 10, 15, 0);
assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));

cronExpression = new CronExpression("0 15 10 L-1W,L-1 * ? 2010");

cal.set(2010, Calendar.OCTOBER, 29, 10, 15, 0); // nearest weekday to last day - 1 (29th is a friday in 2010)
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.OCTOBER, 30, 10, 15, 0); // last day - 1
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.FEBRUARY, 26, 10, 15, 0); // nearest weekday to last day - 1 (26th is a friday in 2010)
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.FEBRUARY, 27, 10, 15, 0); // last day - 1
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cronExpression = new CronExpression("0 15 10 2W,16 * ? 2010");

Expand All @@ -157,6 +344,11 @@ void testLastDayOffset() throws Exception {
cal.set(2010, Calendar.OCTOBER, 16, 10, 15, 0);
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.NOVEMBER, 2, 10, 15, 0); // 2nd is a Tuesday in November 2010
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));

cal.set(2010, Calendar.NOVEMBER, 16, 10, 15, 0);
assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));
}

/*
Expand Down