F

Cron 表达式实战:6 个高频调度场景与 Linux/Quartz 的字段差异Cron Expressions in Practice: 6 Common Scheduling Scenarios and the Linux/Quartz Field Difference

Cron 表达式看起来只有五六个字段,但"每 5 分钟"和"每 5 分钟从第 3 分开始"是两回事,Linux cron 和 Quartz 的字段数还不一样。本文用 6 个真实场景把写法讲透,附字段差异对照表A cron expression looks like just five or six fields, but "every 5 minutes" and "every 5 minutes starting from minute 3" are different things — and Linux cron and Quartz don't even have the same number of fields. This article works through six real scenarios and includes a field-difference comparison table.

五段还是六段:Linux cron 与 Quartz 的字段差异Five fields or six: the Linux cron versus Quartz field difference

最容易踩的坑就是字段数。Linux 系统的 crontab 是 5 段:`分 时 日 月 周`,没有秒。而 Java 生态的 Quartz、Spring `@Scheduled`、以及很多云厂商的定时任务(如阿里云 SchedulerX)用的是 6 段或 7 段:`秒 分 时 日 月 周 [年]`。把 Linux 的 `*/5 * * * *`(每 5 分钟)直接粘到 Quartz 里,它会解释成"每 5 秒",行为完全不同。The easiest pitfall is the number of fields. Linux crontab has 5 fields: `minute hour day-of-month month day-of-week`, no seconds. The Java ecosystem — Quartz, Spring `@Scheduled`, and many cloud schedulers (like Alibaba Cloud SchedulerX) — uses 6 or 7 fields: `second minute hour day-of-month month day-of-week [year]`. Pasting Linux's `*/5 * * * *` (every 5 minutes) directly into Quartz makes it interpret as "every 5 seconds" — completely different behavior.

还有一个关键差异在"周"字段。Linux cron 里,周字段的 0 和 7 都表示周日,取值范围 0–7。Quartz 的周字段是 1–7,1 表示周日,7 表示周六,而且 Quartz 还支持用英文缩写(SUN–SAT)。另外,Quartz 的"日"和"周"字段不能同时指定具体值(必须有一个是 `?`),因为这两个字段在语义上有重叠——这是 Linux cron 没有的约束。Another key difference is the day-of-week field. In Linux cron, both 0 and 7 mean Sunday, and the range is 0–7. In Quartz, the range is 1–7 where 1 is Sunday and 7 is Saturday, and Quartz also supports English abbreviations (SUN–SAT). Additionally, Quartz's day-of-month and day-of-week fields cannot both have concrete values — one must be `?` — because they overlap semantically. This constraint doesn't exist in Linux cron.

六个高频场景的表达式写法Six common scenarios and their expressions

场景一:每 5 分钟执行一次。Linux:`*/5 * * * *`。Quartz:`0 */5 * * * ?`(秒位填 0,避免每秒都触发)。注意 `*/5` 在分钟位表示"从 0 分开始,每 5 分钟",即 0、5、10……如果你想从第 3 分开始,写成 `3/5 * * * *`,即 3、8、13……很多人不知道 `/` 左边可以指定起始值。Scenario one: run every 5 minutes. Linux: `*/5 * * * *`. Quartz: `0 */5 * * * ?` (set seconds to 0 to avoid triggering every second). Note that `*/5` in the minute field means "starting from minute 0, every 5 minutes" — i.e., 0, 5, 10… If you want to start at minute 3, write `3/5 * * * *`, giving 3, 8, 13… Many people don't know the left side of `/` can specify a starting value.

场景二:工作日每天早上 9 点半。Linux:`30 9 * * 1-5`。Quartz:`0 30 9 ? * MON-FRI`。这里周字段用范围 `1-5`(Linux)或 `MON-FRI`(Quartz),比写 `1,2,3,4,5` 更清晰。Scenario two: 9:30 AM every weekday. Linux: `30 9 * * 1-5`. Quartz: `0 30 9 ? * MON-FRI`. Here the day-of-week field uses a range `1-5` (Linux) or `MON-FRI` (Quartz), clearer than writing `1,2,3,4,5`.

场景三:每月最后一天执行。Linux cron 没有原生支持"最后一天",常见的 hack 是 `0 0 28-31 * *` 配合脚本里判断 `date +%d -d tomorrow` 是否为 01。Quartz 则有专门的 `L` 字符:`0 0 0 L * ?` 表示每月最后一天的 0 点。这是 Quartz 比 Linux cron 强大的地方之一。Scenario three: run on the last day of every month. Linux cron has no native "last day" support; the common hack is `0 0 28-31 * *` combined with a script that checks whether `date +%d -d tomorrow` equals 01. Quartz has a dedicated `L` character: `0 0 0 L * ?` means midnight on the last day of the month. This is one area where Quartz outshines Linux cron.

场景四:每小时的第 15 和 45 分。Linux:`15,45 * * * *`。Quartz:`0 15,45 * * * ?`。逗号分隔多个具体值,这是两个实现都支持的语法。Scenario four: at minutes 15 and 45 of every hour. Linux: `15,45 * * * *`. Quartz: `0 15,45 * * * ?`. Comma-separated concrete values are supported by both implementations.

场景五:从 9 点到 18 点,每 2 小时执行。Linux:`0 9-18/2 * * *`。Quartz:`0 0 9-18/2 ? * *`。范围和步长可以组合使用,`9-18/2` 等价于 `9,11,13,15,17`。Scenario five: every 2 hours from 9 AM to 6 PM. Linux: `0 9-18/2 * * *`. Quartz: `0 0 9-18/2 ? * *`. Ranges and steps can be combined; `9-18/2` is equivalent to `9,11,13,15,17`.

场景六:每周一和周三的凌晨 2 点。Linux:`0 2 * * 1,3`。Quartz:`0 0 2 ? * MON,WED`。注意 Quartz 里日字段必须填 `?`,因为周字段已经指定了具体值。Scenario six: 2 AM every Monday and Wednesday. Linux: `0 2 * * 1,3`. Quartz: `0 0 2 ? * MON,WED`. Note that in Quartz the day-of-month field must be `?` because the day-of-week field already has concrete values.

时区、夏令时与调试技巧Timezone, daylight saving, and debugging tips

cron 的执行时间基于运行机器的本地时区。我踩过一个经典坑:服务器时区设成了 UTC,但业务期望是北京时间,结果"每天凌晨 2 点"的任务实际在北京时间上午 10 点跑。排查时先 `date` 看机器时区,再确认 crontab 或调度框架的时区配置——Spring `@Scheduled` 可以用 `zone = "Asia/Shanghai"` 显式指定,Quartz 的触发器也有 timezone 属性。Cron execution time is based on the running machine's local timezone. I hit a classic pitfall: the server timezone was set to UTC but the business expected Beijing time, so the "daily 2 AM" task actually ran at 10 AM Beijing time. When debugging, first run `date` to check the machine timezone, then confirm the timezone config in crontab or your scheduling framework — Spring `@Scheduled` can explicitly set `zone = "Asia/Shanghai"`, and Quartz triggers also have a timezone property.

夏令时是另一个隐蔽问题。在实行夏令时的地区,春天调快一小时那天,凌晨 2:30 这个时间点不存在;秋天调慢一小时那天,凌晨 1:30 会出现两次。如果你的任务恰好落在这些模糊时间点,行为取决于具体实现——有的跳过,有的执行两次。中国不实行夏令时,但如果你的服务部署在海外节点就要注意。Daylight saving time is another subtle issue. In regions that observe DST, on the spring-forward day the time 2:30 AM doesn't exist; on the fall-back day, 1:30 AM occurs twice. If your task lands exactly on these ambiguous times, behavior depends on the implementation — some skip, some run twice. China doesn't observe DST, but if your services are deployed on overseas nodes, pay attention.

调试技巧方面,我推荐三个做法。第一,用 `crontab -l` 列出当前用户的所有任务,确认没有遗漏或重复。第二,给任务加重定向日志:`* * * * * /path/to/script.sh >> /var/log/job.log 2>&1`,没有日志的定时任务出了问题根本无从查起。第三,不确定表达式对不对时,先用在线 Cron 表达式生成器验证——它能可视化未来 N 次的触发时间,比人脑算快得多,也能避免把 Linux 和 Quartz 的写法搞混。For debugging, I recommend three practices. First, use `crontab -l` to list all tasks for the current user and confirm nothing is missing or duplicated. Second, add redirected logging to tasks: `* * * * * /path/to/script.sh >> /var/log/job.log 2>&1` — a scheduled task without logs is impossible to troubleshoot. Third, when unsure whether an expression is correct, verify it with an online cron expression generator — it visualizes the next N trigger times, much faster than mental arithmetic, and prevents mixing up Linux and Quartz syntax.

← 返回教程列表← Back to all guides