Cron 表达式最常见的形式是五个字段,用空格分开:
┌───── 分钟 (0-59)
│ ┌───── 小时 (0-23)
│ │ ┌───── 日 (1-31)
│ │ │ ┌───── 月 (1-12)
│ │ │ │ ┌───── 星期 (0-6,0 = 周日)
│ │ │ │ │
* * * * *
关键在于:这五个字段之间是"与"的关系,一个时间点只有在五个字段全部匹配时才会触发。
每个字段能写什么
| 写法 | 含义 | 例子 |
|---|---|---|
* | 任意值 | 小时写 * = 每小时 |
5 | 具体值 | 分钟写 5 = 每小时的第 5 分钟 |
1-5 | 范围 | 星期写 1-5 = 周一到周五 |
1,15 | 枚举 | 日写 1,15 = 每月 1 号和 15 号 |
*/10 | 步长 | 分钟写 */10 = 每 10 分钟 |
9-17/2 | 范围内步长 | 小时写它 = 9 到 17 点之间每隔 2 小时 |
几个常见表达式
0 * * * * 每小时整点
*/5 * * * * 每 5 分钟
0 3 * * * 每天凌晨 3:00
0 3 * * 0 每周日凌晨 3:00
0 0 1 * * 每月 1 号零点
30 9 * * 1-5 工作日 9:30
0 */6 * * * 每 6 小时
最容易踩的坑:日与星期的"或"
这是 Cron 里最反直觉的一条规则。当日和星期两个字段都不是 * 时,大多数实现(包括经典的 Vixie Cron 和大多数 Linux 发行版)会变成"或"的关系,而不是"与"。
0 0 1 * 1
直觉上会读成"每月 1 号且是周一的零点",但实际含义是"每月 1 号,或者任意一个周一"。想表达"每月 1 号且是周一",只能把其中一个字段设为 *,然后在脚本里自己判断。
这条规则每年都会坑到一批人,尤其是写账单、报表这类对日期敏感的任务时。
另一个坑:时区
Cron 用的是服务器本地的时区。同一份配置放在不同机房,触发时间可能差好几个小时。夏令时切换的日子里,被跳过的那个小时内的任务可能整天都不执行。
如果对时间精度有要求,要么把服务器时区统一成 UTC 并做好换算,要么改用支持显式指定时区的调度系统。
怎么验证自己写对了
最直接的办法是看它接下来会怎么触发。Cron 表达式工具 会把你写的表达式翻译成中文说明,并列出未来 8 次触发时间 —— 上面那个 0 0 1 * 1 到底什么时候跑,一眼就看得出来了。