CRON 表达式用来描述“一个任务什么时候执行”。
它常见于 Linux 定时任务、后端任务调度、Spring、Quartz、Node.js 定时任务库、云函数、CI/CD 定时流水线、Kubernetes CronJob 等场景。
但学习 CRON 时,最容易卡住的不是符号,而是不同平台的表达式格式不完全一样。
所以不要一上来就背 *、,、/。更好的学习顺序是:
先确认平台格式
↓
再拆字段含义
↓
理解特殊符号
↓
套常见时间模板
↓
最后检查时区和运行环境一、先识别 CRON 方言
同样是“每天 9 点执行”,不同平台可能写成不同形式。
| 平台类型 | 表达式 | 字段结构 |
|---|---|---|
| Linux crontab | 0 9 * * * | 5 位,没有秒 |
| 带秒的任务库 | 0 0 9 * * * | 6 位,第一位是秒 |
| Quartz / Spring 常见写法 | 0 0 9 * * ? | 6 位,支持 ? |
| Quartz 带年份 | 0 0 9 * * ? 2026 | 7 位,最后一位是年 |
写 CRON 前,先问一个问题:当前平台到底要几位?
这是最重要的一步。字段数量错了,后面每一位都会错位。
二、三种常见格式
1. Linux crontab:5 位
Linux crontab 的经典格式是:
分钟 小时 日 月 星期示例:
30 2 * * *含义:
每天 2:30 执行Linux crontab 没有秒字段。如果想做秒级任务,通常要用其他调度系统,或者在脚本内部循环处理。
2. 带秒格式:6 位
一些后端框架、Node.js 定时任务库、云函数平台会使用 6 位格式:
秒 分钟 小时 日 月 星期示例:
0 30 2 * * *含义:
每天 2:30:00 执行这个格式最容易和 Linux crontab 混淆,因为它只是多了最前面的秒字段。
3. Quartz 风格:6 位或 7 位
Quartz 常见格式是:
秒 分钟 小时 日 月 星期 [年]年字段通常可选。
示例:
0 30 2 * * ? 2026含义:
2026 年每天 2:30:00 执行Quartz 的特点是支持更多特殊符号,比如 ?、L、W、#。
不要把 Linux crontab、Quartz、Spring、云函数、GitHub Actions、Kubernetes CronJob 的表达式直接互相复制。先确认字段数量、星期编号、特殊符号和时区规则。
三、字段地图
以 Quartz 7 位格式为例:
秒 分钟 小时 日 月 星期 年| 位置 | 字段 | 常见取值 | 例子 |
|---|---|---|---|
| 1 | 秒 | 0-59 | 0 表示第 0 秒 |
| 2 | 分钟 | 0-59 | 30 表示第 30 分钟 |
| 3 | 小时 | 0-23 | 9 表示上午 9 点 |
| 4 | 日 | 1-31 | 1 表示每月 1 号 |
| 5 | 月 | 1-12 或 JAN-DEC | 1 或 JAN |
| 6 | 星期 | 平台差异较大,也可用 SUN-SAT | MON-FRI |
| 7 | 年 | 常见为可选 | 2026 |
如果是 Linux crontab,就只保留:
分钟 小时 日 月 星期四、符号不是孤立背的
CRON 的特殊符号可以按“表达意图”来记。
| 想表达什么 | 符号 | 示例 | 含义 |
|---|---|---|---|
| 任意值 | * | * | 该字段的所有值 |
| 固定几个值 | , | 9,12,18 | 9、12、18 |
| 一段范围 | - | 9-18 | 9 到 18 |
| 每隔一段时间 | / | */5 | 每 5 个单位 |
| 不指定日或星期 | ? | ? | Quartz 中常用 |
| 最后一个 | L | L | 最后一天或最后一个星期几 |
| 最近工作日 | W | 15W | 离 15 号最近的工作日 |
| 第几个星期几 | # | MON#2 | 每月第二个周一 |
注意:Linux crontab 通常不支持 ?、L、W、#。看到这些符号,大概率是在 Quartz 或类似平台里。
五、用模板理解常见时间
比起背语法,更实用的方法是记模板。
模板 1:每隔 N 分钟
Linux crontab:
*/5 * * * *含义:
每 5 分钟执行一次Quartz:
0 0/5 * * * ?含义:
每 5 分钟的第 0 秒执行一次模板 2:每天固定时间
每天凌晨 2 点:
0 2 * * *Quartz:
0 0 2 * * ?每天 9 点、12 点、18 点:
0 9,12,18 * * *Quartz:
0 0 9,12,18 * * ?模板 3:工作日固定时间
Linux crontab:
0 9 * * 1-5含义:
周一到周五每天 9:00 执行Quartz:
0 0 9 ? * MON-FRI这里 Quartz 用 ? 放在“日”字段,表示不指定每月第几天,只按星期判断。
模板 4:每月固定日期
每月 1 号 10 点:
0 10 1 * *Quartz:
0 0 10 1 * ?每月 1 号和 15 号 10 点:
0 10 1,15 * *Quartz:
0 0 10 1,15 * ?模板 5:每年固定日期
每年 1 月 1 日 0 点:
0 0 1 1 *Quartz:
0 0 0 1 1 ?模板 6:秒级任务
Linux crontab 本身不支持秒级调度。
Quartz 每 10 秒执行一次:
0/10 * * * * ?一些 6 位 Node.js 任务库可能写成:
*/10 * * * * *这类表达式一定要看库文档,因为不同库对秒字段和特殊符号的支持不同。
六、“日”和“星期”要单独讲
CRON 中最容易产生误解的是“日”和“星期”两个字段。
看这个 Linux crontab:
0 9 1 * 1它很容易被理解成:
每月 1 号,并且这一天是周一时执行但很多传统 crontab 实现会按“或”的关系处理:
每月 1 号执行,或者每周一执行Quartz 为了避免这个歧义,引入了 ?。
| 需求 | Quartz 写法 |
|---|---|
| 每月 1 号 9 点 | 0 0 9 1 * ? |
| 每周一 9 点 | 0 0 9 ? * MON |
记法很简单:
指定“日”时,“星期”用 ?
指定“星期”时,“日”用 ?七、速查表
Linux crontab
| 需求 | 表达式 |
|---|---|
| 每分钟 | * * * * * |
| 每 5 分钟 | */5 * * * * |
| 每小时整点 | 0 * * * * |
| 每天 0 点 | 0 0 * * * |
| 每天 2 | 30 2 * * * |
| 工作日 9 点 | 0 9 * * 1-5 |
| 每天 9 点和 18 点 | 0 9,18 * * * |
| 每月 1 号 0 点 | 0 0 1 * * |
| 每年 1 月 1 日 0 点 | 0 0 1 1 * |
Quartz
| 需求 | 表达式 |
|---|---|
| 每 10 秒 | 0/10 * * * * ? |
| 每 5 分钟 | 0 0/5 * * * ? |
| 每小时整点 | 0 0 * * * ? |
| 每天 0 点 | 0 0 0 * * ? |
| 每天 2 | 0 30 2 * * ? |
| 工作日 9 点 | 0 0 9 ? * MON-FRI |
| 每月 1 号 9 点 | 0 0 9 1 * ? |
| 每月第一个周一 9 点 | 0 0 9 ? * MON#1 |
| 每月最后一天 18 点 | 0 0 18 L * ? |
八、在 Linux crontab 中落地
Linux crontab 不只是一个表达式,它还要跟命令一起写。
编辑当前用户的定时任务:
crontab -e查看当前用户的定时任务:
crontab -l删除当前用户的全部定时任务:
crontab -r一个完整任务:
0 2 * * * /usr/bin/node /app/scripts/backup.js >> /var/log/backup.log 2>&1结构拆开看:
| 片段 | 含义 |
|---|---|
0 2 * * * | 每天 2 点 |
/usr/bin/node | 使用绝对路径调用 Node |
/app/scripts/backup.js | 要执行的脚本 |
>> /var/log/backup.log | 追加标准输出到日志 |
2>&1 | 把错误输出也写进同一个日志 |
如果脚本依赖项目目录,可以显式 cd:
0 2 * * * cd /app && /usr/bin/pnpm run backup >> /var/log/backup.log 2>&1九、时区是隐藏坑
CRON 写对了,任务仍然可能在“错误时间”执行。
常见原因是时区:
| 环境 | 常见情况 |
|---|---|
| Linux 服务器 | 取决于系统时区 |
| Docker 容器 | 默认可能是 UTC |
| 云函数 | 可能固定使用 UTC |
| CI/CD | 常见为 UTC |
| Kubernetes CronJob | 新版本可配置 timeZone |
先看当前机器时间:
dateKubernetes CronJob 如果支持 timeZone,可以显式配置:
spec:
timeZone: Asia/Shanghai
schedule: "0 2 * * *"如果平台不支持时区配置,就要把本地时间换算成平台使用的时区。
十、排错清单
1. 字段数量是否正确
这是第一优先级。
| 你写的 | 放错平台后的风险 |
|---|---|
0 9 * * * | 在 6 位平台中可能字段不足或含义错位 |
*/5 * * * * * | 在 6 位平台表示每 5 秒,不是每 5 分钟 |
如果平台要求 6 位,每天 9 点通常应写成:
0 0 9 * * *Quartz 通常写成:
0 0 9 * * ?2. 星期编号是否符合平台规则
星期字段差异很大:
| 可能规则 | 含义 |
|---|---|
0 | 周日 |
1 | 周一 |
1 | 周日 |
7 | 周日 |
MON-FRI | 周一到周五 |
能写英文缩写时,MON-FRI 通常比 1-5 更清楚。
3. “日”和“星期”是否同时指定
Quartz 中建议用 ? 避免歧义:
0 0 9 1 * ?表示每月 1 号 9 点。
0 0 9 ? * MON表示每周一 9 点。
4. 命令是否能在非交互环境执行
手动执行成功,crontab 失败,常见原因不是表达式,而是环境不同。
重点检查:
- 命令是否使用绝对路径;
- 工作目录是否正确;
- 环境变量是否存在;
- 当前用户是否有权限;
- 是否把日志输出到文件;
- 脚本是否依赖交互式 shell。
5. 是否真的到了执行时间
调试时不要只盯着表达式,可以手动推演接下来三次执行时间。
例如:
0 0 9 ? * MON-FRI翻译成自然语言:
周一到周五每天 9:00:00 执行如果现在是周五 10 点,下一次执行应该是下周一 9 点。
十一、学习 CRON 的检查方法
每写一个表达式,都按下面的问题过一遍:
- 这个平台要 5 位、6 位还是 7 位?
- 第一位是不是秒?
- 是否支持
?、L、W、#? - 星期字段从哪一天开始编号?
- “日”和“星期”有没有同时指定?
- 平台使用哪个时区?
- 命令本身有没有日志、权限和环境变量问题?
这比单纯背表达式更可靠。
十二、总结
CRON 可以分成四层理解:
- 格式层:先确认 5 位、6 位还是 7 位。
- 字段层:明确每一位代表分钟、小时、日、月、星期,还是秒和年。
- 符号层:用
*、,、-、/、?表达任意、枚举、范围、间隔和空置。 - 运行层:检查平台差异、时区、命令路径、环境变量和日志。
只要先判断方言,再套模板,最后检查运行环境,CRON 表达式就不会只是几串难背的星号,而会变成一套可以稳定落地的时间规则。