Cron 表达式生成器指南:五段表达式与时区坑
服务器要定时跑脚本、每天凌晨 3 点清理缓存、每周一 9 点同步数据……这些计划任务用 Cron 表达式 写。写错分隔符、搞混星期与日期、或不考虑时区,就会「跑了却没按预期」。本指南讲清楚五段语法、特殊符号、预设、时区坑,配套 WebUtils 页面上的可操作步骤与决策表。
核心一句话
五段表达式:分钟 小时 日期 月份 星期;预设+描述+时区三板斧。
WebUtils 提供可视化字段、九个预设与实时自然语言描述,主工具:Cron 表达式生成器。
Cron 语法详解
标准 Linux Cron(5 位):
| 字段 | 取值范围 | 特殊符号 | 含义 |
|---|---|---|---|
| 分钟 | 0-59 | * / - |
0-59 分钟 |
| 小时 | 0-23 | * / - |
0-23 小时 |
| 日期 | 1-31 | * / - |
每月第几天 |
| 月份 | 1-12 | * / - |
1-12 月 |
| 星期 | 0-7 | * / - |
0=周日、7=周六 |
6 位 Cron(秒)有秒字段。时区 由 crontab 文件或 cron -l 决定,本工具描述不包含时区(页面仅显示表达式)。
在 WebUtils 上怎么做
工具页:Cron 表达式生成器。
手动字段
- 打开页面,找到 Cron 表达式 区域。
- 五个输入框(分钟 小时 日期 月份 星期)依次填
0-59、0-23、1-31、1-12、0-7。 - 页面实时:上方 表达式 框显示
min hour dom mon dow,下方 描述 框给出自然语言解释(如「每小时整点」)。 - 点 复制 带走表达式。
- 用 清空 重置。
预设
页面有 常用预设 网格,点击按钮即自动填字段并刷新描述。示例:
*→ 每分钟0 9 1-5→ 每天 9:00-17:00 工作时间0 0 1→ 每月 1 号午夜
能力边界
- 有: 五字段手动、九个预设、实时描述、复制。
- 无: 6 位秒级字段、时区选择(服务器端决定)、复杂
?占位符。 - 不替代: 生产 crontab 需确认服务器时区与权限。
常见错误与对照
| 现象 | 原因 | 处理 |
|---|---|---|
| 任务跑但不是预想时间 | 星期日用 0/7,日期用 1-31 混淆 | 统一:日期不填则 *,星期日用 0 |
| 描述「每天 9 点」却跑 8 点 | 时区差 1 小时 | 服务器端 crontab 文件需确认时区 |
预设 0 9-17 1-5 没按预期 |
日期字段用了 9-17(非法) |
只用合法范围,日期用 * |
| 任务莫名多跑 | 多个字段用 / 如 */2 意为每隔 |
确认 */2 仅在该字段 |
| 描述区空 | 输入值有非法字符 | 输入 * 或 0-59 范围数字 |
| 生产跑但日志无记录 | 权限、crontab 文件错误、时区 | crontab -l 验证;改用 sudo crontab -e |
排错顺序
- 确认字段范围与特殊符号。
- 比对描述与预期。
- 检查服务器时区与
crontab -l。 - 最小可复现表达式测试。
真实工作场景
场景 A:每天凌晨 3 点清理缓存
0 3 * → 每天 03:00 执行。描述区显示「每天凌晨 3:00」。若服务器时区是 UTC+8,任务在 03:00 触发。
场景 B:每周一 9 点同步数据
0 9 1 → 星期一(0=周日)09:00。预设网格里「每周一」即选此。避免用 1(日期)混 1(星期)。
场景 C:每 5 分钟刷新一次
/5 * → 每分钟 0、5、10、15、20、25、30、35、40、45、50、55 分钟执行。描述区「每隔 5 分钟」。
场景 D:每月 1 号 0 点备份
0 0 1 → 每月 1 号午夜。日期字段 1,月份 *。
场景 E:工作时间 9:00-17:00 每小时
0 9-17 1-5 → 星期一至五,09:00-17:00 每小时。日期用 *。若日期也填 9-17 会报错或非法。
场景 F:时区坑
服务器时区 UTC+8,0 3 * 跑 03:00;若你本地电脑 UTC+0,描述看「每天凌晨 3:00」但实际可能跑 15:00。生产 crontab 需确认 date 命令显示的时区。
安全与运维最佳实践
- 生产任务最小权限:
crontab -l确认无root以外命令。 - 备份 crontab:
crontab -l > cron.bak。 - 测试环境先跑;线上勿跑大文件任务。
- 描述区与实际表达式保持一致,写进运维手册。
- 不要把密钥或敏感路径放进 cron 命令。
- 定期
crontab -l检查,避免过期任务。 - 时区用
TZ=UTC-8 crontab -e显式指定,或用系统时区常量。
实践清单
- 生产表达式写死,不要依赖预设。
- 最小可复现测试所有预设。
- 确认服务器
date命令显示时区。 - 任务跑后看日志,确认是否按预期触发。
- 团队约定:日期字段与星期字段分别用
*与0-7。 - 备份 crontab 文件。
- 勿把 cron 命令放进可公开的文档。
常见问题
为什么我的 cron 跑了却没按预期时间?
确认服务器时区。crontab -l 显示的时区与 date 命令一致。描述区仅表达表达式,不含时区。
日期字段和星期字段怎么选?
日期(1-31)控制每月第几天,星期(0-7)控制星期几。两者常配合:日期 * + 星期 1-5 工作日。
预设 `0 9-17 * * 1-5` 是什么意思?
每天 09:00-17:00(每小时)在星期一至五执行。日期用 *。
6 位 Cron 怎么办?
Linux 默认 5 位。sudo crontab -e 后在最后一行加秒字段(如 0 0 0 0 0 0 )。
时区坑是什么?
服务器与客户端时区不同。0 3 * 在 UTC+8 跑 03:00,在 UTC+0 跑 15:00。生产 crontab 显式指定 TZ。
描述区与实际执行不一致怎么办?
表达式写对了,描述应一致。描述区由工具推导,若有特殊符号(如 */2)描述可能简化为「每隔 2 分钟」。
在线工具会上传我的 cron 吗?
页面本地计算,但表达式的实际执行依赖服务器。敏感命令勿在工具里长期粘贴。
生产环境的时间与并发边界
Cron 只负责在某个时间点启动命令,不负责保证上一次任务已经结束。如果 /5 * 的脚本偶尔运行超过 5 分钟,下一次触发可能与上一实例重叠,造成重复扣款、同时写同一文件或把外部接口打爆。对有副作用的任务,应在脚本内加锁,或使用系统提供的 flock、队列和幂等键;生成器只能生成时间表达式,不能替你解决并发控制。
夏令时切换也会产生特殊结果。跳过一个小时的日期里,0 2 * 可能不会执行;重复一个小时的日期里,它可能执行两次,具体行为取决于 cron 实现。涉及结算、提醒或跨地区业务时,优先使用带时区和日历语义的调度平台,并在测试环境模拟切换日。普通服务器的 date 输出和应用容器的时区设置也可能不同,不能只看笔记本电脑的时钟。
上线前先把命令改成写入时间戳的探针,例如 echo $(date) >> /tmp/job.log,确认用户、路径和时区后再替换为正式脚本。Cron 的环境变量通常比交互式终端少,PATH、工作目录、凭据和代理地址可能缺失;脚本应使用绝对路径并显式加载必要配置。日志要包含开始、结束、退出码和输入批次,失败时设置非零退出码,方便监控系统报警。
如果同一表达式既配置在用户 crontab 又配置在 /etc/cron.d,任务会重复运行。排查时把 crontab -l、系统目录和容器编排配置一起搜索,再临时禁用副本。生成器输出通过语法检查只是第一关,真正上线还要进行一次小范围、可回滚的演练。
发布前的观测与回滚
定时任务上线前,先把正式命令替换成只写日志的探针,验证触发时间、运行用户、工作目录和环境变量。探针至少记录开始时间、结束时间、退出码、输入批次和生成文件的位置;确认日志能被监控系统采集后,再切回正式命令。这样可以区分“没有触发”“触发但找不到命令”和“命令执行后返回错误”三类问题。
有副作用的任务还要设计幂等和回滚。比如每天结算不能因为重试而重复扣款,清理脚本不能把最近备份一起删除,发送通知要保存业务批次号以便去重。表达式通过生成器检查,只说明五段语法可解析,并不说明业务动作安全。上线时采用小范围演练,保留上一版 crontab 和脚本版本,出现异常就先停用副本,再恢复稳定版本。
继续浏览
另外要检查任务使用的权限。Cron 可能以不同于登录用户的账号运行,读取配置文件、访问网络和写入目录的权限也会不同。把脚本需要的目录、凭据和代理配置列成清单,使用最小权限账号做一次演练;不要为了让任务“先跑起来”就给出全盘写权限。容器环境还要确认容器是否会重启、时区是否继承宿主机,以及持久化日志是否会在实例销毁后丢失。
排查时还要确认命令的退出码。很多脚本打印了错误却仍然返回 0,监控系统会把失败当成成功;相反,依赖外部服务的短暂超时也不应无限重试。为每个任务写清重试次数、退避间隔和最终告警条件,并把重复执行的影响记录在运维手册中。这样生成器负责表达“什么时候运行”,脚本和监控才负责表达“运行失败后怎么办”。
用 0 3 走一次「预设 → 描述 → 复制」,再用 0 0 1 * 测试每月 1 号,即可覆盖最常见坑。