mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-08-18 20:38:02 +08:00
change parser
This commit is contained in:
@@ -251,7 +251,7 @@ public class PropDesc {
|
||||
* @param value 属性值,可以为任意类型
|
||||
* @param ignoreNull 是否忽略{@code null}值,true表示忽略
|
||||
* @param ignoreError 是否忽略错误,包括转换错误和注入错误
|
||||
* @param override 是否覆盖目标值,如果不覆盖,会先读取bean的值,非{@code null}则写,否则忽略。如果覆盖,则不判断直接写
|
||||
* @param override 是否覆盖目标值,如果不覆盖,会先读取bean的值,{@code null}则写,否则忽略。如果覆盖,则不判断直接写
|
||||
* @return this
|
||||
* @since 5.7.17
|
||||
*/
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package cn.hutool.core.date;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.util.Calendar;
|
||||
|
||||
@@ -48,6 +50,12 @@ public enum Week {
|
||||
SATURDAY(Calendar.SATURDAY);
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
/**
|
||||
* Weeks aliases.
|
||||
*/
|
||||
private static final String[] ALIASES = {"sun", "mon", "tue", "wed", "thu", "fri", "sat"};
|
||||
private static final Week[] ENUMS = Week.values();
|
||||
|
||||
/**
|
||||
* 星期对应{@link Calendar} 中的Week值
|
||||
*/
|
||||
@@ -71,6 +79,20 @@ public enum Week {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取ISO8601规范的int值,from 1 (Monday) to 7 (Sunday).
|
||||
*
|
||||
* @return ISO8601规范的int值
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public int getIso8601Value(){
|
||||
int iso8601IntValue = getValue() -1;
|
||||
if(0 == iso8601IntValue){
|
||||
iso8601IntValue = 7;
|
||||
}
|
||||
return iso8601IntValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为中文名
|
||||
*
|
||||
@@ -109,6 +131,16 @@ public enum Week {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为{@link DayOfWeek}
|
||||
*
|
||||
* @return {@link DayOfWeek}
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public DayOfWeek toJdkDayOfWeek() {
|
||||
return DayOfWeek.of(getIso8601Value());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 {@link Calendar}星期相关值转换为Week枚举对象<br>
|
||||
*
|
||||
@@ -123,24 +155,26 @@ public enum Week {
|
||||
* @see #SATURDAY
|
||||
*/
|
||||
public static Week of(int calendarWeekIntValue) {
|
||||
switch (calendarWeekIntValue) {
|
||||
case Calendar.SUNDAY:
|
||||
return SUNDAY;
|
||||
case Calendar.MONDAY:
|
||||
return MONDAY;
|
||||
case Calendar.TUESDAY:
|
||||
return TUESDAY;
|
||||
case Calendar.WEDNESDAY:
|
||||
return WEDNESDAY;
|
||||
case Calendar.THURSDAY:
|
||||
return THURSDAY;
|
||||
case Calendar.FRIDAY:
|
||||
return FRIDAY;
|
||||
case Calendar.SATURDAY:
|
||||
return SATURDAY;
|
||||
default:
|
||||
return null;
|
||||
if (calendarWeekIntValue >= ENUMS.length || calendarWeekIntValue < 0) {
|
||||
return null;
|
||||
}
|
||||
return ENUMS[calendarWeekIntValue];
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析别名为Week对象,别名如:sun或者SUNDAY,不区分大小写
|
||||
*
|
||||
* @param name 别名值
|
||||
* @return 周int值
|
||||
* @throws IllegalArgumentException 如果别名无对应的枚举,抛出此异常
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public static Week of(String name) throws IllegalArgumentException {
|
||||
Week of = of(ArrayUtil.indexOfIgnoreCase(ALIASES, name));
|
||||
if (null == of) {
|
||||
of = Week.valueOf(name.toUpperCase());
|
||||
}
|
||||
return of;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -188,7 +188,7 @@ public class Range<T> implements Iterable<T>, Iterator<T>, Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置{@link Range}
|
||||
* 重置Range
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
|
@@ -3,7 +3,7 @@ package cn.hutool.core.lang.mutable;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
|
||||
/**
|
||||
* 可变 <code>long</code> 类型
|
||||
* 可变 {@code long} 类型
|
||||
*
|
||||
* @see Long
|
||||
* @since 3.0.1
|
||||
@@ -21,6 +21,7 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param value 值
|
||||
*/
|
||||
public MutableLong(final long value) {
|
||||
@@ -29,6 +30,7 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param value 值
|
||||
*/
|
||||
public MutableLong(final Number value) {
|
||||
@@ -37,6 +39,7 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param value String值
|
||||
* @throws NumberFormatException 数字转换错误
|
||||
*/
|
||||
@@ -51,6 +54,7 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
|
||||
|
||||
/**
|
||||
* 设置值
|
||||
*
|
||||
* @param value 值
|
||||
*/
|
||||
public void set(final long value) {
|
||||
@@ -63,8 +67,10 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 值+1
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public MutableLong increment() {
|
||||
@@ -74,6 +80,7 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
|
||||
|
||||
/**
|
||||
* 值减一
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public MutableLong decrement() {
|
||||
@@ -82,8 +89,10 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 增加值
|
||||
*
|
||||
* @param operand 被增加的值
|
||||
* @return this
|
||||
*/
|
||||
@@ -94,6 +103,7 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
|
||||
|
||||
/**
|
||||
* 增加值
|
||||
*
|
||||
* @param operand 被增加的值,非空
|
||||
* @return this
|
||||
* @throws NullPointerException if the object is null
|
||||
@@ -148,16 +158,17 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 相等需同时满足如下条件:
|
||||
* <ol>
|
||||
* <li>非空</li>
|
||||
* <li>类型为 {@link MutableLong}</li>
|
||||
* <li>类型为 MutableLong</li>
|
||||
* <li>值相等</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param obj 比对的对象
|
||||
* @return 相同返回<code>true</code>,否则 <code>false</code>
|
||||
* @return 相同返回<code>true</code>,否则 {@code false}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
@@ -173,10 +184,11 @@ public class MutableLong extends Number implements Comparable<MutableLong>, Muta
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 比较
|
||||
*
|
||||
* @param other 其它 {@link MutableLong} 对象
|
||||
* @param other 其它 MutableLong 对象
|
||||
* @return x==y返回0,x<y返回-1,x>y返回1
|
||||
*/
|
||||
@Override
|
||||
|
@@ -1,5 +1,10 @@
|
||||
package cn.hutool.core.util;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.func.Func1;
|
||||
import cn.hutool.core.lang.func.LambdaUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -9,11 +14,6 @@ import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.func.Func1;
|
||||
import cn.hutool.core.lang.func.LambdaUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
|
||||
/**
|
||||
* 枚举工具类
|
||||
*
|
||||
@@ -56,7 +56,7 @@ public class EnumUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转枚举,调用{@link Enum#valueOf(Class, String)}
|
||||
* 获取给定位置的枚举值
|
||||
*
|
||||
* @param <E> 枚举类型泛型
|
||||
* @param enumClass 枚举类
|
||||
|
50
hutool-core/src/test/java/cn/hutool/core/date/WeekTest.java
Normal file
50
hutool-core/src/test/java/cn/hutool/core/date/WeekTest.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package cn.hutool.core.date;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
|
||||
public class WeekTest {
|
||||
|
||||
@Test
|
||||
public void ofTest(){
|
||||
//测试别名及大小写
|
||||
Assert.assertEquals(Week.SUNDAY, Week.of("sun"));
|
||||
Assert.assertEquals(Week.SUNDAY, Week.of("SUN"));
|
||||
Assert.assertEquals(Week.SUNDAY, Week.of("Sun"));
|
||||
//测试全名及大小写
|
||||
Assert.assertEquals(Week.SUNDAY, Week.of("sunday"));
|
||||
Assert.assertEquals(Week.SUNDAY, Week.of("Sunday"));
|
||||
Assert.assertEquals(Week.SUNDAY, Week.of("SUNDAY"));
|
||||
|
||||
Assert.assertEquals(Week.MONDAY, Week.of("Mon"));
|
||||
Assert.assertEquals(Week.MONDAY, Week.of("Monday"));
|
||||
|
||||
Assert.assertEquals(Week.TUESDAY, Week.of("tue"));
|
||||
Assert.assertEquals(Week.TUESDAY, Week.of("tuesday"));
|
||||
|
||||
Assert.assertEquals(Week.WEDNESDAY, Week.of("wed"));
|
||||
Assert.assertEquals(Week.WEDNESDAY, Week.of("WEDNESDAY"));
|
||||
|
||||
Assert.assertEquals(Week.THURSDAY, Week.of("thu"));
|
||||
Assert.assertEquals(Week.THURSDAY, Week.of("THURSDAY"));
|
||||
|
||||
Assert.assertEquals(Week.FRIDAY, Week.of("fri"));
|
||||
Assert.assertEquals(Week.FRIDAY, Week.of("FRIDAY"));
|
||||
|
||||
Assert.assertEquals(Week.SATURDAY, Week.of("sat"));
|
||||
Assert.assertEquals(Week.SATURDAY, Week.of("SATURDAY"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toJdkDayOfWeekTest(){
|
||||
Assert.assertEquals(DayOfWeek.MONDAY, Week.MONDAY.toJdkDayOfWeek());
|
||||
Assert.assertEquals(DayOfWeek.TUESDAY, Week.TUESDAY.toJdkDayOfWeek());
|
||||
Assert.assertEquals(DayOfWeek.WEDNESDAY, Week.WEDNESDAY.toJdkDayOfWeek());
|
||||
Assert.assertEquals(DayOfWeek.THURSDAY, Week.THURSDAY.toJdkDayOfWeek());
|
||||
Assert.assertEquals(DayOfWeek.FRIDAY, Week.FRIDAY.toJdkDayOfWeek());
|
||||
Assert.assertEquals(DayOfWeek.SATURDAY, Week.SATURDAY.toJdkDayOfWeek());
|
||||
Assert.assertEquals(DayOfWeek.SUNDAY, Week.SUNDAY.toJdkDayOfWeek());
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
package cn.hutool.cron.pattern;
|
||||
|
||||
import cn.hutool.cron.pattern.matcher.MatcherTable;
|
||||
import cn.hutool.cron.pattern.parser.CronPatternParser;
|
||||
import cn.hutool.cron.pattern.parser.PatternParser;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
@@ -68,6 +68,17 @@ public class CronPattern {
|
||||
private final String pattern;
|
||||
private final MatcherTable matcherTable;
|
||||
|
||||
/**
|
||||
* 解析表达式为 CronPattern
|
||||
*
|
||||
* @param pattern 表达式
|
||||
* @return CronPattern
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public static CronPattern of(String pattern) {
|
||||
return new CronPattern(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
@@ -75,10 +86,11 @@ public class CronPattern {
|
||||
*/
|
||||
public CronPattern(String pattern) {
|
||||
this.pattern = pattern;
|
||||
this.matcherTable = CronPatternParser.parse(pattern);
|
||||
this.matcherTable = PatternParser.parse(pattern);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------- match start
|
||||
|
||||
/**
|
||||
* 给定时间是否匹配定时任务表达式
|
||||
*
|
||||
@@ -104,6 +116,24 @@ 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定时间是否匹配定时任务表达式
|
||||
*
|
||||
@@ -111,7 +141,7 @@ public class CronPattern {
|
||||
* @param isMatchSecond 是否匹配秒
|
||||
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
|
||||
*/
|
||||
public boolean match(GregorianCalendar calendar, boolean isMatchSecond) {
|
||||
public boolean match(Calendar calendar, boolean isMatchSecond) {
|
||||
final int second = isMatchSecond ? calendar.get(Calendar.SECOND) : -1;
|
||||
final int minute = calendar.get(Calendar.MINUTE);
|
||||
final int hour = calendar.get(Calendar.HOUR_OF_DAY);
|
||||
|
@@ -0,0 +1,23 @@
|
||||
package cn.hutool.cron.pattern;
|
||||
|
||||
import cn.hutool.core.builder.Builder;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
public class CronPatternBuilder implements Builder<String> {
|
||||
|
||||
final String[] parts = new String[7];
|
||||
|
||||
public static CronPatternBuilder of(){
|
||||
return new CronPatternBuilder();
|
||||
}
|
||||
|
||||
public CronPatternBuilder set(Part part, String value){
|
||||
parts[part.ordinal()] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String build() {
|
||||
return StrUtil.join(StrUtil.SPACE, (Object[]) parts);
|
||||
}
|
||||
}
|
73
hutool-cron/src/main/java/cn/hutool/cron/pattern/Part.java
Normal file
73
hutool-cron/src/main/java/cn/hutool/cron/pattern/Part.java
Normal file
@@ -0,0 +1,73 @@
|
||||
package cn.hutool.cron.pattern;
|
||||
|
||||
import cn.hutool.core.date.Month;
|
||||
import cn.hutool.core.date.Week;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.cron.CronException;
|
||||
|
||||
/**
|
||||
* 表达式各个部分的枚举,用于限定位置和规则<br>
|
||||
* {@link #ordinal()}表示此部分在表达式中的位置,如0表示秒
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public enum Part {
|
||||
SECOND(0, 59),
|
||||
MINUTE(0, 59),
|
||||
HOUR(0, 23),
|
||||
DAY_OF_MONTH(1, 31),
|
||||
MONTH(Month.JANUARY.getValueBaseOne(), Month.DECEMBER.getValueBaseOne()),
|
||||
DAY_OF_WEEK(Week.SUNDAY.ordinal(), Week.SATURDAY.ordinal()),
|
||||
YEAR(1970, 2099);
|
||||
|
||||
private final int min;
|
||||
private final int max;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param min 限定最小值(包含)
|
||||
* @param max 限定最大值(包含)
|
||||
*/
|
||||
Part(int min, int max) {
|
||||
if (min > max) {
|
||||
this.min = max;
|
||||
this.max = min;
|
||||
} else {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最小值
|
||||
*
|
||||
* @return 最小值
|
||||
*/
|
||||
public int getMin() {
|
||||
return this.min;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最大值
|
||||
*
|
||||
* @return 最大值
|
||||
*/
|
||||
public int getMax() {
|
||||
return this.max;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查单个值是否有效
|
||||
*
|
||||
* @param value 值
|
||||
* @return 检查后的值
|
||||
* @throws CronException 检查无效抛出此异常
|
||||
*/
|
||||
public int checkValue(int value) throws CronException {
|
||||
Assert.checkBetween(value, min, max,
|
||||
() -> new CronException("Value {} out of range: [{} , {}]", value, min, max));
|
||||
return value;
|
||||
}
|
||||
}
|
@@ -31,8 +31,8 @@ public class DayOfMonthValueMatcher extends BoolArrayValueMatcher {
|
||||
*/
|
||||
public boolean match(int value, int month, boolean isLeapYear) {
|
||||
return (super.match(value) // 在约定日范围内的某一天
|
||||
//匹配器中用户定义了最后一天(32表示最后一天)
|
||||
|| (value > 27 && match(32) && isLastDayOfMonth(value, month, isLeapYear)));
|
||||
//匹配器中用户定义了最后一天(31表示最后一天)
|
||||
|| (value > 27 && match(31) && isLastDayOfMonth(value, month, isLeapYear)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,10 +1,13 @@
|
||||
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.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* 时间匹配表,用于存放定时任务表达式解析后的结构信息
|
||||
@@ -28,54 +31,107 @@ public class MatcherTable {
|
||||
matchers = new ArrayList<>(size);
|
||||
}
|
||||
|
||||
public LocalDateTime nextMatchAfter(int second, int minute, int hour, int dayOfMonth, int month, int dayOfWeek, int year) {
|
||||
List<LocalDateTime> nextMatchs = new ArrayList<>(second);
|
||||
/**
|
||||
* 获取下一个最近的匹配日期时间
|
||||
*
|
||||
* @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));
|
||||
dayOfMonth, month, dayOfWeek, year, zone));
|
||||
}
|
||||
// 返回最先匹配到的日期
|
||||
// 返回匹配到的最早日期
|
||||
return CollUtil.min(nextMatchs);
|
||||
}
|
||||
|
||||
private static LocalDateTime singleNextMatchAfter(DateTimeMatcher matcher, int second, int minute, int hour,
|
||||
int dayOfMonth, int month, int dayOfWeek, int year){
|
||||
boolean isNextNotEquals = true;
|
||||
/**
|
||||
* 获取下一个匹配日期时间
|
||||
*
|
||||
* @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 = matcher.yearMatcher.nextAfter(year);
|
||||
isNextNotEquals &= (year != nextYear);
|
||||
final int nextYear = nextAfter(matcher.yearMatcher, year, isNextEquals);
|
||||
calendar.set(Calendar.YEAR, nextYear);
|
||||
|
||||
// 周
|
||||
int nextDayOfWeek;
|
||||
if(isNextNotEquals){
|
||||
// 上一个字段不一致,说明产生了新值,本字段使用最小值
|
||||
nextDayOfWeek = ((BoolArrayValueMatcher)matcher.dayOfWeekMatcher).getMinValue();
|
||||
}else{
|
||||
nextDayOfWeek = matcher.dayOfWeekMatcher.nextAfter(dayOfWeek);
|
||||
isNextNotEquals &= (dayOfWeek != nextDayOfWeek);
|
||||
}
|
||||
final int nextDayOfWeek = nextAfter(matcher.dayOfWeekMatcher, dayOfWeek, isNextEquals);
|
||||
calendar.set(Calendar.DAY_OF_WEEK, nextDayOfWeek);
|
||||
|
||||
// 月
|
||||
int nextMonth;
|
||||
if(isNextNotEquals){
|
||||
// 上一个字段不一致,说明产生了新值,本字段使用最小值
|
||||
nextMonth = ((BoolArrayValueMatcher)matcher.monthMatcher).getMinValue();
|
||||
}else{
|
||||
nextMonth = matcher.monthMatcher.nextAfter(dayOfWeek);
|
||||
isNextNotEquals &= (month != nextMonth);
|
||||
}
|
||||
final int nextMonth = nextAfter(matcher.monthMatcher, month + 1, isNextEquals);
|
||||
calendar.set(Calendar.MONTH, nextMonth - 1);
|
||||
|
||||
// 日
|
||||
int nextDayOfMonth;
|
||||
if(isNextNotEquals){
|
||||
// 上一个字段不一致,说明产生了新值,本字段使用最小值
|
||||
nextDayOfMonth = ((BoolArrayValueMatcher)matcher.dayOfMonthMatcher).getMinValue();
|
||||
}else{
|
||||
nextDayOfMonth = matcher.dayOfMonthMatcher.nextAfter(dayOfWeek);
|
||||
isNextNotEquals &= (dayOfMonth != nextDayOfMonth);
|
||||
}
|
||||
final int nextDayOfMonth = nextAfter(matcher.dayOfMonthMatcher, dayOfMonth, isNextEquals);
|
||||
calendar.set(Calendar.DAY_OF_MONTH, nextDayOfMonth);
|
||||
|
||||
return null;
|
||||
// 时
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,8 +141,8 @@ public class MatcherTable {
|
||||
* @param minute 分钟
|
||||
* @param hour 小时
|
||||
* @param dayOfMonth 天
|
||||
* @param month 月
|
||||
* @param dayOfWeek 周几
|
||||
* @param month 月,从1开始
|
||||
* @param dayOfWeek 周,从0开始,0和7都表示周日
|
||||
* @param year 年
|
||||
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
|
||||
*/
|
||||
|
@@ -1,39 +0,0 @@
|
||||
package cn.hutool.cron.pattern.parser;
|
||||
|
||||
import cn.hutool.cron.CronException;
|
||||
import cn.hutool.cron.pattern.matcher.DayOfMonthValueMatcher;
|
||||
import cn.hutool.cron.pattern.matcher.ValueMatcher;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 每月的几号值处理<br>
|
||||
* 每月最多31天,32和“L”都表示最后一天
|
||||
*
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class DayOfMonthValueParser extends AbsValueParser {
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public DayOfMonthValueParser() {
|
||||
super(1, 31);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parse(String value) throws CronException {
|
||||
if ("L".equalsIgnoreCase(value) || "32".equals(value)) {// 每月最后一天
|
||||
return 32;
|
||||
} else {
|
||||
return super.parse(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValueMatcher buildValueMatcher(List<Integer> values) {
|
||||
//考虑每月的天数不同,且存在闰年情况,日匹配单独使用
|
||||
return new DayOfMonthValueMatcher(values);
|
||||
}
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
package cn.hutool.cron.pattern.parser;
|
||||
|
||||
import cn.hutool.cron.CronException;
|
||||
|
||||
/**
|
||||
* 星期值处理<br>
|
||||
* 1表示星期一,2表示星期二,依次类推,0和7都可以表示星期日<br>
|
||||
* {@code L}表示周六
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class DayOfWeekValueParser extends AbsValueParser {
|
||||
|
||||
/**
|
||||
* Weeks aliases.
|
||||
*/
|
||||
private static final String[] ALIASES = {"sun", "mon", "tue", "wed", "thu", "fri", "sat"};
|
||||
|
||||
public DayOfWeekValueParser() {
|
||||
super(0, 7);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对于星期提供转换<br>
|
||||
* 1表示星期一,2表示星期二,依次类推,0和7都可以表示星期日
|
||||
*/
|
||||
@Override
|
||||
public int parse(String value) throws CronException {
|
||||
try {
|
||||
return super.parse(value) % 7;
|
||||
} catch (Exception e) {
|
||||
return parseAlias(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析别名
|
||||
*
|
||||
* @param value 别名值
|
||||
* @return 月份int值
|
||||
* @throws CronException 无效别名抛出此异常
|
||||
*/
|
||||
private int parseAlias(String value) throws CronException {
|
||||
if ("L".equalsIgnoreCase(value)) {
|
||||
//最后一天为星期六
|
||||
return ALIASES.length - 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ALIASES.length; i++) {
|
||||
if (ALIASES[i].equalsIgnoreCase(value)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new CronException("Invalid month alias: {}", value);
|
||||
}
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
package cn.hutool.cron.pattern.parser;
|
||||
|
||||
/**
|
||||
* 小时值处理<br>
|
||||
* 小时被限定在0-23
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class HourValueParser extends AbsValueParser {
|
||||
public HourValueParser() {
|
||||
super(0, 23);
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
package cn.hutool.cron.pattern.parser;
|
||||
|
||||
/**
|
||||
* 分钟值处理<br>
|
||||
* 限定于0-59
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class MinuteValueParser extends AbsValueParser {
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public MinuteValueParser() {
|
||||
super(0, 59);
|
||||
}
|
||||
}
|
@@ -1,40 +0,0 @@
|
||||
package cn.hutool.cron.pattern.parser;
|
||||
|
||||
import cn.hutool.core.date.Month;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.cron.CronException;
|
||||
|
||||
/**
|
||||
* 月份值处理<br>
|
||||
* 限定于1-12,1表示一月,支持别名(忽略大小写),如一月是{@code jan}
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class MonthValueParser extends AbsValueParser {
|
||||
|
||||
public MonthValueParser() {
|
||||
super(1, 12);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parse(String value) throws CronException {
|
||||
try {
|
||||
return super.parse(value);
|
||||
} catch (Exception e) {
|
||||
return parseAlias(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析别名
|
||||
*
|
||||
* @param monthName 别名值
|
||||
* @return 月份int值,从1开始
|
||||
* @throws CronException 无效月别名抛出此异常
|
||||
*/
|
||||
private int parseAlias(String monthName) throws CronException {
|
||||
final Month month = Month.of(monthName);
|
||||
Assert.notNull(month, () -> new CronException("Invalid month alias: {}", monthName));
|
||||
return month.getValueBaseOne();
|
||||
}
|
||||
}
|
@@ -1,109 +1,75 @@
|
||||
package cn.hutool.cron.pattern.parser;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.Month;
|
||||
import cn.hutool.core.date.Week;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
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.YearValueMatcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 简易值转换器。将给定String值转为int,并限定最大值和最小值<br>
|
||||
* 此类同时识别{@code L} 为最大值。
|
||||
* 定时任务表达式各个部分的解析器
|
||||
*
|
||||
* @author Looly
|
||||
* @author looly
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public abstract class AbsValueParser implements ValueParser {
|
||||
public class PartParser {
|
||||
|
||||
private final Part part;
|
||||
|
||||
/**
|
||||
* 最小值(包括)
|
||||
* 创建解析器
|
||||
*
|
||||
* @param part 对应解析的部分枚举
|
||||
* @return 解析器
|
||||
*/
|
||||
protected int min;
|
||||
/**
|
||||
* 最大值(包括)
|
||||
*/
|
||||
protected int max;
|
||||
public static PartParser of(Part part) {
|
||||
return new PartParser(part);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param min 最小值(包括)
|
||||
* @param max 最大值(包括)
|
||||
* @param part 对应解析的部分枚举
|
||||
*/
|
||||
public AbsValueParser(int min, int max) {
|
||||
if (min > max) {
|
||||
this.min = max;
|
||||
this.max = min;
|
||||
} else {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parse(String value) throws CronException {
|
||||
if ("L".equalsIgnoreCase(value)) {
|
||||
// L表示最大值
|
||||
return max;
|
||||
}
|
||||
|
||||
int i;
|
||||
try {
|
||||
i = Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new CronException(e, "Invalid integer value: '{}'", value);
|
||||
}
|
||||
if (i < min || i > max) {
|
||||
throw new CronException("Value {} out of range: [{} , {}]", i, min, max);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMin() {
|
||||
return this.min;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMax() {
|
||||
return this.max;
|
||||
public PartParser(Part part) {
|
||||
this.part = part;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理定时任务表达式每个时间字段<br>
|
||||
* 多个时间使用逗号分隔
|
||||
* 将表达式解析为{@link ValueMatcher}
|
||||
*
|
||||
* @param value 某个时间字段
|
||||
* @return List
|
||||
* @param value 表达式
|
||||
* @return {@link ValueMatcher}
|
||||
*/
|
||||
@Override
|
||||
public ValueMatcher parseAsValueMatcher(String value) {
|
||||
if (isMatchAllStr(value)) {
|
||||
//兼容Quartz的"?"表达式,不会出现互斥情况,与"*"作用相同
|
||||
return new AlwaysTrueValueMatcher();
|
||||
}
|
||||
|
||||
List<Integer> values = parseArray(value);
|
||||
final List<Integer> values = parseArray(value);
|
||||
if (values.size() == 0) {
|
||||
throw new CronException("Invalid field: [{}]", value);
|
||||
throw new CronException("Invalid part value: [{}]", value);
|
||||
}
|
||||
|
||||
return buildValueMatcher(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据解析的数字值列表构建{@link ValueMatcher}<br>
|
||||
* 默认为{@link BoolArrayValueMatcher},如果有特殊实现,子类须重写此方法
|
||||
*
|
||||
* @param values 数字值列表
|
||||
* @return {@link ValueMatcher}
|
||||
*/
|
||||
protected ValueMatcher buildValueMatcher(List<Integer> values){
|
||||
return new BoolArrayValueMatcher(values);
|
||||
switch (this.part) {
|
||||
case DAY_OF_MONTH:
|
||||
return new DayOfMonthValueMatcher(values);
|
||||
case YEAR:
|
||||
return new YearValueMatcher(values);
|
||||
default:
|
||||
return new BoolArrayValueMatcher(values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,10 +79,11 @@ public abstract class AbsValueParser implements ValueParser {
|
||||
* <li><strong>a</strong> 或 <strong>*</strong></li>
|
||||
* <li><strong>a,b,c,d</strong></li>
|
||||
* </ul>
|
||||
*
|
||||
* @param value 子表达式值
|
||||
* @return 值列表
|
||||
*/
|
||||
private List<Integer> parseArray(String value){
|
||||
private List<Integer> parseArray(String value) {
|
||||
final List<Integer> values = new ArrayList<>();
|
||||
|
||||
final List<String> parts = StrUtil.split(value, StrUtil.C_COMMA);
|
||||
@@ -146,7 +113,7 @@ public abstract class AbsValueParser implements ValueParser {
|
||||
if (size == 1) {// 普通形式
|
||||
results = parseRange(value, -1);
|
||||
} else if (size == 2) {// 间隔形式
|
||||
final int step = parse(parts.get(1));
|
||||
final int step = parseNumber(parts.get(1));
|
||||
if (step < 1) {
|
||||
throw new CronException("Non positive divisor for field: [{}]", value);
|
||||
}
|
||||
@@ -168,7 +135,7 @@ public abstract class AbsValueParser implements ValueParser {
|
||||
* </ul>
|
||||
*
|
||||
* @param value 范围表达式
|
||||
* @param step 步进
|
||||
* @param step 步进
|
||||
* @return List
|
||||
*/
|
||||
private List<Integer> parseRange(String value, int step) {
|
||||
@@ -177,22 +144,22 @@ public abstract class AbsValueParser implements ValueParser {
|
||||
// 全部匹配形式
|
||||
if (value.length() <= 2) {
|
||||
//根据步进的第一个数字确定起始时间,类似于 12/3则从12(秒、分等)开始
|
||||
int minValue = getMin();
|
||||
if(false == isMatchAllStr(value)) {
|
||||
minValue = Math.max(minValue, parse(value));
|
||||
}else {
|
||||
int minValue = part.getMin();
|
||||
if (false == isMatchAllStr(value)) {
|
||||
minValue = Math.max(minValue, parseNumber(value));
|
||||
} else {
|
||||
//在全匹配模式下,如果步进不存在,表示步进为1
|
||||
if(step < 1) {
|
||||
if (step < 1) {
|
||||
step = 1;
|
||||
}
|
||||
}
|
||||
if(step > 0) {
|
||||
final int maxValue = getMax();
|
||||
if(minValue > maxValue) {
|
||||
if (step > 0) {
|
||||
final int maxValue = part.getMax();
|
||||
if (minValue > maxValue) {
|
||||
throw new CronException("Invalid value {} > {}", minValue, maxValue);
|
||||
}
|
||||
//有步进
|
||||
for (int i = minValue; i <= maxValue; i+=step) {
|
||||
for (int i = minValue; i <= maxValue; i += step) {
|
||||
results.add(i);
|
||||
}
|
||||
} else {
|
||||
@@ -206,26 +173,26 @@ public abstract class AbsValueParser implements ValueParser {
|
||||
List<String> parts = StrUtil.split(value, '-');
|
||||
int size = parts.size();
|
||||
if (size == 1) {// 普通值
|
||||
final int v1 = parse(value);
|
||||
if(step > 0) {//类似 20/2的形式
|
||||
NumberUtil.appendRange(v1, getMax(), step, results);
|
||||
}else {
|
||||
final int v1 = parseNumber(value);
|
||||
if (step > 0) {//类似 20/2的形式
|
||||
NumberUtil.appendRange(v1, part.getMax(), step, results);
|
||||
} else {
|
||||
results.add(v1);
|
||||
}
|
||||
} else if (size == 2) {// range值
|
||||
final int v1 = parse(parts.get(0));
|
||||
final int v2 = parse(parts.get(1));
|
||||
if(step < 1) {
|
||||
final int v1 = parseNumber(parts.get(0));
|
||||
final int v2 = parseNumber(parts.get(1));
|
||||
if (step < 1) {
|
||||
//在range模式下,如果步进不存在,表示步进为1
|
||||
step = 1;
|
||||
}
|
||||
if (v1 < v2) {// 正常范围,例如:2-5
|
||||
NumberUtil.appendRange(v1, v2, step, results);
|
||||
} else if (v1 > v2) {// 逆向范围,反选模式,例如:5-2
|
||||
NumberUtil.appendRange(v1, getMax(), step, results);
|
||||
NumberUtil.appendRange(getMin(), v2, step, results);
|
||||
NumberUtil.appendRange(v1, part.getMax(), step, results);
|
||||
NumberUtil.appendRange(part.getMin(), v2, step, results);
|
||||
} else {// v1 == v2,此时与单值模式一致
|
||||
NumberUtil.appendRange(v1, getMax(), step, results);
|
||||
NumberUtil.appendRange(v1, part.getMax(), step, results);
|
||||
}
|
||||
} else {
|
||||
throw new CronException("Invalid syntax of field: [{}]", value);
|
||||
@@ -244,4 +211,56 @@ public abstract class AbsValueParser implements ValueParser {
|
||||
private static boolean isMatchAllStr(String value) {
|
||||
return (1 == value.length()) && ("*".equals(value) || "?".equals(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析单个int值,支持别名
|
||||
*
|
||||
* @param value 被解析的值
|
||||
* @return 解析结果
|
||||
* @throws CronException 当无效数字或无效别名时抛出
|
||||
*/
|
||||
private int parseNumber(String value) throws CronException {
|
||||
if ("L".equalsIgnoreCase(value)) {
|
||||
// L表示最大值
|
||||
return part.getMax();
|
||||
}
|
||||
|
||||
int i;
|
||||
try {
|
||||
i = Integer.parseInt(value);
|
||||
} catch (NumberFormatException ignore) {
|
||||
i = parseAlias(value);
|
||||
}
|
||||
|
||||
// 周日可以用0或7表示,统一转换为0
|
||||
if(this.part == Part.DAY_OF_WEEK && Week.SUNDAY.getIso8601Value() == i){
|
||||
i = Week.SUNDAY.ordinal();
|
||||
}
|
||||
|
||||
return part.checkValue(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析别名,只支持{@link Part#MONTH}和{@link Part#DAY_OF_WEEK}
|
||||
*
|
||||
* @param name 别名
|
||||
* @return 解析int值
|
||||
* @throws CronException 无匹配别名时抛出异常
|
||||
*/
|
||||
private int parseAlias(String name) throws CronException {
|
||||
switch (this.part) {
|
||||
case MONTH:
|
||||
final Month month = Month.of(name);
|
||||
Assert.notNull(month, () -> new CronException("Invalid month alias: {}", name));
|
||||
// 月份从1开始
|
||||
return month.getValueBaseOne();
|
||||
case DAY_OF_WEEK:
|
||||
final Week week = Week.of(name);
|
||||
Assert.notNull(week, () -> new CronException("Invalid day of week alias: {}", name));
|
||||
// 周从0开始,0表示周日
|
||||
return week.ordinal();
|
||||
}
|
||||
|
||||
throw new CronException("Invalid alias value: [{}]", name);
|
||||
}
|
||||
}
|
@@ -1,8 +1,9 @@
|
||||
package cn.hutool.cron.pattern.parser;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
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;
|
||||
@@ -16,15 +17,15 @@ import java.util.List;
|
||||
* @author looly
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public class CronPatternParser {
|
||||
public class PatternParser {
|
||||
|
||||
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 static final PartParser SECOND_VALUE_PARSER = PartParser.of(Part.SECOND);
|
||||
private static final PartParser MINUTE_VALUE_PARSER = PartParser.of(Part.MINUTE);
|
||||
private static final PartParser HOUR_VALUE_PARSER = PartParser.of(Part.HOUR);
|
||||
private static final PartParser DAY_OF_MONTH_VALUE_PARSER = PartParser.of(Part.DAY_OF_MONTH);
|
||||
private static final PartParser MONTH_VALUE_PARSER = PartParser.of(Part.MONTH);
|
||||
private static final PartParser DAY_OF_WEEK_VALUE_PARSER = PartParser.of(Part.DAY_OF_WEEK);
|
||||
private static final PartParser YEAR_VALUE_PARSER = PartParser.of(Part.YEAR);
|
||||
|
||||
/**
|
||||
* 解析表达式到匹配表中
|
||||
@@ -46,7 +47,7 @@ public class CronPatternParser {
|
||||
* @return {@link MatcherTable}
|
||||
*/
|
||||
private static MatcherTable parseGroupPattern(String groupPattern) {
|
||||
final List<String> patternList = StrUtil.split(groupPattern, '|');
|
||||
final List<String> patternList = StrUtil.splitTrim(groupPattern, '|');
|
||||
final MatcherTable matcherTable = new MatcherTable(patternList.size());
|
||||
for (String pattern : patternList) {
|
||||
matcherTable.matchers.add(parseSinglePattern(pattern));
|
||||
@@ -61,17 +62,18 @@ public class CronPatternParser {
|
||||
* @return {@link DateTimeMatcher}
|
||||
*/
|
||||
private static DateTimeMatcher parseSinglePattern(String pattern) {
|
||||
final String[] parts = pattern.split("\\s");
|
||||
final String[] parts = pattern.split("\\s+");
|
||||
Assert.checkBetween(parts.length, 5, 7,
|
||||
() -> new CronException("Pattern [{}] is invalid, it must be 5-7 parts!", pattern));
|
||||
|
||||
int offset = 0;// 偏移量用于兼容Quartz表达式,当表达式有6或7项时,第一项为秒
|
||||
// 偏移量用于兼容Quartz表达式,当表达式有6或7项时,第一项为秒
|
||||
int offset = 0;
|
||||
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);
|
||||
}
|
||||
|
||||
// 秒,如果不支持秒的表达式,则第一位按照表达式生成时间的秒数赋值,表示整分匹配
|
||||
final String secondPart = (1 == offset) ? parts[0] : String.valueOf(DateUtil.date().second());
|
||||
// 秒,如果不支持秒的表达式,则第一位赋值0,表示整分匹配
|
||||
final String secondPart = (1 == offset) ? parts[0] : "0";
|
||||
|
||||
// 年
|
||||
ValueMatcher yearMatcher;
|
@@ -1,10 +0,0 @@
|
||||
package cn.hutool.cron.pattern.parser;
|
||||
|
||||
/**
|
||||
* 秒值处理<br>
|
||||
* 限定于0-59
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class SecondValueParser extends MinuteValueParser {
|
||||
}
|
@@ -1,53 +0,0 @@
|
||||
package cn.hutool.cron.pattern.parser;
|
||||
|
||||
import cn.hutool.cron.pattern.matcher.ValueMatcher;
|
||||
|
||||
/**
|
||||
* 值处理接口<br>
|
||||
* 值处理用于限定表达式中相应位置的值范围,并转换表达式值为int值
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public interface ValueParser {
|
||||
|
||||
/**
|
||||
* 解析表达式对应部分为{@link ValueMatcher},支持的表达式包括:
|
||||
* <ul>
|
||||
* <li>单值或通配符形式,如 <strong>a</strong> 或 <strong>*</strong></li>
|
||||
* <li>数组形式,如 <strong>1,2,3</strong></li>
|
||||
* <li>间隔形式,如 <strong>a/b</strong> 或 <strong>*/b</strong></li>
|
||||
* <li>范围形式,如 <strong>3-8</strong></li>
|
||||
* </ul>
|
||||
*
|
||||
* @param pattern 对应时间部分的表达式
|
||||
* @return {@link ValueMatcher}
|
||||
*/
|
||||
ValueMatcher parseAsValueMatcher(String pattern);
|
||||
|
||||
/**
|
||||
* 处理String值并转为int<br>
|
||||
* 转换包括:
|
||||
* <ol>
|
||||
* <li>数字字符串转为数字</li>
|
||||
* <li>别名转为对应的数字(如月份和星期)</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param value String值
|
||||
* @return int
|
||||
*/
|
||||
int parse(String value);
|
||||
|
||||
/**
|
||||
* 返回最小值
|
||||
*
|
||||
* @return 最小值
|
||||
*/
|
||||
int getMin();
|
||||
|
||||
/**
|
||||
* 返回最大值
|
||||
*
|
||||
* @return 最大值
|
||||
*/
|
||||
int getMax();
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
package cn.hutool.cron.pattern.parser;
|
||||
|
||||
import cn.hutool.cron.pattern.matcher.ValueMatcher;
|
||||
import cn.hutool.cron.pattern.matcher.YearValueMatcher;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 年值处理<br>
|
||||
* 年的限定在1970-2099年
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class YearValueParser extends AbsValueParser {
|
||||
|
||||
public YearValueParser() {
|
||||
super(1970, 2099);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValueMatcher buildValueMatcher(List<Integer> values) {
|
||||
//考虑年数字太大,不适合boolean数组,单独使用列表遍历匹配
|
||||
return new YearValueMatcher(values);
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package cn.hutool.cron.pattern;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
public class CronPatternNextMatchTest {
|
||||
|
||||
@Test
|
||||
public void nextMatchAfterTest(){
|
||||
CronPattern pattern = new CronPattern("23 12 * 12 * * *");
|
||||
final Calendar calendar = pattern.nextMatchAfter(
|
||||
DateUtil.parse("2022-04-12 09:12:23").toCalendar());
|
||||
Console.log(DateUtil.date(calendar));
|
||||
}
|
||||
}
|
@@ -10,7 +10,6 @@ import org.junit.Test;
|
||||
* 定时任务单元测试类
|
||||
*
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class CronPatternTest {
|
||||
|
||||
@@ -31,7 +30,7 @@ public class CronPatternTest {
|
||||
CronPattern pattern;
|
||||
// 任何时间匹配
|
||||
pattern = new CronPattern("* * * * *");
|
||||
for(int i = 0; i < 1; i++) {
|
||||
for (int i = 0; i < 1; i++) {
|
||||
Assert.assertTrue(pattern.match(DateUtil.current(), false));
|
||||
}
|
||||
}
|
||||
@@ -83,12 +82,23 @@ public class CronPatternTest {
|
||||
|
||||
pattern = new CronPattern("39 0 0 7 aug *");
|
||||
assertMatch(pattern, "2016-08-07 00:00:39");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchDayOfWeekTest() {
|
||||
// 星期四
|
||||
pattern = new CronPattern("39 0 0 * * Thu");
|
||||
assertMatch(pattern, "2017-02-09 00:00:39");
|
||||
CronPattern pattern = CronPattern.of("39 0 0 * * Thu");
|
||||
assertMatch(pattern, "2017-02-09 00:00:39");
|
||||
|
||||
// 星期日的三种形式
|
||||
pattern = CronPattern.of("39 0 0 * * Sun");
|
||||
assertMatch(pattern, "2022-03-27 00:00:39");
|
||||
|
||||
pattern = CronPattern.of("39 0 0 * * 0");
|
||||
assertMatch(pattern, "2022-03-27 00:00:39");
|
||||
|
||||
pattern = CronPattern.of("39 0 0 * * 7");
|
||||
assertMatch(pattern, "2022-03-27 00:00:39");
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@@ -153,11 +163,11 @@ public class CronPatternTest {
|
||||
* 表达式是否匹配日期
|
||||
*
|
||||
* @param pattern 表达式
|
||||
* @param date 日期,标准日期时间字符串
|
||||
* @param date 日期,标准日期时间字符串
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private void assertMatch(CronPattern pattern, String date) {
|
||||
Assert.assertTrue(pattern.match(DateUtil.parse(date).getTime(), false));
|
||||
Assert.assertTrue(pattern.match(DateUtil.parse(date).getTime(), true));
|
||||
Assert.assertTrue(pattern.match(DateUtil.parse(date).toCalendar(), false));
|
||||
Assert.assertTrue(pattern.match(DateUtil.parse(date).toCalendar(), true));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user