Cron 表达式速查:常用写法、时区陷阱和平台差异
5 个字段分别是什么意思、常用写法速查表、“日”和“周”同时设置的特殊规则,以及 Linux、Kubernetes、GitHub Actions、Spring 之间的差异。
Cron 表达式几乎每个后端开发者都写过,但很少有人能一次写对所有细节。这篇是一份可以收藏的速查表,最后附上几个常见的坑。
5 个字段
标准 cron 表达式由 5 个字段组成,用空格分隔:
┌───────── 分钟 (0–59)
│ ┌─────── 小时 (0–23)
│ │ ┌───── 日 (1–31)
│ │ │ ┌─── 月 (1–12 或 JAN–DEC)
│ │ │ │ ┌─ 星期 (0–7,0 和 7 都是周日,或 SUN–SAT)
│ │ │ │ │
* * * * *
每个字段可以使用:
| 符号 | 含义 | 例子 |
|---|---|---|
* | 任意值 | * 在小时字段表示每小时 |
, | 列表 | 1,15 表示 1 号和 15 号 |
- | 范围 | 9-17 表示 9 点到 17 点 |
/ | 步长 | */15 表示每隔 15 个单位 |
常用写法
| 表达式 | 含义 |
|---|---|
* * * * * | 每分钟 |
*/5 * * * * | 每 5 分钟 |
0 * * * * | 每小时整点 |
0 */2 * * * | 每 2 小时 |
0 0 * * * | 每天 0 点 |
30 8 * * * | 每天 8:30 |
0 9 * * 1-5 | 工作日 9:00 |
0 9,18 * * 1-5 | 工作日 9:00 和 18:00 |
*/10 9-18 * * 1-5 | 工作日 9 点到 18 点之间,每 10 分钟 |
0 0 * * 0 | 每周日 0 点 |
0 3 * * 6 | 每周六 3:00 |
0 0 1 * * | 每月 1 号 0 点 |
0 0 1,15 * * | 每月 1 号和 15 号 |
0 0 1 */3 * | 每季度第一天 |
0 0 1 1 * | 每年 1 月 1 日 |
@daily | 等同于 0 0 * * * |
@weekly | 等同于 0 0 * * 0 |
@monthly | 等同于 0 0 1 * * |
写完不确定对不对?粘贴到 Cron 表达式解析 里,会给出中文说明和接下来 10 次的执行时间。
坑 1:“日”和“周”同时设置时是“或”
0 0 1 * 1
很多人以为这表示“每月 1 号,且是周一”。实际上,在标准 cron 中,日和周都不是 * 时,只要满足其中一个就会执行:每月 1 号会执行,每个周一也会执行。
坑 2:时区
cron 按运行它的那台机器(或平台)的时区执行:
- 云服务器、容器的默认时区经常是 UTC,比北京时间晚 8 小时。写
0 9 * * *想要北京时间早上 9 点,在 UTC 的机器上实际会在北京时间 17 点执行; - GitHub Actions 的
schedule默认按 UTC 计算; - Kubernetes CronJob 支持通过
timeZone字段指定时区,例如Asia/Shanghai。
要换算时间,可以用 时间戳转换 对照 UTC 和本地时间。
坑 3:不同平台的“方言”
| 平台 | 字段数 | 说明 |
|---|---|---|
| Linux crontab | 5 | 标准写法 |
| Kubernetes CronJob | 5 | 标准写法,可用 timeZone 指定时区 |
| GitHub Actions | 5 | 默认 UTC;高峰期可能延迟,最短间隔为 5 分钟 |
Spring @Scheduled | 6 | 第一个字段是秒 |
| Quartz | 6–7 | 带秒和可选的年;日、周之一需要写 ?;支持 L、W、# |
L(最后一天)、W(最近的工作日)、#(第几个星期几)这些写法只在 Quartz 等部分实现中可用,标准 crontab 不支持。比如“每月最后一天”在标准 cron 里无法直接表达,常见做法是每天执行,在脚本里判断明天是不是 1 号。
坑 4:任务重叠和静默失败
- 如果任务的执行时间可能超过调度间隔,上一次还没结束、下一次就开始了,需要加锁(例如用
flock)或在平台上配置并发策略; - cron 默认不会通知失败,记得把输出重定向到日志,并为重要任务配置告警。
小结
- 5 个字段:分、时、日、月、周;
- 日和周同时设置时是“或”的关系;
- 先确认运行环境的时区;
- 跨平台复制表达式时,注意秒字段和扩展语法的差异。