Java Tips | 日付・時間:CRON解析

Java Java
スポンサーリンク

CRON解析とは何をするものか

CRON解析とは、
0 0 * * * のような CRON 表記を読み取り、次にいつ実行されるかを計算する」
「この CRON は毎日? 毎週? 毎月? どんなタイミングで動くのかを理解する」
といった“スケジュールの意味を解釈する”処理のことです。

業務システムでは、バッチ処理、定期メンテナンス、ログローテーション、外部APIの定期取得など、
CRON 形式でスケジュールを指定する場面が非常に多いです。

CRON解析ユーティリティを作っておくと、
「次の実行時刻を計算する」「設定ミスを検出する」「スケジュールの説明を表示する」
といった処理が簡単になります。


CRON の基本構造を理解する

5フィールド(UNIX系の標準CRON)

一般的な CRON は次の5つのフィールドで構成されます。

分 時 日 月 曜日

例:
0 3 * * * → 毎日 03:00
*/5 * * * * → 5分ごと
0 0 1 * * → 毎月1日の 00:00
0 9 * * 1-5 → 平日 09:00

Java で CRON を扱うときは、
「この文字列を解析して、次の実行時刻を計算する」
という処理が中心になります。


Java で CRON を解析する方法

Java 標準には CRON パーサーがない

Java 標準 API (java.time) には CRON を直接解析する機能はありません。
そのため、実務では次のどちらかを選びます。

  1. 自分で CRON 解析ユーティリティを書く
  2. ライブラリ(Quartz など)を使う

ここでは「自作ユーティリティで CRON を解析する」ことにフォーカスします。
初心者でも理解できるよう、まずは「簡易 CRON(分・時のみ)」から始めて、
徐々に本格的な CRON に近づけていきます。


まずは「分・時」だけの簡易 CRON を解析する

例:"0 3" → 毎日 03:00

import java.time.LocalDateTime;
import java.time.LocalTime;

public class SimpleCronParser {

    public static LocalDateTime nextRun(String cron) {
        String[] parts = cron.split(" ");
        int minute = Integer.parseInt(parts[0]);
        int hour = Integer.parseInt(parts[1]);

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime todayRun = now.withHour(hour).withMinute(minute).withSecond(0).withNano(0);

        if (now.isBefore(todayRun)) {
            return todayRun;
        } else {
            return todayRun.plusDays(1);
        }
    }
}
Java

使い方:

LocalDateTime next = SimpleCronParser.nextRun("0 3");
System.out.println("次の実行: " + next);
Java

ここで深掘りしたいのは、
「今日の実行時刻が過ぎていたら、明日に回す」
というロジックです。

これは CRON 解析の基本中の基本で、
どんな複雑な CRON でも、この考え方が土台になります。


「*/5」などのステップ表記を解析する

例:*/5 → 5分ごと

CRON のステップ表記は、
「0,5,10,15,20…」のように一定間隔の値を意味します。

public class CronStep {

    public static int nextStepValue(int current, int step) {
        return ((current / step) + 1) * step;
    }
}
Java

例:

int next = CronStep.nextStepValue(7, 5); // 7分の次は10分
System.out.println(next);
Java

この「ステップ計算」は、
CRON の「/5」「/10」「*/30」などを解析するうえで必須のテクニックです。


本格的な CRON(分・時・日・月・曜日)を解析する

CRON の各フィールドを「候補値の集合」として扱う

CRON 解析の本質は、
「各フィールドを“許可された値の集合”として扱う」
という考え方です。

例:
0 9 * * 1-5(平日9時)

分 → {0}
時 → {9}
日 → {1〜31}
月 → {1〜12}
曜日 → {1,2,3,4,5}

この「集合」を使って、
「現在時刻より後で、最も近い組み合わせ」を探すのが CRON 解析です。

例:曜日フィールドの解析

import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class CronFieldParser {

    public static Set<Integer> parseRange(String field) {
        if (field.equals("*")) {
            return IntStream.rangeClosed(0, 6).boxed().collect(Collectors.toSet());
        }
        if (field.contains("-")) {
            String[] parts = field.split("-");
            int start = Integer.parseInt(parts[0]);
            int end = Integer.parseInt(parts[1]);
            return IntStream.rangeClosed(start, end).boxed().collect(Collectors.toSet());
        }
        return Set.of(Integer.parseInt(field));
    }
}
Java

例:

Set<Integer> weekdays = CronFieldParser.parseRange("1-5");
System.out.println(weekdays); // [1,2,3,4,5]
Java

ここで深掘りしたいのは、
「CRON は“値の集合”として扱うと理解しやすい」
という点です。


CRON 解析の最重要ポイント:次の実行時刻の探索

「秒 → 分 → 時 → 日 → 月 → 年」の順に繰り上げる

CRON 解析の本質は、
「現在時刻から未来に向かって、条件を満たす最初の日時を探す」
という処理です。

これは、時計の針を進めるように、

秒が合わなければ次の秒
分が合わなければ次の分
時が合わなければ次の時
日が合わなければ次の日
月が合わなければ次の月

という順番で繰り上げていきます。

この考え方を理解すると、
CRON 解析のロジックが一気にクリアになります。


CRON解析ユーティリティの完成形(簡易版)

「次の実行時刻」を返すユーティリティ

以下は、分・時・曜日に対応した簡易 CRON 解析の例です。

import java.time.*;
import java.util.Set;

public class SimpleCronScheduler {

    private final Set<Integer> minutes;
    private final Set<Integer> hours;
    private final Set<Integer> weekdays;

    public SimpleCronScheduler(Set<Integer> minutes, Set<Integer> hours, Set<Integer> weekdays) {
        this.minutes = minutes;
        this.hours = hours;
        this.weekdays = weekdays;
    }

    public LocalDateTime nextRun(LocalDateTime now) {
        LocalDateTime t = now.plusMinutes(1).withSecond(0).withNano(0);

        while (true) {
            if (minutes.contains(t.getMinute()) &&
                hours.contains(t.getHour()) &&
                weekdays.contains(t.getDayOfWeek().getValue())) {
                return t;
            }
            t = t.plusMinutes(1);
        }
    }
}
Java

使い方:

SimpleCronScheduler scheduler =
    new SimpleCronScheduler(
        Set.of(0),      // 分
        Set.of(9),      // 時
        Set.of(1,2,3,4,5) // 平日
    );

LocalDateTime next = scheduler.nextRun(LocalDateTime.now());
System.out.println("次の実行: " + next);
Java

このように、
「CRON を解析して、次の実行時刻を返す」
という処理をユーティリティ化しておくと、
スケジューラの安定性が大きく向上します。


セキュリティ・運用の観点から見た CRON解析

CRON の誤設定は“障害”につながる

CRON の設定ミスは、次のような重大な問題を引き起こします。

・バッチが重複実行される
・外部APIを短時間に叩きすぎてレート制限に引っかかる
・バックアップが重なってシステムが重くなる
・ログローテーションがずれてディスクが埋まる

CRON解析ユーティリティを使って、
「設定が正しいか」「次の実行時刻が妥当か」を検証することは、
セキュリティと運用の両面で非常に重要です。

タイムゾーンを意識しないと危険

CRON は「ローカルタイム」で動くことが多いため、
サマータイムのある地域では「存在しない時刻」「重複する時刻」が発生します。

Java の ZonedDateTime を使えば、
こうした“時間の歪み”を安全に扱えます。


まとめ:CRON解析で身につけてほしい感覚

CRON解析は、単なる文字列処理ではありません。
「時間の集合を扱い、未来の実行時刻を探索する」という、
業務システムに欠かせない重要な技術です。

CRON は「値の集合」として扱う。
次の実行時刻は「未来に向かって探索する」。
ステップ表記(*/5)や範囲(1-5)は集合に展開する。
スケジューラのズレは障害につながるため、CRON解析は非常に重要。
タイムゾーンやサマータイムも考慮する。

もしあなたのプロジェクトで、
「CRON の意味が分からない」「次の実行時刻がズレる」
といった問題があるなら、
まずは今回の CRON解析ユーティリティを導入してみてください。

スケジュールの安定性が大きく向上し、
運用トラブルの予防にもつながります。

タイトルとURLをコピーしました