如何读懂 cron 表达式
cron 表达式用几个以空格分隔的字段描述重复执行的计划。思路很简单,但不同系统的字段顺序和特殊字符各不相同。本指南介绍如何解读常见的格式。
Unix 的五个字段
经典的 Unix 和 Linux cron 使用五个字段,从左到右依次为:
分 时 日 月 星期
因此 30 9 * * 1-5 的意思是“周一至周五的 09:30”。分钟的取值为 0 到 59,小时为 0 到 23,日为 1 到 31,月为 1 到 12。星期的取值为 0 到 6,0 表示星期日(许多实现也接受用 7 表示星期日)。大多数版本还接受 JAN 或 MON 这样的名称,以及 @daily 或 @hourly 这样的宏。
Spring、Quartz 和 AWS 格式
其他调度器增加了字段,这是把表达式从一个系统复制到另一个系统时最常见的混淆来源。
| 格式 | 字段 | 星期 |
|---|---|---|
| Unix / Linux | 5 个:分 时 日 月 星期 | 0–6,星期日 = 0 |
| Spring | 6 个:秒 分 时 日 月 星期 | 0–7,星期日 = 0 或 7 |
| Quartz | 6 或 7 个:秒 分 时 日 月 星期 [年] | 1–7,星期日 = 1 |
| AWS EventBridge | 6 个:分 时 日 月 星期 年 | 1–7,星期日 = 1 |
同样的数字可能有不同的含义:0 0 12 * * ? 在 Quartz 中表示“每天中午”,而按 5 个字段解读则是无效的。星期的编号也会错位:在 Quartz 和 AWS 中 2 表示星期一,而在 Unix cron 中表示星期二。在支持的地方,使用 MON 这样的星期名称可以避免这种歧义。
特殊字符
*字段的所有值。,值列表,例如1,15。-范围,例如9-17或MON-FRI。/步长:分钟字段中的*/15表示每 15 分钟;5/15表示从 5 开始(经典 Unix cron 不支持,需改用5-59/15这样的范围)。?“不指定值”;Quartz 和 AWS 要求在日或星期字段中使用它。L最后:日字段中的L表示当月最后一天;在 Quartz 中,星期字段中的6L表示最后一个星期五。W最近的工作日:15W表示最接近 15 日的工作日。#当月第 N 个星期几:按 Quartz 和 AWS 的编号,6#3表示第三个星期五。
L、W 和 # 是扩展语法,经典 Unix cron 不支持。
日与星期字段
当两个日期字段都受到限制时,不同系统对如何组合它们的看法不一。在经典 Unix cron 中,只要任一字段匹配,任务就会运行:0 9 13 * 5 会在每月 13 日以及每个星期五运行,而不只是在 13 日恰逢星期五时运行。Spring 要求两者同时匹配。Quartz 和 AWS 则要求两个字段之一必须为 ?,从而回避了这个问题。
LeedyBox Cron 解析器会应用您所选格式的规则,并在摘要中说明是满足其一即可,还是两者都必须匹配。
时区与夏令时
cron 表达式本身不包含时区。它按执行它的系统或服务的时区运行,可能是服务器的本地时间,也可能是 UTC。按本地时间 09:00 编写的任务,如果服务器使用 UTC,可能会在另一个钟点运行。
夏令时还会带来更多边界情况。时钟拨快时,02:30 这样的时间在当天可能不存在;时钟拨回时,它可能出现两次。根据实现的不同,处于该时段的任务可能被跳过、延迟运行或运行两次。将重要任务安排在切换时段之外,或使用 UTC,可以减少意外。Cron 解析器允许您选择时区并列出下次运行时间,方便您检查切换前后的日期。
常见问题
如何每 5 分钟运行一次任务?
在 Unix cron 中使用 */5 * * * *。在 Spring 中,需要在开头加上秒字段:0 */5 * * * *。
为什么我的 Quartz 表达式在 Linux crontab 中无法使用?
Quartz 表达式以秒字段开头,并且可能使用 ? 或 L,而 crontab 不接受这些写法。请将表达式改写为 5 个字段的格式。
解析器显示的下次运行时间准确吗?
它们是在您的浏览器中按所选格式和时区计算的。由于各实现可能有所不同,请在实际运行任务的系统上确认计划。