diff --git a/common/src/main/java/org/tron/common/cron/CronExpression.java b/common/src/main/java/org/tron/common/cron/CronExpression.java
index b656bdafb33..0445ec2ce5b 100644
--- a/common/src/main/java/org/tron/common/cron/CronExpression.java
+++ b/common/src/main/java/org/tron/common/cron/CronExpression.java
@@ -1,5 +1,6 @@
/*
* 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
@@ -22,9 +23,9 @@
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
+import java.util.Optional;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TimeZone;
@@ -35,83 +36,86 @@
* expressions provide the ability to specify complex time combinations such as
* "At 8:00am every Monday through Friday" or "At 1:30am every
* last Friday of the month".
- *
* Cron expressions are comprised of 6 required fields and one optional field
* separated by white space. The fields respectively are described as follows:
- *
- *
+ *
+ *
+ * Examples of cron expressions and their meanings.
*
- * | Field Name |
- * |
- * Allowed Values |
- * |
- * Allowed Special Characters |
+ * Field Name |
+ * |
+ * Allowed Values |
+ * |
+ * Allowed Special Characters |
*
*
- * Seconds |
- *
- * | 0-59 |
- *
- * | , - * / |
+ * Seconds |
+ * |
+ * 0-59 |
+ * |
+ * , - * / |
*
*
- * Minutes |
- *
- * | 0-59 |
- *
- * | , - * / |
+ * Minutes |
+ * |
+ * 0-59 |
+ * |
+ * , - * / |
*
*
- * Hours |
- *
- * | 0-23 |
- *
- * | , - * / |
+ * Hours |
+ * |
+ * 0-23 |
+ * |
+ * , - * / |
*
*
- * Day-of-month |
- *
- * | 1-31 |
- *
- * | , - * ? / L W |
+ * Day-of-month |
+ * |
+ * 1-31 |
+ * |
+ * , - * ? / L W |
*
*
- * Month |
- *
- * | 0-11 or JAN-DEC |
- *
- * | , - * / |
+ * Month |
+ * |
+ * 0-11 or JAN-DEC |
+ * |
+ * , - * / |
*
*
- * Day-of-Week |
- *
- * | 1-7 or SUN-SAT |
- *
- * | , - * ? / L # |
+ * Day-of-Week |
+ * |
+ * 1-7 or SUN-SAT |
+ * |
+ * , - * ? / L # |
*
*
- * Year (Optional) |
- *
- * | empty, 1970-2199 |
- *
- * | , - * / |
+ * Year (Optional) |
+ * |
+ * empty, 1970-2199 |
+ * |
+ * , - * / |
*
*
- *
+ *
* The '*' character is used to specify all values. For example, "*"
* in the minute field means "every minute".
- *
+ *
+ *
* The '?' character is allowed for the day-of-month and day-of-week fields. It
* is used to specify 'no specific value'. This is useful when you need to
* specify something in one of the two fields, but not the other.
- *
+ *
* The '-' character is used to specify ranges For example "10-12" in
* the hour field means "the hours 10, 11 and 12".
- *
+ *
* The ',' character is used to specify additional values. For example
* "MON,WED,FRI" in the day-of-week field means "the days Monday,
* Wednesday, and Friday".
- *
+ *
+ *
* The '/' character is used to specify increments. For example "0/15"
* in the seconds field means "the seconds 0, 15, 30, and 45". And
* "5/15" in the seconds field means "the seconds 5, 20, 35, and
@@ -123,7 +127,8 @@
* on every "nth" value in the given set. Thus "7/6" in the
* month field only turns on month "7", it does NOT mean every 6th
* month, please note that subtlety.
- *
+ *
+ *
* The 'L' character is allowed for the day-of-month and day-of-week fields.
* This character is short-hand for "last", but it has different
* meaning in each of the two fields. For example, the value "L" in
@@ -136,7 +141,8 @@
* from the last day of the month, such as "L-3" which would mean the third-to-last
* day of the calendar month. When using the 'L' option, it is important not to
* specify lists, or ranges of values, as you'll get confusing/unexpected results.
- *
+ *
+ *
* The 'W' character is allowed for the day-of-month field. This character
* is used to specify the weekday (Monday-Friday) nearest the given day. As an
* example, if you were to specify "15W" as the value for the
@@ -148,11 +154,13 @@
* 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. The 'W' character can only be
* specified when the day-of-month is a single day, not a range or list of days.
- *
+ *
+ *
* The 'L' and 'W' characters can also be combined for the day-of-month
* expression to yield 'LW', which translates to "last weekday of the
* month".
- *
+ *
+ *
* The '#' character is allowed for the day-of-week field. This character is
* used to specify "the nth" XXX day of the month. For example, the
* value of "6#3" in the day-of-week field means the third Friday of
@@ -163,7 +171,7 @@
* no firing will occur that month. If the '#' character is used, there can
* only be one expression in the day-of-week field ("3#1,6#3" is
* not valid, since there are two expressions).
- *
+ *
*
- *
+ *
* The legal characters and the names of months and days of the week are not
* case sensitive.
*
*
* NOTES:
+ *
*
* - Support for specifying both a day-of-week and a day-of-month value is
* not complete (you'll need to use the '?' character in one of these fields).
@@ -189,29 +198,30 @@
* interpretation CronExpression chooses. An example would be
* "0 0 14-6 ? * FRI-MON".
*
- *
*
* @author Sharada Jambula, James House
* @author Contributions from Mads Henderson
* @author Refactoring from CronTrigger to CronExpression by Aaron Craven
*/
-public final class CronExpression implements Serializable, Cloneable {
+public final class CronExpression implements Serializable {
public static final int MAX_YEAR = Calendar.getInstance().get(Calendar.YEAR) + 100;
- protected static final int SECOND = 0;
- protected static final int MINUTE = 1;
- protected static final int HOUR = 2;
- protected static final int DAY_OF_MONTH = 3;
- protected static final int MONTH = 4;
- protected static final int DAY_OF_WEEK = 5;
- protected static final int YEAR = 6;
- protected static final int ALL_SPEC_INT = 99; // '*'
- protected static final int NO_SPEC_INT = 98; // '?'
- protected static final Integer ALL_SPEC = ALL_SPEC_INT;
- protected static final Integer NO_SPEC = NO_SPEC_INT;
-
- protected static final Map monthMap = new HashMap(20);
- protected static final Map dayMap = new HashMap(60);
+ private static final int SECOND = 0;
+ private static final int MINUTE = 1;
+ private static final int HOUR = 2;
+ private static final int DAY_OF_MONTH = 3;
+ private static final int MONTH = 4;
+ private static final int DAY_OF_WEEK = 5;
+ private static final int YEAR = 6;
+ private static final int ALL_SPEC_INT = 99; // '*'
+ private static final int NO_SPEC_INT = 98; // '?'
+ private static final int MAX_LAST_DAY_OFFSET = 30;
+ private static final int LAST_DAY_OFFSET_START = 32; // "L-30"
+ private static final int LAST_DAY_OFFSET_END = LAST_DAY_OFFSET_START + MAX_LAST_DAY_OFFSET; // 'L'
+ private static final Integer ALL_SPEC = ALL_SPEC_INT;
+ private static final Integer NO_SPEC = NO_SPEC_INT;
+ private static final Map monthMap = new HashMap<>(20);
+ private static final Map dayMap = new HashMap<>(60);
private static final long serialVersionUID = 12423409423L;
static {
@@ -238,21 +248,17 @@ public final class CronExpression implements Serializable, Cloneable {
}
private final String cronExpression;
- protected transient TreeSet seconds;
- protected transient TreeSet minutes;
- protected transient TreeSet hours;
- protected transient TreeSet daysOfMonth;
- protected transient TreeSet months;
- protected transient TreeSet daysOfWeek;
- protected transient TreeSet years;
-
- protected transient boolean lastdayOfWeek = false;
- protected transient int nthdayOfWeek = 0;
- protected transient boolean lastdayOfMonth = false;
- protected transient boolean nearestWeekday = false;
- protected transient int lastdayOffset = 0;
- protected transient boolean expressionParsed = false;
private TimeZone timeZone = null;
+ private transient TreeSet seconds;
+ private transient TreeSet minutes;
+ private transient TreeSet hours;
+ private transient TreeSet daysOfMonth;
+ private transient TreeSet nearestWeekdays;
+ private transient TreeSet months;
+ private transient TreeSet daysOfWeek;
+ private transient TreeSet years;
+ private transient boolean lastDayOfWeek = false;
+ private transient int nthDayOfWeek = 0;
/**
* Constructs a new CronExpression based on the specified
@@ -261,7 +267,7 @@ public final class CronExpression implements Serializable, Cloneable {
* @param cronExpression String representation of the cron expression the
* new object should represent
* @throws java.text.ParseException if the string expression cannot be parsed into a valid
- * CronExpression
+ * CronExpression
*/
public CronExpression(String cronExpression) throws ParseException {
if (cronExpression == null) {
@@ -273,29 +279,6 @@ public CronExpression(String cronExpression) throws ParseException {
buildExpression(this.cronExpression);
}
- /**
- * Constructs a new {@code CronExpression} as a copy of an existing
- * instance.
- *
- * @param expression The existing cron expression to be copied
- */
- public CronExpression(CronExpression expression) {
- /*
- * We don't call the other constructor here since we need to swallow the
- * ParseException. We also elide some of the sanity checking as it is
- * not logically trippable.
- */
- this.cronExpression = expression.getCronExpression();
- try {
- buildExpression(cronExpression);
- } catch (ParseException ex) {
- throw new AssertionError();
- }
- if (expression.getTimeZone() != null) {
- setTimeZone((TimeZone) expression.getTimeZone().clone());
- }
- }
-
/**
* Indicates whether the specified cron expression can be parsed into a
* valid cron expression
@@ -305,18 +288,15 @@ public CronExpression(CronExpression expression) {
* expression
*/
public static boolean isValidExpression(String cronExpression) {
-
try {
- new CronExpression(cronExpression);
+ validateExpression(cronExpression);
} catch (ParseException pe) {
return false;
}
-
return true;
- }
+ }
public static void validateExpression(String cronExpression) throws ParseException {
-
new CronExpression(cronExpression);
}
@@ -327,7 +307,7 @@ public static void validateExpression(String cronExpression) throws ParseExcepti
*
* @param date the date to evaluate
* @return a boolean indicating whether the given date satisfies the cron
- * expression
+ * expression
*/
public boolean isSatisfiedBy(Date date) {
Calendar testDateCal = Calendar.getInstance(getTimeZone());
@@ -354,45 +334,6 @@ public Date getNextValidTimeAfter(Date date) {
return getTimeAfter(date);
}
- /**
- * Returns the next date/time after the given date/time which does
- * not satisfy the expression
- *
- * @param date the date/time at which to begin the search for the next
- * invalid date/time
- * @return the next valid date/time
- */
- public Date getNextInvalidTimeAfter(Date date) {
- long difference = 1000;
-
- //move back to the nearest second so differences will be accurate
- Calendar adjustCal = Calendar.getInstance(getTimeZone());
- adjustCal.setTime(date);
- adjustCal.set(Calendar.MILLISECOND, 0);
- Date lastDate = adjustCal.getTime();
-
- Date newDate;
-
- //FUTURE_TODO: (QUARTZ-481) IMPROVE THIS! The following is a BAD solution to this problem. Performance will be very bad here, depending on the cron expression. It is, however A solution.
-
- //keep getting the next included time until it's farther than one second
- // apart. At that point, lastDate is the last valid fire time. We return
- // the second immediately following it.
- while (difference == 1000) {
- newDate = getTimeAfter(lastDate);
- if (newDate == null)
- break;
-
- difference = newDate.getTime() - lastDate.getTime();
-
- if (difference == 1000) {
- lastDate = newDate;
- }
- }
-
- return new Date(lastDate.getTime() + 1000);
- }
-
/**
* Returns the time zone for which this CronExpression
* will be resolved.
@@ -423,38 +364,32 @@ public String toString() {
return cronExpression;
}
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // Expression Parsing Functions
- //
- ////////////////////////////////////////////////////////////////////////////
-
- protected void buildExpression(String expression) throws ParseException {
- expressionParsed = true;
-
+ private void buildExpression(String expression) throws ParseException {
try {
if (seconds == null) {
- seconds = new TreeSet();
+ seconds = new TreeSet<>();
}
if (minutes == null) {
- minutes = new TreeSet();
+ minutes = new TreeSet<>();
}
if (hours == null) {
- hours = new TreeSet();
+ hours = new TreeSet<>();
}
if (daysOfMonth == null) {
- daysOfMonth = new TreeSet();
+ daysOfMonth = new TreeSet<>();
+ }
+ if (nearestWeekdays == null) {
+ nearestWeekdays = new TreeSet<>();
}
if (months == null) {
- months = new TreeSet();
+ months = new TreeSet<>();
}
if (daysOfWeek == null) {
- daysOfWeek = new TreeSet();
+ daysOfWeek = new TreeSet<>();
}
if (years == null) {
- years = new TreeSet();
+ years = new TreeSet<>();
}
int exprOn = SECOND;
@@ -462,19 +397,23 @@ protected void buildExpression(String expression) throws ParseException {
StringTokenizer exprsTok = new StringTokenizer(expression, " \t",
false);
+ if(exprsTok.countTokens() > 7) {
+ throw new ParseException("Invalid expression has too many terms: " + expression, -1);
+ }
+
while (exprsTok.hasMoreTokens() && exprOn <= YEAR) {
String expr = exprsTok.nextToken().trim();
- // throw an exception if L is used with other days of the month
- if (exprOn == DAY_OF_MONTH && expr.indexOf('L') != -1 && expr.length() > 1 && expr.contains(",")) {
- throw new ParseException("Support for specifying 'L' and 'LW' with other days of the month is not implemented", -1);
- }
// throw an exception if L is used with other days of the week
- if (exprOn == DAY_OF_WEEK && expr.indexOf('L') != -1 && expr.length() > 1 && expr.contains(",")) {
- throw new ParseException("Support for specifying 'L' with other days of the week is not implemented", -1);
+ if (exprOn == DAY_OF_WEEK && expr.indexOf('L') != -1 && expr.length() > 1
+ && expr.contains(",")) {
+ throw new ParseException("Support for specifying 'L' "
+ + "with other days of the week is not implemented", -1);
}
- if (exprOn == DAY_OF_WEEK && expr.indexOf('#') != -1 && expr.indexOf('#', expr.indexOf('#') + 1) != -1) {
- throw new ParseException("Support for specifying multiple \"nth\" days is not implemented.", -1);
+ if (exprOn == DAY_OF_WEEK && expr.indexOf('#') != -1
+ && expr.indexOf('#', expr.indexOf('#') +1) != -1) {
+ throw new ParseException(
+ "Support for specifying multiple \"nth\" days is not implemented.", -1);
}
StringTokenizer vTok = new StringTokenizer(expr, ",");
@@ -487,8 +426,7 @@ protected void buildExpression(String expression) throws ParseException {
}
if (exprOn <= DAY_OF_WEEK) {
- throw new ParseException("Unexpected end of expression.",
- expression.length());
+ throw new ParseException("Unexpected end of expression.", expression.length());
}
if (exprOn <= YEAR) {
@@ -502,21 +440,19 @@ protected void buildExpression(String expression) throws ParseException {
boolean dayOfMSpec = !dom.contains(NO_SPEC);
boolean dayOfWSpec = !dow.contains(NO_SPEC);
- if (!dayOfMSpec || dayOfWSpec) {
- if (!dayOfWSpec || dayOfMSpec) {
- throw new ParseException(
- "Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.", 0);
- }
+ if ((!dayOfMSpec || dayOfWSpec) && (!dayOfWSpec || dayOfMSpec)) {
+ throw new ParseException(
+ "Support for specifying both a day-of-week AND a day-of-month"
+ + " parameter is not implemented.", 0);
}
} catch (ParseException pe) {
throw pe;
} catch (Exception e) {
- throw new ParseException("Illegal cron expression format ("
- + e.toString() + ")", 0);
+ throw new ParseException("Illegal cron expression format (" + e + ")", 0);
}
}
- protected int storeExpressionVals(int pos, String s, int type)
+ private int storeExpressionVals(int pos, String s, int type)
throws ParseException {
int incr = 0;
@@ -525,9 +461,10 @@ protected int storeExpressionVals(int pos, String s, int type)
return i;
}
char c = s.charAt(i);
- if ((c >= 'A') && (c <= 'Z') && (!s.equals("L")) && (!s.equals("LW")) && (!s.matches("^L-[0-9]*[W]?"))) {
+ if ((c >= 'A') && (c <= 'Z') && (!"L".equals(s)) && (!"LW".equals(s))
+ && (!s.matches("^L-[0-9]*[W]?"))) {
String sub = s.substring(i, i + 3);
- int sval = -1;
+ int sval;
int eval = -1;
if (type == MONTH) {
sval = getMonthNumber(sub) + 1;
@@ -548,8 +485,7 @@ protected int storeExpressionVals(int pos, String s, int type)
} else if (type == DAY_OF_WEEK) {
sval = getDayOfWeekNumber(sub);
if (sval < 0) {
- throw new ParseException("Invalid Day-of-Week value: '"
- + sub + "'", i);
+ throw new ParseException("Invalid Day-of-Week value: '" + sub + "'", i);
}
if (s.length() > i + 3) {
c = s.charAt(i + 3);
@@ -558,32 +494,27 @@ protected int storeExpressionVals(int pos, String s, int type)
sub = s.substring(i, i + 3);
eval = getDayOfWeekNumber(sub);
if (eval < 0) {
- throw new ParseException(
- "Invalid Day-of-Week value: '" + sub
- + "'", i);
+ throw new ParseException("Invalid Day-of-Week value: '" + sub + "'", i);
}
} else if (c == '#') {
try {
i += 4;
- nthdayOfWeek = Integer.parseInt(s.substring(i));
- if (nthdayOfWeek < 1 || nthdayOfWeek > 5) {
+ nthDayOfWeek = Integer.parseInt(s.substring(i));
+ if (nthDayOfWeek < 1 || nthDayOfWeek > 5) {
throw new Exception();
}
} catch (Exception e) {
throw new ParseException(
- "A numeric value between 1 and 5 must follow the '#' option",
- i);
+ "A numeric value between 1 and 5 must follow the '#' option", i);
}
} else if (c == 'L') {
- lastdayOfWeek = true;
+ lastDayOfWeek = true;
i++;
}
}
} else {
- throw new ParseException(
- "Illegal characters for this position: '" + sub + "'",
- i);
+ throw new ParseException("Illegal characters for this position: '" + sub + "'", i);
}
if (eval != -1) {
incr = 1;
@@ -596,21 +527,14 @@ protected int storeExpressionVals(int pos, String s, int type)
i++;
if ((i + 1) < s.length()
&& (s.charAt(i) != ' ' && s.charAt(i + 1) != '\t')) {
- throw new ParseException("Illegal character after '?': "
- + s.charAt(i), i);
+ throw new ParseException("Illegal character after '?': " + s.charAt(i), i);
}
if (type != DAY_OF_WEEK && type != DAY_OF_MONTH) {
throw new ParseException(
- "'?' can only be specified for Day-of-Month or Day-of-Week.",
- i);
+ "'?' can only be specified for Day-of-Month or Day-of-Week.", i);
}
- if (type == DAY_OF_WEEK && !lastdayOfMonth) {
- int val = daysOfMonth.last();
- if (val == NO_SPEC_INT) {
- throw new ParseException(
- "'?' can only be specified for Day-of-Month -OR- Day-of-Week.",
- i);
- }
+ if ((type == DAY_OF_WEEK) && (!daysOfMonth.isEmpty() && daysOfMonth.last() == NO_SPEC_INT)) {
+ throw new ParseException("'?' can only be specified for Day-of-Month -OR- Day-of-Week.", i);
}
addToSet(NO_SPEC_INT, -1, 0, type);
@@ -650,27 +574,36 @@ protected int storeExpressionVals(int pos, String s, int type)
return i;
} else if (c == 'L') {
i++;
- if (type == DAY_OF_MONTH) {
- lastdayOfMonth = true;
- }
if (type == DAY_OF_WEEK) {
addToSet(7, 7, 0, type);
}
- if (type == DAY_OF_MONTH && s.length() > i) {
- c = s.charAt(i);
- if (c == '-') {
- ValueSet vs = getValue(0, s, i + 1);
- lastdayOffset = vs.value;
- if (lastdayOffset > 30)
- throw new ParseException("Offset from last day must be <= 30", i + 1);
- i = vs.pos;
- }
+ if (type == DAY_OF_MONTH) {
+ int dom = LAST_DAY_OFFSET_END;
+ boolean nearestWeekday = false;
if (s.length() > i) {
c = s.charAt(i);
- if (c == 'W') {
- nearestWeekday = true;
- i++;
+ if (c == '-') {
+ ValueSet vs = getValue(0, s, i + 1);
+ int offset = vs.value;
+ if (offset > MAX_LAST_DAY_OFFSET) {
+ throw new ParseException(
+ "Offset from last day must be <= " + MAX_LAST_DAY_OFFSET, i + 1);
+ }
+ dom -= offset;
+ i = vs.pos;
}
+ if (s.length() > i) {
+ c = s.charAt(i);
+ if (c == 'W') {
+ nearestWeekday = true;
+ i++;
+ }
+ }
+ }
+ if (nearestWeekday) {
+ nearestWeekdays.add(dom);
+ } else {
+ daysOfMonth.add(dom);
}
}
return i;
@@ -710,7 +643,7 @@ private void checkIncrementRange(int incr, int type, int idxPos) throws ParseExc
}
}
- protected int checkNext(int pos, String s, int val, int type)
+ private int checkNext(int pos, String s, int val, int type)
throws ParseException {
int end = -1;
@@ -725,9 +658,9 @@ protected int checkNext(int pos, String s, int val, int type)
if (c == 'L') {
if (type == DAY_OF_WEEK) {
- if (val < 1 || val > 7)
+ if(val < 1 || val > 7)
throw new ParseException("Day-of-Week values must be between 1 and 7", -1);
- lastdayOfWeek = true;
+ lastDayOfWeek = true;
} else {
throw new ParseException("'L' option is not valid here. (pos=" + i + ")", i);
}
@@ -738,15 +671,15 @@ protected int checkNext(int pos, String s, int val, int type)
}
if (c == 'W') {
- if (type == DAY_OF_MONTH) {
- nearestWeekday = true;
- } else {
+ if (type != DAY_OF_MONTH) {
throw new ParseException("'W' option is not valid here. (pos=" + i + ")", i);
}
- if (val > 31)
- throw new ParseException("The 'W' option does not make sense with values larger than 31 (max number of days in a month)", i);
- TreeSet set = getSet(type);
- set.add(val);
+ if (val > 31) {
+ throw new ParseException("The 'W' option does not make sense with values larger than"
+ + " 31 (max number of days in a month)", i);
+ }
+
+ nearestWeekdays.add(val);
i++;
return i;
}
@@ -757,8 +690,8 @@ protected int checkNext(int pos, String s, int val, int type)
}
i++;
try {
- nthdayOfWeek = Integer.parseInt(s.substring(i));
- if (nthdayOfWeek < 1 || nthdayOfWeek > 5) {
+ nthDayOfWeek = Integer.parseInt(s.substring(i));
+ if (nthDayOfWeek < 1 || nthDayOfWeek > 5) {
throw new Exception();
}
} catch (Exception e) {
@@ -789,7 +722,7 @@ protected int checkNext(int pos, String s, int val, int type)
end = vs.value;
i = vs.pos;
}
- if (i < s.length() && ((c = s.charAt(i)) == '/')) {
+ if (i < s.length() && (s.charAt(i) == '/')) {
i++;
c = s.charAt(i);
int v2 = Integer.parseInt(String.valueOf(c));
@@ -851,147 +784,45 @@ public String getCronExpression() {
return cronExpression;
}
- public String getExpressionSummary() {
- StringBuilder buf = new StringBuilder();
-
- buf.append("seconds: ");
- buf.append(getExpressionSetSummary(seconds));
- buf.append("\n");
- buf.append("minutes: ");
- buf.append(getExpressionSetSummary(minutes));
- buf.append("\n");
- buf.append("hours: ");
- buf.append(getExpressionSetSummary(hours));
- buf.append("\n");
- buf.append("daysOfMonth: ");
- buf.append(getExpressionSetSummary(daysOfMonth));
- buf.append("\n");
- buf.append("months: ");
- buf.append(getExpressionSetSummary(months));
- buf.append("\n");
- buf.append("daysOfWeek: ");
- buf.append(getExpressionSetSummary(daysOfWeek));
- buf.append("\n");
- buf.append("lastdayOfWeek: ");
- buf.append(lastdayOfWeek);
- buf.append("\n");
- buf.append("nearestWeekday: ");
- buf.append(nearestWeekday);
- buf.append("\n");
- buf.append("NthDayOfWeek: ");
- buf.append(nthdayOfWeek);
- buf.append("\n");
- buf.append("lastdayOfMonth: ");
- buf.append(lastdayOfMonth);
- buf.append("\n");
- buf.append("years: ");
- buf.append(getExpressionSetSummary(years));
- buf.append("\n");
-
- return buf.toString();
- }
-
- protected String getExpressionSetSummary(java.util.Set set) {
-
- if (set.contains(NO_SPEC)) {
- return "?";
- }
- if (set.contains(ALL_SPEC)) {
- return "*";
- }
-
- StringBuilder buf = new StringBuilder();
-
- Iterator itr = set.iterator();
- boolean first = true;
- while (itr.hasNext()) {
- Integer iVal = itr.next();
- String val = iVal.toString();
- if (!first) {
- buf.append(",");
- }
- buf.append(val);
- first = false;
- }
-
- return buf.toString();
- }
-
- protected String getExpressionSetSummary(java.util.ArrayList list) {
-
- if (list.contains(NO_SPEC)) {
- return "?";
- }
- if (list.contains(ALL_SPEC)) {
- return "*";
- }
-
- StringBuilder buf = new StringBuilder();
-
- Iterator itr = list.iterator();
- boolean first = true;
- while (itr.hasNext()) {
- Integer iVal = itr.next();
- String val = iVal.toString();
- if (!first) {
- buf.append(",");
- }
- buf.append(val);
- first = false;
- }
-
- return buf.toString();
- }
-
- protected int skipWhiteSpace(int i, String s) {
+ private int skipWhiteSpace(int i, String s) {
for (; i < s.length() && (s.charAt(i) == ' ' || s.charAt(i) == '\t'); i++) {
- ;
}
return i;
}
- protected int findNextWhiteSpace(int i, String s) {
+ private int findNextWhiteSpace(int i, String s) {
for (; i < s.length() && (s.charAt(i) != ' ' || s.charAt(i) != '\t'); i++) {
- ;
}
return i;
}
- protected void addToSet(int val, int end, int incr, int type)
+ private void addToSet(int val, int end, int incr, int type)
throws ParseException {
TreeSet set = getSet(type);
if (type == SECOND || type == MINUTE) {
if ((val < 0 || val > 59 || end > 59) && (val != ALL_SPEC_INT)) {
- throw new ParseException(
- "Minute and Second values must be between 0 and 59",
- -1);
+ throw new ParseException("Minute and Second values must be between 0 and 59", -1);
}
} else if (type == HOUR) {
if ((val < 0 || val > 23 || end > 23) && (val != ALL_SPEC_INT)) {
- throw new ParseException(
- "Hour values must be between 0 and 23", -1);
+ throw new ParseException("Hour values must be between 0 and 23", -1);
}
} else if (type == DAY_OF_MONTH) {
if ((val < 1 || val > 31 || end > 31) && (val != ALL_SPEC_INT)
&& (val != NO_SPEC_INT)) {
- throw new ParseException(
- "Day of month values must be between 1 and 31", -1);
+ throw new ParseException("Day of month values must be between 1 and 31", -1);
}
} else if (type == MONTH) {
if ((val < 1 || val > 12 || end > 12) && (val != ALL_SPEC_INT)) {
- throw new ParseException(
- "Month values must be between 1 and 12", -1);
- }
- } else if (type == DAY_OF_WEEK) {
- if ((val == 0 || val > 7 || end > 7) && (val != ALL_SPEC_INT)
- && (val != NO_SPEC_INT)) {
- throw new ParseException(
- "Day-of-Week values must be between 1 and 7", -1);
+ throw new ParseException("Month values must be between 1 and 12", -1);
}
+ } else if (type == DAY_OF_WEEK && ((val == 0 || val > 7 || end > 7) && (val != ALL_SPEC_INT)
+ && (val != NO_SPEC_INT))) {
+ throw new ParseException("Day-of-Week values must be between 1 and 7", -1);
}
if ((incr == 0 || incr == -1) && val != ALL_SPEC_INT) {
@@ -1063,8 +894,6 @@ protected void addToSet(int val, int end, int incr, int type)
if (stopAt < startAt) {
switch (type) {
case SECOND:
- max = 60;
- break;
case MINUTE:
max = 60;
break;
@@ -1123,11 +952,11 @@ TreeSet getSet(int type) {
case YEAR:
return years;
default:
- return null;
+ throw new IllegalArgumentException("Invalid type encountered: " + type);
}
}
- protected ValueSet getValue(int v, String s, int i) {
+ private ValueSet getValue(int v, String s, int i) {
char c = s.charAt(i);
StringBuilder s1 = new StringBuilder(String.valueOf(v));
while (c >= '0' && c <= '9') {
@@ -1145,13 +974,13 @@ protected ValueSet getValue(int v, String s, int i) {
return val;
}
- protected int getNumericValue(String s, int i) {
+ private int getNumericValue(String s, int i) {
int endOfVal = findNextWhiteSpace(i, s);
String val = s.substring(i, endOfVal);
return Integer.parseInt(val);
}
- protected int getMonthNumber(String s) {
+ private int getMonthNumber(String s) {
Integer integer = monthMap.get(s);
if (integer == null) {
@@ -1161,7 +990,7 @@ protected int getMonthNumber(String s) {
return integer;
}
- protected int getDayOfWeekNumber(String s) {
+ private int getDayOfWeekNumber(String s) {
Integer integer = dayMap.get(s);
if (integer == null) {
@@ -1171,11 +1000,6 @@ protected int getDayOfWeekNumber(String s) {
return integer;
}
- ////////////////////////////////////////////////////////////////////////////
- //
- // Computation Functions
- //
- ////////////////////////////////////////////////////////////////////////////
public Date getTimeAfter(Date afterTime) {
@@ -1193,20 +1017,18 @@ public Date getTimeAfter(Date afterTime) {
// loop until we've computed the next time, or we've past the endTime
while (!gotOne) {
- //if (endTime != null && cl.getTime().after(endTime)) return null;
- if (cl.get(Calendar.YEAR) > 2999) { // prevent endless loop...
+
+ if(cl.get(Calendar.YEAR) > 2999) { // prevent endless loop...
return null;
}
- SortedSet st = null;
- int t = 0;
int sec = cl.get(Calendar.SECOND);
int min = cl.get(Calendar.MINUTE);
// get second.................................................
- st = seconds.tailSet(sec);
- if (st != null && st.size() != 0) {
+ SortedSet st = seconds.tailSet(sec);
+ if (!st.isEmpty()) {
sec = st.first();
} else {
sec = seconds.first();
@@ -1217,11 +1039,11 @@ public Date getTimeAfter(Date afterTime) {
min = cl.get(Calendar.MINUTE);
int hr = cl.get(Calendar.HOUR_OF_DAY);
- t = -1;
+ int t = -1;
// get minute.................................................
st = minutes.tailSet(min);
- if (st != null && st.size() != 0) {
+ if (!st.isEmpty()) {
t = min;
min = st.first();
} else {
@@ -1242,7 +1064,7 @@ public Date getTimeAfter(Date afterTime) {
// get hour...................................................
st = hours.tailSet(hr);
- if (st != null && st.size() != 0) {
+ if (!st.isEmpty()) {
t = hr;
hr = st.first();
} else {
@@ -1262,68 +1084,20 @@ public Date getTimeAfter(Date afterTime) {
int mon = cl.get(Calendar.MONTH) + 1;
// '+ 1' because calendar is 0-based for this field, and we are
// 1-based
- t = -1;
int tmon = mon;
// get day...................................................
boolean dayOfMSpec = !daysOfMonth.contains(NO_SPEC);
boolean dayOfWSpec = !daysOfWeek.contains(NO_SPEC);
if (dayOfMSpec && !dayOfWSpec) { // get day by day of month rule
- st = daysOfMonth.tailSet(day);
- if (lastdayOfMonth) {
- if (!nearestWeekday) {
- t = day;
- day = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
- day -= lastdayOffset;
- if (t > day) {
- mon++;
- if (mon > 12) {
- mon = 1;
- tmon = 3333; // ensure test of mon != tmon further below fails
- cl.add(Calendar.YEAR, 1);
- }
- day = 1;
- }
- } else {
- t = day;
- day = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
- day -= lastdayOffset;
-
- java.util.Calendar tcal = java.util.Calendar.getInstance(getTimeZone());
- tcal.set(Calendar.SECOND, 0);
- tcal.set(Calendar.MINUTE, 0);
- tcal.set(Calendar.HOUR_OF_DAY, 0);
- tcal.set(Calendar.DAY_OF_MONTH, day);
- tcal.set(Calendar.MONTH, mon - 1);
- tcal.set(Calendar.YEAR, cl.get(Calendar.YEAR));
-
- int ldom = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
- int dow = tcal.get(Calendar.DAY_OF_WEEK);
-
- if (dow == Calendar.SATURDAY && day == 1) {
- day += 2;
- } else if (dow == Calendar.SATURDAY) {
- day -= 1;
- } else if (dow == Calendar.SUNDAY && day == ldom) {
- day -= 2;
- } else if (dow == Calendar.SUNDAY) {
- day += 1;
- }
-
- tcal.set(Calendar.SECOND, sec);
- tcal.set(Calendar.MINUTE, min);
- tcal.set(Calendar.HOUR_OF_DAY, hr);
- tcal.set(Calendar.DAY_OF_MONTH, day);
- tcal.set(Calendar.MONTH, mon - 1);
- Date nTime = tcal.getTime();
- if (nTime.before(afterTime)) {
- day = 1;
- mon++;
- }
- }
- } else if (nearestWeekday) {
- t = day;
- day = daysOfMonth.first();
+ final Optional smallestDay = findSmallestDay(day, mon, cl.get(Calendar.YEAR),
+ daysOfMonth);
+ Optional smallestDayForWeekday = findSmallestDay(day, mon, cl.get(Calendar.YEAR),
+ nearestWeekdays);
+ t = day;
+ day = -1;
+ if (smallestDayForWeekday.isPresent()) {
+ day = smallestDayForWeekday.get();
java.util.Calendar tcal = java.util.Calendar.getInstance(getTimeZone());
tcal.set(Calendar.SECOND, 0);
@@ -1354,23 +1128,17 @@ public Date getTimeAfter(Date afterTime) {
tcal.set(Calendar.MONTH, mon - 1);
Date nTime = tcal.getTime();
if (nTime.before(afterTime)) {
- day = daysOfMonth.first();
- mon++;
+ day = -1;
}
- } else if (st != null && st.size() != 0) {
- t = day;
- day = st.first();
- // make sure we don't over-run a short month, such as february
- int lastDay = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
- if (day > lastDay) {
- day = daysOfMonth.first();
- mon++;
+ }
+ if (smallestDay.isPresent()) {
+ if (day == -1 || smallestDay.get() < day) {
+ day = smallestDay.get();
}
- } else {
- day = daysOfMonth.first();
+ } else if (day == -1) {
+ day = 1;
mon++;
}
-
if (day != t || mon != tmon) {
cl.set(Calendar.SECOND, 0);
cl.set(Calendar.MINUTE, 0);
@@ -1382,7 +1150,7 @@ public Date getTimeAfter(Date afterTime) {
continue;
}
} else if (dayOfWSpec && !dayOfMSpec) { // get day by day of week rule
- if (lastdayOfWeek) { // are we looking for the last XXX day of
+ if (lastDayOfWeek) { // are we looking for the last XXX day of
// the month?
int dow = daysOfWeek.first(); // desired
// d-o-w
@@ -1425,7 +1193,7 @@ public Date getTimeAfter(Date afterTime) {
continue;
}
- } else if (nthdayOfWeek != 0) {
+ } else if (nthDayOfWeek != 0) {
// are we looking for the Nth XXX day in the month?
int dow = daysOfWeek.first(); // desired
// d-o-w
@@ -1437,10 +1205,7 @@ public Date getTimeAfter(Date afterTime) {
daysToAdd = dow + (7 - cDow);
}
- boolean dayShifted = false;
- if (daysToAdd > 0) {
- dayShifted = true;
- }
+ final boolean dayShifted = daysToAdd > 0;
day += daysToAdd;
int weekOfMonth = day / 7;
@@ -1448,7 +1213,7 @@ public Date getTimeAfter(Date afterTime) {
weekOfMonth++;
}
- daysToAdd = (nthdayOfWeek - weekOfMonth) * 7;
+ daysToAdd = (nthDayOfWeek - weekOfMonth) * 7;
day += daysToAdd;
if (daysToAdd < 0
|| day > getLastDayOfMonth(mon, cl
@@ -1474,7 +1239,7 @@ public Date getTimeAfter(Date afterTime) {
int dow = daysOfWeek.first(); // desired
// d-o-w
st = daysOfWeek.tailSet(cDow);
- if (st != null && st.size() > 0) {
+ if (!st.isEmpty()) {
dow = st.first();
}
@@ -1497,7 +1262,7 @@ public Date getTimeAfter(Date afterTime) {
cl.set(Calendar.MONTH, mon);
// no '- 1' here because we are promoting the month
continue;
- } else if (daysToAdd > 0) { // are we swithing days?
+ } else if (daysToAdd > 0) { // are we switching days?
cl.set(Calendar.SECOND, 0);
cl.set(Calendar.MINUTE, 0);
cl.set(Calendar.HOUR_OF_DAY, 0);
@@ -1510,7 +1275,8 @@ public Date getTimeAfter(Date afterTime) {
}
} else { // dayOfWSpec && !dayOfMSpec
throw new UnsupportedOperationException(
- "Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.");
+ "Support for specifying both a day-of-week"
+ + " AND a day-of-month parameter is not implemented.");
}
cl.set(Calendar.DAY_OF_MONTH, day);
@@ -1528,7 +1294,7 @@ public Date getTimeAfter(Date afterTime) {
// get month...................................................
st = months.tailSet(mon);
- if (st != null && st.size() != 0) {
+ if (!st.isEmpty()) {
t = mon;
mon = st.first();
} else {
@@ -1551,11 +1317,10 @@ public Date getTimeAfter(Date afterTime) {
// 1-based
year = cl.get(Calendar.YEAR);
- t = -1;
// get year...................................................
st = years.tailSet(year);
- if (st != null && st.size() != 0) {
+ if (!st.isEmpty()) {
t = year;
year = st.first();
} else {
@@ -1576,7 +1341,7 @@ public Date getTimeAfter(Date afterTime) {
cl.set(Calendar.YEAR, year);
gotOne = true;
- } // while( !done )
+ }
return cl.getTime();
}
@@ -1585,65 +1350,37 @@ public Date getTimeAfter(Date afterTime) {
* Advance the calendar to the particular hour paying particular attention
* to daylight saving problems.
*
- * @param cal the calendar to operate on
+ * @param cal the calendar to operate on
* @param hour the hour to set
*/
- protected void setCalendarHour(Calendar cal, int hour) {
+ private void setCalendarHour(Calendar cal, int hour) {
cal.set(java.util.Calendar.HOUR_OF_DAY, hour);
if (cal.get(java.util.Calendar.HOUR_OF_DAY) != hour && hour != 24) {
cal.set(java.util.Calendar.HOUR_OF_DAY, hour + 1);
}
}
- /**
- * NOT YET IMPLEMENTED: Returns the time before the given time
- * that the CronExpression matches.
- */
- public Date getTimeBefore(Date endTime) {
- // FUTURE_TODO: implement QUARTZ-423
- return null;
- }
-
- /**
- * NOT YET IMPLEMENTED: Returns the final time that the
- * CronExpression will match.
- */
- public Date getFinalFireTime() {
- // FUTURE_TODO: implement QUARTZ-423
- return null;
- }
-
- protected boolean isLeapYear(int year) {
+ private boolean isLeapYear(int year) {
return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));
}
- protected int getLastDayOfMonth(int monthNum, int year) {
-
+ private int getLastDayOfMonth(int monthNum, int year) {
switch (monthNum) {
case 1:
- return 31;
- case 2:
- return (isLeapYear(year)) ? 29 : 28;
case 3:
- return 31;
- case 4:
- return 30;
case 5:
- return 31;
- case 6:
- return 30;
case 7:
- return 31;
case 8:
- return 31;
- case 9:
- return 30;
case 10:
+ case 12:
return 31;
+ case 2:
+ return (isLeapYear(year)) ? 29 : 28;
+ case 4:
+ case 6:
+ case 9:
case 11:
return 30;
- case 12:
- return 31;
default:
throw new IllegalArgumentException("Illegal month number: "
+ monthNum);
@@ -1651,19 +1388,24 @@ protected int getLastDayOfMonth(int monthNum, int year) {
}
- private void readObject(java.io.ObjectInputStream stream)
- throws java.io.IOException, ClassNotFoundException {
+ private Optional findSmallestDay(int day, int mon, int year, TreeSet set) {
+ if (set.isEmpty()) {
+ return Optional.empty();
+ }
+
+ final int lastDay = getLastDayOfMonth(mon, year);
+ // For "L", "L-1", etc.
+ 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 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();
+ }
- stream.defaultReadObject();
- try {
- buildExpression(cronExpression);
- } catch (Exception ignore) {
- } // never happens
+ return smallestDay == Integer.MAX_VALUE ? Optional.empty() : Optional.of(smallestDay);
}
-
- @Override
- @Deprecated
- public Object clone() {
- return new CronExpression(this);
}
-}
diff --git a/common/src/main/java/org/tron/common/cron/ValueSet.java b/common/src/main/java/org/tron/common/cron/ValueSet.java
index 74469a62b48..e8ffa73b1e2 100644
--- a/common/src/main/java/org/tron/common/cron/ValueSet.java
+++ b/common/src/main/java/org/tron/common/cron/ValueSet.java
@@ -1,7 +1,7 @@
package org.tron.common.cron;
class ValueSet {
- public int value;
+ protected int value;
- public int pos;
+ protected int pos;
}
diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java
index b09b3b8c847..50ba7eb83e8 100644
--- a/framework/src/main/java/org/tron/core/config/args/Args.java
+++ b/framework/src/main/java/org/tron/core/config/args/Args.java
@@ -42,13 +42,13 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
-import org.tron.common.cron.CronExpression;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tron.common.args.Account;
import org.tron.common.args.GenesisBlock;
import org.tron.common.args.Witness;
import org.tron.common.config.DbBackupConfig;
+import org.tron.common.cron.CronExpression;
import org.tron.common.crypto.SignInterface;
import org.tron.common.logsfilter.EventPluginConfig;
import org.tron.common.logsfilter.FilterQuery;
diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java
index d856ccb8a11..4542b96a737 100644
--- a/framework/src/main/java/org/tron/core/db/Manager.java
+++ b/framework/src/main/java/org/tron/core/db/Manager.java
@@ -46,12 +46,12 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.bouncycastle.util.encoders.Hex;
-import org.tron.common.cron.CronExpression;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tron.api.GrpcAPI.TransactionInfoList;
import org.tron.common.args.GenesisBlock;
import org.tron.common.bloom.Bloom;
+import org.tron.common.cron.CronExpression;
import org.tron.common.es.ExecutorServiceManager;
import org.tron.common.exit.ExitManager;
import org.tron.common.logsfilter.EventPluginLoader;
diff --git a/framework/src/test/java/org/tron/common/cron/CronExpressionTest.java b/framework/src/test/java/org/tron/common/cron/CronExpressionTest.java
new file mode 100644
index 00000000000..5e41670763c
--- /dev/null
+++ b/framework/src/test/java/org/tron/common/cron/CronExpressionTest.java
@@ -0,0 +1,450 @@
+/*
+ * 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.
+ */
+
+package org.tron.common.cron;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import java.text.ParseException;
+import java.util.Calendar;
+import java.util.Date;
+import org.junit.Test;
+
+public class CronExpressionTest {
+
+ private static void assertTrue(boolean condition, String message) {
+ if (!condition) {
+ fail(message);
+ }
+ }
+
+ private static void assertTrue(boolean condition) {
+ if (!condition) {
+ fail();
+ }
+ }
+
+ @Test
+ public void testTooManyTokens() {
+ try {
+ new CronExpression("0 15 10 * * ? 2005 *"); // too many tokens/terms in expression
+ fail("Expected ParseException did not occur for invalid expression");
+ } catch (ParseException pe) {
+ assertTrue(pe.getMessage().contains("too many"),
+ "Incorrect ParseException thrown");
+ }
+
+ }
+
+ @Test
+ public void testIsSatisfiedBy() throws Exception {
+ CronExpression cronExpression = new CronExpression("0 15 10 * * ? 2005");
+
+ Calendar cal = Calendar.getInstance();
+
+ cal.set(2005, Calendar.JUNE, 1, 10, 15, 0);
+ assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));
+
+ cal.set(Calendar.YEAR, 2006);
+ assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));
+
+ cal = Calendar.getInstance();
+ cal.set(2005, Calendar.JUNE, 1, 10, 16, 0);
+ assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));
+
+ cal = Calendar.getInstance();
+ cal.set(2005, Calendar.JUNE, 1, 10, 14, 0);
+ assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));
+ }
+
+ @Test
+ public void testIsValidExpression() {
+ assertTrue(CronExpression.isValidExpression("0 0 0 L-2 * ? *"));
+ assertTrue(CronExpression.isValidExpression("0 0 0 LW * ? *"));
+ assertFalse(CronExpression.isValidExpression("0 0 0 Foo * ? *"));
+ assertFalse(CronExpression.isValidExpression("61 15 10 L-2 * ? 2010"));
+ assertFalse(CronExpression.isValidExpression("0 61 10 L-2 * ? 2010"));
+ assertFalse(CronExpression.isValidExpression("0 15 25 L-2 * ? 2010"));
+ assertTrue(CronExpression.isValidExpression("0/5 * * * * ?"));
+ assertTrue(CronExpression.isValidExpression("0 0 2 * * ?"));
+ assertTrue(CronExpression.isValidExpression("0 15 8 ? * MON-FRI"));
+ assertTrue(CronExpression.isValidExpression("0 45 15 1,15 * ? 2005"));
+ assertTrue(CronExpression.isValidExpression("0 10 * * * ?"));
+ assertTrue(CronExpression.isValidExpression("0 0 12 L 3,6,9,12 ?"));
+ assertTrue(CronExpression.isValidExpression("0 0 6 ? DEC,JAN SUN,SAT"));
+ assertTrue(CronExpression.isValidExpression("0 0 12 1/5 * ?"));
+ assertTrue(CronExpression.isValidExpression("0 0 8-18 ? * MON,WED,FRI"));
+ assertTrue(CronExpression.isValidExpression("0 10,44 14 ? 3 WED 2022/2"));
+ assertTrue(CronExpression.isValidExpression("0 0/30 9-17 * * ? 2022-2025"));
+ assertTrue(CronExpression.isValidExpression("0 15 10 ? * 6#3 2022,2023"));
+ assertTrue(CronExpression.isValidExpression("0 10,44 14 ? 3 WED 2022/2"));
+ assertTrue(CronExpression.isValidExpression("0 0/5 14,18 * * ?"));
+ assertTrue(CronExpression.isValidExpression("0 15 10 ? * 6#3"));
+ assertFalse(CronExpression.isValidExpression(" 0 15 10 ? * 6#3 2014-2012"));
+ assertTrue(CronExpression.isValidExpression("0 0 20-18 ? * MON,WED,FRI"));
+ assertTrue(CronExpression.isValidExpression("0 0/30 17-9 * 10-9 ? 2022"));
+
+ }
+
+ @Test
+ public void testLastDayOffset() throws Exception {
+ CronExpression cronExpression = new CronExpression("0 15 10 L-2 * ? 2010");
+ cronExpression.setTimeZone(Calendar.getInstance().getTimeZone());
+
+ Calendar cal = Calendar.getInstance();
+ // last day - 2
+ cal.set(2010, Calendar.OCTOBER, 29, 10, 15, 0);
+ assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));
+
+ cal.set(2010, Calendar.OCTOBER, 28, 10, 15, 0);
+ assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));
+
+ cronExpression = new CronExpression("0 15 10 L-5W * ? 2010");
+ // last day - 5
+ cal.set(2010, Calendar.OCTOBER, 26, 10, 15, 0);
+ assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));
+
+ cronExpression = new CronExpression("0 15 10 L-1 * ? 2010");
+ // last day - 1
+ cal.set(2010, Calendar.OCTOBER, 30, 10, 15, 0);
+ assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));
+
+ cronExpression = new CronExpression("0 15 10 L-1W * ? 2010");
+ // nearest weekday to last day - 1 (29th is a friday in 2010)
+ cal.set(2010, Calendar.OCTOBER, 29, 10, 15, 0);
+ assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));
+
+ cronExpression = new CronExpression("0 15 10 1,L * ? 2010");
+
+ cal.set(2010, Calendar.OCTOBER, 1, 10, 15, 0);
+ assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));
+
+ cal.set(2010, Calendar.OCTOBER, 31, 10, 15, 0);
+ assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));
+
+ cal.set(2010, Calendar.OCTOBER, 30, 10, 15, 0);
+ assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));
+
+ cronExpression = new CronExpression("0 15 10 L-1W,L-1 * ? 2010");
+ // nearest weekday to last day - 1 (29th is a friday in 2010)
+ cal.set(2010, Calendar.OCTOBER, 29, 10, 15, 0);
+ assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));
+ // last day - 1
+ cal.set(2010, Calendar.OCTOBER, 30, 10, 15, 0);
+
+ cronExpression = new CronExpression("0 15 10 2W,16 * ? 2010");
+ // nearest weekday to the 2nd of the month (1st is a friday in 2010)
+ cal.set(2010, Calendar.OCTOBER, 1, 10, 15, 0);
+ assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));
+
+ cal.set(2010, Calendar.OCTOBER, 2, 10, 15, 0);
+ assertFalse(cronExpression.isSatisfiedBy(cal.getTime()));
+
+ cal.set(2010, Calendar.OCTOBER, 16, 10, 15, 0);
+ assertTrue(cronExpression.isSatisfiedBy(cal.getTime()));
+
+ }
+
+ @Test
+ public void testQuartz() throws Exception {
+ CronExpression cronExpression = new CronExpression("19 15 10 4 Apr ? ");
+ assertEquals("19 15 10 4 Apr ? ".toUpperCase(), cronExpression.getCronExpression());
+ assertEquals("19 15 10 4 Apr ? ".toUpperCase(), cronExpression.toString());
+
+ // if broken, this will throw an exception
+ cronExpression.getNextValidTimeAfter(new Date());
+
+ try {
+ new CronExpression(null);
+ fail("Expected ParseException did not fire for null ");
+ } catch (IllegalArgumentException e) {
+ assertTrue(e.getMessage().equals("cronExpression cannot be null"),
+ "Incorrect ParseException thrown");
+ }
+
+ try {
+ new CronExpression("* * * * Foo ? ");
+ fail("Expected ParseException did not fire for nonexistent month");
+ } catch (ParseException pe) {
+ assertTrue(pe.getMessage().startsWith("Invalid Month value:"),
+ "Incorrect ParseException thrown");
+ }
+
+ try {
+ new CronExpression("* * * * Jan-Foo ? ");
+ fail("Expected ParseException did not fire for nonexistent month");
+ } catch (ParseException pe) {
+ assertTrue(pe.getMessage().startsWith("Invalid Month value:"),
+ "Incorrect ParseException thrown");
+ }
+
+ try {
+ new CronExpression("0 0 * * * *");
+ fail("Expected ParseException did not fire for wildcard day-of-month and day-of-week");
+ } catch (ParseException pe) {
+ assertTrue(pe.getMessage().startsWith(
+ "Support for specifying both a day-of-week AND"
+ + " a day-of-month parameter is not implemented."),
+ "Incorrect ParseException thrown");
+ }
+ try {
+ new CronExpression("0 0 * 4 * *");
+ fail("Expected ParseException did not fire for specified day-of-month and"
+ + " wildcard day-of-week");
+ } catch (ParseException pe) {
+ assertTrue(pe.getMessage().startsWith(
+ "Support for specifying both a day-of-week AND a day-of-month"
+ + " parameter is not implemented."), "Incorrect ParseException thrown");
+ }
+ try {
+ new CronExpression("0 0 * * * 4");
+ fail("Expected ParseException did not fire for wildcard day-of-month"
+ + " and specified day-of-week");
+ } catch (ParseException pe) {
+ assertTrue(pe.getMessage().startsWith(
+ "Support for specifying both a day-of-week AND a day-of-month"
+ + " parameter is not implemented."), "Incorrect ParseException thrown");
+ }
+
+ try {
+ new CronExpression("0 43 9 ? * SAT,SUN,L");
+ fail("Expected ParseException did not fire for L combined with other days of the week");
+ } catch (ParseException pe) {
+ assertTrue(pe.getMessage().startsWith(
+ "Support for specifying 'L' with other days of the week is not implemented"),
+ "Incorrect ParseException thrown");
+ }
+ try {
+ new CronExpression("0 43 9 ? * 6,7,L");
+ fail("Expected ParseException did not fire for L combined with other days of the week");
+ } catch (ParseException pe) {
+ assertTrue(pe.getMessage().startsWith(
+ "Support for specifying 'L' with other days of the week is not implemented"),
+ "Incorrect ParseException thrown");
+ }
+ try {
+ new CronExpression("0 43 9 ? * 5L");
+ } catch (ParseException pe) {
+ fail("Unexpected ParseException thrown for supported '5L' expression.");
+ }
+ }
+
+ @Test
+ public void testQtz96() throws ParseException {
+ try {
+ new CronExpression("0/5 * * 32W 1 ?");
+ fail("Expected ParseException did not fire for W with value larger than 31");
+ } catch (ParseException pe) {
+ assertTrue(pe.getMessage().startsWith(
+ "The 'W' option does not make sense with values larger than"),
+ "Incorrect ParseException thrown");
+ }
+
+ // Test case 1
+ try {
+ new CronExpression("/120 0 8-18 ? * 2-6");
+ fail("Cron did not validate bad range interval in '_blank/xxx' form");
+ } catch (ParseException e) {
+ assertEquals("Increment > 60 : 120", e.getMessage());
+ }
+
+ // Test case 2
+ try {
+ new CronExpression("0/120 0 8-18 ? * 2-6");
+ fail("Cron did not validate bad range interval in in '0/xxx' form");
+ } catch (ParseException e) {
+ assertEquals("Increment > 60 : 120", e.getMessage());
+ }
+
+ // Test case 3
+ try {
+ new CronExpression("/ 0 8-18 ? * 2-6");
+ fail("Cron did not validate bad range interval in '_blank/_blank'");
+ } catch (ParseException e) {
+ assertEquals("'/' must be followed by an integer.", e.getMessage());
+ }
+
+ // Test case 4
+ try {
+ new CronExpression("0/ 0 8-18 ? * 2-6");
+ fail("Cron did not validate bad range interval in '0/_blank'");
+ } catch (ParseException e) {
+ assertEquals("'/' must be followed by an integer.", e.getMessage());
+ }
+
+ // Test case 1
+ try {
+ new CronExpression("0 /120 8-18 ? * 2-6");
+ fail("Cron did not validate bad range interval in '_blank/xxx' form");
+ } catch (ParseException e) {
+ assertEquals("Increment > 60 : 120", e.getMessage());
+ }
+
+ // Test case 2
+ try {
+ new CronExpression("0 0/120 8-18 ? * 2-6");
+ fail("Cron did not validate bad range interval in in '0/xxx' form");
+ } catch (ParseException e) {
+ assertEquals("Increment > 60 : 120", e.getMessage());
+ }
+
+ // Test case 3
+ try {
+ new CronExpression("0 / 8-18 ? * 2-6");
+ fail("Cron did not validate bad range interval in '_blank/_blank'");
+ } catch (ParseException e) {
+ assertEquals("'/' must be followed by an integer.", e.getMessage());
+ }
+
+ // Test case 4
+ try {
+ new CronExpression("0 0/ 8-18 ? * 2-6");
+ fail("Cron did not validate bad range interval in '0/_blank'");
+ } catch (ParseException e) {
+ assertEquals("'/' must be followed by an integer.", e.getMessage());
+ }
+
+ // Test case 1
+ try {
+ new CronExpression("0 0 /120 ? * 2-6");
+ fail("Cron did not validate bad range interval in '_blank/xxx' form");
+ } catch (ParseException e) {
+ assertEquals("Increment > 24 : 120", e.getMessage());
+ }
+
+ // Test case 2
+ try {
+ new CronExpression("0 0 0/120 ? * 2-6");
+ fail("Cron did not validate bad range interval in in '0/xxx' form");
+ } catch (ParseException e) {
+ assertEquals("Increment > 24 : 120", e.getMessage());
+ }
+
+ // Test case 3
+ try {
+ new CronExpression("0 0 / ? * 2-6");
+ fail("Cron did not validate bad range interval in '_blank/_blank'");
+ } catch (ParseException e) {
+ assertEquals("'/' must be followed by an integer.", e.getMessage());
+ }
+
+ // Test case 4
+ try {
+ new CronExpression("0 0 0/ ? * 2-6");
+ fail("Cron did not validate bad range interval in '0/_blank'");
+ } catch (ParseException e) {
+ assertEquals("'/' must be followed by an integer.", e.getMessage());
+ }
+
+ // Test case 1
+ try {
+ new CronExpression("0 0 0 /120 * 2-6");
+ fail("Cron did not validate bad range interval in '_blank/xxx' form");
+ } catch (ParseException e) {
+ assertEquals("Increment > 31 : 120", e.getMessage());
+ }
+
+ // Test case 2
+ try {
+ new CronExpression("0 0 0 0/120 * 2-6");
+ fail("Cron did not validate bad range interval in in '0/xxx' form");
+ } catch (ParseException e) {
+ assertEquals("Increment > 31 : 120", e.getMessage());
+ }
+
+ // Test case 3
+ try {
+ new CronExpression("0 0 0 / * 2-6");
+ fail("Cron did not validate bad range interval in '_blank/_blank'");
+ } catch (ParseException e) {
+ assertEquals("'/' must be followed by an integer.", e.getMessage());
+ }
+
+ // Test case 4
+ try {
+ new CronExpression("0 0 0 0/ * 2-6");
+ fail("Cron did not validate bad range interval in '0/_blank'");
+ } catch (ParseException e) {
+ assertEquals("'/' must be followed by an integer.", e.getMessage());
+ }
+ // Test case 1
+ try {
+ new CronExpression("0 0 0 ? /120 2-6");
+ fail("Cron did not validate bad range interval in '_blank/xxx' form");
+ } catch (ParseException e) {
+ assertEquals("Increment > 12 : 120", e.getMessage());
+ }
+
+ // Test case 2
+ try {
+ new CronExpression("0 0 0 ? 0/120 2-6");
+ fail("Cron did not validate bad range interval in in '0/xxx' form");
+ } catch (ParseException e) {
+ assertEquals("Increment > 12 : 120", e.getMessage());
+ }
+
+ // Test case 3
+ try {
+ new CronExpression("0 0 0 ? / 2-6");
+ fail("Cron did not validate bad range interval in '_blank/_blank'");
+ } catch (ParseException e) {
+ assertEquals("'/' must be followed by an integer.", e.getMessage());
+ }
+
+ // Test case 4
+ try {
+ new CronExpression("0 0 0 ? 0/ 2-6");
+ fail("Cron did not validate bad range interval in '0/_blank'");
+ } catch (ParseException e) {
+ assertEquals("'/' must be followed by an integer.", e.getMessage());
+ }
+ // Test case 1
+ try {
+ new CronExpression("0 0 0 ? * /120");
+ fail("Cron did not validate bad range interval in '_blank/xxx' form");
+ } catch (ParseException e) {
+ assertEquals("Increment > 7 : 120", e.getMessage());
+ }
+
+ // Test case 2
+ try {
+ new CronExpression("0 0 0 ? * 0/120");
+ fail("Cron did not validate bad range interval in in '0/xxx' form");
+ } catch (ParseException e) {
+ assertEquals("Increment > 7 : 120", e.getMessage());
+ }
+
+ // Test case 3
+ try {
+ new CronExpression("0 0 0 ? * /");
+ fail("Cron did not validate bad range interval in '_blank/_blank'");
+ } catch (ParseException e) {
+ assertEquals("'/' must be followed by an integer.", e.getMessage());
+ }
+
+ // Test case 4
+ try {
+ new CronExpression("0 0 0 ? * 0/");
+ fail("Cron did not validate bad range interval in '0/_blank'");
+ } catch (ParseException e) {
+ assertEquals("'/' must be followed by an integer.", e.getMessage());
+ }
+ }
+
+}
diff --git a/framework/src/test/java/org/tron/core/services/stop/BlockTimeStopTest.java b/framework/src/test/java/org/tron/core/services/stop/BlockTimeStopTest.java
index c529cafbba9..1e16ad6c3b0 100644
--- a/framework/src/test/java/org/tron/core/services/stop/BlockTimeStopTest.java
+++ b/framework/src/test/java/org/tron/core/services/stop/BlockTimeStopTest.java
@@ -5,7 +5,6 @@
import java.util.Date;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
-import org.junit.Test;
import org.tron.common.cron.CronExpression;
import org.tron.common.parameter.CommonParameter;
@@ -26,21 +25,6 @@ public class BlockTimeStopTest extends ConditionallyStopTest {
}
}
- @Test
- public void isValidExpression() {
- Assert.assertTrue(CronExpression.isValidExpression(cronExpression.getCronExpression()));
- ParseException err = Assert.assertThrows(ParseException.class, () ->
- CronExpression.validateExpression("invalid expression"));
- Assert.assertEquals("Illegal characters for this position: 'INV'", err.getMessage());
- }
-
- @Test
- public void getNextTime() {
- Date date = cronExpression.getNextValidTimeAfter(new Date());
- Date invalidDate = cronExpression.getNextInvalidTimeAfter(new Date());
- Assert.assertNotEquals(date, invalidDate);
- }
-
protected void initParameter(CommonParameter parameter) {
parameter.setShutdownBlockTime(cronExpression);