C# Tips | 日付・時間処理:Cron生成

C# C#
スポンサーリンク

はじめに:「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# プロジェクトに組み込めるようになります。

C#C#
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました