DevToolPal
简

Cron 表达式

每月最后一天

标准 cron 没有“最后一天”的符号,所以任务在 28–31 号运行,再用一条 shell 判断只在明天是 1 号时继续。

0 0 28-31 * * 在 Cron 表达式解析中打开

crontab 这行用的是 GNU date(date -d tomorrow);macOS 上用 date -v+1d。Quartz 和 Spring 直接支持 L。在 Kubernetes 和 GitHub Actions 中,在任务开头加上同样的日期判断。

逐字段解释

字段 值 匹配
分钟 0 0
小时 0 0
日 28-31 28, 29, 30, 31
月 * 任意值
星期 * 任意值

接下来的运行时间

示例:2026年1月1日 之后的前 6 次运行(UTC)。

  1. 2026年1月31日周六 00:00
  2. 2026年2月28日周六 00:00
  3. 2026年3月31日周二 00:00
  4. 2026年4月30日周四 00:00
  5. 2026年5月31日周日 00:00
  6. 2026年6月30日周二 00:00

各平台写法

crontab
0 0 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /path/to/command

用 crontab -e 添加这一行。除非设置了 CRON_TZ 或 TZ,否则按服务器时区运行。

Kubernetes CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
  name: last-day-of-month
spec:
  schedule: "0 0 28-31 * *"
  timeZone: "Etc/UTC"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: job
              image: busybox:1.36
              command: ["/bin/sh", "-c", "date; echo run"]

设置 spec.timeZone(Kubernetes 1.27+);不设置时按 controller manager 的时区,通常是 UTC。

GitHub Actions
on:
  schedule:
    - cron: '0 0 28-31 * *'
  workflow_dispatch:

GitHub Actions 始终使用 UTC,最短间隔为 5 分钟,负载高时定时任务可能延迟启动。

Quartz
0 0 0 L * ?

Quartz 把秒放在最前面,并且日和星期中必须有一个写 ?。星期用 1–7 表示(从周日开始),用英文缩写更不容易出错。

Spring
@Scheduled(cron = "0 0 0 L * *")

Spring 的 @Scheduled(cron = …) 使用六个字段:秒,再加上常规的五个字段。

常见问题

› 为什么 crontab 里的 % 要写成 \%?

在 crontab 行中,% 表示换行(作为命令的输入)。写成 \% 才能把日期格式原样传给 shell。

› 0 0 28-31 * * 按哪个时区运行?

Cron 使用运行它的机器或服务的时区。在 Linux 上是系统时区,除非设置了 CRON_TZ 或 TZ;Kubernetes CronJob 可以设置 spec.timeZone;GitHub Actions 始终使用 UTC。

› 部署前怎么检查这个计划对不对?

在本站的 Cron 表达式解析中打开 0 0 28-31 * *。它会解释每个字段,并按本地时间或 UTC 列出接下来的运行时间,全部在浏览器里完成。