mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
fix code
This commit is contained in:
parent
c2e1bbafc8
commit
5966dcc626
@ -1,10 +1,13 @@
|
||||
package cn.hutool.cron.pattern;
|
||||
|
||||
import cn.hutool.cron.pattern.matcher.MatcherTable;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.cron.pattern.matcher.PatternMatcher;
|
||||
import cn.hutool.cron.pattern.parser.PatternParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
@ -66,7 +69,7 @@ import java.util.TimeZone;
|
||||
public class CronPattern {
|
||||
|
||||
private final String pattern;
|
||||
private final MatcherTable matcherTable;
|
||||
private final List<PatternMatcher> matchers;
|
||||
|
||||
/**
|
||||
* 解析表达式为 CronPattern
|
||||
@ -86,11 +89,9 @@ public class CronPattern {
|
||||
*/
|
||||
public CronPattern(String pattern) {
|
||||
this.pattern = pattern;
|
||||
this.matcherTable = PatternParser.parse(pattern);
|
||||
this.matchers = PatternParser.parse(pattern);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------- match start
|
||||
|
||||
/**
|
||||
* 给定时间是否匹配定时任务表达式
|
||||
*
|
||||
@ -116,24 +117,6 @@ public class CronPattern {
|
||||
return match(calendar, isMatchSecond);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回匹配到的下一个时间
|
||||
*
|
||||
* @param calendar 时间
|
||||
* @return 匹配到的下一个时间
|
||||
*/
|
||||
public Calendar nextMatchAfter(Calendar calendar) {
|
||||
final int second = calendar.get(Calendar.SECOND);
|
||||
final int minute = calendar.get(Calendar.MINUTE);
|
||||
final int hour = calendar.get(Calendar.HOUR_OF_DAY);
|
||||
final int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
|
||||
final int month = calendar.get(Calendar.MONTH) + 1;// 月份从1开始
|
||||
final int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1; // 星期从0开始,0和7都表示周日
|
||||
final int year = calendar.get(Calendar.YEAR);
|
||||
|
||||
return this.matcherTable.nextMatchAfter(second, minute, hour, dayOfMonth, month, dayOfWeek, year, calendar.getTimeZone());
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定时间是否匹配定时任务表达式
|
||||
*
|
||||
@ -150,12 +133,73 @@ public class CronPattern {
|
||||
final int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1; // 星期从0开始,0和7都表示周日
|
||||
final int year = calendar.get(Calendar.YEAR);
|
||||
|
||||
return this.matcherTable.match(second, minute, hour, dayOfMonth, month, dayOfWeek, year);
|
||||
return match(second, minute, hour, dayOfMonth, month, dayOfWeek, year);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回匹配到的下一个时间
|
||||
*
|
||||
* @param calendar 时间
|
||||
* @return 匹配到的下一个时间
|
||||
*/
|
||||
public Calendar nextMatchAfter(Calendar calendar) {
|
||||
final int second = calendar.get(Calendar.SECOND);
|
||||
final int minute = calendar.get(Calendar.MINUTE);
|
||||
final int hour = calendar.get(Calendar.HOUR_OF_DAY);
|
||||
final int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
|
||||
final int month = calendar.get(Calendar.MONTH) + 1;// 月份从1开始
|
||||
final int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1; // 星期从0开始,0和7都表示周日
|
||||
final int year = calendar.get(Calendar.YEAR);
|
||||
|
||||
return nextMatchAfter(second, minute, hour, dayOfMonth, month, dayOfWeek, year, calendar.getTimeZone());
|
||||
}
|
||||
// --------------------------------------------------------------------------------------- match end
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定时间是否匹配定时任务表达式
|
||||
*
|
||||
* @param second 秒数,-1表示不匹配此项
|
||||
* @param minute 分钟
|
||||
* @param hour 小时
|
||||
* @param dayOfMonth 天
|
||||
* @param month 月,从1开始
|
||||
* @param dayOfWeek 周,从0开始,0和7都表示周日
|
||||
* @param year 年
|
||||
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
|
||||
*/
|
||||
private boolean match(int second, int minute, int hour, int dayOfMonth, int month, int dayOfWeek, int year) {
|
||||
for (PatternMatcher matcher : matchers) {
|
||||
if (matcher.match(second, minute, hour, dayOfMonth, month, dayOfWeek, year)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一个最近的匹配日期时间
|
||||
*
|
||||
* @param second 秒
|
||||
* @param minute 分
|
||||
* @param hour 时
|
||||
* @param dayOfMonth 天
|
||||
* @param month 月(从1开始)
|
||||
* @param dayOfWeek 周(从0开始, 0表示周日)
|
||||
* @param year 年
|
||||
* @param zone 时区
|
||||
* @return {@link Calendar}
|
||||
*/
|
||||
private Calendar nextMatchAfter(int second, int minute, int hour, int dayOfMonth, int month, int dayOfWeek, int year, TimeZone zone) {
|
||||
List<Calendar> nextMatchs = new ArrayList<>(second);
|
||||
for (PatternMatcher matcher : matchers) {
|
||||
nextMatchs.add(matcher.nextMatchAfter(
|
||||
second, minute, hour, dayOfMonth, month, dayOfWeek, year, zone));
|
||||
}
|
||||
// 返回匹配到的最早日期
|
||||
return CollUtil.min(nextMatchs);
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ import cn.hutool.core.util.StrUtil;
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class AlwaysTrueValueMatcher implements ValueMatcher {
|
||||
public class AlwaysTrueMatcher implements PartMatcher {
|
||||
|
||||
public static AlwaysTrueValueMatcher INSTANCE = new AlwaysTrueValueMatcher();
|
||||
public static AlwaysTrueMatcher INSTANCE = new AlwaysTrueMatcher();
|
||||
|
||||
@Override
|
||||
public boolean match(Integer t) {
|
@ -12,7 +12,7 @@ import java.util.List;
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class BoolArrayValueMatcher implements ValueMatcher {
|
||||
public class BoolArrayMatcher implements PartMatcher {
|
||||
|
||||
/**
|
||||
* 用户定义此字段的最小值
|
||||
@ -25,7 +25,7 @@ public class BoolArrayValueMatcher implements ValueMatcher {
|
||||
*
|
||||
* @param intValueList 匹配值列表
|
||||
*/
|
||||
public BoolArrayValueMatcher(List<Integer> intValueList) {
|
||||
public BoolArrayMatcher(List<Integer> intValueList) {
|
||||
Assert.isTrue(CollUtil.isNotEmpty(intValueList), "Values must be not empty!");
|
||||
bValues = new boolean[Collections.max(intValueList) + 1];
|
||||
int min = Integer.MAX_VALUE;
|
@ -1,95 +0,0 @@
|
||||
package cn.hutool.cron.pattern.matcher;
|
||||
|
||||
import java.time.Year;
|
||||
|
||||
/**
|
||||
* 日期和时间的单一匹配器
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public class DateTimeMatcher {
|
||||
|
||||
/**
|
||||
* 秒字段匹配列表
|
||||
*/
|
||||
final ValueMatcher secondMatcher;
|
||||
/**
|
||||
* 分字段匹配列表
|
||||
*/
|
||||
final ValueMatcher minuteMatcher;
|
||||
/**
|
||||
* 时字段匹配列表
|
||||
*/
|
||||
final ValueMatcher hourMatcher;
|
||||
/**
|
||||
* 每月几号字段匹配列表
|
||||
*/
|
||||
final ValueMatcher dayOfMonthMatcher;
|
||||
/**
|
||||
* 月字段匹配列表
|
||||
*/
|
||||
final ValueMatcher monthMatcher;
|
||||
/**
|
||||
* 星期字段匹配列表
|
||||
*/
|
||||
final ValueMatcher dayOfWeekMatcher;
|
||||
/**
|
||||
* 年字段匹配列表
|
||||
*/
|
||||
final ValueMatcher yearMatcher;
|
||||
|
||||
public DateTimeMatcher(ValueMatcher secondMatcher,
|
||||
ValueMatcher minuteMatcher,
|
||||
ValueMatcher hourMatchers,
|
||||
ValueMatcher dayOfMonthMatchers,
|
||||
ValueMatcher monthMatchers,
|
||||
ValueMatcher dayOfWeekMatchers,
|
||||
ValueMatcher yearMatchers) {
|
||||
|
||||
this.secondMatcher = secondMatcher;
|
||||
this.minuteMatcher = minuteMatcher;
|
||||
this.hourMatcher = hourMatchers;
|
||||
this.dayOfMonthMatcher = dayOfMonthMatchers;
|
||||
this.monthMatcher = monthMatchers;
|
||||
this.dayOfWeekMatcher = dayOfWeekMatchers;
|
||||
this.yearMatcher = yearMatchers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定时间是否匹配定时任务表达式
|
||||
*
|
||||
* @param second 秒数,-1表示不匹配此项
|
||||
* @param minute 分钟
|
||||
* @param hour 小时
|
||||
* @param dayOfMonth 天
|
||||
* @param month 月,从1开始
|
||||
* @param dayOfWeek 周,从0开始,0和7都表示周日
|
||||
* @param year 年
|
||||
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
|
||||
*/
|
||||
public boolean match(int second, int minute, int hour, int dayOfMonth, int month, int dayOfWeek, int year) {
|
||||
return ((second < 0) || secondMatcher.match(second)) // 匹配秒(非秒匹配模式下始终返回true)
|
||||
&& minuteMatcher.match(minute)// 匹配分
|
||||
&& hourMatcher.match(hour)// 匹配时
|
||||
&& isMatchDayOfMonth(dayOfMonthMatcher, dayOfMonth, month, Year.isLeap(year))// 匹配日
|
||||
&& monthMatcher.match(month) // 匹配月
|
||||
&& dayOfWeekMatcher.match(dayOfWeek)// 匹配周
|
||||
&& yearMatcher.match(year);// 匹配年
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否匹配日(指定月份的第几天)
|
||||
*
|
||||
* @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));
|
||||
}
|
||||
}
|
@ -10,14 +10,14 @@ import java.util.List;
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class DayOfMonthValueMatcher extends BoolArrayValueMatcher {
|
||||
public class DayOfMonthMatcher extends BoolArrayMatcher {
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param intValueList 匹配的日值
|
||||
*/
|
||||
public DayOfMonthValueMatcher(List<Integer> intValueList) {
|
||||
public DayOfMonthMatcher(List<Integer> intValueList) {
|
||||
super(intValueList);
|
||||
}
|
||||
|
@ -1,157 +0,0 @@
|
||||
package cn.hutool.cron.pattern.matcher;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.lang.mutable.MutableBool;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* 时间匹配表,用于存放定时任务表达式解析后的结构信息
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public class MatcherTable {
|
||||
|
||||
/**
|
||||
* 秒字段匹配列表
|
||||
*/
|
||||
public final List<DateTimeMatcher> matchers;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param size 表达式个数,用于表示复合表达式中单个表达式个数
|
||||
*/
|
||||
public MatcherTable(int size) {
|
||||
matchers = new ArrayList<>(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一个最近的匹配日期时间
|
||||
*
|
||||
* @param second 秒
|
||||
* @param minute 分
|
||||
* @param hour 时
|
||||
* @param dayOfMonth 天
|
||||
* @param month 月(从0开始)
|
||||
* @param dayOfWeek 周
|
||||
* @param year 年
|
||||
* @param zone 时区
|
||||
* @return {@link Calendar}
|
||||
*/
|
||||
public Calendar nextMatchAfter(int second, int minute, int hour, int dayOfMonth, int month, int dayOfWeek, int year, TimeZone zone) {
|
||||
List<Calendar> nextMatchs = new ArrayList<>(second);
|
||||
for (DateTimeMatcher matcher : matchers) {
|
||||
nextMatchs.add(singleNextMatchAfter(matcher, second, minute, hour,
|
||||
dayOfMonth, month, dayOfWeek, year, zone));
|
||||
}
|
||||
// 返回匹配到的最早日期
|
||||
return CollUtil.min(nextMatchs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一个匹配日期时间
|
||||
*
|
||||
* @param matcher 匹配器
|
||||
* @param second 秒
|
||||
* @param minute 分
|
||||
* @param hour 时
|
||||
* @param dayOfMonth 天
|
||||
* @param month 月(从0开始)
|
||||
* @param dayOfWeek 周
|
||||
* @param year 年
|
||||
* @param zone 时区
|
||||
* @return {@link Calendar}
|
||||
*/
|
||||
private static Calendar singleNextMatchAfter(DateTimeMatcher matcher, int second, int minute, int hour,
|
||||
int dayOfMonth, int month, int dayOfWeek, int year, TimeZone zone) {
|
||||
|
||||
Calendar calendar = Calendar.getInstance(zone);
|
||||
|
||||
// 上一个字段不一致,说明产生了新值,下一个字段使用最小值
|
||||
MutableBool isNextEquals = new MutableBool(true);
|
||||
// 年
|
||||
final int nextYear = nextAfter(matcher.yearMatcher, year, isNextEquals);
|
||||
calendar.set(Calendar.YEAR, nextYear);
|
||||
|
||||
// 周
|
||||
final int nextDayOfWeek = nextAfter(matcher.dayOfWeekMatcher, dayOfWeek, isNextEquals);
|
||||
calendar.set(Calendar.DAY_OF_WEEK, nextDayOfWeek);
|
||||
|
||||
// 月
|
||||
final int nextMonth = nextAfter(matcher.monthMatcher, month + 1, isNextEquals);
|
||||
calendar.set(Calendar.MONTH, nextMonth - 1);
|
||||
|
||||
// 日
|
||||
final int nextDayOfMonth = nextAfter(matcher.dayOfMonthMatcher, dayOfMonth, isNextEquals);
|
||||
calendar.set(Calendar.DAY_OF_MONTH, nextDayOfMonth);
|
||||
|
||||
// 时
|
||||
final int nextHour = nextAfter(matcher.hourMatcher, hour, isNextEquals);
|
||||
calendar.set(Calendar.HOUR, nextHour);
|
||||
|
||||
// 分
|
||||
int nextMinute = nextAfter(matcher.minuteMatcher, minute, isNextEquals);
|
||||
calendar.set(Calendar.MINUTE, nextMinute);
|
||||
|
||||
// 秒
|
||||
final int nextSecond = nextAfter(matcher.secondMatcher, second, isNextEquals);
|
||||
calendar.set(Calendar.SECOND, nextSecond);
|
||||
|
||||
Console.log(nextYear, nextDayOfWeek, nextMonth, nextDayOfMonth, nextHour, nextMinute, nextSecond);
|
||||
return calendar;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对应字段匹配器的下一个值
|
||||
*
|
||||
* @param matcher 匹配器
|
||||
* @param value 值
|
||||
* @param isNextEquals 是否下一个值和值相同。不同获取初始值,相同获取下一值,然后修改。
|
||||
* @return 下一个值,-1标识匹配所有值的情况,应获取整个字段的最小值
|
||||
*/
|
||||
private static int nextAfter(ValueMatcher matcher, int value, MutableBool isNextEquals) {
|
||||
int nextValue;
|
||||
if (isNextEquals.get()) {
|
||||
// 上一层级得到相同值,下级获取下个值
|
||||
nextValue = matcher.nextAfter(value);
|
||||
isNextEquals.set(nextValue == value);
|
||||
} else {
|
||||
// 上一层级的值得到了不同值,下级的所有值使用最小值
|
||||
if (matcher instanceof AlwaysTrueValueMatcher) {
|
||||
nextValue = value;
|
||||
} else if (matcher instanceof BoolArrayValueMatcher) {
|
||||
nextValue = ((BoolArrayValueMatcher) matcher).getMinValue();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid matcher: " + matcher.getClass().getName());
|
||||
}
|
||||
}
|
||||
return nextValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定时间是否匹配定时任务表达式
|
||||
*
|
||||
* @param second 秒数,-1表示不匹配此项
|
||||
* @param minute 分钟
|
||||
* @param hour 小时
|
||||
* @param dayOfMonth 天
|
||||
* @param month 月,从1开始
|
||||
* @param dayOfWeek 周,从0开始,0和7都表示周日
|
||||
* @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 (DateTimeMatcher matcher : matchers) {
|
||||
if (matcher.match(second, minute, hour, dayOfMonth, month, dayOfWeek, year)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -3,12 +3,12 @@ package cn.hutool.cron.pattern.matcher;
|
||||
import cn.hutool.core.lang.Matcher;
|
||||
|
||||
/**
|
||||
* 值匹配器<br>
|
||||
* 表达式中的某个位置部分匹配器<br>
|
||||
* 用于匹配日期位中对应数字是否匹配
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public interface ValueMatcher extends Matcher<Integer> {
|
||||
public interface PartMatcher extends Matcher<Integer> {
|
||||
/**
|
||||
* 获取指定值之后的匹配值,也可以是指定值本身
|
||||
* @param value 指定的值
|
@ -0,0 +1,166 @@
|
||||
package cn.hutool.cron.pattern.matcher;
|
||||
|
||||
import cn.hutool.core.lang.mutable.MutableBool;
|
||||
import cn.hutool.cron.pattern.Part;
|
||||
|
||||
import java.time.Year;
|
||||
import java.util.Calendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* 单一表达式的匹配器,匹配器由7个{@link PartMatcher}组成
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public class PatternMatcher {
|
||||
|
||||
private final PartMatcher[] matchers;
|
||||
|
||||
public PatternMatcher(PartMatcher secondMatcher,
|
||||
PartMatcher minuteMatcher,
|
||||
PartMatcher hourMatcher,
|
||||
PartMatcher dayOfMonthMatcher,
|
||||
PartMatcher monthMatcher,
|
||||
PartMatcher dayOfWeekMatcher,
|
||||
PartMatcher yearMatcher) {
|
||||
|
||||
matchers = new PartMatcher[]{
|
||||
secondMatcher,
|
||||
minuteMatcher,
|
||||
hourMatcher,
|
||||
dayOfMonthMatcher,
|
||||
monthMatcher,
|
||||
dayOfWeekMatcher,
|
||||
yearMatcher
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据表达式位置,获取对应的{@link PartMatcher}
|
||||
* @param part 表达式位置
|
||||
* @return {@link PartMatcher}
|
||||
*/
|
||||
public PartMatcher get(Part part){
|
||||
return matchers[part.ordinal()];
|
||||
}
|
||||
|
||||
//region match
|
||||
/**
|
||||
* 给定时间是否匹配定时任务表达式
|
||||
*
|
||||
* @param second 秒数,-1表示不匹配此项
|
||||
* @param minute 分钟
|
||||
* @param hour 小时
|
||||
* @param dayOfMonth 天
|
||||
* @param month 月,从1开始
|
||||
* @param dayOfWeek 周,从0开始,0和7都表示周日
|
||||
* @param year 年
|
||||
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
|
||||
*/
|
||||
public boolean match(int second, int minute, int hour, int dayOfMonth, int month, int dayOfWeek, int year) {
|
||||
return ((second < 0) || matchers[0].match(second)) // 匹配秒(非秒匹配模式下始终返回true)
|
||||
&& matchers[1].match(minute)// 匹配分
|
||||
&& matchers[2].match(hour)// 匹配时
|
||||
&& isMatchDayOfMonth(matchers[3], dayOfMonth, month, Year.isLeap(year))// 匹配日
|
||||
&& matchers[4].match(month) // 匹配月
|
||||
&& matchers[5].match(dayOfWeek)// 匹配周
|
||||
&& matchers[6].match(year);// 匹配年
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否匹配日(指定月份的第几天)
|
||||
*
|
||||
* @param matcher {@link PartMatcher}
|
||||
* @param dayOfMonth 日
|
||||
* @param month 月
|
||||
* @param isLeapYear 是否闰年
|
||||
* @return 是否匹配
|
||||
*/
|
||||
private static boolean isMatchDayOfMonth(PartMatcher matcher, int dayOfMonth, int month, boolean isLeapYear) {
|
||||
return ((matcher instanceof DayOfMonthMatcher) //
|
||||
? ((DayOfMonthMatcher) matcher).match(dayOfMonth, month, isLeapYear) //
|
||||
: matcher.match(dayOfMonth));
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region nextMatchAfter
|
||||
/**
|
||||
* 获取下一个匹配日期时间
|
||||
*
|
||||
* @param second 秒
|
||||
* @param minute 分
|
||||
* @param hour 时
|
||||
* @param dayOfMonth 天
|
||||
* @param month 月(从1开始)
|
||||
* @param dayOfWeek 周(从0开始, 0表示周日)
|
||||
* @param year 年
|
||||
* @param zone 时区
|
||||
* @return {@link Calendar}
|
||||
*/
|
||||
public Calendar nextMatchAfter(int second, int minute, int hour,
|
||||
int dayOfMonth, int month, int dayOfWeek, int year, TimeZone zone) {
|
||||
|
||||
Calendar calendar = Calendar.getInstance(zone);
|
||||
|
||||
// 上一个字段不一致,说明产生了新值,下一个字段使用最小值
|
||||
MutableBool isNextEquals = new MutableBool(true);
|
||||
// 年
|
||||
final int nextYear = nextAfter(get(Part.YEAR), year, isNextEquals);
|
||||
calendar.set(Calendar.YEAR, nextYear);
|
||||
|
||||
// 周
|
||||
final int nextDayOfWeek = nextAfter(get(Part.DAY_OF_WEEK), dayOfWeek, isNextEquals);
|
||||
calendar.set(Calendar.DAY_OF_WEEK, nextDayOfWeek + 1);
|
||||
|
||||
// 月
|
||||
final int nextMonth = nextAfter(get(Part.MONTH), month, isNextEquals);
|
||||
calendar.set(Calendar.MONTH, nextMonth - 1);
|
||||
|
||||
// 日
|
||||
final int nextDayOfMonth = nextAfter(get(Part.DAY_OF_MONTH), dayOfMonth, isNextEquals);
|
||||
calendar.set(Calendar.DAY_OF_MONTH, nextDayOfMonth);
|
||||
|
||||
// 时
|
||||
final int nextHour = nextAfter(get(Part.HOUR), hour, isNextEquals);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, nextHour);
|
||||
|
||||
// 分
|
||||
int nextMinute = nextAfter(get(Part.MINUTE), minute, isNextEquals);
|
||||
calendar.set(Calendar.MINUTE, nextMinute);
|
||||
|
||||
// 秒
|
||||
final int nextSecond = nextAfter(get(Part.SECOND), second, isNextEquals);
|
||||
calendar.set(Calendar.SECOND, nextSecond);
|
||||
|
||||
return calendar;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对应字段匹配器的下一个值
|
||||
*
|
||||
* @param matcher 匹配器
|
||||
* @param value 值
|
||||
* @param isNextEquals 是否下一个值和值相同。不同获取初始值,相同获取下一值,然后修改。
|
||||
* @return 下一个值,-1标识匹配所有值的情况,应获取整个字段的最小值
|
||||
*/
|
||||
private static int nextAfter(PartMatcher matcher, int value, MutableBool isNextEquals) {
|
||||
int nextValue;
|
||||
if (isNextEquals.get()) {
|
||||
// 上一层级得到相同值,下级获取下个值
|
||||
nextValue = matcher.nextAfter(value);
|
||||
isNextEquals.set(nextValue == value);
|
||||
} else {
|
||||
// 上一层级的值得到了不同值,下级的所有值使用最小值
|
||||
if (matcher instanceof AlwaysTrueMatcher) {
|
||||
nextValue = value;
|
||||
} else if (matcher instanceof BoolArrayMatcher) {
|
||||
nextValue = ((BoolArrayMatcher) matcher).getMinValue();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid matcher: " + matcher.getClass().getName());
|
||||
}
|
||||
}
|
||||
return nextValue;
|
||||
}
|
||||
//endregion
|
||||
}
|
@ -9,7 +9,7 @@ import java.util.LinkedHashSet;
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class YearValueMatcher implements ValueMatcher {
|
||||
public class YearValueMatcher implements PartMatcher {
|
||||
|
||||
private final LinkedHashSet<Integer> valueList;
|
||||
|
||||
|
@ -7,17 +7,17 @@ import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.cron.CronException;
|
||||
import cn.hutool.cron.pattern.Part;
|
||||
import cn.hutool.cron.pattern.matcher.AlwaysTrueValueMatcher;
|
||||
import cn.hutool.cron.pattern.matcher.BoolArrayValueMatcher;
|
||||
import cn.hutool.cron.pattern.matcher.DayOfMonthValueMatcher;
|
||||
import cn.hutool.cron.pattern.matcher.ValueMatcher;
|
||||
import cn.hutool.cron.pattern.matcher.AlwaysTrueMatcher;
|
||||
import cn.hutool.cron.pattern.matcher.BoolArrayMatcher;
|
||||
import cn.hutool.cron.pattern.matcher.DayOfMonthMatcher;
|
||||
import cn.hutool.cron.pattern.matcher.PartMatcher;
|
||||
import cn.hutool.cron.pattern.matcher.YearValueMatcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 定时任务表达式各个部分的解析器,根据{@link Part}指定不同部分,解析为{@link ValueMatcher}<br>
|
||||
* 定时任务表达式各个部分的解析器,根据{@link Part}指定不同部分,解析为{@link PartMatcher}<br>
|
||||
* 每个部分支持:
|
||||
* <ul>
|
||||
* <li><strong>*</strong> :表示匹配这个位置所有的时间</li>
|
||||
@ -56,21 +56,21 @@ public class PartParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将表达式解析为{@link ValueMatcher}<br>
|
||||
* 将表达式解析为{@link PartMatcher}<br>
|
||||
* <ul>
|
||||
* <li>* 或者 ? 返回{@link AlwaysTrueValueMatcher}</li>
|
||||
* <li>{@link Part#DAY_OF_MONTH} 返回{@link DayOfMonthValueMatcher}</li>
|
||||
* <li>* 或者 ? 返回{@link AlwaysTrueMatcher}</li>
|
||||
* <li>{@link Part#DAY_OF_MONTH} 返回{@link DayOfMonthMatcher}</li>
|
||||
* <li>{@link Part#YEAR} 返回{@link YearValueMatcher}</li>
|
||||
* <li>其他 返回{@link BoolArrayValueMatcher}</li>
|
||||
* <li>其他 返回{@link BoolArrayMatcher}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param value 表达式
|
||||
* @return {@link ValueMatcher}
|
||||
* @return {@link PartMatcher}
|
||||
*/
|
||||
public ValueMatcher parseAsValueMatcher(String value) {
|
||||
public PartMatcher parse(String value) {
|
||||
if (isMatchAllStr(value)) {
|
||||
//兼容Quartz的"?"表达式,不会出现互斥情况,与"*"作用相同
|
||||
return new AlwaysTrueValueMatcher();
|
||||
return new AlwaysTrueMatcher();
|
||||
}
|
||||
|
||||
final List<Integer> values = parseArray(value);
|
||||
@ -80,11 +80,11 @@ public class PartParser {
|
||||
|
||||
switch (this.part) {
|
||||
case DAY_OF_MONTH:
|
||||
return new DayOfMonthValueMatcher(values);
|
||||
return new DayOfMonthMatcher(values);
|
||||
case YEAR:
|
||||
return new YearValueMatcher(values);
|
||||
default:
|
||||
return new BoolArrayValueMatcher(values);
|
||||
return new BoolArrayMatcher(values);
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,6 +243,11 @@ public class PartParser {
|
||||
i = parseAlias(value);
|
||||
}
|
||||
|
||||
// 支持负数
|
||||
if(i < 0){
|
||||
i += part.getMax();
|
||||
}
|
||||
|
||||
// 周日可以用0或7表示,统一转换为0
|
||||
if(Part.DAY_OF_WEEK.equals(this.part) && Week.SUNDAY.getIso8601Value() == i){
|
||||
i = Week.SUNDAY.ordinal();
|
||||
|
@ -4,15 +4,15 @@ import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.cron.CronException;
|
||||
import cn.hutool.cron.pattern.Part;
|
||||
import cn.hutool.cron.pattern.matcher.AlwaysTrueValueMatcher;
|
||||
import cn.hutool.cron.pattern.matcher.DateTimeMatcher;
|
||||
import cn.hutool.cron.pattern.matcher.MatcherTable;
|
||||
import cn.hutool.cron.pattern.matcher.ValueMatcher;
|
||||
import cn.hutool.cron.pattern.matcher.AlwaysTrueMatcher;
|
||||
import cn.hutool.cron.pattern.matcher.PartMatcher;
|
||||
import cn.hutool.cron.pattern.matcher.PatternMatcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 定时任务表达式解析器,用于将表达式字符串解析为{@link MatcherTable}
|
||||
* 定时任务表达式解析器,用于将表达式字符串解析为{@link PatternMatcher}的列表
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.8.0
|
||||
@ -28,12 +28,12 @@ public class PatternParser {
|
||||
private static final PartParser YEAR_VALUE_PARSER = PartParser.of(Part.YEAR);
|
||||
|
||||
/**
|
||||
* 解析表达式到匹配表中
|
||||
* 解析表达式到匹配列表中
|
||||
*
|
||||
* @param cronPattern 复合表达式
|
||||
* @return {@link MatcherTable}
|
||||
* @return {@link List}
|
||||
*/
|
||||
public static MatcherTable parse(String cronPattern) {
|
||||
public static List<PatternMatcher> parse(String cronPattern) {
|
||||
return parseGroupPattern(cronPattern);
|
||||
}
|
||||
|
||||
@ -44,24 +44,24 @@ public class PatternParser {
|
||||
* </pre>
|
||||
*
|
||||
* @param groupPattern 复合表达式
|
||||
* @return {@link MatcherTable}
|
||||
* @return {@link List}
|
||||
*/
|
||||
private static MatcherTable parseGroupPattern(String groupPattern) {
|
||||
private static List<PatternMatcher> parseGroupPattern(String groupPattern) {
|
||||
final List<String> patternList = StrUtil.splitTrim(groupPattern, '|');
|
||||
final MatcherTable matcherTable = new MatcherTable(patternList.size());
|
||||
final List<PatternMatcher> patternMatchers = new ArrayList<>(patternList.size());
|
||||
for (String pattern : patternList) {
|
||||
matcherTable.matchers.add(parseSinglePattern(pattern));
|
||||
patternMatchers.add(parseSinglePattern(pattern));
|
||||
}
|
||||
return matcherTable;
|
||||
return patternMatchers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析单一定时任务表达式
|
||||
*
|
||||
* @param pattern 表达式
|
||||
* @return {@link DateTimeMatcher}
|
||||
* @return {@link PatternMatcher}
|
||||
*/
|
||||
private static DateTimeMatcher parseSinglePattern(String pattern) {
|
||||
private static PatternMatcher parseSinglePattern(String pattern) {
|
||||
final String[] parts = pattern.split("\\s+");
|
||||
Assert.checkBetween(parts.length, 5, 7,
|
||||
() -> new CronException("Pattern [{}] is invalid, it must be 5-7 parts!", pattern));
|
||||
@ -76,26 +76,26 @@ public class PatternParser {
|
||||
final String secondPart = (1 == offset) ? parts[0] : "0";
|
||||
|
||||
// 年
|
||||
ValueMatcher yearMatcher;
|
||||
PartMatcher yearMatcher;
|
||||
if (parts.length == 7) {// 支持年的表达式
|
||||
yearMatcher = YEAR_VALUE_PARSER.parseAsValueMatcher(parts[6]);
|
||||
yearMatcher = YEAR_VALUE_PARSER.parse(parts[6]);
|
||||
} else {// 不支持年的表达式,全部匹配
|
||||
yearMatcher = AlwaysTrueValueMatcher.INSTANCE;
|
||||
yearMatcher = AlwaysTrueMatcher.INSTANCE;
|
||||
}
|
||||
|
||||
return new DateTimeMatcher(
|
||||
return new PatternMatcher(
|
||||
// 秒
|
||||
SECOND_VALUE_PARSER.parseAsValueMatcher(secondPart),
|
||||
SECOND_VALUE_PARSER.parse(secondPart),
|
||||
// 分
|
||||
MINUTE_VALUE_PARSER.parseAsValueMatcher(parts[offset]),
|
||||
MINUTE_VALUE_PARSER.parse(parts[offset]),
|
||||
// 时
|
||||
HOUR_VALUE_PARSER.parseAsValueMatcher(parts[1 + offset]),
|
||||
HOUR_VALUE_PARSER.parse(parts[1 + offset]),
|
||||
// 天
|
||||
DAY_OF_MONTH_VALUE_PARSER.parseAsValueMatcher(parts[2 + offset]),
|
||||
DAY_OF_MONTH_VALUE_PARSER.parse(parts[2 + offset]),
|
||||
// 月
|
||||
MONTH_VALUE_PARSER.parseAsValueMatcher(parts[3 + offset]),
|
||||
MONTH_VALUE_PARSER.parse(parts[3 + offset]),
|
||||
// 周
|
||||
DAY_OF_WEEK_VALUE_PARSER.parseAsValueMatcher(parts[4 + offset]),
|
||||
DAY_OF_WEEK_VALUE_PARSER.parse(parts[4 + offset]),
|
||||
// 年
|
||||
yearMatcher
|
||||
);
|
||||
|
@ -2,6 +2,7 @@ package cn.hutool.cron.pattern;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Calendar;
|
||||
@ -13,7 +14,9 @@ public class CronPatternNextMatchTest {
|
||||
CronPattern pattern = new CronPattern("23 12 * 12 * * *");
|
||||
//noinspection ConstantConditions
|
||||
final Calendar calendar = pattern.nextMatchAfter(
|
||||
DateUtil.parse("2022-04-12 09:12:23").toCalendar());
|
||||
DateUtil.parse("2022-04-12 09:12:12").toCalendar());
|
||||
|
||||
Console.log(DateUtil.date(calendar));
|
||||
Assert.assertTrue(pattern.match(calendar, true));
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +122,14 @@ public class CronPatternTest {
|
||||
assertMatch(pattern, "2017-02-19 04:00:33");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void patternNegativeTest() {
|
||||
// -4表示倒数的数字,此处在小时上,-4表示 23 - 4,为19
|
||||
CronPattern pattern = CronPattern.of("* 0 -4 * * ?");
|
||||
assertMatch(pattern, "2017-02-09 19:00:00");
|
||||
assertMatch(pattern, "2017-02-19 19:00:33");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rangePatternTest() {
|
||||
CronPattern pattern = CronPattern.of("* 20/2 * * * ?");
|
||||
|
Loading…
x
Reference in New Issue
Block a user