如何逐个字段读懂 Cron 表达式
Cron 表达式是一行简短的文本,用来告诉调度器什么时候运行任务。常见格式有五个字段,用空格分隔:分钟、小时、日期、月份和星期。这一行 0 9 * * 1-5 表示周一到周五的 09:00。每个字段可以是数字、名称、列表、范围或步长,也可以是表示所有值的 *。当所有字段都匹配时,任务才会在那个时刻运行。
五个字段
| 字段 | 可用取值 | 示例 |
|---|---|---|
| 分钟 | 0 到 59 | 30 表示每小时的第 30 分钟 |
| 小时 | 0 到 23 | 9 表示 09:00,采用 24 小时制 |
| 日期 | 1 到 31 | 15 表示每月 15 日 |
| 月份 | 1 到 12,或 JAN 到 DEC | 3 或 MAR 表示三月 |
| 星期 | 0 到 6,或 SUN 到 SAT | 0 或 SUN 表示星期日,1 表示星期一 |
有些系统也接受 7 表示星期日,但并非所有系统都支持,所以建议使用 0 或 SUN。这种格式的 cron 没有秒字段,所以最小的时间单位是一分钟。
列表、范围、步长和名称
表示该字段的所有值。* * * *表示每分钟运行一次。- 列表用逗号分隔。
0 9,17 * * *表示每天 09:00 和 17:00 运行。 - 范围用连字符表示。星期字段中的
1-5表示周一到周五。 - 步长用斜杠表示。分钟字段中的
*/15表示 0、15、30 和 45 分。10-30/5表示 10、15、20、25 和 30 分。 - 名称如
MON-FRI可以代替数字,更容易读懂,前提是你的调度器支持。
这些写法可以组合使用。0 8-18/2 * * * 表示从 08:00 到 18:00,每隔两小时的第 0 分钟运行。
常见的时间安排
| 表达式 | 含义 |
|---|---|
* * * * * | 每分钟 |
/15 * * * | 每 15 分钟 |
0 * * * * | 每小时的第 0 分钟 |
0 2 * * * | 每天 02:00 |
0 9 * * 1-5 | 周一到周五的 09:00 |
0 9,17 * * * | 每天 09:00 和 17:00 |
30 6 1 * * | 每月 1 日 06:30 |
0 0 * * 0 | 每周日午夜 |
0 0 1 1 * | 1 月 1 日午夜 |
/5 9-17 * 1-5 | 周一到周五,从 09:00 到 17:55 每 5 分钟一次 |
注意,0 0 * * 是午夜,0 12 * * 是中午。把小时写错是最常见的错误之一。
日期与星期
当日期字段和星期字段都做了限制时,只要其中任意一个匹配,任务就会运行。这是 Vixie cron 以及许多由它衍生的系统所采用的经典规则。因此,0 0 13 * 5 会在每月 13 日的午夜运行,也会在每个星期五运行。很多人以为它只会在"星期五且是 13 日"时运行,结果运行次数比预期多得多。
如果两个字段中有一个是 *,那么只有另一个字段生效。要想只在"星期五的 13 日"运行,可以每周五都安排任务,然后在脚本内部检查日期。其他调度器,包括一些云服务,可能采用不同的规则,使用前请查阅它们的文档。
时区与夏令时
Cron 按照运行它的机器或服务的时钟运行,通常就是该机器所在时区的本地时间。设置为 02:30 的任务,会在该时钟显示 02:30 时运行。先检查时区(在 Linux 上可以用 date 查看)。如果希望任务在各处都按同一个时钟运行,许多团队会把服务器设为 UTC,有些调度器也允许为每个任务单独设置时区。
夏令时每年会调整两次时钟。春季,许多地方的时钟会从 02:00 直接跳到 03:00,因此 02:30 的任务根本没有机会运行。秋季,01:00 到 01:59 这一小时会出现两次。不同的调度器对这些情况的处理方式不同:有的跳过任务,有的只运行一次,有的运行两次。如果差异对你很重要,就避开凌晨的时间调整窗口,或者改用 UTC。
六个字段与 Quartz
一些调度器会在开头增加一个秒字段,所以 /10 * * * * 表示每 10 秒运行一次。Java 的 Quartz 调度器使用秒、分钟、小时、日期、月份、星期以及可选的年份字段,并增加了四个特殊字符:
?表示"不指定具体值"。Quartz 要求日期字段或星期字段中,必须有一个使用它,具体是哪个取决于你没有设置的那个字段。L表示最后一个。日期字段中的L表示月末最后一天,星期字段中的6L表示最后一个星期五。W表示最近的工作日。15W是离 15 日最近的工作日,并且不会跨到另一个月。#表示第 n 个某星期几。6#3表示第三个星期五。
Quartz 对星期的编号与五字段格式不同。它的 1 表示星期日,7 表示星期六,因此周一到周五应写作 2-6,而不是 1-5。在复制任何一行之前,请先确认你的调度器使用哪种格式。
常见错误
- 分钟字段写
*/7。取值是 0、7、14,一直到 56。下一个值本应是 63,但它超出了范围,所以 56 之后的运行会在下一小时的第 0 分钟。间隔只有四分钟,而且分布不均匀。应使用能被 60 整除的步长,比如 5、10、15 或 30。 - 2 月 30 日。 这样的日期不存在,所以
0 0 30 2 *永远不会运行。在只有 30 天的月份里写 31 日也是同样的道理。按照"二者任一"的规则,星期字段的限制仍可能让任务在其他日子运行,所以要检查结果。 - 小时写 24 或分钟写 60。 这些值超出了范围。大多数调度器在保存时会拒绝,但并非全部都会。
- 以为有秒。 五字段的表达式只能精确到整分钟。
- 把 cron 和正则表达式混为一谈。 在 cron 中,
表示所有值;在正则表达式中,表示重复前面的项目。要测试模式,请使用正则表达式测试工具,并且把 cron 表达式和正则分开看待。
检查接下来的运行时间
在依赖某个时间安排之前,先用白话读一遍这一行,并列出接下来几次的运行时间。Cron 表达式解析器可以同时完成这两件事,这是发现"二者任一"规则或奇怪步长最快的方法。
快速解答
第一个字段是分钟还是秒?
在标准的五字段格式中是分钟。只有部分六字段的调度器会增加秒,并且把秒放在最前面。
为什么我的任务既在星期五运行,又在 13 日运行?
因为日期字段和星期字段都已设置,所以只要其中一个匹配,任务就会运行。如果只想让其中一个生效,就把另一个字段设为 *。
Cron 使用 UTC 吗?
只有当机器或服务被设置为 UTC 时才是。否则,它使用该系统所在的本地时区。
由 AI 辅助翻译自英文原文。
评论
还没有评论。快来抢沙发。


