diff --git a/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/CronPattern.java b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/CronPattern.java
index bcbd37795..09cf74e11 100644
--- a/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/CronPattern.java
+++ b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/CronPattern.java
@@ -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
*/
diff --git a/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/matcher/BoolArrayMatcher.java b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/matcher/BoolArrayMatcher.java
index 3956f16cc..11e4a54a5 100644
--- a/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/matcher/BoolArrayMatcher.java
+++ b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/matcher/BoolArrayMatcher.java
@@ -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;
diff --git a/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/matcher/DayOfMonthMatcher.java b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/matcher/DayOfMonthMatcher.java
index 1012b00fa..2ddc1e6f3 100644
--- a/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/matcher/DayOfMonthMatcher.java
+++ b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/matcher/DayOfMonthMatcher.java
@@ -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)));
}
/**
- * 是否为本月最后一天,规则如下:
+ * 获取指定值之后的匹配值,也可以是指定值本身
+ * 如果表达式中存在最后一天(如使用"L"),则:
+ *
* 1、闰年2月匹配是否为29 * 2、其它月份是否匹配最后一天的日期(可能为30或者31) + * 3、表达式包含最后一天(使用31表示) ** * @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); } } diff --git a/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/matcher/PatternMatcher.java b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/matcher/PatternMatcher.java index 01a7deaf4..5acde90b6 100644 --- a/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/matcher/PatternMatcher.java +++ b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/matcher/PatternMatcher.java @@ -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 { * * *
- * 秒 分 时 日 月 周 年 + * 秒 分 时 日 月(1) 周(0) 年 * 下 <-----------------> 上 ** - * @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 { * 下 <-----------------> 上 * * - * @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--; diff --git a/hutool-cron/src/test/java/org/dromara/hutool/cron/pattern/IssueI92H5HTest.java b/hutool-cron/src/test/java/org/dromara/hutool/cron/pattern/IssueI92H5HTest.java new file mode 100644 index 000000000..c633e96eb --- /dev/null +++ b/hutool-cron/src/test/java/org/dromara/hutool/cron/pattern/IssueI92H5HTest.java @@ -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)); + } +}