返回博客
tutorial 2026-06-14

Cron 表达式解析器:给非运维同学的入门(2026)

Cron 表达式解析器:给非运维同学的入门

你交付了一个小功能,需要每个工作日早上 9 点跑一次,于是顺手拿起了 cron。三次失败、外加一次生产事故(任务每分钟运行一次,跑了整整一个周末)之后,这已经是你本月第四次在查 cron 表达式语法表。维基百科那一栏表格密密麻麻,StackOverflow 上的回答互相矛盾,AI 助手给你的答案又跟你调度器实际接受的格式对不上。

本文会从第一性原理讲清楚 cron 表达式,面向不每天写它的人——产品经理、创始人、设计师、分析师、以及一年只用几次 cron 的后端开发者。我们会介绍**Ai2Done 的 Cron 解析器**,它能把任何表达式翻译成人话,也能避开那些连资深工程师都会踩的坑。

TL;DR

  • 一条 cron 表达式 = 5 个字段:分钟、小时、月份中的某天、月份、星期 —— 空格分隔。
  • 最坑的一点:当月份天数(day-of-month)和星期(day-of-week)同时被指定时,经典 Unix cron 把它们当作 OR(任一匹配就触发),这通常并不是你想要的。
  • Ai2Done Cron 解析器 在上线前把表达式翻译成人话,看清楚未来 5-10 次触发时间。
  • 时区很重要:大多数 cron 调度器跑在 UTC;请在调用方系统(Kubernetes CronJob、AWS EventBridge 等)里显式指定时区。
  • 特殊字符 * / , - L W ? # 在经典 cron 与 Quartz cron 中含义不同——按你调度器的方言挑用法。

为什么这件事比看起来更难

1975 年的原版 Unix cron 用 5 个字段。现代调度器(Quartz、Kubernetes CronJob、AWS EventBridge、GitLab schedule、GitHub Actions)以互不兼容的方式扩展或修改了这个语法。如今至少有 4 种广泛使用的方言:

  1. 经典 Unix cron(5 字段,Linux/macOS 的 crontab
  2. Vixie cron(Linux 上事实标准的实现,带一些扩展)
  3. Quartz cron(7 字段,包含秒和年,Java 调度器和 AWS EventBridge 都在用)
  4. Kubernetes CronJob(5 字段,但没有 ?,DST 处理略有不同)

在一个系统里跑得完美的表达式,到另一个系统里可能校验失败、在意外时间触发、或干脆永不触发。"这个表达式合法吗?"这个问题,真的取决于方言

第二个困惑源:"5 个字段"的说法默认你记得顺序。多数工程师在压力下只能想起"有分钟、有小时、再来是日还是月?"正确顺序是 分钟、小时、月份中的某天(day-of-month)、月份、星期(day-of-week)

第三个困惑源:特殊字符之间的交互很不直观0 0 1,15 * 1-5 在每月的 1 号和 15 号触发,或者周一到周五(因为经典 cron 对 day-of-month 与 day-of-week 取 OR)。要表达"每月 1 号和 15 号,且必须是工作日",你需要 Quartz cron 的 ? 占位符。

一款好的 cron 解析器会在你部署前,按你的时区显示未来 5-10 次实际触发时间——你可以亲眼检查它是否如你所愿。

5 个字段详解

*   *   *   *   *
│   │   │   │   │
│   │   │   │   └── 星期(0-6 或 SUN-SAT;某些方言用 1-7)
│   │   │   └────── 月份(1-12 或 JAN-DEC)
│   │   └────────── 月份中的某天(1-31)
│   └────────────── 小时(0-23)
└────────────────── 分钟(0-59)

每个字段可以是:

  • 具体数字5(意思是"恰好这个值")
  • 列表1,15,30(意思是"这些值中的任何一个")
  • 范围1-5(意思是"从此到彼")
  • 步进*/15(意思是"从 0 起,每 N 个值")——所以分钟字段里的 */15 表示"0、15、30、45"
  • 通配符*(意思是"任意值")

记住语法之后常见的几种写法:

  • * * * * * —— 每分钟(著名的"我忘了写 schedule"表达式,会在凌晨 3 点把你叫醒)
  • */5 * * * * —— 每 5 分钟
  • 0 * * * * —— 每小时整点
  • 0 9 * * * —— 每天上午 9 点
  • 0 9 * * 1-5 —— 工作日上午 9 点(教科书级的"工作时间"任务)
  • 0 0 1 * * —— 每月 1 号 00:00
  • 0 0 * * 0 —— 周日 00:00(一周边界)
  • 15 14 1 * * —— 每月 1 号下午 2:15
  • 0 */6 * * * —— 每 6 小时(0、6、12、18 点)

方法一:Ai2Done Cron 解析器(部署前目视确认)

Ai2Done Cron 解析器 会把任何表达式翻译成人话,并展示未来 10 次的实际触发时间。流程:

  1. 在任何浏览器中打开 /tools/cron_parser
  2. 粘贴你的表达式——例如 0 9 * * 1-5
  3. 立刻看到
    • 人话翻译:"周一到周五 09:00 AM"
    • 按本地时区列出的未来 10 次触发时间
    • 按字段拆解,告诉你每段匹配了哪些值
    • 不合法表达式的校验错误(例如 0 9 32 * *——根本没有"32 号")
  4. 切换方言:在经典 cron、Quartz、AWS EventBridge、Kubernetes 间切换,匹配你的调度器。

工具完全在客户端运行,底层用 cronstrue 做自然语言翻译、cron-parser 算下次触发。你的表达式绝不会传到服务器。

实战小贴士:任何由 cron 驱动的生产任务,部署前先把表达式粘到解析器里,亲眼确认未来至少 3 次触发时间符合预期。这能在 5 秒内拦下"我以为是每 6 小时一次,但实际写成了 0 6 * * *,结果每天 6 点只跑一次"这类经典错误。

方法二:crontab.guru 网站

如果你已经在浏览器里了,crontab.guru 是 cron 解释的非官方业界标准。它是一个单页网页工具,核心能力相同——粘表达式、看英文翻译。与 Ai2Done 解析器的差别:

  • 服务端运行(你的表达式会出现在他们的访问日志里)
  • 没有按字段拆解的视图
  • 只支持经典 cron(无 Quartz、无 AWS EventBridge 方言)
  • 免费且优秀近 10 年

对非敏感表达式来说它是不错的选择。对可能泄漏信息的表达式(比如某些 Quartz 方言中带自定义字段名的写法),本地解析器更安全。

方法三:在 dry-run 调度器里试运行

对高风险的 cron 任务(数据管道、计费跑批、面向客户的定时操作),最有把握的做法是先部署到 dry-run 环境

  • Kubernetes:以 suspend: true 部署 CronJob,再通过 kubectl describe 检查算出来的下次触发时间。
  • AWS EventBridge:创建规则但不挂任何 target,看其 NextInvocations
  • Airflow / Dagster:调度器 UI 通常会显示 DAG 未来 5-20 次的触发时间。

它能捕获解析器看不到的边角情况(你集群的 UTC 偏移、夏令时切换、调度器特有的小怪癖,例如 AWS EventBridge 的 cron(0 9 ? * MON-FRI *) 需要 6 字段+ ?)。

经典坑

Day-of-month 与 day-of-week 是 OR,不是 AND。0 0 1 * 1 在每月 1 号 00:00 触发,同时每个周一也触发——不是"1 号且必须是周一"。要 AND,用 Quartz cron 的 ? 表示"不指定具体值"——0 0 1 * ? 只在每月 1 号触发,不管星期几。

***/N 不是"每 N 单位",而是"从 0 起每 N 单位"。**所以 */30 * * * * 在每小时的 :00 与 :30 触发,不是"距上次运行 30 分钟"。如果你要真正的固定间隔调度,请用任务队列(Celery、BullMQ、Sidekiq)——cron 本质上是基于日历的,不是基于间隔的。

**时区默认值不可靠。**Linux cron 跑在系统时区;Docker 容器通常默认 UTC;AWS EventBridge 默认 UTC;Kubernetes CronJob 默认 API server 的时区(通常是 UTC)。如果你的任务需要"本地时间上午 9 点",显式配置时区——不要相信默认。

**夏令时会造成幽灵触发。**春季时钟跳过凌晨 2 点时,原本定在 2:30 AM 的任务那天不会运行;秋季回拨时则会运行两次。多数调度器有应对选项(Quartz 的 misfire 策略、Kubernetes 的 concurrencyPolicy),认真读你那一家的文档。

0 0 31 2 *——这是 2 月 31 日,它不存在。多数调度器会默默跳过。部署前先测一下。

我们是怎么做这个解析器的(技术细节)

Ai2Done Cron 解析器 构建于:

  • cronstrue 做英语翻译(业界最好、支持所有主流方言、约 30 KB gzip)
  • cron-parser 算未来触发时间(正确处理 DST、闰日、范围、步进)
  • 一小段方言适配层:把 AWS EventBridge 的 cron(0 9 ? * MON-FRI *)(6 字段、年份可选)翻译成底层解析器期待的格式
  • Web Crypto / SubtleCrypto 未使用——这里没有安全敏感内容,解析器只是便利工具

一个有意思的设计选择:我们刻意不提供 "Google Calendar 式选择器"。那种 UI 对新手友好,但容易让人记住选择器、而非底层语法。Cron 虽然密集,但 20 分钟可学会;我们更愿意帮你读懂它,而不是把它藏起来。

常见问题

Q:为什么有些调度器是 5 字段,有些是 6 或 7? A:经典 Unix cron 是 5 字段(分钟、小时、日、月、周)。Quartz 在前面加秒、在后面加年(* * * * * * *)。AWS EventBridge 是 6 字段,且要求 day-of-month 与 day-of-week 必有一个为 ?。挑你调度器对应的方言。

Q:cron 中的 ? 是什么意思? A:Quartz / AWS EventBridge 的扩展,表示"不指定具体值"。用在 day-of-month 与 day-of-week 之间消歧——只想其中之一匹配时用。经典 Unix cron 不支持。

Q:cron 中的 L 是什么意思? A:Quartz 扩展,意为 "last"。L 在 day-of-month 表示"该月最后一天"(28/29/30/31 视情况);5L 在 day-of-week 表示"该月最后一个周五"。适合做月末任务而不必硬编码 28/29/30/31。

Q:cron 中的 W 是什么意思? A:Quartz 扩展,意为 "weekday"。15W 在 day-of-month 表示"距 15 号最近的工作日"——用于"每月发薪日,但不要在周末"这类场景。

Q:怎么写"工作时间内每 5 分钟跑一次"? A:*/5 9-17 * * 1-5 —— 每 5 分钟、9-17 点、工作日。在解析器里验证一下,未来 10 次触发时间应当符合预期。

Q:我的任务在夏令时切换时跑了两次,怎么修? A:这是调度器特定的问题。Kubernetes CronJob 的 concurrencyPolicy: Forbid 能阻止并发;Quartz 有 misfire 策略;AWS EventBridge 通过始终运行在 UTC 来回避(也就是说,DST 是你应用层面的事)。

Q:能在 cron 里表达"每月第一个周一"吗? A:经典 Unix cron 不行——需要应用层逻辑配合。Quartz cron 可以:0 0 0 ? * MON#1(每月第一个周一 00:00 触发)。

现在就试

在它跑进生产之前,先确认任何 cron 表达式:

打开 Cron 解析器 →

粘贴、看清、再部署。无上传、无注册,你的表达式留在浏览器里。

相关阅读


最后更新于 2026-06-14。Cron 解析器 100% 在你的浏览器内运行——你的表达式永远不会离开设备。我们没有任何关于你测试内容的服务器日志。