メインコンテンツへ移動

Cron式の構文

Cron式は5つのフィールド——分、時、月内の日、月、曜日——から成り、それぞれに*、数値、1-5のような範囲、*/15のようなステップ、1,3,5のようなリストを書けます。すべてのフィールドはANDで結合します。ただし定番の罠がひとつだけあります。月内の日と曜日の両方を限定すると、cronはANDではなくORで扱います。実行時刻はサーバーの時計に従います。

端末内で処理
ツールだけ使いたい方はcron式へスケジュールを読み解き、実際に発動する日時を確認ツールを開く

Cron式の構文とは

Cronは1970年代からUnixのスケジューラであり続け、その文法はほとんど動いていません。「いつ実行されるか」に答える、小さな5つのフィールドです。この構文はcrontab、CIシステム、Kubernetes CronJobs、データベースのメンテナンス計画、そしてネット上のスケジューラの半分で姿を現します。選挙権を得られる年月を経た構文ですが、1行に収まることを武器に、いまだに勝ち続けています。

文法は単純です。しかし隅は単純ではありません。フィールド同士は1つを除いてANDで結合され、その唯一の例外——月内の日と曜日——は、直感の求める正反対に振る舞います。日曜日は0でも7でも表せます。親切な@dailyなどのショートカットは拡張機能であり、標準の一部ではありません。そして構文のどこにもタイムゾーンフィールドはないため、すべての式は、ホストが使う時計を黙って引き継ぎます。

このガイドでは5つのフィールド、演算子、日フィールドの罠、ショートカット、そしてタイムゾーンの現実を順にたどり、本番のジョブを預ける前に式をテストする方法で締めくくります。ここでリンクしているテスターは完全にブラウザ内で動作します。式がタブの外に出ることはありません。

こうしたルールを身に付ける最速の方法は、わざと破ってパーサの反応を見ることです。だからCron式テスターを隣のタブで開いておいてください。式の意味を説明し、次の実行時刻を一覧表示してくれます。計算はすべてローカルで行われます。

現代のスケジュールは、crontabよりもデプロイファイルに書かれることが多いものです。式がパイプライン定義の中に収まるなら、YAML基礎ガイドが、そのファイルが従うインデント、引用、型のルールを解説します。

5つのフィールド

標準のCron式は、スペースで区切ったちょうど5つのフィールドで、順序は固定です:分、時、月内の日、月、曜日。すべてのフィールドが現在時刻に一致したとき、ジョブは発火します。フィールド同士はANDで結合されています。30 4 * * *と書けば、ジョブは毎日04:30に動きます。その瞬間が成立するには、全フィールドの合意が要ります。

各フィールドは範囲内の数値を受け付け、月と曜日のフィールドはさらに名前も受け付けます。JAN-DECとSUN-SATで、大文字小文字を区別しないスケジューラもあります。範囲は暗記する価値があります。2つだけ、意外な起点から始まるからです。月は1から、曜日は0から数えます。Cron構文の他の部分はすべて、このフィールドの上に演算子を重ねたものです。

フィールド位置許容される値名前の指定
分1番目0-59—
時2番目0-23—
月内の日3番目1-31—
月4番目1-12JAN-DEC
曜日5番目0-7(0と7はどちらも日曜日)SUN-SAT

範囲、ステップ、リスト

実際の式でほとんどの仕事をするのは3つの演算子です。範囲(1-5)は、両端を含む間のすべての値に一致します。スラッシュの後に書くステップは、直前のフィールドまたは範囲のN番目ごとに一致します。分フィールドの*/15は0、15、30、45を意味し、時フィールドの9-17/2は9時から17時までの2時間おきを意味します。リスト(1,3,5)は、名指しされた値そのものだけに一致します。演算子は組み合わせ可能で、1-5,10も0-30/5もどちらも有効です。

ステップを正しく使うための習慣が2つあります。第一に、*/Nは常にフィールドの最小値を含みます。*/15は0分に当たるということです。だから*/15のジョブの大群は、そろって毎時0分に目を覚まします。第二に、明示的な範囲にステップを重ねるほうが、リストより読みやすいことが多いものです。0-59/10は「10分おき」と読めますが、0,10,20,30,40,50はそうは読めません。

式読み方
0 9 * * 1-5月曜から金曜の09:00
*/15 * * * *15分おき
0 0 1 * *毎月1日の0時
30 4 1,15 * SUN1日、15日、そして毎週日曜日の04:30
0 22 * * 1-5平日の22:00

テストした式

30 9 * * 1-5

テスターの表示

At 09:30, on Monday through Friday.
Next runs (UTC):
  Mon 2026-09-21 09:30
  Tue 2026-09-22 09:30

月内の日と曜日の罠

どのフィールドのペアもANDで結合されます。ただし月内の日と曜日は例外で、両方を限定したときだけcronはORで扱います。定番の驚き:0 0 13 * 5は「13日の金曜日」を意味しません。毎月13日の0時、または任意の金曜日の0時を意味します。週によっては2回走り、意図しない日付にも走るジョブです。どちらかの日フィールドが*なら、通常のANDの挙動に戻り、意味を持つのはもう片方だけです。

このORのルールはバグではありません。POSIXとVixie cronのドキュメントに明記されており、生き残っているのは、2つの日フィールドが別々の主人に仕えられるからです。1組のフィールドペアの中で、毎月1日の月次ジョブと日曜の週次ジョブは共存できます。とはいえ直感を裏切るので、防御の習慣としては:ORを数えて理解したのでない限り両方を限定しない。そして本当に「13日の金曜日」が必要なら、毎日実行するようスケジュールし、実行するコマンドの中で日付を判定します。

日曜は0と7、ショートカットは拡張機能

多くのシステムの祖先であるVixie cronでは、曜日フィールドは日曜を0でも7でも受け付け、7は入力時に0へ畳み込まれます。範囲への配慮です。SUN-SATは0-6とも1-7とも書けます。しかしこれは移植性の継ぎ目でもあります。他の実装の中には7を丸ごと拒否するものがあり、パースの細かい癖のせいで、リストの中の7は危険になりえます。日曜は0と書くのが移植性の高い選択です。

@ショートカット——@reboot、@yearly、@annually、@monthly、@weekly、@daily、@midnight、@hourly——も同様にVixieの拡張であり、POSIXの文法の一部ではありません。それでも、cron互換を名乗るものならほぼどこでもサポートされているので、実務上は安心して使えます。頭の中で展開しておく価値はあります。@dailyは0 0 * * *、つまり毎日の0時。@rebootはデーモンの起動時に1回という意味で、再スケジュールの後には、その起動がいつになるか次第で、予想より早くも遅くもなります。

ショートカット展開後の式意味
@reboot—cronデーモンの起動時に1回
@yearly / @annually0 0 1 1 *1月1日の0時
@monthly0 0 1 * *毎月1日の0時
@weekly0 0 * * 0毎週日曜日の0時
@daily / @midnight0 0 * * *毎日の0時
@hourly0 * * * *毎時0分

存在しないタイムゾーンフィールド

Cronにはタイムゾーンフィールドがありません。式はデーモンの時計——サーバーのローカルなシステム時刻——に対して評価されます。同じcrontabでも、タイムゾーンの異なるマシンでは別の時刻を意味し、東京のホストからフランクフルトのホストへジョブを移すと、スケジュールは黙ってずれます。コンテナはこれを際立たせます。多くのコンテナは、運用者がどこに座っていようとUTCで動くため、「いつ走るか」と「意図した時刻」が、誰も告げなかったオフセットだけずれていきます。

ローカル時刻のタイムゾーンでは、サマータイムが独自の危険を持ち込みます。春に時計が進む夜には02:30のジョブは存在せず、秋に戻る夜には01:30のジョブが2回走ります。実装によっては、移植性のない措置を受け付けます。crontabにTZやCRON_TZの行を書く、スケジューラ独自の設定にタイムゾーンを書く、などです。しかしサポートはまちまちで、その行を書いた時点で移植性は終わります。誠実なやり方は、自分の式がどの時計で動くのかを知っておくことです。このサイトのテスターはすべてをUTCで計算し、そのことを各結果の横に明示します。

本番に乗せる前に式をテストする

文法的に正しいのに絶対に成立しない式があります。0 0 31 2 *は2月31日を要求しますが、日付を素朴に順送りする探索ループは永遠にそれを見つけられません。だからこそ、本物のテスターは探索範囲を年数で区切り、固まる代わりに「決して発火しないスケジュール」と報告するのです。また、文法的に正しいのに単に意図と違う式もあります。13日か金曜日の罠は、驚きが訪れるまでの何日もの間、正しく見えるスケジュールを作り出します。

両方を拾い上げるワークフローはこうです。式を書く。言葉で説明し返してもらう。次の具体的な実行時刻をいくつか眺める。説明は、パーサがあなたの書いたものをどう解釈したかを語り、具体的なタイムスタンプは、実際にどこで発火するかを示します。このサイトのテスターはその両方を行います。5フィールドの標準構文に加えて、6フィールドと7フィールドのQuartz風の方言にも対応し、すべてブラウザ内で完結します。社内のジョブ名やホスト名を含む式も、タブの外に出ることはありません。

よくある質問

Cronテスターは、私の式やジョブ名をアップロードしますか?

いいえ。パース、説明の生成、次回実行時刻の計算は、すべてブラウザのタブ内でJavaScriptとして行われます。式がマシンの外に出ることはありません。DevToolsで確認できます。Networkパネルを開き、式を評価すると、リクエスト一覧が空のままなのが分かります。サイトのContent-Security-Policyも、ページスクリプトによる外部への接続を禁じています。

0 0 13 * 5が「13日の金曜日」を意味しないのはなぜですか?

両方の日フィールドを限定するとcronはORで結合するからです。このスケジュールは、毎月13日の0時か任意の金曜日の0時の、先に来たほうで発火します。「13日の金曜日」ではありません。これはPOSIXもVixie cronも同じく、仕様として定義された挙動です。本当にその重なりを狙うには、ジョブを毎日実行するようスケジュールし、実行するコマンドの中で日付を判定してください。

7は曜日フィールドとして有効な値ですか?

Vixie cronとその子孫では有効です。0も7も日曜を意味し、7は入力時に0へ畳み込まれます。しかし、この構文で最も移植性の低い隅でもあります。実装によっては7を丸ごと拒否するものがあり、その環境ではリストや範囲の中で奇妙な振る舞いをします。日曜を0と書けば何も失わず、不一致を丸ごと回避できます。

Cron式はどのタイムゾーンで動きますか?

デーモンが使うどの時計でもあり得ます。cronにタイムゾーンフィールドはないので、サーバーのローカルなシステム時刻です。コンテナなら通常はUTCです。実装によってはcrontabにTZやCRON_TZの行を受け付けますが、それは移植性がなく、ローカル時刻のタイムゾーンはサマータイムの癖を持ち込みます。02:30のジョブが存在しない夜があり、01:30のジョブが2回走る夜があります。スケジュールを信用する前に、どの時計かを確かめてください。

@daily、@weekly、@rebootは標準のCronですか?

POSIXではなくVixie cronの拡張ですが、cronを名乗るものならほぼどこでもサポートされています。何に展開されるかは知っておく価値があります。@dailyは0 0 * * *、@weeklyは0 0 * * 0、そして@rebootはデーモンの起動時に発火します。マシンの再スケジュールやコンテナの再起動の後には、それが起きたそのときに動き、固定の時刻ではありません。

月末にジョブが実行されないのはなぜですか?

Cronに「最終日」の構文はありません。日のフィールドは31までで、31は年のうち7か月にしか一致しません。定番の回避策は小さなシェル判定です。毎日0時に実行するようスケジュールし、翌日が1日であれば実行する、という形です。カレンダー機能の豊富なスケジューラ(systemdタイマー、Lフラグを持つQuartz)ならこれをネイティブに表現できます。POSIX cronは意図的にそれをしていません。