根据指定日期和cron表达式生成下一周期的执行时间
文章目录
- 前言
- 一、日期计算单元类UtilityClass
- 二、时间维度Enum类
- 三、测试类
前言
根据指定日期和cron表达式生成下一周期的执行时间
一、日期计算单元类UtilityClass
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.base.Preconditions;
import lombok.experimental.UtilityClass;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.support.CronExpression;
import java.time.LocalDateTime;
import java.util.Date;/*** 日期计算*/
@UtilityClass
public class WoDateUtil {/*** 根据 {@code periodUnit+value} 计算下次日期* <li>单位是fixed_date:存储的是JSON array字符串,日期格式:yyyy-MM-dd,最多支持30个日期,如:["2025-05-20", "2025-06-03"] 日期和月份必须是2位</li>* <li>单位是weekly:存储的是0-6, 0代表周日</li>* <li>单位是monthly:存储的是01-31,必须是2位</li>* <li>单位是yearly:存储的是MM-dd,如: 05-20 日期和月份必须是2位</li>* @param periodUnit 周期单位* @param value 周期值* @param currentDate 当前日期 如果null则默认当前系统日期* @return 返回计算结果下次日期 格式:yyyy-MM-dd。注意:<br>* <li>1. 返回的可能是currentDate,比如:每周四,currentDate恰好是周四</li>* <li>2. 如果是fixed_date可能返回null</li>*/public String nextDate(PeriodUnitEnum periodUnit, String value, Date currentDate) {Preconditions.checkNotNull(periodUnit);Preconditions.checkNotNull(value);if (periodUnit == PeriodUnitEnum.UNKNOWN) {throw new QuantdoServiceException(-1, "PeriodUnitEnum.UNKNOWN无法计算");}String format = "yyyy-MM-dd";DateTime current = currentDate != null ? DateUtil.date(currentDate) : DateUtil.date();current.setMutable(false);DateTime next = null;switch (periodUnit) {case WEEKLY:case MONTHLY:case YEARLY:next = next(periodUnit, current, value);break;case FIXED_DATE:next = JSON.parseArray(value, String.class).stream().distinct().sorted().map(x -> DateUtil.parse(x, format)).filter(x -> DateUtil.compare(x, current, format) >= 0).findFirst().orElse(null);break;}return next != null ? next.toString(format) : null;}private DateTime next(PeriodUnitEnum unit, DateTime start, String value) {// cron设置的时间点为: 23:59:59 ,为了保证计算结果//包含当天需要强制将锚点start时间点设置为00:00:00。// nextDate是否包含当天选择权因由上层调用者抉择。start = DateUtil.beginOfDay(start);String cron;if (unit == PeriodUnitEnum.YEARLY) {String[] arr = StringUtils.split(value, "-");cron = StrUtil.format(unit.getCron(), Integer.parseInt(arr[1]), Integer.parseInt(arr[0]));} else {cron = StrUtil.format(unit.getCron(), Integer.parseInt(value));}LocalDateTime next = CronExpression.parse(cron).next(start.toLocalDateTime());return new DateTime(next);}
}
二、时间维度Enum类
import lombok.Getter;
import lombok.RequiredArgsConstructor;/*** 订单按时创建任务创建周期单位。* cron设置的时间点为: 23:59:59 ,为了保证计算结果包含当天。*/
@RequiredArgsConstructor
@Getter
public enum PeriodUnitEnum {WEEKLY("weekly", "每周", "59 59 23 ? * {}"),//周天至周6,0-6,如每周2 23:59:59为59 59 23 ? * 2MONTHLY("monthly", "每月", "59 59 23 {} * ?"),//如每月15号 23:59:59为59 59 23 15 * ?YEARLY("yearly", "每年", "59 59 23 {} {} ?"),//如每年1月15号 23:59:59为59 59 23 15 1 ?FIXED_DATE("fixed_date", "固定日期", ""),UNKNOWN("unknown", "未知", "");private final String code;private final String title;private final String cron;public static PeriodUnitEnum getByCode(String code) {for (PeriodUnitEnum anEnum : PeriodUnitEnum.values()) {if (anEnum.getCode().equalsIgnoreCase(code)) {return anEnum;}}return UNKNOWN;}
}
三、测试类
public static void main(String[] args) {// 日期必须要+1,因为WoDateUtil.nextDate可能会返回当天DateTime offset = DateUtil.date().offset(DateField.DAY_OF_YEAR, 1);//当前是2025年6月4号String nextYearlyExecuteDate = WoDateUtil.nextDate(PeriodUnitEnum.getByCode("yearly"), "06-02", offset);//每年6月2号String nextMonthlyExecuteDate = WoDateUtil.nextDate(PeriodUnitEnum.getByCode("monthly"), "02", offset);//每月2号String nextWeeklyExecuteDate = WoDateUtil.nextDate(PeriodUnitEnum.getByCode("weekly"), "2", offset);//每周2System.out.println(JSON.toJSONString(nextYearlyExecuteDate));System.out.println(JSON.toJSONString(nextMonthlyExecuteDate));System.out.println(JSON.toJSONString(nextWeeklyExecuteDate));DateTime dataTime = DateUtil.parse("2025-06-04","yyyy-MM-dd").offset(DateField.DAY_OF_YEAR, 1);String nextYearlyExecuteDate2 = WoDateUtil.nextDate(PeriodUnitEnum.getByCode("yearly"), "06-15", dataTime);//每年6月15号String nextMonthlyExecuteDate3 = WoDateUtil.nextDate(PeriodUnitEnum.getByCode("monthly"), "15", dataTime);//每月15号String nextWeeklyExecuteDate4 = WoDateUtil.nextDate(PeriodUnitEnum.getByCode("weekly"), "2", dataTime);//每周2System.out.println(JSON.toJSONString(nextYearlyExecuteDate2));System.out.println(JSON.toJSONString(nextMonthlyExecuteDate3));System.out.println(JSON.toJSONString(nextWeeklyExecuteDate4));
}
输出:
"2026-06-02"
"2025-07-02"
"2025-06-10"
"2025-06-15"
"2025-06-15"
"2025-06-10"
Cron在线工具