mirror of
https://gitee.com/dromara/hutool.git
synced 2025-04-05 17:37:59 +08:00
add class
This commit is contained in:
parent
be72eab9a0
commit
4fbda4565a
@ -33,6 +33,7 @@
|
|||||||
* 【poi 】 优化ExcelBase,将alias放入
|
* 【poi 】 优化ExcelBase,将alias放入
|
||||||
* 【poi 】 优化ExcelBase,将alias放入
|
* 【poi 】 优化ExcelBase,将alias放入
|
||||||
* 【core 】 改进StrUtil#startWith、endWith性能
|
* 【core 】 改进StrUtil#startWith、endWith性能
|
||||||
|
* 【cron 】 增加CronPatternParser、MatcherTable
|
||||||
|
|
||||||
### 🐞Bug修复
|
### 🐞Bug修复
|
||||||
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee)
|
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee)
|
||||||
|
@ -1756,7 +1756,7 @@ public class DateUtil extends CalendarUtil {
|
|||||||
* @return 是否闰年
|
* @return 是否闰年
|
||||||
*/
|
*/
|
||||||
public static boolean isLeapYear(int year) {
|
public static boolean isLeapYear(int year) {
|
||||||
return new GregorianCalendar().isLeapYear(year);
|
return Year.isLeap(year);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,25 +1,10 @@
|
|||||||
package cn.hutool.cron.pattern;
|
package cn.hutool.cron.pattern;
|
||||||
|
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.cron.pattern.matcher.MatcherTable;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.cron.pattern.parser.CronPatternParser;
|
||||||
import cn.hutool.cron.CronException;
|
|
||||||
import cn.hutool.cron.pattern.matcher.AlwaysTrueValueMatcher;
|
|
||||||
import cn.hutool.cron.pattern.matcher.DayOfMonthValueMatcher;
|
|
||||||
import cn.hutool.cron.pattern.matcher.ValueMatcher;
|
|
||||||
import cn.hutool.cron.pattern.matcher.ValueMatcherBuilder;
|
|
||||||
import cn.hutool.cron.pattern.parser.DayOfMonthValueParser;
|
|
||||||
import cn.hutool.cron.pattern.parser.DayOfWeekValueParser;
|
|
||||||
import cn.hutool.cron.pattern.parser.HourValueParser;
|
|
||||||
import cn.hutool.cron.pattern.parser.MinuteValueParser;
|
|
||||||
import cn.hutool.cron.pattern.parser.MonthValueParser;
|
|
||||||
import cn.hutool.cron.pattern.parser.SecondValueParser;
|
|
||||||
import cn.hutool.cron.pattern.parser.ValueParser;
|
|
||||||
import cn.hutool.cron.pattern.parser.YearValueParser;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
import java.util.List;
|
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,14 +17,14 @@ import java.util.TimeZone;
|
|||||||
* <li><strong>月</strong> :范围:1~12,同时支持不区分大小写的别名:"jan","feb", "mar", "apr", "may","jun", "jul", "aug", "sep","oct", "nov", "dec"</li>
|
* <li><strong>月</strong> :范围:1~12,同时支持不区分大小写的别名:"jan","feb", "mar", "apr", "may","jun", "jul", "aug", "sep","oct", "nov", "dec"</li>
|
||||||
* <li><strong>周</strong> :范围:0 (Sunday)~6(Saturday),7也可以表示周日,同时支持不区分大小写的别名:"sun","mon", "tue", "wed", "thu","fri", "sat",<strong>"L"</strong> 表示周六</li>
|
* <li><strong>周</strong> :范围:0 (Sunday)~6(Saturday),7也可以表示周日,同时支持不区分大小写的别名:"sun","mon", "tue", "wed", "thu","fri", "sat",<strong>"L"</strong> 表示周六</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
* <p>
|
||||||
* 为了兼容Quartz表达式,同时支持6位和7位表达式,其中:<br>
|
* 为了兼容Quartz表达式,同时支持6位和7位表达式,其中:<br>
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* 当为6位时,第一位表示<strong>秒</strong> ,范围0~59,但是第一位不做匹配
|
* 当为6位时,第一位表示<strong>秒</strong> ,范围0~59,但是第一位不做匹配
|
||||||
* 当为7位时,最后一位表示<strong>年</strong> ,范围1970~2099,但是第7位不做解析,也不做匹配
|
* 当为7位时,最后一位表示<strong>年</strong> ,范围1970~2099,但是第7位不做解析,也不做匹配
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
* <p>
|
||||||
* 当定时任务运行到的时间匹配这些表达式后,任务被启动。<br>
|
* 当定时任务运行到的时间匹配这些表达式后,任务被启动。<br>
|
||||||
* 注意:
|
* 注意:
|
||||||
*
|
*
|
||||||
@ -47,7 +32,7 @@ import java.util.TimeZone;
|
|||||||
* 当isMatchSecond为{@code true}时才会匹配秒部分
|
* 当isMatchSecond为{@code true}时才会匹配秒部分
|
||||||
* 默认都是关闭的
|
* 默认都是关闭的
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
* <p>
|
||||||
* 对于每一个子表达式,同样支持以下形式:
|
* 对于每一个子表达式,同样支持以下形式:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li><strong>*</strong> :表示匹配这个位置所有的时间</li>
|
* <li><strong>*</strong> :表示匹配这个位置所有的时间</li>
|
||||||
@ -62,10 +47,10 @@ import java.util.TimeZone;
|
|||||||
* <pre>
|
* <pre>
|
||||||
* 间隔(/) > 区间(-) > 列表(,)
|
* 间隔(/) > 区间(-) > 列表(,)
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
* <p>
|
||||||
* 例如 2,3,6/3中,由于“/”优先级高,因此相当于2,3,(6/3),结果与 2,3,6等价<br>
|
* 例如 2,3,6/3中,由于“/”优先级高,因此相当于2,3,(6/3),结果与 2,3,6等价<br>
|
||||||
* <br>
|
* <br>
|
||||||
*
|
* <p>
|
||||||
* 一些例子:
|
* 一些例子:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li><strong>5 * * * *</strong> :每个点钟的5分执行,00:05,01:05……</li>
|
* <li><strong>5 * * * *</strong> :每个点钟的5分执行,00:05,01:05……</li>
|
||||||
@ -77,36 +62,11 @@ import java.util.TimeZone;
|
|||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Looly
|
* @author Looly
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class CronPattern {
|
public class CronPattern {
|
||||||
|
|
||||||
private static final ValueParser SECOND_VALUE_PARSER = new SecondValueParser();
|
|
||||||
private static final ValueParser MINUTE_VALUE_PARSER = new MinuteValueParser();
|
|
||||||
private static final ValueParser HOUR_VALUE_PARSER = new HourValueParser();
|
|
||||||
private static final ValueParser DAY_OF_MONTH_VALUE_PARSER = new DayOfMonthValueParser();
|
|
||||||
private static final ValueParser MONTH_VALUE_PARSER = new MonthValueParser();
|
|
||||||
private static final ValueParser DAY_OF_WEEK_VALUE_PARSER = new DayOfWeekValueParser();
|
|
||||||
private static final ValueParser YEAR_VALUE_PARSER = new YearValueParser();
|
|
||||||
|
|
||||||
private final String pattern;
|
private final String pattern;
|
||||||
|
private final MatcherTable matcherTable;
|
||||||
/** 秒字段匹配列表 */
|
|
||||||
private final List<ValueMatcher> secondMatchers = new ArrayList<>();
|
|
||||||
/** 分字段匹配列表 */
|
|
||||||
private final List<ValueMatcher> minuteMatchers = new ArrayList<>();
|
|
||||||
/** 时字段匹配列表 */
|
|
||||||
private final List<ValueMatcher> hourMatchers = new ArrayList<>();
|
|
||||||
/** 每月几号字段匹配列表 */
|
|
||||||
private final List<ValueMatcher> dayOfMonthMatchers = new ArrayList<>();
|
|
||||||
/** 月字段匹配列表 */
|
|
||||||
private final List<ValueMatcher> monthMatchers = new ArrayList<>();
|
|
||||||
/** 星期字段匹配列表 */
|
|
||||||
private final List<ValueMatcher> dayOfWeekMatchers = new ArrayList<>();
|
|
||||||
/** 年字段匹配列表 */
|
|
||||||
private final List<ValueMatcher> yearMatchers = new ArrayList<>();
|
|
||||||
/** 匹配器个数,取决于复合任务表达式中的单一表达式个数 */
|
|
||||||
private int matcherSize;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
@ -115,14 +75,15 @@ public class CronPattern {
|
|||||||
*/
|
*/
|
||||||
public CronPattern(String pattern) {
|
public CronPattern(String pattern) {
|
||||||
this.pattern = pattern;
|
this.pattern = pattern;
|
||||||
parseGroupPattern(pattern);
|
this.matcherTable = CronPatternParser.create().parse(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------- match start
|
// --------------------------------------------------------------------------------------- match start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 给定时间是否匹配定时任务表达式
|
* 给定时间是否匹配定时任务表达式
|
||||||
*
|
*
|
||||||
* @param millis 时间毫秒数
|
* @param millis 时间毫秒数
|
||||||
* @param isMatchSecond 是否匹配秒
|
* @param isMatchSecond 是否匹配秒
|
||||||
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
|
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
|
||||||
*/
|
*/
|
||||||
@ -133,8 +94,8 @@ public class CronPattern {
|
|||||||
/**
|
/**
|
||||||
* 给定时间是否匹配定时任务表达式
|
* 给定时间是否匹配定时任务表达式
|
||||||
*
|
*
|
||||||
* @param timezone 时区 {@link TimeZone}
|
* @param timezone 时区 {@link TimeZone}
|
||||||
* @param millis 时间毫秒数
|
* @param millis 时间毫秒数
|
||||||
* @param isMatchSecond 是否匹配秒
|
* @param isMatchSecond 是否匹配秒
|
||||||
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
|
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
|
||||||
*/
|
*/
|
||||||
@ -147,11 +108,12 @@ public class CronPattern {
|
|||||||
/**
|
/**
|
||||||
* 给定时间是否匹配定时任务表达式
|
* 给定时间是否匹配定时任务表达式
|
||||||
*
|
*
|
||||||
* @param calendar 时间
|
* @param calendar 时间
|
||||||
* @param isMatchSecond 是否匹配秒
|
* @param isMatchSecond 是否匹配秒
|
||||||
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
|
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
|
||||||
*/
|
*/
|
||||||
public boolean match(GregorianCalendar calendar, boolean isMatchSecond) {
|
public boolean match(GregorianCalendar calendar, boolean isMatchSecond) {
|
||||||
|
final int second = isMatchSecond ? calendar.get(Calendar.SECOND) : -1;
|
||||||
final int minute = calendar.get(Calendar.MINUTE);
|
final int minute = calendar.get(Calendar.MINUTE);
|
||||||
final int hour = calendar.get(Calendar.HOUR_OF_DAY);
|
final int hour = calendar.get(Calendar.HOUR_OF_DAY);
|
||||||
final int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
|
final int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
|
||||||
@ -159,20 +121,7 @@ public class CronPattern {
|
|||||||
final int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1; // 星期从0开始,0和7都表示周日
|
final int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1; // 星期从0开始,0和7都表示周日
|
||||||
final int year = calendar.get(Calendar.YEAR);
|
final int year = calendar.get(Calendar.YEAR);
|
||||||
|
|
||||||
boolean eval;
|
return this.matcherTable.match(second, minute, hour, dayOfMonth, month, dayOfWeek, year);
|
||||||
for (int i = 0; i < matcherSize; i++) {
|
|
||||||
eval = ((false == isMatchSecond) || secondMatchers.get(i).match(calendar.get(Calendar.SECOND))) // 匹配秒(非秒匹配模式下始终返回true)
|
|
||||||
&& minuteMatchers.get(i).match(minute)// 匹配分
|
|
||||||
&& hourMatchers.get(i).match(hour)// 匹配时
|
|
||||||
&& isMatchDayOfMonth(dayOfMonthMatchers.get(i), dayOfMonth, month, calendar.isLeapYear(year))// 匹配日
|
|
||||||
&& monthMatchers.get(i).match(month) // 匹配月
|
|
||||||
&& dayOfWeekMatchers.get(i).match(dayOfWeek)// 匹配周
|
|
||||||
&& isMatch(yearMatchers, i, year);// 匹配年
|
|
||||||
if (eval) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
// --------------------------------------------------------------------------------------- match end
|
// --------------------------------------------------------------------------------------- match end
|
||||||
|
|
||||||
@ -180,114 +129,4 @@ public class CronPattern {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return this.pattern;
|
return this.pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------- Private method start
|
|
||||||
/**
|
|
||||||
* 是否匹配日(指定月份的第几天)
|
|
||||||
*
|
|
||||||
* @param matcher {@link ValueMatcher}
|
|
||||||
* @param dayOfMonth 日
|
|
||||||
* @param month 月
|
|
||||||
* @param isLeapYear 是否闰年
|
|
||||||
* @return 是否匹配
|
|
||||||
*/
|
|
||||||
private static boolean isMatchDayOfMonth(ValueMatcher matcher, int dayOfMonth, int month, boolean isLeapYear) {
|
|
||||||
return ((matcher instanceof DayOfMonthValueMatcher) //
|
|
||||||
? ((DayOfMonthValueMatcher) matcher).match(dayOfMonth, month, isLeapYear) //
|
|
||||||
: matcher.match(dayOfMonth));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否匹配指定的日期时间位置
|
|
||||||
*
|
|
||||||
* @param matchers 匹配器列表
|
|
||||||
* @param index 位置
|
|
||||||
* @param value 被匹配的值
|
|
||||||
* @return 是否匹配
|
|
||||||
* @since 4.0.2
|
|
||||||
*/
|
|
||||||
private static boolean isMatch(List<ValueMatcher> matchers, int index, int value) {
|
|
||||||
return (matchers.size() <= index) || matchers.get(index).match(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析复合任务表达式
|
|
||||||
*
|
|
||||||
* @param groupPattern 复合表达式
|
|
||||||
*/
|
|
||||||
private void parseGroupPattern(String groupPattern) {
|
|
||||||
List<String> patternList = StrUtil.split(groupPattern, '|');
|
|
||||||
for (String pattern : patternList) {
|
|
||||||
parseSinglePattern(pattern);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析单一定时任务表达式
|
|
||||||
*
|
|
||||||
* @param pattern 表达式
|
|
||||||
*/
|
|
||||||
private void parseSinglePattern(String pattern) {
|
|
||||||
final String[] parts = pattern.split("\\s");
|
|
||||||
|
|
||||||
int offset = 0;// 偏移量用于兼容Quartz表达式,当表达式有6或7项时,第一项为秒
|
|
||||||
if (parts.length == 6 || parts.length == 7) {
|
|
||||||
offset = 1;
|
|
||||||
} else if (parts.length != 5) {
|
|
||||||
throw new CronException("Pattern [{}] is invalid, it must be 5-7 parts!", pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 秒
|
|
||||||
if (1 == offset) {// 支持秒的表达式
|
|
||||||
try {
|
|
||||||
this.secondMatchers.add(ValueMatcherBuilder.build(parts[0], SECOND_VALUE_PARSER));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new CronException(e, "Invalid pattern [{}], parsing 'second' field error!", pattern);
|
|
||||||
}
|
|
||||||
} else {// 不支持秒的表达式,则第一位按照表达式生成时间的秒数赋值,表示整分匹配
|
|
||||||
this.secondMatchers.add(ValueMatcherBuilder.build(String.valueOf(DateUtil.date().second()), SECOND_VALUE_PARSER));
|
|
||||||
}
|
|
||||||
// 分
|
|
||||||
try {
|
|
||||||
this.minuteMatchers.add(ValueMatcherBuilder.build(parts[offset], MINUTE_VALUE_PARSER));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new CronException(e, "Invalid pattern [{}], parsing 'minute' field error!", pattern);
|
|
||||||
}
|
|
||||||
// 小时
|
|
||||||
try {
|
|
||||||
this.hourMatchers.add(ValueMatcherBuilder.build(parts[1 + offset], HOUR_VALUE_PARSER));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new CronException(e, "Invalid pattern [{}], parsing 'hour' field error!", pattern);
|
|
||||||
}
|
|
||||||
// 每月第几天
|
|
||||||
try {
|
|
||||||
this.dayOfMonthMatchers.add(ValueMatcherBuilder.build(parts[2 + offset], DAY_OF_MONTH_VALUE_PARSER));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new CronException(e, "Invalid pattern [{}], parsing 'day of month' field error!", pattern);
|
|
||||||
}
|
|
||||||
// 月
|
|
||||||
try {
|
|
||||||
this.monthMatchers.add(ValueMatcherBuilder.build(parts[3 + offset], MONTH_VALUE_PARSER));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new CronException(e, "Invalid pattern [{}], parsing 'month' field error!", pattern);
|
|
||||||
}
|
|
||||||
// 星期几
|
|
||||||
try {
|
|
||||||
this.dayOfWeekMatchers.add(ValueMatcherBuilder.build(parts[4 + offset], DAY_OF_WEEK_VALUE_PARSER));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new CronException(e, "Invalid pattern [{}], parsing 'day of week' field error!", pattern);
|
|
||||||
}
|
|
||||||
// 年
|
|
||||||
if (parts.length == 7) {// 支持年的表达式
|
|
||||||
try {
|
|
||||||
this.yearMatchers.add(ValueMatcherBuilder.build(parts[6], YEAR_VALUE_PARSER));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new CronException(e, "Invalid pattern [{}], parsing 'year' field error!", pattern);
|
|
||||||
}
|
|
||||||
} else {// 不支持年的表达式,全部匹配
|
|
||||||
this.yearMatchers.add(new AlwaysTrueValueMatcher());
|
|
||||||
}
|
|
||||||
matcherSize++;
|
|
||||||
}
|
|
||||||
// -------------------------------------------------------------------------------------- Private method end
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,119 @@
|
|||||||
|
package cn.hutool.cron.pattern.matcher;
|
||||||
|
|
||||||
|
import java.time.Year;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间匹配表,用于存放定时任务表达式解析后的结构信息
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public class MatcherTable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匹配器个数,取决于复合任务表达式中的单一表达式个数
|
||||||
|
*/
|
||||||
|
public int matcherSize;
|
||||||
|
/**
|
||||||
|
* 秒字段匹配列表
|
||||||
|
*/
|
||||||
|
public final List<ValueMatcher> secondMatchers;
|
||||||
|
/**
|
||||||
|
* 分字段匹配列表
|
||||||
|
*/
|
||||||
|
public final List<ValueMatcher> minuteMatchers;
|
||||||
|
/**
|
||||||
|
* 时字段匹配列表
|
||||||
|
*/
|
||||||
|
public final List<ValueMatcher> hourMatchers;
|
||||||
|
/**
|
||||||
|
* 每月几号字段匹配列表
|
||||||
|
*/
|
||||||
|
public final List<ValueMatcher> dayOfMonthMatchers;
|
||||||
|
/**
|
||||||
|
* 月字段匹配列表
|
||||||
|
*/
|
||||||
|
public final List<ValueMatcher> monthMatchers;
|
||||||
|
/**
|
||||||
|
* 星期字段匹配列表
|
||||||
|
*/
|
||||||
|
public final List<ValueMatcher> dayOfWeekMatchers;
|
||||||
|
/**
|
||||||
|
* 年字段匹配列表
|
||||||
|
*/
|
||||||
|
public final List<ValueMatcher> yearMatchers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param size 表达式个数,用于表示复合表达式中单个表达式个数
|
||||||
|
*/
|
||||||
|
public MatcherTable(int size) {
|
||||||
|
matcherSize = size;
|
||||||
|
secondMatchers = new ArrayList<>(size);
|
||||||
|
minuteMatchers = new ArrayList<>(size);
|
||||||
|
hourMatchers = new ArrayList<>(size);
|
||||||
|
dayOfMonthMatchers = new ArrayList<>(size);
|
||||||
|
monthMatchers = new ArrayList<>(size);
|
||||||
|
dayOfWeekMatchers = new ArrayList<>(size);
|
||||||
|
yearMatchers = new ArrayList<>(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给定时间是否匹配定时任务表达式
|
||||||
|
*
|
||||||
|
* @param second 秒数,-1表示不匹配此项
|
||||||
|
* @param minute 分钟
|
||||||
|
* @param hour 小时
|
||||||
|
* @param dayOfMonth 天
|
||||||
|
* @param month 月
|
||||||
|
* @param dayOfWeek 周几
|
||||||
|
* @param year 年
|
||||||
|
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
|
||||||
|
*/
|
||||||
|
public boolean match(int second, int minute, int hour, int dayOfMonth, int month, int dayOfWeek, int year) {
|
||||||
|
for (int i = 0; i < matcherSize; i++) {
|
||||||
|
boolean eval = ((second < 0) || secondMatchers.get(i).match(second)) // 匹配秒(非秒匹配模式下始终返回true)
|
||||||
|
&& minuteMatchers.get(i).match(minute)// 匹配分
|
||||||
|
&& hourMatchers.get(i).match(hour)// 匹配时
|
||||||
|
&& isMatchDayOfMonth(dayOfMonthMatchers.get(i), dayOfMonth, month, Year.isLeap(year))// 匹配日
|
||||||
|
&& monthMatchers.get(i).match(month) // 匹配月
|
||||||
|
&& dayOfWeekMatchers.get(i).match(dayOfWeek)// 匹配周
|
||||||
|
&& isMatch(yearMatchers, i, year);// 匹配年
|
||||||
|
if (eval) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否匹配日(指定月份的第几天)
|
||||||
|
*
|
||||||
|
* @param matcher {@link ValueMatcher}
|
||||||
|
* @param dayOfMonth 日
|
||||||
|
* @param month 月
|
||||||
|
* @param isLeapYear 是否闰年
|
||||||
|
* @return 是否匹配
|
||||||
|
*/
|
||||||
|
private static boolean isMatchDayOfMonth(ValueMatcher matcher, int dayOfMonth, int month, boolean isLeapYear) {
|
||||||
|
return ((matcher instanceof DayOfMonthValueMatcher) //
|
||||||
|
? ((DayOfMonthValueMatcher) matcher).match(dayOfMonth, month, isLeapYear) //
|
||||||
|
: matcher.match(dayOfMonth));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否匹配指定的日期时间位置
|
||||||
|
*
|
||||||
|
* @param matchers 匹配器列表
|
||||||
|
* @param index 位置
|
||||||
|
* @param value 被匹配的值
|
||||||
|
* @return 是否匹配
|
||||||
|
* @since 4.0.2
|
||||||
|
*/
|
||||||
|
private static boolean isMatch(List<ValueMatcher> matchers, int index, int value) {
|
||||||
|
return (matchers.size() <= index) || matchers.get(index).match(value);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,132 @@
|
|||||||
|
package cn.hutool.cron.pattern.parser;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.cron.CronException;
|
||||||
|
import cn.hutool.cron.pattern.matcher.AlwaysTrueValueMatcher;
|
||||||
|
import cn.hutool.cron.pattern.matcher.MatcherTable;
|
||||||
|
import cn.hutool.cron.pattern.matcher.ValueMatcherBuilder;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定时任务表达式解析器,用于将表达式字符串解析为{@link MatcherTable}
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public class CronPatternParser {
|
||||||
|
|
||||||
|
private static final ValueParser SECOND_VALUE_PARSER = new SecondValueParser();
|
||||||
|
private static final ValueParser MINUTE_VALUE_PARSER = new MinuteValueParser();
|
||||||
|
private static final ValueParser HOUR_VALUE_PARSER = new HourValueParser();
|
||||||
|
private static final ValueParser DAY_OF_MONTH_VALUE_PARSER = new DayOfMonthValueParser();
|
||||||
|
private static final ValueParser MONTH_VALUE_PARSER = new MonthValueParser();
|
||||||
|
private static final ValueParser DAY_OF_WEEK_VALUE_PARSER = new DayOfWeekValueParser();
|
||||||
|
private static final ValueParser YEAR_VALUE_PARSER = new YearValueParser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建表达式解析器
|
||||||
|
*
|
||||||
|
* @return CronPatternParser
|
||||||
|
*/
|
||||||
|
public static CronPatternParser create() {
|
||||||
|
return new CronPatternParser();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MatcherTable matcherTable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析表达式到匹配表中
|
||||||
|
*
|
||||||
|
* @param cronPattern 复合表达式
|
||||||
|
* @return {@link MatcherTable}
|
||||||
|
*/
|
||||||
|
public MatcherTable parse(String cronPattern) {
|
||||||
|
parseGroupPattern(cronPattern);
|
||||||
|
return this.matcherTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析复合任务表达式,格式为:
|
||||||
|
* <pre>
|
||||||
|
* cronA | cronB | ...
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param groupPattern 复合表达式
|
||||||
|
*/
|
||||||
|
private void parseGroupPattern(String groupPattern) {
|
||||||
|
final List<String> patternList = StrUtil.split(groupPattern, '|');
|
||||||
|
matcherTable = new MatcherTable(patternList.size());
|
||||||
|
for (String pattern : patternList) {
|
||||||
|
parseSinglePattern(pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析单一定时任务表达式
|
||||||
|
*
|
||||||
|
* @param pattern 表达式
|
||||||
|
*/
|
||||||
|
private void parseSinglePattern(String pattern) {
|
||||||
|
final String[] parts = pattern.split("\\s");
|
||||||
|
|
||||||
|
int offset = 0;// 偏移量用于兼容Quartz表达式,当表达式有6或7项时,第一项为秒
|
||||||
|
if (parts.length == 6 || parts.length == 7) {
|
||||||
|
offset = 1;
|
||||||
|
} else if (parts.length != 5) {
|
||||||
|
throw new CronException("Pattern [{}] is invalid, it must be 5-7 parts!", pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 秒
|
||||||
|
if (1 == offset) {// 支持秒的表达式
|
||||||
|
try {
|
||||||
|
matcherTable.secondMatchers.add(ValueMatcherBuilder.build(parts[0], SECOND_VALUE_PARSER));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CronException(e, "Invalid pattern [{}], parsing 'second' field error!", pattern);
|
||||||
|
}
|
||||||
|
} else {// 不支持秒的表达式,则第一位按照表达式生成时间的秒数赋值,表示整分匹配
|
||||||
|
matcherTable.secondMatchers.add(ValueMatcherBuilder.build(String.valueOf(DateUtil.date().second()), SECOND_VALUE_PARSER));
|
||||||
|
}
|
||||||
|
// 分
|
||||||
|
try {
|
||||||
|
matcherTable.minuteMatchers.add(ValueMatcherBuilder.build(parts[offset], MINUTE_VALUE_PARSER));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CronException(e, "Invalid pattern [{}], parsing 'minute' field error!", pattern);
|
||||||
|
}
|
||||||
|
// 小时
|
||||||
|
try {
|
||||||
|
matcherTable.hourMatchers.add(ValueMatcherBuilder.build(parts[1 + offset], HOUR_VALUE_PARSER));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CronException(e, "Invalid pattern [{}], parsing 'hour' field error!", pattern);
|
||||||
|
}
|
||||||
|
// 每月第几天
|
||||||
|
try {
|
||||||
|
matcherTable.dayOfMonthMatchers.add(ValueMatcherBuilder.build(parts[2 + offset], DAY_OF_MONTH_VALUE_PARSER));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CronException(e, "Invalid pattern [{}], parsing 'day of month' field error!", pattern);
|
||||||
|
}
|
||||||
|
// 月
|
||||||
|
try {
|
||||||
|
matcherTable.monthMatchers.add(ValueMatcherBuilder.build(parts[3 + offset], MONTH_VALUE_PARSER));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CronException(e, "Invalid pattern [{}], parsing 'month' field error!", pattern);
|
||||||
|
}
|
||||||
|
// 星期几
|
||||||
|
try {
|
||||||
|
matcherTable.dayOfWeekMatchers.add(ValueMatcherBuilder.build(parts[4 + offset], DAY_OF_WEEK_VALUE_PARSER));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CronException(e, "Invalid pattern [{}], parsing 'day of week' field error!", pattern);
|
||||||
|
}
|
||||||
|
// 年
|
||||||
|
if (parts.length == 7) {// 支持年的表达式
|
||||||
|
try {
|
||||||
|
matcherTable.yearMatchers.add(ValueMatcherBuilder.build(parts[6], YEAR_VALUE_PARSER));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CronException(e, "Invalid pattern [{}], parsing 'year' field error!", pattern);
|
||||||
|
}
|
||||||
|
} else {// 不支持年的表达式,全部匹配
|
||||||
|
matcherTable.yearMatchers.add(new AlwaysTrueValueMatcher());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@ import cn.hutool.cron.CronException;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 月份值处理<br>
|
* 月份值处理<br>
|
||||||
* 限定于1-12,1表示一月,支持别名,如一月是{@code jan}
|
* 限定于1-12,1表示一月,支持别名(忽略大小写),如一月是{@code jan}
|
||||||
*
|
*
|
||||||
* @author Looly
|
* @author Looly
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user