C# Tips | ファイル・ディレクトリ操作:ファイル更新日時取得

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

はじめに なぜ「ファイル更新日時取得」が業務で重要なのか

業務システムでは、「前回処理してから更新されたファイルだけを再処理したい」「一定期間更新されていないログを削除したい」「外部システムからのファイルが本当に最新か確認したい」といった場面で、ファイルの更新日時を扱うことがとても多いです。
更新日時を見ずに処理すると、「古いファイルを何度も処理してしまう」「本当は更新されているのに気づかない」といった、じわじわ効いてくる不具合につながります。

C# では、File クラスや FileInfo クラスを使って、ファイルの更新日時を簡単に取得できます。
ここでは、プログラミング初心者向けに、基本の API から、実務で使えるユーティリティ化、「更新されたかどうか」の判定ロジックまで、かみ砕いて解説していきます。


基本の API File.GetLastWriteTime と FileInfo.LastWriteTime

一番シンプルな更新日時取得

まずは、「特定のファイルの更新日時を取得して表示する」という、一番シンプルな例から見てみましょう。

using System;
using System.IO;

class Program
{
    static void Main()
    {
        string path = @"C:\data\report.csv";

        DateTime lastWrite = File.GetLastWriteTime(path);

        Console.WriteLine($"更新日時: {lastWrite}");
    }
}
C#

File.GetLastWriteTime は、指定したパスのファイルの「最終更新日時」を DateTime 型で返します。
ここで返ってくるのは「ローカル時刻(PC のタイムゾーンを反映した時刻)」です。
日本の PC であれば、日本時間として解釈できる値になります。

更新日時は、「そのファイルの中身が最後に書き換えられた時刻」を表します。
「いつ作られたか」ではなく、「いつ最後に変更されたか」を知りたいときに使います。

FileInfo を使った書き方

同じことは FileInfo クラスでも書けます。
サイズや作成日時など、他の情報も一緒に扱いたいときは、FileInfo を使うとコードがまとまりやすくなります。

using System;
using System.IO;

class Program
{
    static void Main()
    {
        string path = @"C:\data\report.csv";

        FileInfo info = new FileInfo(path);

        DateTime lastWrite = info.LastWriteTime;

        Console.WriteLine($"更新日時: {lastWrite}");
    }
}
C#

FileInfo.LastWriteTime も、ローカル時刻の DateTime を返します。
「サイズも欲しい」「作成日時も欲しい」といった場合は、FileInfo を使うのが定番です。


ローカル時刻と UTC 時刻 LastWriteTimeUtc の意味

LastWriteTime と LastWriteTimeUtc の違い

FileInfo には、LastWriteTime のほかに LastWriteTimeUtc というプロパティもあります。
名前の通り、こちらは「UTC(協定世界時)」での最終更新日時を返します。

using System;
using System.IO;

class Program
{
    static void Main()
    {
        string path = @"C:\data\report.csv";

        FileInfo info = new FileInfo(path);

        DateTime localTime = info.LastWriteTime;
        DateTime utcTime   = info.LastWriteTimeUtc;

        Console.WriteLine($"ローカル時刻: {localTime}");
        Console.WriteLine($"UTC 時刻    : {utcTime}");
    }
}
C#

日本時間は UTC より 9 時間進んでいるので、例えばローカルが「2025-01-23 18:00」であれば、UTC は「2025-01-23 09:00」のようになります。
国内だけで完結するシステムならローカル時刻だけでも困らないことが多いですが、ログの統一や他システムとの連携を考えると、「内部では UTC で持つ」という設計もよく使われます。

業務でのざっくりした使い分け

初心者向けの指針としては、次のように考えると分かりやすいです。
画面表示やログ出力など、「人間が直接見るもの」はローカル時刻(LastWriteTime)で扱う。
システム間連携や DB 保存など、「後でタイムゾーンを変換する可能性があるもの」は UTC(LastWriteTimeUtc)で扱う。

たとえば、「DB には UTC で保存し、画面に出すときだけ日本時間に変換する」といったパターンです。
最初から完璧を目指さなくてよいですが、「ローカルと UTC の二種類がある」ということだけ知っておくと、後で困りにくくなります。


実務で使えるユーティリティ 単体ファイルの更新日時取得

存在チェック込みのユーティリティメソッド

毎回 File.ExistsFile.GetLastWriteTime を書くのは面倒なので、「存在チェック込みの更新日時取得」ユーティリティを用意しておくと便利です。

using System;
using System.IO;

public static class FileLastWriteTimeUtil
{
    public static DateTime GetLastWriteTime(string filePath)
    {
        if (!File.Exists(filePath))
        {
            throw new FileNotFoundException($"ファイルが見つかりません: {filePath}", filePath);
        }

        return File.GetLastWriteTime(filePath);
    }

    public static DateTime GetLastWriteTimeUtc(string filePath)
    {
        if (!File.Exists(filePath))
        {
            throw new FileNotFoundException($"ファイルが見つかりません: {filePath}", filePath);
        }

        return File.GetLastWriteTimeUtc(filePath);
    }
}
C#

使い方の例は次の通りです。

class Program
{
    static void Main()
    {
        string path = @"C:\data\report.csv";

        try
        {
            DateTime lastWrite = FileLastWriteTimeUtil.GetLastWriteTime(path);
            Console.WriteLine($"更新日時(ローカル): {lastWrite}");

            DateTime lastWriteUtc = FileLastWriteTimeUtil.GetLastWriteTimeUtc(path);
            Console.WriteLine($"更新日時(UTC)     : {lastWriteUtc}");
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("エラー: " + ex.Message);
        }
    }
}
C#

ここでのポイントは、「存在しない場合は既定値ではなく、例外として扱う」設計にしていることです。
業務的に「ファイルがないのは異常」であることが多いので、その場合は例外で止めたほうがバグに気づきやすくなります。

「存在しないなら null」を返すバージョン

一方で、「存在しなければ null として扱いたい」というケースもあります。
その場合は、DateTime?(null 許容)で返すメソッドを用意します。

public static class FileLastWriteTimeUtil
{
    public static DateTime? TryGetLastWriteTime(string filePath)
    {
        if (!File.Exists(filePath))
        {
            return null;
        }

        return File.GetLastWriteTime(filePath);
    }
}
C#

呼び出し側では、null かどうかで存在有無を判定できます。

class Program
{
    static void Main()
    {
        string path = @"C:\data\report.csv";

        DateTime? lastWrite = FileLastWriteTimeUtil.TryGetLastWriteTime(path);

        if (lastWrite == null)
        {
            Console.WriteLine("ファイルが存在しません。");
        }
        else
        {
            Console.WriteLine($"更新日時: {lastWrite.Value}");
        }
    }
}
C#

「例外で止めるか」「null で返すか」をメソッドごとに分けておくと、呼び出し側の意図が明確になります。


更新日時を使った業務ロジックの例

「前回処理以降に更新されたファイルだけ処理する」

業務で非常によくあるのが、「前回バッチ実行以降に更新されたファイルだけを対象にしたい」という要件です。
たとえば、「前回実行時刻を DB に保存しておき、それ以降に更新されたファイルだけ取り込む」といったケースです。

using System;
using System.IO;

public static class FileUpdateFilterUtil
{
    public static string[] GetUpdatedFiles(
        string directoryPath,
        DateTime lastProcessedTime,
        string searchPattern = "*.*")
    {
        if (!Directory.Exists(directoryPath))
        {
            throw new DirectoryNotFoundException($"ディレクトリが見つかりません: {directoryPath}");
        }

        string[] files = Directory.GetFiles(directoryPath, searchPattern);

        var updatedList = new System.Collections.Generic.List<string>();

        foreach (string file in files)
        {
            DateTime lastWrite = File.GetLastWriteTime(file);

            if (lastWrite > lastProcessedTime)
            {
                updatedList.Add(file);
            }
        }

        return updatedList.ToArray();
    }
}
C#

使い方の例は次の通りです。

class Program
{
    static void Main()
    {
        string dir = @"C:\import";

        DateTime lastProcessed = new DateTime(2025, 1, 20, 0, 0, 0);

        string[] updatedFiles = FileUpdateFilterUtil.GetUpdatedFiles(
            dir,
            lastProcessed,
            "*.csv"
        );

        Console.WriteLine("前回処理以降に更新されたファイル:");
        foreach (string file in updatedFiles)
        {
            Console.WriteLine(file);
        }
    }
}
C#

ここでの重要ポイントは、「更新日時を基準にして『前回より新しいかどうか』を判定している」ことです。
これにより、「毎回全ファイルを処理する」のではなく、「差分だけ処理する」バッチを簡単に作れます。

一定期間更新されていないファイルを削除する

もう一つよくあるのが、「30 日以上更新されていないログファイルを削除する」といった、期限付きのクリーンアップ処理です。
この場合も、更新日時を使って判定できます。

using System;
using System.IO;

public static class OldFileCleanupUtil
{
    public static int DeleteNotUpdatedFiles(string directoryPath, int days)
    {
        if (!Directory.Exists(directoryPath))
        {
            throw new DirectoryNotFoundException($"ディレクトリが見つかりません: {directoryPath}");
        }

        DateTime threshold = DateTime.Now.AddDays(-days);

        int deletedCount = 0;

        foreach (string filePath in Directory.GetFiles(directoryPath))
        {
            DateTime lastWrite = File.GetLastWriteTime(filePath);

            if (lastWrite < threshold)
            {
                File.Delete(filePath);
                deletedCount++;
            }
        }

        return deletedCount;
    }
}
C#

使い方の例は次の通りです。

class Program
{
    static void Main()
    {
        string dir = @"C:\logs\app";

        int deleted = OldFileCleanupUtil.DeleteNotUpdatedFiles(dir, days: 30);

        Console.WriteLine($"削除したファイル数: {deleted}");
    }
}
C#

ここでのポイントは、「基準日時(threshold)を先に計算しておき、それより古いかどうかで判定している」ことです。
DateTime.Now.AddDays(-30) のように、「今から 30 日前」を求める書き方は、作成日時のときと同様、更新日時でも頻出します。


作成日時との違いと使い分け

「いつ作られたか」と「いつ最後に変わったか」

ファイルには、作成日時(CreationTime)と更新日時(LastWriteTime)の両方があります。
ざっくり言うと、次のように使い分けます。

いつ作られたファイルかを知りたいときは、作成日時。
いつ最後に中身が変わったかを知りたいときは、更新日時。

たとえば、「古いログを削除したい」場合、どちらを使うかは運用ルール次第です。
「作られてから 30 日経ったら消す」のか、「最後に更新されてから 30 日経ったら消す」のかで、使うプロパティが変わります。

実務では更新日時を基準にすることが多い

実務では、「最近使われていないファイルを消したい」「前回処理以降に変更されたファイルだけ処理したい」といった要件が多く、その場合は更新日時を基準にすることが多いです。
一方、「いつ受信したファイルかを知りたい」といった場合は、作成日時を使うこともあります。

大事なのは、「自分がやりたいことに対して、どちらの意味が合っているか」を意識して選ぶことです。
「なんとなく CreationTime を使う」「なんとなく LastWriteTime を使う」ではなく、「何を基準にしたいのか」を先に言葉にしてみると、迷いにくくなります。


例外とエラー処理を意識した更新日時取得

どんな例外が起こり得るか

ファイル更新日時の取得では、次のような理由で例外が発生する可能性があります。
ファイルが存在しない、権限がなくてアクセスできない、パスが不正または長すぎる、ファイルシステムに問題がある、などです。

呼び出し側での例外処理の例を見てみます。

using System;
using System.IO;

class Program
{
    static void Main()
    {
        string path = @"C:\data\report.csv";

        try
        {
            DateTime lastWrite = FileLastWriteTimeUtil.GetLastWriteTime(path);
            Console.WriteLine($"更新日時: {lastWrite}");
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("ファイルが見つかりません: " + ex.Message);
        }
        catch (UnauthorizedAccessException ex)
        {
            Console.WriteLine("権限エラーが発生しました: " + ex.Message);
        }
        catch (IOException ex)
        {
            Console.WriteLine("入出力エラーが発生しました: " + ex.Message);
        }
        catch (Exception ex)
        {
            Console.WriteLine("想定外のエラーが発生しました: " + ex.Message);
        }
    }
}
C#

実務では、これらのメッセージをログに残しておくことで、「どのファイルの更新日時取得に、どんな理由で失敗したか」を後から追跡できます。


まとめ 実務で使えるファイル更新日時取得ユーティリティの考え方

ファイル更新日時の取得は、「差分処理」「古いファイルの整理」「最新データの判定」など、業務システムの効率と安定性を支える重要な要素です。
だからこそ、「ただ LastWriteTime を読む」だけで終わらせず、ユーティリティとして整理しておく価値があります。

File.GetLastWriteTimeFileInfo.LastWriteTime を使って、更新日時を DateTime として正しく扱うこと。
存在チェック込みのユーティリティ(例外を投げる版と、null を返す版)を用意し、用途に応じて使い分けること。
ローカル時刻と UTC(LastWriteTimeUtc)の違いを理解し、「人が見る用」と「システム内部用」で使い分けること。
更新日時を使って「前回処理以降に更新されたファイルだけを対象にする」「一定期間更新されていないファイルを削除する」といった業務ロジックを組み立てること。
作成日時との違いを意識し、「自分が知りたいのは『いつ作られたか』なのか『いつ最後に変わったか』なのか」を言葉にしてから、どちらを使うか決めること。

ここまで押さえておけば、「どのファイルが最新なのか分からない」「古いファイルが溜まり続けてディスクが圧迫される」といった、現場でよくある悩みをかなり減らせます。

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