cron式ジェネレーター・パーサー

*/15minute
*hour
*day of month
*month
*day of week

プリセット

次回の実行

ローカル時刻
@yearly · @annually · @monthly · @weekly

入力した内容が保存・記録・共有されることはありません。

cron式を入力すると、実際に次回実行される日時が表示されます。各フィールドを検証し、決して実行されない設定は警告します。

使い方

  1. 式を入力するか、プリセットから始めます。
  2. 5つのフィールドの意味が下に表示されます。
  3. デプロイ前に次回実行日時を確認します。

5つのフィールド

標準的なcron式は、スペースで区切った5つのフィールドでできています。分(0〜59)、時(0〜23)、日(1〜31)、月(1〜12)、曜日(0〜7で、0と7はどちらも日曜日)です。月と曜日は英語3文字の名前でも書けます。

どのフィールドでも同じ4つの書き方が使えます。アスタリスクはすべての値に一致します。カンマ区切りのリストは、そのいずれかに一致します。ハイフンでの範囲指定は、その間のすべてに一致します。スラッシュでのステップは範囲からn個おきに値を取るので、分フィールドの*/15は15分ごと、0-30/10は0分、10分、20分、30分です。

@hourly、@daily、@weekly、@monthly、@yearlyといった略記は、それぞれ見たとおりの式に展開されます。読み違えようがないので、当てはまる場面では使う価値があります。

日と曜日のルール

cronの挙動の中で、ほぼ全員が引っかかるのがこの1点で、しかも偶然ではなく仕様です。日フィールドと曜日フィールドの両方が制限されている(どちらもアスタリスクでない)場合、2つはANDではなくORで組み合わされます。

つまり0 0 1 * MONは「月曜日にあたる1日」ではありません。「毎月1日」と「毎週月曜日」の両方です。月曜日だけにしたいなら日フィールドをアスタリスクに、1日だけにしたいなら曜日フィールドをアスタリスクにしなければなりません。5フィールドの式で両方を満たす日を表す標準的な方法はなく、よくある回避策は、どちらかに該当する日にスケジュールしておき、ジョブの中で日付を確認することです。

次回実行日時の一覧は、まさにこのケースを見つけるためにあります。フィールドを読めば自分が何を書いたかがわかり、日時を読めば実際に何が起きるかがわかります。

タイムゾーンと夏時間

ここに表示している実行日時は、ブラウザのタイムゾーンによるものです。実際にジョブを実行するサーバーには独自のタイムゾーンがあり、UTCであることがよくあります。この2つのずれは、ジョブが間違った時刻に動く原因として日常的に起きています。正確な時刻を信じる前に、サーバーの設定か、スケジューラーのタイムゾーン設定を確認してください。

夏時間はさらに厄介です。夏時間を採用している地域では、時計が進む日の朝、2時30分に設定したジョブはまったく実行されません。その時刻が存在しないからです。そして時計が戻る日の朝には2回実行されます。請求、照合、メール送信など、実行漏れや二重実行が問題になるものは、UTCでスケジュールするか、切り替えが起きる午前1時〜3時の時間帯を避けてください。

マシンがスリープや電源オフの間に実行されなかったジョブをどう扱うかも、実装によって異なります。スキップするもの、復帰時に実行するもの、実行されなかった回をまとめて一気に実行するものがあります。

ほかの場所で出会う拡張

標準は5フィールドで、Linuxのcron、KubernetesのCronJob、ほとんどのホスティング型スケジューラーが受け付けるのはこの形式です。広く使われているいくつかのシステムはこれを拡張しており、その間でコピーした式がそのまま通用するとは限りません。

Quartzと、それを使うSpringのスケジューラーは、先頭に秒、末尾に省略可能な年を加えた6〜7フィールドを使います。そのため5フィールドの式を貼り付けると、すべてが1つずつずれて解釈され、エラーになるのではなく間違った時刻に実行されます。Quartzはさらに、最終を表すL、最も近い平日を表すW、月の第n曜日を表す#も追加しています。Linuxで標準のVixie cronは@rebootを追加しています。AWS EventBridgeは6フィールドを使い、2つの日付フィールドのうちちょうど1つをアスタリスクではなく疑問符にする必要があります。

このツールが検証し解説するのは5フィールドの標準形式です。使っているスケジューラーが上記のいずれかなら、式が同じ意味になると決めつける前に、そのドキュメントを確認してください。

cron ジョブが実際に動く環境

スケジュール実行の失敗は、スケジュール指定そのものより環境が原因であることのほうが多いです。cron はシェルのプロファイルを読み込まないため、PATH は最小限、多くの場合 /usr/bin と /bin だけになり、ターミナルでは動くコマンドが command not found で落ちます。確実なやり方は、インタープリターとスクリプトを絶対パスで書くか、crontab の先頭で PATH を明示的に設定することです。

カレントディレクトリはプロジェクトではなくユーザーのホームなので、相対パスは思わぬ場所を指します。ログインシェルが設定する環境変数も、シェルフックで読み込んでいる .env の内容も、いっさい引き継がれません。さらに crontab ではパーセント記号が特別な意味を持ち、改行に変換されます。%Y のような日付フォーマット文字列をエスケープせずに書くと、コマンドが黙って壊れるのはこのためです。

もう半分は出力の話です。ジョブが stdout や stderr に書いた内容は、MTA があれば所有者にメールされ、なければ捨てられます。ジョブが何か月も黙って失敗し続けるのはこれが原因です。自分が実際に読むログファイルにリダイレクトするか、失敗時だけでなく成功時にもジョブから監視システムへ通知させてください。

多重起動、冪等性、そして実行されなかった回

cron は前回の実行が終わっているかどうかにかかわらず、スケジュールどおりにジョブを起動します。5分かかるジョブを5分間隔で回せば、いずれ2つ同時に走り、次は3つになります。そしてその障害は、スケジュールのエラーとしてではなく、処理の二重実行やデッドロックとして表面化するのが普通です。ロック(Linux なら flock、データベースのアドバイザリロック、共有ストア上のリースなど)が定石の対処ですが、クラッシュした実行が以降すべてをブロックしないよう、タイムアウトを付ける必要があります。

各ジョブは、2回動いても無害になるように設計してください。多くの場合それは、スケジュールが処理範囲を決めていると仮定せず、何を処理したかを記録するという意味になります。未処理の行を選び、処理済みの印を付け、2回目の実行では処理対象が見つからないようにするのです。

裏返しの問題が、実行されなかった回です。マシンがスリープしていたりプロセスが落ちていたりした場合、実装によってその回をスキップするもの、遅れて実行するもの、取りこぼした回をまとめて一気に実行するものがあります。Anacron は常時稼働していないマシンを扱うために存在しますし、コンテナのスケジューラーにもそれぞれのポリシーがあるので、頼る前に目を通しておく価値があります。

crontab に代わる最近の選択肢

現在の主要な Linux ディストリビューションでは systemd タイマーが cron を置き換えており、cron にはなかったものを提供します。依存関係による順序制御、リソース制限、journal への自動ログ記録、一斉起動を避けるためのランダム遅延、そして取りこぼした実行を次回起動時に行う Persistent=true などです。代償は設定量で、1行ではなくタイマーユニットとサービスユニットの2ファイルが必要になります。

Kubernetes の CronJob は同じ5フィールドの構文を採り、そこに並行実行ポリシー、履歴の保持件数、期限の扱いを追加しています。これはまさに、ここまで述べてきた問題群を明示的に扱えるようにしたものです。クラウドのイベントバス、CI のスケジュール、サーバーレスのタイマーといったマネージドのスケジューラーは、通常 exactly-once ではなく at-least-once の配信を保証するため、やはり冪等性はジョブ側の責任になります。

アプリケーション内部のジョブであれば、遅延メッセージを使ったキューのほうが、どのスケジューラーよりも適していることがよくあります。リトライも可視性も監視も、後付けではなくキューに最初から備わっているからです。

実際によく書かれるスケジュール

15分ごとは */15 * * * * です。毎晩 02:30 は 30 2 * * *。平日の 09:00 は 0 9 * * 1-5。毎月1日の0時は 0 0 1 * * です。一方、月末を表す標準的な式は存在せず、毎日実行して翌日が1日でなければ終了する、というのが定番の回避策です。

crontab を後から読めるものにする習慣が2つあります。ひとつは、すべてを毎時0分に寄せずに負荷を散らすこと。7 3 * * * は 0 3 * * * と書く手間が変わらないのに、マシン上の他のジョブとの競合を避けられます。もうひとつは、各行に何をするジョブで誰が持ち主かをコメントしておくこと。crontab は、そのエントリを追加した人の記憶より長く生き残るからです。

本当に複雑なスケジュールなら、凝った式を1本書くより、単純なエントリ2本に分けたほうがたいてい明快です。1年間静かに動いていたジョブが突然動かなくなった午前3時に欲しいのは、その明快さです。

そもそもジョブが動いたのかを知る

スケジュール実行の典型的な失敗は、沈黙です。派手に失敗するジョブはその日のうちに直りますが、動かなくなったジョブは何の出力も出さず、そのジョブが生成する週次レポートが3週続けて届かないまで誰も気づきません。

これを解決するパターンがデッドマンスイッチ型の監視です。ジョブは成功時に監視サービスへ ping を送り、想定の時間内に ping が届かなければサービス側がアラートを上げます。アラートの向きを逆転させるこの方法なら、ホストのクラッシュ、crontab の削除、デプロイの失敗、再起動から戻ってこなかったマシンまで、すべてを捕捉できます。外から見れば、どれも「何も起きていない」という同じ姿をしているからです。

実行されたという事実だけでなく、その実行が何をしたかを記録してください。開始時刻、終了時刻、処理件数、終了ステータスを1行に残すだけで、漠然とした疑いが答えに変わります。ジョブが動いたと知ることと、正しく仕事をしたと知ることの違いはここにあります。夜間ジョブが1か月のあいだ黙って0件を処理し続けていたとき、いつから始まったのかを示す唯一の証拠は、たいていログに残った件数です。

よくある質問

なぜ説明だけでなく次回実行日時を表示するのですか?
構文として正しくても、意図したとおりの意味とは限らないためです。5つのフィールドを読めば「何を書いたか」は分かりますが、次の6回の日時を見れば「実際に何が起きるか」が分かります。スケジュールの不具合はたいていこの差で見つかります。
日(日付)と曜日の両方を指定するとどうなりますか?
両者はANDではなくORで結合されます。つまり「0 0 1 * MON」は毎月1日「かつ」毎週月曜日に実行され、1日が月曜の場合だけではありません。ほとんどの人が誤解しますが、これは仕様どおりの動作であり、次回実行日時を見れば一目で分かります。
対応しているcron構文を教えてください。
標準の5フィールド(分・時・日・月・曜日)に対応しています。ワイルドカード、範囲、リスト、ステップ、月と曜日の3文字表記、および@daily・@hourly・@weekly・@monthly・@yearlyの短縮形が利用できます。日曜日は0でも7でも指定できます。
表示される日時のタイムゾーンは?
お使いのブラウザのタイムゾーンです。サーバー上のcronはそのサーバーのタイムゾーン(多くはUTC)で動作し、手元の端末とは異なることが多いため、正確な時刻に依存する場合はサーバー設定をご確認ください。
「実行されることがありません」と表示されるのはなぜですか?
2月30日のように成立しない組み合わせがあるためです。空の一覧を表示する代わりに、その旨を明示しています。

最終更新:

問題を報告

操作手順、期待した結果、実際の結果を記載してください。秘密情報や個人情報は削除してください。

関連ツール