逐字段理解 Cron 表达式

理解 Cron 的五个字段、控制范围的运算符、时区陷阱,以及 Unix Cron 与现代变体的差异。

五个字段,一行表达式

经典 Unix Cron 表达式由空格分隔的五个字段组成:分钟(0–59)、小时(0–23)、日(1–31)、月(1–12)、星期(0–7,其中 0 和 7 都表示星期日)。“30 9 * * 1-5”的含义是:第 30 分钟、第 9 小时、每月每天、每月、周一至周五,也就是工作日早上 09:30。

┌ 分钟 (0-59)
│ ┌ 小时 (0-23)
│ │ ┌ 日 (1-31)
│ │ │ ┌ 月 (1-12)
│ │ │ │ ┌ 星期 (0-7)
│ │ │ │ │
30 9 * * 1-5

运算符

容易被忽略的是日字段和星期字段的交互:当两者都有限制(都不是 *)时,Cron 通常在任意一个条件匹配时运行,而不是两个条件同时匹配。“0 0 13 * 5”会在每月 13 日以及每个星期五运行,这几乎从来不是作者真正想表达的意思。

  • *——字段中的任意值。
  • ,——列表:日字段中的 1,15 表示每月 1 日和 15 日。
  • -——范围:星期字段中的 1-5 表示周一到周五。
  • /——步长:分钟字段中的 */10 表示每 10 分钟;5/20 表示从第 5 分钟开始,每 20 分钟执行一次(5、25、45)。

时区与夏令时

除非实现支持 CRON_TZ 变量或显式时区设置,否则 Cron 会使用系统本地时区。夏令时切换期间,02:30 的任务在秋季可能运行两次,在春季可能一次都不运行。对于计费、报表生成等不能重复执行的任务,应避开 01:00–03:00 时间段,或统一使用 UTC。

Cron 不总是同一种 Cron

五字段格式是 POSIX 经典形式,但不同变体并不完全一致。有些解析器增加开头的秒字段(Quartz、Spring 以及 AWS EventBridge 使用六字段);有些支持名称(JAN、MON)、L(当月最后一天)、W(最近的工作日)和 #(第几个星期几)。Kubernetes CronJob 和 GitHub Actions 使用普通五字段 Cron,但 GitHub Actions 始终按 UTC 执行。

当表达式“在本地有效”却在生产环境表现不同,首先要问的是目标调度器使用哪一种方言。最稳妥的表达式只使用数字和四种基础运算符。

参考资料

相关工具