はじめに:「Cron生成」は“人間の言葉をスケジュール文字列に変換する作業”
「毎日3時に実行したい」
「平日の9時ちょうどにだけ動かしたい」
「5分ごとにポーリングしたい」
人間にとっては分かりやすいこの言い方を、
プログラムやジョブスケジューラが理解できる「Cron文字列」に変換するのが、ここでいう「Cron生成」です。
C# で小さなユーティリティを用意しておくと、
「毎日〇時」→ "0 3 * * *"
「平日9時」→ "0 9 * * 1-5"
「5分ごと」→ "*/5 * * * *"
のような変換を、安全に・間違いなく行えるようになります。
ここでは、まず Cron の基本形を軽くおさらいしつつ、
「よくあるパターンをメソッド化する」という実務的なアプローチで解説していきます。
Cron の基本形を“生成側の目線”で押さえる
5フィールドの並びを覚える
標準的な Cron は、次の5フィールドで構成されます。
分 時 日 月 曜日
これを「生成する側の目線」で覚えると、
「いつ実行したいか」を、この5つに分解して文字列に並べる
というイメージになります。
例えば「毎日3時ちょうど」は、
分 = 0
時 = 3
日 = 毎日なので *
月 = 毎月なので *
曜日 = 全曜日なので *
なので、文字列としては "0 3 * * *" になります。
この「5つの箱に値を入れて、スペースでつなぐ」という感覚を持っておくと、生成ロジックがとても書きやすくなります。
パターン1:毎日・毎時・毎分などのシンプルな Cron を生成する
毎日指定時刻に実行する Cron を生成する
まずは一番よくある「毎日〇時〇分に実行」のユーティリティです。
public static class CronBuilder
{
public static string DailyAt(int hour, int minute)
{
ValidateRange(minute, 0, 59, nameof(minute));
ValidateRange(hour, 0, 23, nameof(hour));
return $"{minute} {hour} * * *";
}
private static void ValidateRange(int value, int min, int max, string name)
{
if (value < min || value > max)
{
throw new ArgumentOutOfRangeException(
name, value, $"有効範囲は {min}〜{max} です。");
}
}
}
C#使い方の例です。
string cron = CronBuilder.DailyAt(3, 0);
Console.WriteLine(cron); // 0 3 * * *
C#ここでの重要ポイントは、「引数のバリデーションを必ず行う」ことです。
Cron はただの文字列なので、間違った値を入れてもコンパイルエラーにはなりません。
だからこそ、ユーティリティ側で「0〜59」「0〜23」などの範囲チェックをしておくことが、実務ではとても大事です。
毎時・毎分の Cron を生成する
同じ要領で、「毎時」「毎分」も作れます。
public static string EveryHourAt(int minute)
{
ValidateRange(minute, 0, 59, nameof(minute));
return $"{minute} * * * *";
}
public static string EveryMinute()
{
return "* * * * *";
}
C#使い方のイメージはこうなります。
string cron1 = CronBuilder.EveryHourAt(0); // 毎時0分
string cron2 = CronBuilder.EveryMinute(); // 毎分
Console.WriteLine(cron1); // 0 * * * *
Console.WriteLine(cron2); // * * * * *
C#「よく使うパターン」をこうやって名前付きメソッドにしておくと、
コードを読む人も「これは毎日3時だな」「これは毎時0分だな」と一瞬で理解できます。
パターン2:平日だけ・曜日指定の Cron を生成する
平日9時ちょうどに実行する Cron
次に、「平日だけ」「特定の曜日だけ」というパターンです。
曜日フィールドは、0〜7(0と7が日曜、1が月曜…)で表現されることが多いので、
「月〜金」は "1-5" と書けます。
public static string WeekdaysAt(int hour, int minute)
{
ValidateRange(minute, 0, 59, nameof(minute));
ValidateRange(hour, 0, 23, nameof(hour));
string dayOfWeek = "1-5"; // 月〜金
return $"{minute} {hour} * * {dayOfWeek}";
}
C#使い方の例です。
string cron = CronBuilder.WeekdaysAt(9, 0);
Console.WriteLine(cron); // 0 9 * * 1-5
C#ここでの重要ポイントは、「曜日の表現をユーティリティ側で隠蔽する」ことです。
呼び出し側は「WeekdaysAt(9, 0)」と書くだけでよく、
「1-5 が平日を意味する」という Cron の知識を知らなくても使えます。
特定の曜日の Cron を生成する
もう少し汎用的に、「指定された曜日だけ実行する」メソッドも作れます。
public enum CronDayOfWeek
{
Sunday = 0,
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6
}
public static string AtOnDays(int hour, int minute, params CronDayOfWeek[] days)
{
ValidateRange(minute, 0, 59, nameof(minute));
ValidateRange(hour, 0, 23, nameof(hour));
if (days == null || days.Length == 0)
throw new ArgumentException("少なくとも1つの曜日を指定してください。", nameof(days));
string dayOfWeekField = string.Join(",", days.Select(d => (int)d));
return $"{minute} {hour} * * {dayOfWeekField}";
}
C#使い方の例です。
string cron = CronBuilder.AtOnDays(
10, 30,
CronDayOfWeek.Monday,
CronDayOfWeek.Wednesday,
CronDayOfWeek.Friday);
Console.WriteLine(cron); // 30 10 * * 1,3,5
C#ここでの重要ポイントは、「生の数字ではなく enum を使う」ことです。1,3,5 と直接書くよりも、CronDayOfWeek.Monday のように意味のある名前で指定したほうが、
読みやすさも安全性も大きく向上します。
パターン3:ステップ付き(*/5 など)の Cron を生成する
「5分ごと」「10分ごと」の Cron
「5分ごと」「10分ごと」のようなパターンは、分フィールドに */5 や */10 を使います。
public static string EveryNMinutes(int interval)
{
if (interval <= 0 || interval > 59)
throw new ArgumentOutOfRangeException(nameof(interval), "1〜59 の範囲で指定してください。");
return $"*/{interval} * * * *";
}
C#使い方の例です。
string cron5 = CronBuilder.EveryNMinutes(5);
string cron10 = CronBuilder.EveryNMinutes(10);
Console.WriteLine(cron5); // */5 * * * *
Console.WriteLine(cron10); // */10 * * * *
C#同じように、「毎時 N 時ごと」「毎月 N 日ごと」なども、
必要に応じてステップ付きのフィールドを組み立てることで表現できます。
ここでの重要ポイントは、「ステップ表現(*/N)も、ただの文字列として組み立てればよい」と理解することです。
難しいことはしていなくて、"*/" + interval をフィールドに入れているだけです。
実務での設計ポイント:Cron を“直書きしない”ためのユーティリティ
文字列リテラルを散らさない
実務のコードでよくあるアンチパターンが、
あちこちに "0 3 * * *" のような文字列リテラルが散らばることです。
どれが何を意味しているのか分かりにくい
間違ってもコンパイルエラーにならない
仕様変更(例えば「3時→4時」)のときに探し漏れが起きる
こういった問題を避けるために、
CronBuilder.DailyAt(3, 0)CronBuilder.WeekdaysAt(9, 0)CronBuilder.EveryNMinutes(5)
のような「意味のある名前を持ったメソッド」にまとめておくのが、とても有効です。
「生成」と「解析」をセットで考える
前回の「Cron表現解析」と今回の「Cron生成」は、
本来セットで考えると設計がきれいになります。
生成側では、「どんなパターンをサポートするか」をメソッドとして定義し、
解析側では、「そのパターンで生成された Cron を正しく解釈できる」ようにしておく。
こうしておくと、
設定画面でユーザーが選んだ内容 → Cron生成 → 保存
保存された Cron → Cron解析 → 次の実行時刻計算
という一連の流れを、自分たちのコードの中で完結させられます。
まとめ:「Cron生成ユーティリティ」は“人間の意図を安全に文字列化する橋”
Cron生成の本質は、
「人間にとって分かりやすい“いつ実行したいか”」を、
「機械が理解できる Cron 文字列」に安全に変換することです。
押さえておきたいポイントを言葉でまとめると、こうなります。
Cron は「分 時 日 月 曜日」の5フィールドで、そこに値を入れてスペースでつなぐ。
よく使うパターン(毎日〇時、平日〇時、N分ごとなど)は、名前付きメソッドとしてユーティリティ化する。
引数の範囲チェックを必ず行い、間違った Cron を生成しないようにする。
曜日は enum などで表現し、「1,3,5」のような“意味の分からない数字列”を隠す。
コード中に Cron 文字列を直書きせず、必ず CronBuilder のようなユーティリティを経由する。
ここまでできれば、
「なんとなく Cron を書いている」状態から抜け出して、
“業務・実務で安心して使える Cron生成ユーティリティ”を、自分の C# プロジェクトに組み込めるようになります。
