This commit is contained in:
Looly 2024-03-29 02:26:24 +08:00
parent 0d3eed0bdd
commit d0c7b19369
5 changed files with 133 additions and 31 deletions

View File

@ -191,7 +191,7 @@ public class CronPattern {
/**
* 给定时间是否匹配定时任务表达式
*
* @param fields 时间字段值{second, minute, hour, dayOfMonth, month, dayOfWeek, year}
* @param fields 时间字段值{second, minute, hour, dayOfMonth, monthBase1, dayOfWeekBase0, year}
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
*/
private boolean match(final int[] fields) {
@ -206,7 +206,7 @@ public class CronPattern {
/**
* 获取下一个最近的匹配日期时间
*
* @param values 时间字段值{second, minute, hour, dayOfMonth, month, dayOfWeek, year}
* @param values 时间字段值{second, minute, hour, dayOfMonth, monthBase1, dayOfWeekBase0, year}
* @param zone 时区
* @return {@link Calendar}毫秒数为0
*/

View File

@ -29,8 +29,8 @@ public class BoolArrayMatcher implements PartMatcher {
/**
* 用户定义此字段的最小值
*/
private final int minValue;
private final boolean[] bValues;
protected final int minValue;
protected final boolean[] bValues;
/**
* 构造
@ -50,6 +50,7 @@ public class BoolArrayMatcher implements PartMatcher {
@Override
public boolean test(final Integer value) {
final boolean[] bValues = this.bValues;
if (null == value || value >= bValues.length) {
return false;
}
@ -58,7 +59,9 @@ public class BoolArrayMatcher implements PartMatcher {
@Override
public int nextAfter(int value) {
final int minValue = this.minValue;
if(value > minValue){
final boolean[] bValues = this.bValues;
while(value < bValues.length){
if(bValues[value]){
return value;

View File

@ -43,23 +43,72 @@ public class DayOfMonthMatcher extends BoolArrayMatcher {
*/
public boolean match(final int value, final int month, final boolean isLeapYear) {
return (super.test(value) // 在约定日范围内的某一天
//匹配器中用户定义了最后一天31表示最后一天
|| (value > 27 && test(31) && isLastDayOfMonth(value, month, isLeapYear)));
//匹配器中用户定义了最后一天31表示最后一天
|| matchLastDay(value, getLastDay(month, isLeapYear)));
}
/**
* 是否为本月最后一天规则如下
* 获取指定值之后的匹配值也可以是指定值本身<br>
* 如果表达式中存在最后一天如使用"L"
* <ul>
* <li>4月6月9月11月最多匹配到30日</li>
* <li>4月闰年匹配到29日非闰年28日</li>
* </ul>
*
* @param value 指定的值
* @param month 月份从1开始
* @param isLeapYear 是否为闰年
* @return 匹配到的值或之后的值
*/
public int nextAfter(int value, final int month, final boolean isLeapYear) {
final int minValue = this.minValue;
if (value > minValue) {
final boolean[] bValues = this.bValues;
final int lastDay = getLastDay(month, isLeapYear);
while (value < lastDay) {
if (bValues[value]) {
return value;
}
value++;
}
// value == lastDay
if(test(31)){
// 匹配当月最后一天
return value;
}
}
// 两种情况返回最小值
// 一是给定值小于最小值那下一个匹配值就是最小值
// 二是给定值大于最大值那下一个匹配值也是下一轮的最小值
return minValue;
}
/**
* 是否匹配本月最后一天规则如下
* <pre>
* 1闰年2月匹配是否为29
* 2其它月份是否匹配最后一天的日期可能为30或者31
* 3表达式包含最后一天使用31表示
* </pre>
*
* @param value 被检查的值
* @param month 月份从1开始
* @param isLeapYear 是否闰年
* @param lastDay 月份的最后一天
* @return 是否为本月最后一天
*/
private static boolean isLastDayOfMonth(final int value, final int month, final boolean isLeapYear) {
return value == Month.getLastDay(month - 1, isLeapYear);
private boolean matchLastDay(final int value, final int lastDay) {
return value > 27 && test(31) && value == lastDay;
}
/**
* 获取最后一天
*
* @param month base1
* @param isLeapYear 是否闰年
* @return 最后一天
*/
private static int getLastDay(final int month, final boolean isLeapYear) {
return Month.getLastDay(month - 1, isLeapYear);
}
}

View File

@ -12,6 +12,8 @@
package org.dromara.hutool.cron.pattern.matcher;
import org.dromara.hutool.core.date.DateUtil;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.cron.pattern.Part;
import java.time.Year;
@ -52,13 +54,13 @@ public class PatternMatcher {
final PartMatcher yearMatcher) {
matchers = new PartMatcher[]{
secondMatcher,
minuteMatcher,
hourMatcher,
dayOfMonthMatcher,
monthMatcher,
dayOfWeekMatcher,
yearMatcher
secondMatcher,
minuteMatcher,
hourMatcher,
dayOfMonthMatcher,
monthMatcher,
dayOfWeekMatcher,
yearMatcher
};
}
@ -109,12 +111,12 @@ public class PatternMatcher {
*/
private boolean match(final int second, final int minute, final int hour, final int dayOfMonth, final int month, final int dayOfWeek, final int year) {
return ((second < 0) || matchers[0].test(second)) // 匹配秒非秒匹配模式下始终返回true
&& matchers[1].test(minute)// 匹配分
&& matchers[2].test(hour)// 匹配时
&& matchDayOfMonth(matchers[3], dayOfMonth, month, Year.isLeap(year))// 匹配日
&& matchers[4].test(month) // 匹配月
&& matchers[5].test(dayOfWeek)// 匹配周
&& matchers[6].test(year);// 匹配年
&& matchers[1].test(minute)// 匹配分
&& matchers[2].test(hour)// 匹配时
&& matchDayOfMonth(matchers[3], dayOfMonth, month, Year.isLeap(year))// 匹配日
&& matchers[4].test(month) // 匹配月
&& matchers[5].test(dayOfWeek)// 匹配周
&& matchers[6].test(year);// 匹配年
}
/**
@ -128,8 +130,8 @@ public class PatternMatcher {
*/
private static boolean matchDayOfMonth(final PartMatcher matcher, final int dayOfMonth, final int month, final boolean isLeapYear) {
return ((matcher instanceof DayOfMonthMatcher) //
? ((DayOfMonthMatcher) matcher).match(dayOfMonth, month, isLeapYear) //
: matcher.test(dayOfMonth));
? ((DayOfMonthMatcher) matcher).match(dayOfMonth, month, isLeapYear) //
: matcher.test(dayOfMonth));
}
//endregion
@ -145,11 +147,11 @@ public class PatternMatcher {
* </ul>
*
* <pre>
*
* (1) (0)
* &lt;-----------------&gt;
* </pre>
*
* @param values 时间字段值{second, minute, hour, dayOfMonth, month, dayOfWeek, year}
* @param values 时间字段值{second, minute, hour, dayOfMonth, monthBase1, dayOfWeekBase0, year}
* @param zone 时区
* @return {@link Calendar}毫秒数为0
*/
@ -182,7 +184,7 @@ public class PatternMatcher {
* &lt;-----------------&gt;
* </pre>
*
* @param values 时间字段值{second, minute, hour, dayOfMonth, month, dayOfWeek, year}
* @param values 时间字段值{second, minute, hour, dayOfMonth, monthBase1, dayOfWeekBase0, year}
* @return {@link Calendar}毫秒数为0
*/
private int[] nextMatchValuesAfter(final int[] values) {
@ -197,7 +199,15 @@ public class PatternMatcher {
i--;
continue;
}
nextValue = matchers[i].nextAfter(values[i]);
if (i == Part.DAY_OF_MONTH.ordinal()) {
final boolean isLeapYear = DateUtil.isLeapYear(newValues[Part.YEAR.ordinal()]);
final int month = values[Part.MONTH.ordinal()];
nextValue = ((DayOfMonthMatcher) matchers[i]).nextAfter(values[i], month, isLeapYear);
} else {
nextValue = matchers[i].nextAfter(values[i]);
}
if (nextValue > values[i]) {
// 此部分正常获取新值结束循环后续的部分置最小值
newValues[i] = nextValue;
@ -209,6 +219,7 @@ public class PatternMatcher {
nextValue = -1;// 标记回退查找
break;
}
// 值不变检查下一个部分
i--;
}
@ -221,7 +232,15 @@ public class PatternMatcher {
i++;
continue;
}
nextValue = matchers[i].nextAfter(values[i] + 1);
if (i == Part.DAY_OF_MONTH.ordinal()) {
final boolean isLeapYear = DateUtil.isLeapYear(newValues[Part.YEAR.ordinal()]);
final int month = values[Part.MONTH.ordinal()];
nextValue = ((DayOfMonthMatcher) matchers[i]).nextAfter(values[i] + 1, month, isLeapYear);
} else {
nextValue = matchers[i].nextAfter(values[i] + 1);
}
if (nextValue > values[i]) {
newValues[i] = nextValue;
i--;

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2024. looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* https://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.cron.pattern;
import org.dromara.hutool.core.date.DateTime;
import org.dromara.hutool.core.date.DateUtil;
import org.junit.jupiter.api.Test;
import java.util.Calendar;
public class IssueI92H5HTest {
@Test
void nextMatchAfterTest() {
// 匹配所有月返回下一月
final DateTime date = DateUtil.parse("2022-04-08 07:44:16");
final CronPattern pattern = new CronPattern("0 0 0 L 2 ?");
//noinspection ConstantConditions
final Calendar calendar = pattern.nextMatchAfter(date.toCalendar());
System.out.println(DateUtil.date(calendar));
}
}