|
|
|
|
@ -1,4 +1,22 @@
|
|
|
|
|
package com.xxl.job.admin.scheduler.cron;
|
|
|
|
|
/*
|
|
|
|
|
* All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved.
|
|
|
|
|
* Copyright IBM Corp. 2024, 2025
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
|
|
|
* use this file except in compliance with the License. You may obtain a copy
|
|
|
|
|
* of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
|
* under the License.
|
|
|
|
|
*
|
|
|
|
|
* Borrowed from quartz v2.5.2
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import java.io.Serializable;
|
|
|
|
|
import java.text.ParseException;
|
|
|
|
|
@ -63,7 +81,7 @@ import java.util.TreeSet;
|
|
|
|
|
* <tr>
|
|
|
|
|
* <td><code>Month</code></td>
|
|
|
|
|
* <td> </td>
|
|
|
|
|
* <td><code>0-11 or JAN-DEC</code></td>
|
|
|
|
|
* <td><code>1-12 or JAN-DEC</code></td>
|
|
|
|
|
* <td> </td>
|
|
|
|
|
* <td><code>, - * /</code></td>
|
|
|
|
|
* </tr>
|
|
|
|
|
@ -186,8 +204,6 @@ import java.util.TreeSet;
|
|
|
|
|
* @author Sharada Jambula, James House
|
|
|
|
|
* @author Contributions from Mads Henderson
|
|
|
|
|
* @author Refactoring from CronTrigger to CronExpression by Aaron Craven
|
|
|
|
|
*
|
|
|
|
|
* Borrowed from quartz v2.5.0
|
|
|
|
|
*/
|
|
|
|
|
public final class CronExpression implements Serializable, Cloneable {
|
|
|
|
|
|
|
|
|
|
@ -649,6 +665,10 @@ public final class CronExpression implements Serializable, Cloneable {
|
|
|
|
|
addToSet(ALL_SPEC_INT, -1, incr, type);
|
|
|
|
|
return i;
|
|
|
|
|
} else if (c == 'L') {
|
|
|
|
|
|
|
|
|
|
if(type < DAY_OF_MONTH)
|
|
|
|
|
throw new ParseException("'L' not expected in seconds, minutes or hours fields.", i);
|
|
|
|
|
|
|
|
|
|
i++;
|
|
|
|
|
if (type == DAY_OF_WEEK) {
|
|
|
|
|
addToSet(7, 7, 0, type);
|
|
|
|
|
@ -705,15 +725,15 @@ public final class CronExpression implements Serializable, Cloneable {
|
|
|
|
|
|
|
|
|
|
private void checkIncrementRange(int incr, int type, int idxPos) throws ParseException {
|
|
|
|
|
if (incr > 59 && (type == SECOND || type == MINUTE)) {
|
|
|
|
|
throw new ParseException("Increment > 60 : " + incr, idxPos);
|
|
|
|
|
throw new ParseException("Increment >= 60 : " + incr, idxPos);
|
|
|
|
|
} else if (incr > 23 && (type == HOUR)) {
|
|
|
|
|
throw new ParseException("Increment > 24 : " + incr, idxPos);
|
|
|
|
|
throw new ParseException("Increment >= 24 : " + incr, idxPos);
|
|
|
|
|
} else if (incr > 31 && (type == DAY_OF_MONTH)) {
|
|
|
|
|
throw new ParseException("Increment > 31 : " + incr, idxPos);
|
|
|
|
|
throw new ParseException("Increment >= 31 : " + incr, idxPos);
|
|
|
|
|
} else if (incr > 7 && (type == DAY_OF_WEEK)) {
|
|
|
|
|
throw new ParseException("Increment > 7 : " + incr, idxPos);
|
|
|
|
|
throw new ParseException("Increment >= 7 : " + incr, idxPos);
|
|
|
|
|
} else if (incr > 12 && (type == MONTH)) {
|
|
|
|
|
throw new ParseException("Increment > 12 : " + incr, idxPos);
|
|
|
|
|
throw new ParseException("Increment >= 12 : " + incr, idxPos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -1293,15 +1313,20 @@ public final class CronExpression implements Serializable, Cloneable {
|
|
|
|
|
day = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean needAdvance = false;
|
|
|
|
|
if (smallestDay.isPresent()) {
|
|
|
|
|
if (day == -1 || smallestDay.get() < day) {
|
|
|
|
|
day = smallestDay.get();
|
|
|
|
|
needAdvance = true;
|
|
|
|
|
}
|
|
|
|
|
} else if (day == -1) {
|
|
|
|
|
day = 1;
|
|
|
|
|
mon++;
|
|
|
|
|
needAdvance = true;
|
|
|
|
|
}
|
|
|
|
|
if (day != t || mon != tmon) {
|
|
|
|
|
|
|
|
|
|
if (needAdvance && (day != t || mon != tmon)) {
|
|
|
|
|
cl.set(Calendar.SECOND, 0);
|
|
|
|
|
cl.set(Calendar.MINUTE, 0);
|
|
|
|
|
cl.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
|
|
@ -1311,6 +1336,7 @@ public final class CronExpression implements Serializable, Cloneable {
|
|
|
|
|
// are 1-based
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (dayOfWSpec && !dayOfMSpec) { // get day by day of week rule
|
|
|
|
|
if (lastDayOfWeek) { // are we looking for the last XXX day of
|
|
|
|
|
// the month?
|
|
|
|
|
@ -1523,12 +1549,53 @@ public final class CronExpression implements Serializable, Cloneable {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* NOT YET IMPLEMENTED: Returns the time before the given time
|
|
|
|
|
* Returns the time before the given time
|
|
|
|
|
* that the <code>CronExpression</code> matches.
|
|
|
|
|
*
|
|
|
|
|
* @param endTime a time for which the previous
|
|
|
|
|
* matching time is returned
|
|
|
|
|
* @return the previous matching time before the given end time,
|
|
|
|
|
* or null if there are no previous matching times
|
|
|
|
|
*/
|
|
|
|
|
public Date getTimeBefore(Date endTime) {
|
|
|
|
|
// FUTURE_TODO: implement QUARTZ-423
|
|
|
|
|
return null;
|
|
|
|
|
// the current implementation is not a direct calculation, but rather
|
|
|
|
|
// uses getTimeAfter with a binary search to find the previous match time
|
|
|
|
|
long end = endTime.getTime();
|
|
|
|
|
long min = 0; // the epoch date is the minimum supported by this class
|
|
|
|
|
long max = end;
|
|
|
|
|
// check if it's satisfiable at all
|
|
|
|
|
Date date = new Date(min);
|
|
|
|
|
Date after = getTimeAfter(date);
|
|
|
|
|
if (after == null || after.getTime() >= end)
|
|
|
|
|
return null; // there are no after-times before end
|
|
|
|
|
// from this point forward min's time-after is always less than end,
|
|
|
|
|
// and max's time-after is always equal to or greater than end
|
|
|
|
|
// so we just need to shrink the interval until they meet.
|
|
|
|
|
// optimization - perform inverse binary search to find a tighter lower bound
|
|
|
|
|
long interval = 60 * 60 * 1000; // start with a reasonable interval
|
|
|
|
|
while (interval < max) {
|
|
|
|
|
date.setTime(max - interval);
|
|
|
|
|
after = getTimeAfter(date);
|
|
|
|
|
if (after != null && after.getTime() < max) {
|
|
|
|
|
min = date.getTime(); // found a closer min
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
interval *= 2;
|
|
|
|
|
}
|
|
|
|
|
// perform a regular binary search to find the earliest moment
|
|
|
|
|
// whose time-after is equal to or greater than the end time -
|
|
|
|
|
// this moment is the previous match time itself
|
|
|
|
|
while (max - min > 1000) { // we can stop at 1 second resolution
|
|
|
|
|
long mid = (min + max) >>> 1;
|
|
|
|
|
date.setTime(mid);
|
|
|
|
|
after = getTimeAfter(date);
|
|
|
|
|
if (after != null && after.getTime() < end)
|
|
|
|
|
min = mid;
|
|
|
|
|
else
|
|
|
|
|
max = mid;
|
|
|
|
|
}
|
|
|
|
|
date.setTime(max - max % 1000); // round to second
|
|
|
|
|
return date;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -1585,7 +1652,7 @@ public final class CronExpression implements Serializable, Cloneable {
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
@ -1593,10 +1660,14 @@ public final class CronExpression implements Serializable, Cloneable {
|
|
|
|
|
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)
|
|
|
|
|
|