CI/CD
PR

GitHub Actions の cron に timezone が使えるようになった話(JST の日跨ぎ問題がついに解決)

saratogax
記事内に商品プロモーションを含む場合があります

GitHub Actions で定期実行(スケジュール実行)を組んだことがある方なら、一度は「時刻が UTC 固定でつらい」と感じたことがあるのではないでしょうか。

2026年3月、この長年の悩みにようやく公式の解決策が用意されました。

on.schedule の cron に、タイムゾーンを直接指定できる timezone フィールドが追加されたのです。

本記事では、この新機能でどんな運用が楽になるのかを、日本のエンジニアが特にハマりやすい「JST の日跨ぎ」という具体例を軸に解説します。

結論:timezone フィールドの書き方

先に結論から示します。

cron と同じリスト項目内に、IANA タイムゾーン文字列で timezone を書くだけです。

on:
  schedule:
    - cron: '10 9 * * *'
      timezone: "Asia/Tokyo"   # 毎日 09:10 JST に実行

これで cron 式が Asia/Tokyo(日本時間)として評価され、毎日 09:10 JST に発火します。

ポイントは、これが「ジョブ内部(ランナー)のタイムゾーン」ではなく、スケジューラが cron 式を評価するときのタイムゾーンだという点です。

timezone を省略した場合は、従来どおり UTC で解釈されます。

いつ追加された機能なのか

この timezone サポートは、2026年3月19日の GitHub Actions アップデートで一般提供(GA)となりました。

要望自体は2021年から挙がっていた、数年越しの機能追加です。

「昔から知っていれば」と思うかもしれませんが、そもそも2026年3月まで存在しなかった機能なので、これまで UTC 換算で運用していたのは当時としては正解でした。

なぜ UTC 固定がつらかったのか

これまで GitHub Actions のスケジュール実行は、すべて UTC で評価されていました。

日本時間(JST)は UTC+9 なので、頭の中で常に9時間ずらして考える必要がありました。

単純な時刻ずらしだけなら、まだ我慢できます。

本当に厄介なのは、9時間ずらすと日付をまたいでしまうケースです。

具体例:毎月1日 AM1:00 JST に実行したい

「毎月1日の午前1時(JST)にバッチを回したい」という、ごくありふれた要件を考えてみます。

JST の 01:00 は、UTC では9時間前の 「前月末日の 16:00」にあたります。

つまり UTC で表現しようとすると、「毎月1日」ではなく「毎月の末日」を指定しなければなりません。

ところが、月末日は 28日・29日・30日・31日と月によって変わります。

本来なら cron の「月末(L)」指定を使いたいところですが、ここで2つ目の落とし穴にぶつかります。

落とし穴:GitHub Actions の cron は L(月末)が使えない

GitHub Actions の cron は POSIX cron 準拠で、使える演算子は次の4つだけです。

演算子意味
*任意の値
,値のリスト区切り
-値の範囲
/ステップ値
※表は横スクロールできます

一部の cron 実装(Quartz など)にある L(月末)、W(平日)、#(第N曜日)といった拡張構文はサポートされていません

そのため、「前月末日の 16:00 UTC」を一発で書くことはできません。

これまでの回避策

やむを得ず、月末になりうる日を範囲でまとめて指定し、ジョブ側で本当に月末かどうかを判定する、という運用が定番でした。

on:
  schedule:
    # 28〜31日の 16:00 UTC に一旦起動し、
    # 「翌日が1日か」をジョブ内で判定して実処理を分岐する
    - cron: '0 16 28-31 * *'

jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - name: 翌日が月初(=今日が月末)かどうかを判定
        run: |
          if [ "$(date -u -d tomorrow +%d)" != "01" ]; then
            echo "月末ではないのでスキップ"
            exit 0
          fi
          echo "月末なので本処理を実行"

動きはしますが、cron 式だけを見ても意図が読み取れず、レビューでも「なぜ 28-31 なのか」と毎回説明が必要になる厄介なコードでした。

timezone で素直に書けるようになった

timezone を使えば、この日跨ぎ問題そのものが消えます。

「毎月1日 AM1:00 JST」を、そのままの意味でこう書けます。

on:
  schedule:
    - cron: '0 1 1 * *'
      timezone: "Asia/Tokyo"   # 毎月1日 01:00 JST

日フィールドが素直に 1(1日)と書けるため、月末日を意識する必要も、ジョブ内の判定ロジックも一切不要になりました。

before / after を並べると、可読性の差は一目瞭然です。

項目これまで(UTC 固定)timezone 指定
cron 式0 16 28-31 * *0 1 1 * *
日付の指定月末日を範囲でごまかすそのまま「1日」
ジョブ内の判定必要(月末チェック)不要
式から意図が読めるか読めない読める
※表は横スクロールできます

注意点:サマータイム(DST)と YAML の書き位置

サマータイムのあるタイムゾーンは挙動に注意

サマータイム(DST)を採用しているタイムゾーンでは、時刻の繰り上げ・スキップが発生します。

公式ドキュメントによると、春の切り替え(spring-forward)でスキップされる時刻に当たったスケジュールは、次の有効な時刻に繰り上げられます。

ただし Asia/Tokyo はサマータイムを採用していないため、日本時間での運用ではこの心配は不要です。

timezone は cron と同じ項目に書く

timezone は、対象の cron と同じリスト項目(ハイフンで始まる同じブロック)に書く必要があります。

YAML はキーが増えてもエラーにならず素通りしてしまうため、書く位置を間違えると「怒られないのに効いていない」という状態になりがちです。

# NG: cron と別の位置に書くと効かない可能性がある
on:
  schedule:
    - cron: '0 1 1 * *'
  timezone: "Asia/Tokyo"    # ← 位置が違う

# OK: cron と同じリスト項目に書く
on:
  schedule:
    - cron: '0 1 1 * *'
      timezone: "Asia/Tokyo"

スケジュールの遅延は依然として起こる

timezone はあくまで「cron をどのタイムゾーンで評価するか」を変えるだけの機能です。

GitHub Actions のスケジュール実行が混雑時に遅延・スキップされうる、という従来の性質は変わりません。

秒単位・分単位の厳密な時刻が必要な処理では、引き続き別の仕組みを検討したほうが安全です。

まとめ

  • 2026年3月から、on.schedule の cron に timezone(IANA 文字列)を指定できるようになった。
  • これは cron 式を評価するタイムゾーンであり、省略時は従来どおり UTC。
  • UTC 固定時代は、JST の日跨ぎ(例:毎月1日 AM1:00 JST)を書くのに月末日の範囲指定+ジョブ内判定という回避策が必要だった。
  • GitHub Actions の cron は L(月末)などの拡張構文が使えないため、この回避策が特に厄介だった。
  • timezone: "Asia/Tokyo" を使えば、日付をまたぐ JST スケジュールも素直に書ける。
  • DST のあるタイムゾーンでは繰り上げ挙動に注意。Asia/Tokyo は DST がないので影響なし。

UTC 換算に頭を悩ませてきた方は、この機会に既存のスケジュール定義を timezone ベースへ書き換えてみてはいかがでしょうか。

なお、timezone が使えない環境(EventBridge や Linux の crontab など)で日本時間からスケジュールを組みたい場合は、当サイトのツールが使えます。

あわせて読みたい(GitHub Actions 運用)

ABOUT ME
saratoga
saratoga
フリーランスエンジニア
仕事にも趣味にも IT を駆使するフリーランスエンジニア。技術的な TIPS や日々の生活の中で深堀りしてみたくなったことを備忘録として残していきます。
記事URLをコピーしました