Skip to content

Commit 3c9eed1

Browse files
author
David Syer
committed
SPR-6273: resolved with patch - use recursive call to reset low order bits and then re-seek match for pattern
1 parent 7b54746 commit 3c9eed1

File tree

2 files changed

+97
-18
lines changed

2 files changed

+97
-18
lines changed

org.springframework.context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ class CronSequenceGenerator {
6666

6767
private final String expression;
6868

69-
7069
/**
7170
* Construct a {@link CronSequenceGenerator} from the pattern provided.
7271
* @param expression a space-separated list of time fields
@@ -77,7 +76,6 @@ public CronSequenceGenerator(String expression) {
7776
parse(expression);
7877
}
7978

80-
8179
/**
8280
* Get the next {@link Date} in the sequence matching the Cron pattern and
8381
* after the value provided. The return value will have a whole number of
@@ -86,17 +84,45 @@ public CronSequenceGenerator(String expression) {
8684
* @return the next value matching the pattern
8785
*/
8886
public Date next(Date date) {
87+
88+
/*
89+
The plan:
90+
91+
1 Round up to the next whole second
92+
93+
2 If seconds match move on, otherwise find the next match:
94+
2.1 If next match is in the next minute then roll forwards
95+
96+
3 If minute matches move on, otherwise find the next match
97+
3.1 If next match is in the next hour then roll forwards
98+
3.2 Reset the seconds and go to 2
99+
100+
4 If hour matches move on, otherwise find the next match
101+
4.1 If next match is in the next day then roll forwards,
102+
4.2 Reset the minutes and seconds and go to 2
103+
104+
...
105+
106+
*/
107+
89108
Calendar calendar = new GregorianCalendar();
90109
calendar.setTime(date);
91110

92111
// Truncate to the next whole second
93112
calendar.add(Calendar.SECOND, 1);
94113
calendar.set(Calendar.MILLISECOND, 0);
95114

115+
doNext(calendar);
116+
117+
return calendar.getTime();
118+
}
119+
120+
private void doNext(Calendar calendar) {
96121
List<Integer> resets = new ArrayList<Integer>();
97122

98123
int second = calendar.get(Calendar.SECOND);
99-
int updateSecond = findNext(this.seconds, second, 60, calendar, Calendar.SECOND, Collections.<Integer> emptyList());
124+
List<Integer> emptyList = Collections.<Integer> emptyList();
125+
int updateSecond = findNext(this.seconds, second, 60, calendar, Calendar.SECOND, emptyList);
100126
if (second == updateSecond) {
101127
resets.add(Calendar.SECOND);
102128
}
@@ -105,29 +131,37 @@ public Date next(Date date) {
105131
int updateMinute = findNext(this.minutes, minute, 60, calendar, Calendar.MINUTE, resets);
106132
if (minute == updateMinute) {
107133
resets.add(Calendar.MINUTE);
134+
} else {
135+
doNext(calendar);
108136
}
109137

110138
int hour = calendar.get(Calendar.HOUR_OF_DAY);
111139
int updateHour = findNext(this.hours, hour, 24, calendar, Calendar.HOUR_OF_DAY, resets);
112140
if (hour == updateHour) {
113141
resets.add(Calendar.HOUR_OF_DAY);
142+
} else {
143+
doNext(calendar);
114144
}
115145

116146
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
117147
int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
118148
int updateDayOfMonth = findNextDay(calendar, this.daysOfMonth, dayOfMonth, daysOfWeek, dayOfWeek, 366, resets);
119149
if (dayOfMonth == updateDayOfMonth) {
120150
resets.add(Calendar.DAY_OF_MONTH);
151+
} else {
152+
doNext(calendar);
121153
}
122154

123155
int month = calendar.get(Calendar.MONTH);
124-
month = findNext(this.months, month, 12, calendar, Calendar.MONTH, resets);
156+
int updateMonth = findNext(this.months, month, 12, calendar, Calendar.MONTH, resets);
157+
if (month != updateMonth) {
158+
doNext(calendar);
159+
}
125160

126-
return calendar.getTime();
127161
}
128162

129-
private int findNextDay(Calendar calendar, BitSet daysOfMonth, int dayOfMonth, BitSet daysOfWeek,
130-
int dayOfWeek, int max, List<Integer> resets) {
163+
private int findNextDay(Calendar calendar, BitSet daysOfMonth, int dayOfMonth, BitSet daysOfWeek, int dayOfWeek,
164+
int max, List<Integer> resets) {
131165

132166
int count = 0;
133167
// the DAY_OF_WEEK values in java.util.Calendar start with 1 (Sunday),
@@ -180,7 +214,6 @@ private void reset(Calendar calendar, List<Integer> fields) {
180214
}
181215
}
182216

183-
184217
// Parsing logic invoked by the constructor.
185218

186219
/**
@@ -240,8 +273,7 @@ private void setNumberHits(BitSet bits, String value, int max) {
240273
// Not an incrementer so it must be a range (possibly empty)
241274
int[] range = getRange(field, max);
242275
bits.set(range[0], range[1] + 1);
243-
}
244-
else {
276+
} else {
245277
String[] split = StringUtils.delimitedListToStringArray(field, "/");
246278
if (split.length > 2) {
247279
throw new IllegalArgumentException("Incrementer has more than two fields: " + field);
@@ -267,8 +299,7 @@ private int[] getRange(String field, int max) {
267299
}
268300
if (!field.contains("-")) {
269301
result[0] = result[1] = Integer.valueOf(field);
270-
}
271-
else {
302+
} else {
272303
String[] split = StringUtils.delimitedListToStringArray(field, "-");
273304
if (split.length > 2) {
274305
throw new IllegalArgumentException("Range has more than two fields: " + field);
@@ -279,7 +310,6 @@ private int[] getRange(String field, int max) {
279310
return result;
280311
}
281312

282-
283313
@Override
284314
public boolean equals(Object obj) {
285315
if (!(obj instanceof CronSequenceGenerator)) {

org.springframework.context/src/test/java/org/springframework/scheduling/support/CronTriggerTests.java

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import java.util.GregorianCalendar;
2424

2525
import org.junit.Before;
26-
import org.junit.Ignore;
2726
import org.junit.Test;
2827
import org.springframework.scheduling.TriggerContext;
2928

@@ -316,20 +315,36 @@ public void testIncrementDayOfWeekAndRollover() throws Exception {
316315
}
317316

318317
@Test
319-
@Ignore
320318
public void testSpecificMinuteSecond() throws Exception {
321-
CronTrigger trigger = new CronTrigger("2 5 * * * *");
319+
CronTrigger trigger = new CronTrigger("55 5 * * * *");
322320
calendar.set(Calendar.MINUTE, 4);
321+
calendar.set(Calendar.SECOND, 54);
323322
Date date = calendar.getTime();
324-
calendar.add(Calendar.MINUTE, 1);
325-
calendar.set(Calendar.SECOND, 2);
326323
TriggerContext context1 = getTriggerContext(date);
324+
calendar.add(Calendar.MINUTE, 1);
325+
calendar.set(Calendar.SECOND, 55);
327326
assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1));
328327
calendar.add(Calendar.HOUR, 1);
329328
TriggerContext context2 = getTriggerContext(date);
330329
assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context2));
331330
}
332331

332+
@Test
333+
public void testSpecificHourSecond() throws Exception {
334+
CronTrigger trigger = new CronTrigger("55 * 2 * * *");
335+
calendar.set(Calendar.HOUR_OF_DAY, 1);
336+
calendar.set(Calendar.SECOND, 54);
337+
Date date = calendar.getTime();
338+
TriggerContext context1 = getTriggerContext(date);
339+
calendar.add(Calendar.HOUR_OF_DAY, 1);
340+
calendar.set(Calendar.MINUTE, 0);
341+
calendar.set(Calendar.SECOND, 55);
342+
assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1));
343+
calendar.add(Calendar.MINUTE, 1);
344+
TriggerContext context2 = getTriggerContext(date);
345+
assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context2));
346+
}
347+
333348
@Test
334349
public void testSpecificMinuteHour() throws Exception {
335350
CronTrigger trigger = new CronTrigger("* 5 10 * * *");
@@ -347,6 +362,40 @@ public void testSpecificMinuteHour() throws Exception {
347362
assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context2));
348363
}
349364

365+
@Test
366+
public void testSpecificDayOfMonthSecond() throws Exception {
367+
CronTrigger trigger = new CronTrigger("55 * * 3 * *");
368+
calendar.set(Calendar.DAY_OF_MONTH, 2);
369+
calendar.set(Calendar.SECOND, 54);
370+
Date date = calendar.getTime();
371+
TriggerContext context1 = getTriggerContext(date);
372+
calendar.add(Calendar.DAY_OF_MONTH, 1);
373+
calendar.set(Calendar.HOUR_OF_DAY, 0);
374+
calendar.set(Calendar.MINUTE, 0);
375+
calendar.set(Calendar.SECOND, 55);
376+
assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1));
377+
calendar.add(Calendar.MINUTE, 1);
378+
TriggerContext context2 = getTriggerContext(date);
379+
assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context2));
380+
}
381+
382+
@Test
383+
public void testSpecificDate() throws Exception {
384+
CronTrigger trigger = new CronTrigger("* * * 3 10 *");
385+
calendar.set(Calendar.DAY_OF_MONTH, 2);
386+
calendar.set(Calendar.MONTH, 10);
387+
Date date = calendar.getTime();
388+
TriggerContext context1 = getTriggerContext(date);
389+
calendar.add(Calendar.DAY_OF_MONTH, 1);
390+
calendar.set(Calendar.HOUR_OF_DAY, 0);
391+
calendar.set(Calendar.MINUTE, 0);
392+
calendar.set(Calendar.SECOND, 0);
393+
assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context1));
394+
calendar.add(Calendar.SECOND, 1);
395+
TriggerContext context2 = getTriggerContext(date);
396+
assertEquals(calendar.getTime(), date = trigger.nextExecutionTime(context2));
397+
}
398+
350399
@Test
351400
public void testWeekDaySequence() throws Exception {
352401
CronTrigger trigger = new CronTrigger("0 0 7 ? * MON-FRI");

0 commit comments

Comments
 (0)