📋 目录
一、工具类功能
校验 cron 表达式语法是否合法、算出从某个时刻起下一次什么时候触发、预演接下来 N 次的触发时间,只收 Spring 的 6 段表达式,基于 CronExpression 实现。
二、所需环境
JDK 17或以上 + Spring Boot 3.x或以上
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
三、方法与调用示例(按需调用)
🔢 方法序号:1
🎯 方法用途:
- 判断一句 cron 表达式是否合法,只给布尔不抛异常
- 任务配置入库前的校验、管理后台的实时提示用这个入口
🧩 方法定义:public static boolean isValid(String cron)
🗣️ 入参解释:public static boolean isValid(cron表达式)
📤 返回值解释:boolean,能被 Spring 解析就返回 true
💡 边界口径:
- 只接受 Spring 风格的 6 段(秒 分 时 日 月 周),
?、L、W、#以 Spring 实现为准 - 5 段的 Unix cron 与 7 段的 Quartz 写法都判
false - null、空白、语法错误一律
false,不抛异常,适合直接贴在表单校验里 - 校验通过不代表还会触发,日期型表达式可能永远落不到有效日
🧪 方法示例:
- 每天 3 点、工作日 6 点半、每月最后一天 6 点半、第二个周一 8 点四种写法都拿到
true - 少一段的 5 段写法拿到
false - 秒位越界的表达式拿到
false - 空白与 null 都拿到
false,不抛异常 - 上面几种非法写法用它判都是
[false, false, false]
🔢 方法序号:2
🎯 方法用途:
- 从指定基准时间算下一次触发时刻,结果沿用基准所在时区
- 调度页面上「下次执行时间」这类预演、以及验证表达式写得对不对时用
🧩 方法定义:public static Optional<ZonedDateTime> nextExecution(String cron, ZonedDateTime from)
🗣️ 入参解释:public static Optional<ZonedDateTime> nextExecution(cron表达式, 起始时间)
📤 返回值解释:Optional<ZonedDateTime>,永不再触发时为空
💡 边界口径:
- 「下一次」不含基准时刻本身,基准正好压在触发点上取的是再下一个
- 同一句表达式在 UTC 与东八区得到不同瞬间,本地钟点相同
- 日期不存在(2 月 30 日)时返回空 Optional,不抛异常
- 表达式为 null 或空白、基准为 null 抛
IllegalArgumentException,消息带上原始表达式与 Spring 的原因
🧪 方法示例:
- 从
2026-01-01T00:00(Asia/Shanghai, 2025-12-31T16:00:00Z)起,每天 3 点的下一次是2026-01-01T03:00(Asia/Shanghai, 2025-12-31T19:00:00Z) - 基准正好压在触发点上时,
2026-01-01T03:00(Asia/Shanghai, 2025-12-31T19:00:00Z)得到的是2026-01-02T03:00(Asia/Shanghai, 2026-01-01T19:00:00Z) - 同一句表达式换 UTC 基准,拿到
2026-01-01T03:00(UTC, 2026-01-01T03:00:00Z),本地钟点相同但瞬间不同 - 2 月 30 日不存在这种表达式拿到
Optional.empty
🔢 方法序号:3
🎯 方法用途:
- 不传基准,按此刻与 JVM 默认时区算下一次触发
- 服务端本机调度、只想看「接下来什么时候跑」时用这一版
🧩 方法定义:public static Optional<ZonedDateTime> nextExecution(String cron)
🗣️ 入参解释:public static Optional<ZonedDateTime> nextExecution(cron表达式)
📤 返回值解释:Optional<ZonedDateTime>,永不再触发时为空
💡 边界口径:
- 基准取
ZonedDateTime.now(),服务器时区不同结果不同,跨时区部署要显式传基准 - 判定与不含基准的语义同两参版,仍不含当前瞬间本身
- 表达式为 null 或空白抛
IllegalArgumentException
🧪 方法示例:
- 每分钟的表达式
nextExecution("0 * * * * *")不传基准,拿到2026-10-13T12:01(Asia/Shanghai, 2026-10-13T04:01:00Z) - 表达式传空白串抛
cron表达式不能为空
🔢 方法序号:4
🎯 方法用途:
- 一次算出接下来若干次触发时刻,返回严格递增的列表
- 排班预览、给运维看「今天还会跑哪几次」这类界面用它
🧩 方法定义:public static List<ZonedDateTime> nextExecutions(String cron, ZonedDateTime from, int count)
🗣️ 入参解释:public static List<ZonedDateTime> nextExecutions(cron表达式, 起始时间, 次数)
📤 返回值解释:List<ZonedDateTime>,按时间严格递增,可能少于 count
💡 边界口径:
- 表达式提前失效(永不再触发)时实际数量可以少于
count,不补空位 - 第一个元素与
nextExecution的结果一致,两个入口口径打通 count不大于 0 抛IllegalArgumentException,消息带上实际值- 时区与基准语义与两参版完全相同
🧪 方法示例:
- 每天 3 点取 5 次,拿到
[2026-01-01T03:00(Asia/Shanghai, 2025-12-31T19:00:00Z), 2026-01-02T03:00(Asia/Shanghai, 2026-01-01T19:00:00Z), 2026-01-03T03:00(Asia/Shanghai, 2026-01-02T19:00:00Z), 2026-01-04T03:00(Asia/Shanghai, 2026-01-03T19:00:00Z), 2026-01-05T03:00(Asia/Shanghai, 2026-01-04T19:00:00Z)] - 每 10 分钟取 3 次,列表严格递增且第一个与方法2 一致,判断拿到
true - 永不再触发的表达式拿到
0 个,数量少于要求的次数 - Quartz 的 7 段写法抛
cron表达式非法: 0 0 0 1 1 ? 2026(Spring 只支持 6 段表达式,Quartz 的第 7 段年份需去掉), 原因: Cron expression must consist of 6 fields (found 7 in "0 0 0 1 1 ? 2026") - 次数传 0 抛
次数必须大于0,当前为0,起始时间传 null 抛起始时间不能为null
四、工具类源码(可直接复制)
📦 源码文件