Cron式の構文
Cron式は5つのフィールド——分、時、月内の日、月、曜日——から成り、それぞれに*、数値、1-5のような範囲、*/15のようなステップ、1,3,5のようなリストを書けます。すべてのフィールドはANDで結合します。ただし定番の罠がひとつだけあります。月内の日と曜日の両方を限定すると、cronはANDではなくORで扱います。実行時刻はサーバーの時計に従います。
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-12 | JAN-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 * SUN | 1日、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 / @annually | 0 0 1 1 * | 1月1日の0時 |
| @monthly | 0 0 1 * * | 毎月1日の0時 |
| @weekly | 0 0 * * 0 | 毎週日曜日の0時 |
| @daily / @midnight | 0 0 * * * | 毎日の0時 |
| @hourly | 0 * * * * | 毎時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は意図的にそれをしていません。