Cron 式の解析

読めない */5 9-18 * * 1-5 を日本語に翻訳し、次にいつ実行されるかを計算します。

Cron 式の読み方

Cron は Linux とほとんどのジョブスケジューラが使う定時実行の文法です。スペースで区切られた複数のフィールドからなり、各フィールドが時間の 1 つの軸を表します。読み解くコツは文法を暗記することではなく、フィールド同士が「AND」の関係にあると理解することです。すべてのフィールドを同時に満たしたときだけ、ジョブは実行されます。

5 つのフィールドとは

  • 1 番目 分:0〜59
  • 2 番目 時:0〜23。24 時間制なので、午後 3 時は 15 と書きます
  • 3 番目 日:1〜31
  • 4 番目 月:1〜12
  • 5 番目 曜日:0〜7。0 と 7 はどちらも日曜です(実装によっては 1 が月曜なので注意)

スケジューラが秒単位に対応している場合(Spring の @Scheduled(cron=...) や Quartz など)、先頭に秒のフィールドが増えて 6 段になります。同じ式を別の場所へ貼ると「想定より頻繁に動く」現象が起きるのはこのためです。5 段の書式なのにシステムが 6 段として解釈すると、フィールド全体が 1 つずつずれます。

5 つの書き方

  • 具体値:30 は 30 分ちょうど。
  • 範囲:9-18 は 9 から 18 まで、両端を含みます。
  • リスト:1,3,5 はその 3 つの値すべて。
  • ステップ:*/5 は開始値から 5 個おき。10-40/5 と書けば 10 から 40 の間で 5 分おきになります。
  • ワイルドカード:* は「このフィールドは制限なし」。

これらは混ぜて使えます。たとえば 0,30 9-17 * * 1-5 は「月曜から金曜の 9 時から 17 時の間、毎時 0 分と 30 分に 1 回ずつ」です。

実際の式を分解してみる

*/5 9-18 * * 1-5 を例に、1 段ずつ見ていきます。

  • 分 */5:5 分おき(0、5、10…55)
  • 時 9-18:午前 9 時から午後 6 時
  • 日 *:日付の制限なし
  • 月 *:月の制限なし
  • 曜日 1-5:月曜から金曜

合わせると「平日の 9:00 から 18:55 まで、5 分おきに実行」です。勤務時間中は高頻度でデータを更新し、終業後は止めるという、よくある構成です。

最も踏みやすい 3 つの罠

  • 「日」と「曜日」を同時に指定すると「OR」になる。 Cron で最も直感に反する仕様です。0 0 13 * 5 と書くと、「13 日かつ金曜」のつもりでも、実際の意味は「毎月 13 日、または毎週金曜」で、頻度は十数倍になります。「AND」を表現したいなら発想を変えるか、スクリプト側で判定してください。
  • */n で 24 時間をまたぐ正確な周期は作れない。 分フィールドの */7 は実際には 0、7、14…56 で、次の時間はまた 0 から始まります。つまり 56 分の次は 4 分後です。厳密な周期(7 分おきなど)が必要なら、秒フィールドを使うか別のスケジューラを使ってください。
  • 年フィールドが存在しない。 標準の Cron では「2026 年だけ実行」は表現できません。年を限定したいならジョブのスクリプト側で判定するか、年に対応した拡張文法(Quartz は対応、標準の crontab は非対応)を使ってください。

systemd timer との違い

新しい Linux なら、crontab の代わりに systemd timer も使えます。書き方が異なり、crontab は 1 行で済みますが、systemd は .service と .timer の 2 ファイルが必要です。systemd の利点は OnCalendar のような読みやすい書式に対応し、前回の実行結果を記録でき、実行を逃した場合の補償も設定できること(Persistent=true)です。24 時間稼働しないマシンでは systemd timer のほうが確実です。crontab は停止中に逃したジョブを補償しません。

タイムゾーンにも注意してください。Cron はサーバーのローカル時刻を使います。コンテナの既定は UTC であることが多く、手元で確かめた「毎朝 8 時」が、コンテナ上では午後 4 時になることがあります。デプロイ後は必ずタイムゾーン設定を確認してください。

式をどう検証するか

頭の中で想像しないことです。この式を上の入力欄に貼れば、次の 10 回の実行時刻が一覧で出ます。時刻が合っているかを見るのが最速の検証で、「日と曜日が OR になる」といった罠も一目でわかります。0 0 13 * 5 と書いたなら、一覧には毎月 13 日と毎週金曜の両方が現れ、問題がすぐ露呈します。

解析は完全にブラウザ内で完結し、ネットワークは不要です。

よくある質問

0 6 * * * は何時ですか?

毎朝 6 時ちょうどです。1 番目の 0 が 0 分、2 番目の 6 が 6 時、後ろの 3 つはワイルドカードで制限なし。最もよく使われる書き方のひとつです。

@daily のような書き方に対応していますか?

標準の crontab はマクロを一組サポートしています。@daily(毎日 0 時)、@hourly(毎時)、@weekly、@monthly、@yearly、@reboot(起動後に 1 回)。本ツールは標準の数値式の解析に特化しているので、マクロは同等の式に置き換えてください。たとえば @daily は 0 0 * * * です。

曜日に 7 が使えるのはなぜ?

0 と 7 のどちらも日曜と定義されているからで、どちらを書いても同じです。ただし月曜を表したいときは 1 を使ってください。8 と書いてはいけません。一部のシステムでは曜日が 1〜7(1 が日曜)と定義されているので、プラットフォームを移すときは必ず確認してください。

予測時刻と実際の実行時刻がずれます

まずタイムゾーンを確認してください。コンテナやクラウド関数の既定は UTC が多く、日本時間とは 9 時間(中国標準時とは 8 時間)ずれます。次に、スケジューラが 5 段形式か 6 段形式かを確認してください。1 段足りない、あるいは多いと、フィールド全体がずれます。

今後 1 年分の実行時刻をすべて計算できますか?

本ツールは既定で 10 回分を表示し、ロジックの検証には十分です。総量を見積もりたい場合(ジョブの負荷評価など)は、スケジューラ付属の分析ツールを使うか、サーバー側で計算してください。

関連ツール