工具指南12-Cron表达式在线解析工具

如果你写过定时任务,大概率被 Cron 表达式折磨过。0 */2 * * * 是每两小时还是每两分钟?15 10 * * 1-5 到底包不包含周五?这类问题查文档能解决,但每次都查一遍实在低效。

本文先梳理 Cron 表达式的完整语法,再介绍一个在线解析工具,让你写完表达式立刻看到下次执行时间,不用再靠脑补。

Cron 表达式基础

五字段标准格式

标准的 Unix Cron 表达式由 5 个字段组成,用空格分隔:

┌──────── 分钟 (0-59)
│ ┌────── 小时 (0-23)
│ │ ┌──── 日 (1-31)
│ │ │ ┌── 月 (1-12)
│ │ │ │ ┌ 星期 (0-7, 0和7都是周日)
│ │ │ │ │
* * * * *

每个字段支持以下写法:

符号含义示例
*任意值* * * * * 每分钟
,列举1,15,30 * * * * 第1、15、30分钟
-范围0 9-17 * * * 9点到17点整点
/步长*/5 * * * * 每5分钟

容易搞混的地方

星期字段的起始值。不同系统对星期的定义略有差异:

  • 标准 crontab:0 = 周日,1 = 周一,…,5 = 周五,6 = 周六,7 也是周日
  • 某些扩展格式(如 Quartz):1 = 周日,2 = 周一,…,7 = 周六

实测大多数 Linux 发行版(Ubuntu 22.04、CentOS 7)的 crontab 遵循标准定义,0 和 7 都表示周日。所以 1-5 就是周一到周五,包含周五。如果你用的是 Java 的 Quartz 框架或 Spring 的 @Scheduled,要注意它们的星期编号从 1 开始(1 = 周日),同样的 1-5 在 Quartz 里含义完全不同。

日和星期的”或”关系。当日字段和星期字段同时指定非 * 值时,crontab 的行为是”或”而不是”且”:

# 这条表达式的含义是:每月15号 或 每周一,都会执行
0 9 15 * 1

这不是”每月15号且是周一”。很多人在这里踩坑。如果你需要”且”的语义(例如”每月第一个周一”),标准 crontab 做不到,需要在脚本里额外判断。

常用表达式速查

以下是开发中最常用的几个场景:

# 每天凌晨2点执行数据库备份
0 2 * * *

# 工作日(周一到周五)上午9:30
30 9 * * 1-5

# 每5分钟检查一次服务健康状态
*/5 * * * *

# 每月1号和15号零点执行
0 0 1,15 * *

# 每周日凌晨3点清理日志
0 3 * * 0

# 每小时的第30分钟
30 * * * *

# 每季度第一天(1月、4月、7月、10月的1号)
0 0 1 1,4,7,10 *

一个实际场景:日志轮转

假设你需要配置一个日志轮转任务:工作日每小时执行一次,周末每6小时执行一次。标准 crontab 无法在一条表达式里实现这种条件分支,需要写两条:

# 工作日每小时
0 * * * 1-5 /usr/local/bin/rotate-logs.sh --mode hourly

# 周末每6小时
0 */6 * * 0,6 /usr/local/bin/rotate-logs.sh --mode relaxed

这种拆分很常见,但也很容易出错——尤其是当你修改了其中一条却忘了另一条的时候。

在线 Cron 解析工具

手写 Cron 表达式最大的问题是缺少即时反馈。你写了一个表达式,要么等它实际触发一次来验证(可能要等几小时甚至几天),要么在脑中模拟执行。两种方式都不够高效。

AnyFreeTools 的 Cron 解析器 提供了一个直观的解决方案:

核心功能

实时解析:输入表达式后,工具会立刻将其翻译成自然语言描述。比如输入 30 9 * * 1-5,显示”每周一到周五上午9:30”。这比盯着5个字段在脑中推演要快得多。

下次执行时间预览:工具会列出接下来若干次的执行时间点。这对于验证表达式是否符合预期特别有用。比如你写了 0 */2 * * *,想确认它是不是从0点开始每2小时执行,看一眼预览列表就清楚了。

时区支持:定时任务的时区问题是另一个常见坑。服务器在 UTC 时区,你在 UTC+8,写 0 9 * * * 到底是谁的9点?工具支持切换时区查看执行时间,帮你提前发现时区偏差。

使用场景

场景一:CI/CD 定时构建

你在 GitHub Actions 里配置定时触发:

on:
  schedule:
    - cron: '0 2 * * 1'

GitHub Actions 使用 UTC 时区。如果你在东八区,想让它在周一早上10点(UTC+8)执行,实际应该写 0 2 * * 1(UTC 凌晨2点 = 东八区上午10点)。用工具切换时区验证一下,避免上线后才发现时间不对。

场景二:Kubernetes CronJob

apiVersion: batch/v1
kind: CronJob
metadata:
  name: db-backup
spec:
  schedule: "0 18 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: postgres:15
            command: ["/bin/sh", "-c", "pg_dump -h db-host -U backup_user mydb > /backup/db-$(date +%Y%m%d).sql"]
          restartPolicy: OnFailure

K8s CronJob 同样使用 UTC。如果你的数据库备份要在业务低峰期(比如北京时间凌晨2点)执行,schedule 应该写 0 18 * * *(UTC 18:00 对应北京时间次日凌晨 2:00,注意日期会多一天)。

场景三:排查线上问题

生产环境有一个 crontab 任务,日志显示执行时间不符合预期。你把表达式粘贴到工具里,立刻能看到它实际的执行计划,比在终端里 man 5 crontab 然后心算要快得多。

进阶:6字段和7字段格式

标准 Unix crontab 用 5 个字段,但一些框架扩展了格式:

6字段格式(秒级精度):

┌──────── 秒 (0-59)
│ ┌────── 分钟 (0-59)
│ │ ┌──── 小时 (0-23)
│ │ │ ┌── 日 (1-31)
│ │ │ │ ┌ 月 (1-12)
│ │ │ │ │ ┌ 星期 (0-7)
│ │ │ │ │ │
* * * * * *

Node.js 的 node-cron 库和部分任务调度系统支持这种格式。注意,标准 Linux crontab 不支持秒级精度。不同库对 6 字段的定义可能有差异(比如秒字段是否支持 / 步长),使用前查一下具体库的文档。

7字段格式(含年份):

Spring 和 Quartz 支持在末尾追加年份字段,但实际使用较少。绝大多数场景下 5 字段就够了。

调试 Cron 的几个实用技巧

crontab -l 检查当前任务

# 查看当前用户的 crontab
crontab -l

# 查看特定用户的 crontab(需要 root 权限)
crontab -l -u www-data

检查 cron 日志

# Ubuntu/Debian
grep CRON /var/log/syslog | tail -20

# CentOS/RHEL
cat /var/log/cron | tail -20

常见的”cron 不执行”原因

  1. 环境变量缺失:cron 执行环境和你的 shell 环境不同,PATH 可能不包含你常用的路径。解决方法是在脚本里使用绝对路径,或在 crontab 开头声明 PATH

  2. 权限问题:脚本没有执行权限(chmod +x),或者脚本写入的目录当前用户没有写权限。

  3. 输出未重定向:cron 默认会把输出发邮件(如果配了 MTA)。如果没配,输出就丢了。建议显式重定向:

# 标准输出和错误输出都记录到日志
0 2 * * * /path/to/script.sh >> /var/log/my-cron.log 2>&1
  1. 时区混淆:前面提过,服务器时区和你预期的时区不一致。用 date 命令确认服务器当前时间。

总结

Cron 表达式的语法本身不复杂,但在实际使用中容易出错的地方不少:星期字段的起始值、日和星期的”或”关系、时区差异、环境变量缺失。这些问题靠记忆不太靠谱,用工具验证更稳妥。

在线 Cron 解析器可以帮你快速验证表达式、预览执行时间、排查时区问题。写完表达式先在工具里跑一遍,比部署到线上再等几个小时验证高效得多。

延伸阅读


本系列其他文章