GitHub Actions の cron に timezone が使えるようになった話(JST の日跨ぎ問題がついに解決)
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 運用)
- cron式メーカー&次回実行チェッカー|日本時間からActions・EventBridge・Linux・Springのスケジュールを生成
- GitHub Actionsのバージョン指定 vs コミットハッシュ指定:どちらを使うべき?
- GitHub ActionsでPRを自動作成する方法|gh CLIへの移行と落とし穴
