CRON生成とは何をするユーティリティか
ここまで「CRON解析(読む側)」をやってきましたが、今回は逆方向です。
CRON生成とは、アプリ側で「こういうスケジュールで動かしたい」という条件から、0 3 * * * のような CRON 文字列を安全に組み立てるユーティリティのことです。
業務システムでは、管理画面で「毎日 3:00 に実行」「平日 9:00 に実行」「5分ごとに実行」などを選ばせて、
その設定を CRON 形式で保存したり、ジョブスケジューラに渡したりすることがよくあります。
このとき、文字列を手書きで組み立てるとバグや誤設定が起きやすいので、
「CRON生成ユーティリティ」を用意しておくと、実務でかなり安心できます。
CRONの基本形をもう一度整理する
5フィールドのCRON
一般的な(UNIX系の)CRONは、次の5フィールドで構成されます。
分 時 日 月 曜日
例として、よく見るものを挙げます。
0 3 * * * … 毎日 03:00*/5 * * * * … 5分ごと0 0 1 * * … 毎月1日の 00:000 9 * * 1-5 … 平日 09:00
CRON生成ユーティリティは、
「人間にとって分かりやすい指定(毎日、平日、毎月、◯分ごと…)」から、
この5フィールドの文字列を組み立てる役割を持ちます。
まずは「パターン」を決めてしまう
よくある業務パターンを型にする
いきなり「自由入力で CRON を作る」のは難しいので、
実務でよく出てくるパターンを“型”として決めてしまうのが現実的です。
例えば、次のような列挙型を用意します。
public enum ScheduleType {
EVERY_DAY, // 毎日
EVERY_WEEKDAY, // 平日
EVERY_WEEK, // 毎週◯曜日
EVERY_MONTH, // 毎月◯日
EVERY_MINUTES // ◯分ごと
}
Javaそして、「型+パラメータ」から CRON を生成するクラスを作ります。
こうしておくと、「この画面ではこのパターンしか選べない」と制約できるので、
危険な CRON(例えば毎秒実行など)を防ぎやすくなります。
毎日・平日のCRONを生成する
毎日◯時◯分に実行するCRON
「毎日 03:00」のような CRON を生成するメソッドです。
public class CronGenerator {
public static String everyDayAt(int hour, int minute) {
validateHour(hour);
validateMinute(minute);
return String.format("%d %d * * *", minute, hour);
}
private static void validateHour(int hour) {
if (hour < 0 || hour > 23) {
throw new IllegalArgumentException("hour は 0〜23 で指定してください: " + hour);
}
}
private static void validateMinute(int minute) {
if (minute < 0 || minute > 59) {
throw new IllegalArgumentException("minute は 0〜59 で指定してください: " + minute);
}
}
}
Java使い方の例です。
String cron = CronGenerator.everyDayAt(3, 0);
System.out.println(cron); // 0 3 * * *
Javaここで重要なのは、「必ずバリデーションを通す」ということです。
CRONは文字列なので、間違った値を入れてもコンパイルエラーになりません。
だからこそ、ユーティリティ側で「0〜23」「0〜59」のチェックを必ず行うべきです。
平日◯時◯分に実行するCRON
平日(一般的には月〜金)だけ動かしたい場合です。
public class CronGenerator {
public static String everyWeekdayAt(int hour, int minute) {
validateHour(hour);
validateMinute(minute);
// 曜日フィールド: 1-5 (多くのCRON実装で 1=月, 5=金)
return String.format("%d %d * * 1-5", minute, hour);
}
}
Java例です。
String cron = CronGenerator.everyWeekdayAt(9, 0);
System.out.println(cron); // 0 9 * * 1-5
Javaここで深掘りしたいのは、「曜日の数値は実装によって違うことがある」という点です。
多くの実装では 0=日, 1=月, … ですが、0と7の扱いなど微妙な差があります。
そのため、「どのCRONエンジンを使うか」を決めたうえで、その仕様に合わせて生成する必要があります。
毎週・毎月のCRONを生成する
毎週◯曜日◯時◯分に実行
例えば「毎週月曜の 10:00」のようなパターンです。
public class CronGenerator {
// dayOfWeek: 1=月, 7=日 という前提(使うエンジンに合わせて調整)
public static String everyWeekAt(int dayOfWeek, int hour, int minute) {
validateHour(hour);
validateMinute(minute);
validateDayOfWeek(dayOfWeek);
return String.format("%d %d * * %d", minute, hour, dayOfWeek);
}
private static void validateDayOfWeek(int dayOfWeek) {
if (dayOfWeek < 0 || dayOfWeek > 7) {
throw new IllegalArgumentException("曜日は 0〜7 で指定してください: " + dayOfWeek);
}
}
}
Java例です。
String cron = CronGenerator.everyWeekAt(1, 10, 0); // 月曜 10:00
System.out.println(cron); // 0 10 * * 1
Javaここでのポイントは、「曜日の数値を呼び出し側に生で書かせない」ことです。
本当は DayOfWeek(java.time.DayOfWeek)を受け取って、内部で数値に変換する方が安全です。
毎月◯日◯時◯分に実行
「毎月1日の 00:00」のようなパターンです。
public class CronGenerator {
public static String everyMonthOn(int dayOfMonth, int hour, int minute) {
validateHour(hour);
validateMinute(minute);
validateDayOfMonth(dayOfMonth);
return String.format("%d %d %d * *", minute, hour, dayOfMonth);
}
private static void validateDayOfMonth(int dayOfMonth) {
if (dayOfMonth < 1 || dayOfMonth > 31) {
throw new IllegalArgumentException("日付は 1〜31 で指定してください: " + dayOfMonth);
}
}
}
Java例です。
String cron = CronGenerator.everyMonthOn(1, 0, 0);
System.out.println(cron); // 0 0 1 * *
Javaここで深掘りしたいのは、「存在しない日付(例えば2月30日)」の扱いです。
CRON自体は「31日」と書けてしまいますが、実際の月によっては存在しない日になります。
このあたりは「CRONエンジン側の仕様」に依存するので、
ユーティリティ側で「31日は許可しない」などのポリシーを決めることもあります。
「◯分ごと」「◯時間ごと」のCRONを生成する
◯分ごとに実行するCRON
「5分ごと」「10分ごと」などのパターンです。
public class CronGenerator {
public static String everyMinutes(int intervalMinutes) {
if (intervalMinutes <= 0 || intervalMinutes > 59) {
throw new IllegalArgumentException("intervalMinutes は 1〜59 で指定してください: " + intervalMinutes);
}
return String.format("*/%d * * * *", intervalMinutes);
}
}
Java例です。
String cron = CronGenerator.everyMinutes(5);
System.out.println(cron); // */5 * * * *
Javaここでの重要ポイントは、「危険な値を禁止する」ことです。
例えば「1分ごと」は許可するが、「1秒ごと」は許可しない、などです。
CRONエンジンによっては秒フィールドもあるので、
「秒フィールド付きCRON」を使う場合は、さらに慎重な制限が必要です。
◯時間ごとに実行するCRON
「1時間ごと」「6時間ごと」などのパターンです。
public class CronGenerator {
public static String everyHours(int intervalHours) {
if (intervalHours <= 0 || intervalHours > 23) {
throw new IllegalArgumentException("intervalHours は 1〜23 で指定してください: " + intervalHours);
}
return String.format("0 */%d * * *", intervalHours);
}
}
Java例です。
String cron = CronGenerator.everyHours(6);
System.out.println(cron); // 0 */6 * * *
Javaここでも、「24時間ごと」は「毎日◯時」と同じ意味になるので、everyDayAt を使わせる、などのルールを決めておくと設計がきれいになります。
CRON生成ユーティリティを一つにまとめる
ここまでのメソッドをまとめた、シンプルな CRON生成ユーティリティの例です。
public class CronGenerator {
public static String everyDayAt(int hour, int minute) {
validateHour(hour);
validateMinute(minute);
return String.format("%d %d * * *", minute, hour);
}
public static String everyWeekdayAt(int hour, int minute) {
validateHour(hour);
validateMinute(minute);
return String.format("%d %d * * 1-5", minute, hour);
}
public static String everyWeekAt(int dayOfWeek, int hour, int minute) {
validateHour(hour);
validateMinute(minute);
validateDayOfWeek(dayOfWeek);
return String.format("%d %d * * %d", minute, hour, dayOfWeek);
}
public static String everyMonthOn(int dayOfMonth, int hour, int minute) {
validateHour(hour);
validateMinute(minute);
validateDayOfMonth(dayOfMonth);
return String.format("%d %d %d * *", minute, hour, dayOfMonth);
}
public static String everyMinutes(int intervalMinutes) {
if (intervalMinutes <= 0 || intervalMinutes > 59) {
throw new IllegalArgumentException("intervalMinutes は 1〜59 で指定してください: " + intervalMinutes);
}
return String.format("*/%d * * * *", intervalMinutes);
}
public static String everyHours(int intervalHours) {
if (intervalHours <= 0 || intervalHours > 23) {
throw new IllegalArgumentException("intervalHours は 1〜23 で指定してください: " + intervalHours);
}
return String.format("0 */%d * * *", intervalHours);
}
private static void validateHour(int hour) {
if (hour < 0 || hour > 23) {
throw new IllegalArgumentException("hour は 0〜23 で指定してください: " + hour);
}
}
private static void validateMinute(int minute) {
if (minute < 0 || minute > 59) {
throw new IllegalArgumentException("minute は 0〜59 で指定してください: " + minute);
}
}
private static void validateDayOfWeek(int dayOfWeek) {
if (dayOfWeek < 0 || dayOfWeek > 7) {
throw new IllegalArgumentException("曜日は 0〜7 で指定してください: " + dayOfWeek);
}
}
private static void validateDayOfMonth(int dayOfMonth) {
if (dayOfMonth < 1 || dayOfMonth > 31) {
throw new IllegalArgumentException("日付は 1〜31 で指定してください: " + dayOfMonth);
}
}
}
Javaこのユーティリティを使えば、
画面側では「毎日」「平日」「毎週」「毎月」「◯分ごと」「◯時間ごと」といった選択肢を出し、
選ばれたパターンと時刻・間隔から CRON を安全に生成できます。
セキュリティ・運用の観点から見た CRON生成
CRON生成は、単に「文字列を作る」だけではありません。
セキュリティと運用の観点から、次のようなことを意識する必要があります。
危険なスケジュール(毎秒、1分未満の間隔など)を禁止する。
夜間バッチと重なる時間帯を避けるように制約する。
曜日や日付の範囲を、業務ルールに合わせて制限する。
生成した CRON をログに残し、「誰がいつどんなスケジュールを設定したか」を追跡できるようにする。
CRON生成ユーティリティに「バリデーション」と「制約」をしっかり埋め込んでおくことで、
誤設定や悪意ある設定からシステムを守ることができます。
まとめ:CRON生成で身につけてほしい感覚
CRON生成は、「人間に分かりやすいスケジュール指定」から「機械が解釈できる CRON 文字列」を作る橋渡しです。
よく使うパターン(毎日・平日・毎週・毎月・◯分ごと・◯時間ごと)を“型”として決める。
時刻・日付・曜日・間隔には必ずバリデーションをかける。
危険なスケジュールはユーティリティ側で禁止する。
CRONの仕様(曜日の数値、秒フィールドの有無など)は、使うエンジンに合わせて明確にする。
もし今、あなたのシステムで「CRON文字列を画面からそのまま入力させている」ような箇所があるなら、
そこを一度、「CRON生成ユーティリティ+選択式UI」に置き換えられないか考えてみてください。
それだけで、設定ミスや運用トラブルのリスクがかなり減り、
“実務で使えるスケジューラ”に一歩近づきます。
