03 / 指南
写出不闹意外的 Cron 表达式
五个字段及其取值范围、名称与数字、日期字段的“或”陷阱、周日是 0 还是 7、夏令时的空缺与重叠,以及部署前用未来运行预览来验证。
五个字段,从左向右读
经典 Cron 表达式是五个以空格分隔的字段:分钟(0–59)、小时(0–23)、日(1–31)、月(1–12)、星期(0–7)。每个字段接受单个值、逗号分隔的列表、连字符范围、斜杠步长,或表示所有值的星号。
大多数 Cron 缺陷并非罕见语法,而是普通字段被粗心地读错:比如在范围边界重置的步长,或在按 23 小时计数的系统里按十二小时制写下的小时字段。
- 列表列表挑选离散值:分钟字段里的 0,30 表示整点和半点各触发一次。
- 范围与步长范围挑选一段区间:小时字段里的 9-17 覆盖工作时间,步长可以把区间抽稀——9-17/2 表示区间内每两小时一次。
- 星号匹配全部星号不是“忽略此字段”;它主动匹配所有值,一旦两个日期字段相互作用,这一点就变得关键。
名称是带文档的数字
月份与星期字段接受三字母英文名称——JAN 到 DEC、SUN 到 SAT——作为数字的别名。0 9 * JAN MON 与 0 9 * 1 1 是同一个排程,但名称版能向下一位读者自我解释,也更抗“差一”式修改。
- 不区分大小写名称解析不区分大小写——mon、Mon、MON 都可以——但大写是显得有意的惯例。
- 范围里的名称名称也能用在范围与列表里:MON-FRI 是标准的工作日区间,JAN,APR,JUL,OCT 选出季度月份。
- 读解释Cron 构建与解释器会规范化表达式并逐字段解释——“month: every value”“weekday: 1, 2, 3, 4, 5”——让你确认解析器按你的本意读懂了名称。
两个日期字段是“或”关系
这是 Cron 最昂贵的意外:当“日”和“星期”两个字段都被限定——都不是星号——排程在任一匹配时就会触发。0 9 13 * FRI 不是“13 号星期五”,而是每月 13 号和每个星期五。
解释器的输出对此很明确:它把语义标注为 dom-dow-or 并连同计算出的运行一起展示,于是一个会比预期触发频繁得多的表达式,会在进入 crontab 之前就露出马脚。
- 一个通配很简单只要任一日期字段是星号,另一个就单独做主——这正是多数人以为处处成立的直觉行为。
- 都限定就是“或”两者都限定意味着“或”:经典 Vixie cron 语义(多数 Unix 排程器沿用)在日期匹配或星期匹配时都会运行任务。
- “与”需要变通要真正命中“13 号星期五”,让 cron 选中 13 号,再让命令本身检查星期——或者换一个有“与”语义的排程器,经典 cron 不是。
周日是 0,通常也可以是 7
星期字段把周日编号为 0,周一为 1,依此到周六为 6。多数实现还接受 7 作为第二个周日,照顾从周一开始数的人——Toolars 解析器内部会把 7 归一为 0——但有些系统只接受其一,别处还存在以周一为 1 的 ISO 式编号。
- 0 与 7在接受 7 的地方,0 和 7 都是周日;写 0 是跨排程器的可移植选择。
- 悄悄的差一在拒绝 7 的系统上写 7 会在部署时响亮地失败;但把星期误读为“从周一起数的第七天”则会在错误的那一天悄悄失败。
- 逐平台再确认当表达式在实现之间迁移——系统 crontab、容器平台、CI 排程器——请重新确认星期编号,而不是假设它安全抵达。
夏令时会掰弯排程
Cron 按排程器所在时区的挂钟时间触发,而挂钟每年有两次不守规矩。春拨快的空档里,02:30 这样的本地时间根本不存在;秋拨回时它又存在两次。钉在这一分钟的排程会在三月漏跑一次,还可能在十一月跑两次。
- 消失的一小时春季空档:切换之夜的 02:30 任务没有可匹配的挂钟时间,于是什么都不触发——没有报错,没有重试,只是少了一次运行。
- 翻倍的一小时秋季重叠:同一挂钟时间出现两次,各实现对于任务跑一次还是两次并不一致;除非你验证过,否则按两次来假设。
- UTC 没有夏令时干净的逃生口是 UTC:它没有夏令时切换,30 2 * * * 在一年里的每一天都是同一时刻。解释器提供 UTC、本机时区,以及 America/New_York、Europe/London 等命名时区。
用未来运行验证,不靠运气
正确的表达式与看起来像样的表达式之间,差着一份具体的触发时刻清单。Cron 构建与解释器从你选择的时区出发计算未来十次运行,最远搜索 366 天,并在后台工作线程中求值,再重的表达式也不会卡住标签页。
- 读运行清单对着意图读头几次运行:“工作日 09:00”应该列出周一到周五的九点;列表里出现一个周六,就是“或”陷阱在自报家门。
- 跨一次切换有意跨过夏令时边界:在命名时区里挑一个三月或十一月的日期,确认时钟拨动时排程仍如你所愿。
- 明确的错误失败是明确的:非法字段、字段数错误、或无法在搜索窗口内凑出十次运行的排程,各自得到具名错误,而不是一份悄悄空掉的列表。
排程与正则共享同一套纪律。
两者都是破坏半径远超体量的小字符串——学习正则表达式的编写—测试—重构循环,在带超时的本地工作线程中运行。