在后端开发、运维的日常工作里,Cron 表达式几乎是绕不开的技能 —— 无论是 Linux 服务器的定时脚本备份、日志清理,还是 Spring Boot 项目里的定时任务、数据同步,都离不开它。
但很多同学写 Cron 时总踩坑:要么记不清字段顺序,要么搞不懂 ? 和 * 的区别,写完定时任务死活不生效,排查半天发现只是格式写错了;更常见的是,Linux crontab 和 Spring Scheduler 的表达式格式不通用,复制过去直接报错。
这篇文章就一次性把 Cron 七域表达式(Quartz / Spring Scheduler 标准)讲透,从字段含义、特殊符号到高频场景示例、避坑指南全覆盖,最后再给大家推荐一款可视化配置工具,彻底告别死记硬背。
一、先搞懂:Cron 五域 vs 七域,别再搞混了
很多人接触 Cron 都是从 Linux crontab 开始的,但实际 Java 开发里的 Quartz、Spring Scheduler 用的是七域格式,两者字段数量、顺序都有区别,混用必然出错。
| 格式类型 | 字段顺序(从左到右) | 典型适用场景 |
|---|---|---|
| Linux crontab(五域) | 分 时 日 月 周 | Linux 系统定时任务、crontab 命令 |
| Quartz / Spring Scheduler(七域) | 秒 分 时 日 月 周 年 | Java 定时任务框架、Spring Boot @Scheduled 注解 |
七域格式比五域多了开头的「秒」和结尾的「年」,其中年份字段是可选的,省略时默认每年执行。我们日常开发中最容易混淆的,就是「日」和「周」两个字段的互斥关系,这也是 ? 符号存在的核心原因,后面会详细拆解。
二、七域表达式逐字段详解
标准七域 Cron 表达式格式:秒 分 时 日 月 周 年,每个字段都有独立的取值范围和支持的特殊符号,我们逐个拆解。
1. 秒(Seconds)
- 取值范围:0-59
- 允许符号:
*,-/ - 说明:七域格式独有字段,控制任务在每分钟的第几秒执行,Linux crontab 无此字段。
- 示例:
0代表每分钟第 0 秒执行;*/5代表每 5 秒执行一次。
2. 分(Minutes)
- 取值范围:0-59
- 允许符号:
*,-/ - 说明:控制任务在每小时的第几分钟执行。
- 示例:
0代表每小时整点执行;10,30代表第 10 分和第 30 分各执行一次。
3. 时(Hours)
- 取值范围:0-23
- 允许符号:
*,-/ - 说明:控制任务在当天的几点执行。
- 示例:
2代表凌晨 2 点执行;9-18代表早 9 点到晚 6 点每小时执行。
4. 日(Day-of-Month)
- 取值范围:1-31
- 允许符号:
*,-/?LWC - 说明:控制每月的哪一天执行,和「周」字段互斥—— 指定了具体日期,周字段就必须用
?;反之指定了周几,日期字段就必须用?。
5. 月(Month)
- 取值范围:1-12 或 JAN-DEC
- 允许符号:
*,-/ - 说明:控制每年的哪几个月执行,数字和英文缩写均可使用。
6. 周(Day-of-Week)
- 取值范围:1-7 或 SUN-SAT(Quartz 标准中 1 = 周日,2 = 周一,7 = 周六)
- 允许符号:
*,-/?LC# - 说明:控制每周的周几执行,和「日」字段互斥。为避免数字歧义,推荐直接使用 MON、TUE、FRI 等英文缩写。
7. 年(Year)
- 取值范围:1970-2099
- 允许符号:
*,-/ - 说明:可选字段,控制哪一年执行。日常开发中大多省略,默认
*每年执行。
三、核心特殊符号全解析
Cron 表达式的灵活度全靠特殊符号实现,其中最容易混淆的就是 * 和 ?,我们逐个讲透。
*(每) :代表该字段的所有合法取值,比如日字段写*代表每天都执行,分字段写*代表每分钟都执行。?(不指定) :仅用于「日」和「周」两个字段,专门解决互斥问题。 比如你指定了每月 5 号执行,就无法同时限定周几,此时周字段必须写?;反过来指定每周一执行,日期字段就必须写?。 示例:0 0 0 5 * ?代表每月 5 号凌晨 0 点执行,周字段用?表示不指定周几。,(枚举) :列出多个离散取值,比如时字段9,12,18代表 9 点、12 点、18 点各执行一次。-(范围) :表示连续区间,比如周字段MON-FRI代表周一到周五执行。/(步进) :格式为「起始值 / 步长」,表示间隔执行。 比如分字段*/10代表从 0 分开始,每 10 分钟执行一次;5/20代表从第 5 分钟开始,每 20 分钟执行一次。L(最后) :仅用于日和周字段。日字段中L代表当月最后一天;周字段中6L代表当月最后一个周五。W(工作日) :仅用于日字段,代表离指定日期最近的工作日。比如15W代表离当月 15 号最近的工作日执行,若 15 号是周六则提前到周五,是周日则延后到周一。#(第几个) :仅用于周字段,比如2#3代表当月第 3 个周一。
四、高频场景表达式示例(开箱即用)
整理了日常开发最常用的 10 种定时场景,全部基于 Spring Scheduler 六域格式(省略年份),可以直接复制使用。
- 每天凌晨 0 点执行
0 0 0 * * ? - 每天早上 8 点 30 分执行
0 30 8 * * ? - 每 5 分钟执行一次
0 */5 * * * ? - 每周一到周五早上 9 点执行
0 0 9 ? * MON-FRI - 每月 1 号凌晨 1 点执行
0 0 1 1 * ? - 每天 9 点到 18 点,每小时执行一次
0 0 9-18 * * ? - 每月最后一天晚上 23 点执行
0 0 23 L * ? - 每隔 10 秒执行一次
*/10 * * * * ? - 每月第 3 个周日凌晨 2 点执行
0 0 2 ? * 1#3 - 工作日中午 12 点执行
0 0 12 ? * MON-FRI
五、90% 的人踩过的 Cron 坑点总结
1. 日和周同时指定值,表达式直接不生效
这是最高发的错误:日和周是互斥字段,必须有一个用 ?。比如 0 0 0 * * MON 是错误写法,正确格式为 0 0 0 ? * MON。
2. Linux crontab 与 Spring 表达式混用
Linux 是五域(无秒级),Spring/Quartz 是七域,直接复制会完全不生效。比如 Linux 的 0 2 * * *,对应 Spring 写法是 0 0 2 * * ?。
3. 周字段数字含义搞反
不同框架周字段的起始值不同:Quartz 中 1 代表周日,7 代表周六;Linux crontab 中 0 和 7 都代表周日。最稳妥的方式是直接使用 MON、TUE 等英文缩写,避免歧义。
4. 忽略服务器时区
定时任务的执行时间基于服务器时区,如果服务器是 UTC 时区,写的北京时间会偏差 8 小时。线上环境一定要先确认服务器时区配置。
5. 步进符号 / 理解错误
*/5 是「从 0 开始每 5 个单位执行」,而非「从当前时间开始每隔 5 个单位」。比如分字段 */5 会在 0 分、5 分、10 分… 定点执行。
6. 特殊符号滥用,框架不支持
L、W、# 等复杂符号在部分轻量框架中支持有限,比如 Spring 自带的 Scheduler 对其兼容度不高,复杂场景建议接入 Quartz。
六、可视化配置工具推荐
讲完语法和坑点,很多同学可能还是觉得手写麻烦 —— 尤其是复杂场景,写错一个符号就要排查半天,还要反复核对执行时间。
这里给大家推荐一款我日常在用的免费在线 Cron 表达式生成工具 ——码剑客 Cron 表达式生成器,无需下载安装,打开浏览器就能直接用。
它的几个核心功能非常实用:
- 可视化配置,不用死记语法 秒、分、时、日、月、周、年全部做成了可视化选项,直接勾选就能生成表达式,新手也能零出错配置,不用再回忆字段顺序和符号规则。
- 多格式一键切换 支持 Spring Scheduler、Quartz、Linux crontab 等多种输出格式,不用手动换算五域和七域,运维和开发场景都能覆盖。
- 实时预览执行时间 配置完成后会自动列出未来多次的执行时间点,一眼就能验证表达式是否正确,再也不用上线后才发现定时时间写错了。
- 内置常用模板,一键复用 自带每天凌晨执行、每小时执行、工作日执行等高频场景模板,点一下就能直接生成,省去重复配置的时间。
- 支持表达式反向解析 手里有现成的表达式看不懂?粘贴进去就能自动拆解每个字段的含义,快速理解历史定时任务逻辑。
完全免费,不需要登录注册,日常开发配定时任务的时候打开用一下,能省不少排查时间。
写在最后
Cron 表达式本身并不复杂,核心是搞清楚字段规则、特殊符号,以及不同场景下的格式差异。简单场景手动书写没问题,复杂场景配合可视化工具辅助,既能提升效率,也能避免低级错误。
你们平时写 Cron 表达式最常踩什么坑?欢迎在评论区留言交流。