C#で日本時間(JST)を取得する|TimeZoneInfoを使わない理由

C#・.NET

C#のDateTime.Nowは、日本時間とは限らない

ログの時刻や申請日時を記録するとき、C#では DateTime.Now を使うのがいちばん手軽です。ただ、DateTime.Now が返すのは「そのPCのタイムゾーンでの今」です。

社内のPCでは正しかった時刻が、クラウドのサーバに載せたら9時間ずれた

クラウドのサーバ(Azure・AWS・Linuxのコンテナなど)は、タイムゾーンがUTC(協定世界時)になっていることが多く、そのままでは日本時間より9時間前の時刻が記録されます。

本記事では、C#でPCの設定に左右されずに日本標準時(JST)を取得する方法を紹介します。自作の立替精算システムで、申請・承認の日時、通知、操作ログの時刻に使っている部品です。

立替精算システムで使ったところ

立替精算システムの通知の画面。承認された日時などが一覧で表示されている

通知の画面です。「承認されました」「経理確認が完了しました」などの日時を、日本時間で表示しています。

私が自作した立替精算システムは、社内のWindows PC 1台で動かす作りですが、導入先によってはサーバのタイムゾーンが日本になっていないこともあります。また、データベースをSQLite・PostgreSQL・SQL Serverから選べるため、どこで動かしても日本時間になるようにしています。

日本時間を取る方法の比較

書き方得られるもの注意
DateTime.NowPCのタイムゾーンでの今サーバの設定で変わる
DateTime.UtcNow.AddHours(9)日本時間の今日本にはサマータイムが無いので常に正しい
DateTimeOffset.UtcNow.ToOffset(TimeSpan.FromHours(9))+09:00 付きの今時差の情報も一緒に持てる
TimeZoneInfo.FindSystemTimeZoneById(…)指定したタイムゾーンの今IDがWindows(Tokyo Standard Time)とLinux(Asia/Tokyo)で違う

TimeZoneInfo は、サマータイムのある国の時刻を扱うときには欠かせません。ただ、日本時間だけでよいなら、日本にはサマータイムが無いので「UTCに9時間を足す」だけで常に正しい時刻になります。OSのタイムゾーンの情報(LinuxではICUというライブラリ)にも頼らずに済むため、立替精算システムではこの方法にしています。

C#で日本時間(JST)を取得するサンプルコード

動作確認は Windows 11・.NET 10 です(.NET 8 以降で動きます)。Visual Studio でコンソールアプリを作り、Program.cs に貼り付けて実行してください。.NET 10 なら、ファイルに保存して dotnet run ファイル名.cs でも実行できます。

// 日本時間(JST)を取得する
Console.WriteLine($"DateTime.Now    :{DateTime.Now:yyyy/MM/dd HH:mm:ss}(PC のタイムゾーンしだい)");
Console.WriteLine($"DateTime.UtcNow :{DateTime.UtcNow:yyyy/MM/dd HH:mm:ss}(世界標準時)");
Console.WriteLine($"JST(UTC+9)    :{JstNow():yyyy/MM/dd HH:mm:ss}");
Console.WriteLine($"今日(JST)     :{JstToday():yyyy/MM/dd}");
Console.WriteLine($"Kind            :{JstNow().Kind}");

// 比較:DateTimeOffset で +09:00 付きの時刻にする
var offset = DateTimeOffset.UtcNow.ToOffset(TimeSpan.FromHours(9));
Console.WriteLine($"DateTimeOffset  :{offset:yyyy/MM/dd HH:mm:ss zzz}");

// 比較:TimeZoneInfo でタイムゾーンを探す(ID が OS によって違う)
foreach (var id in new[] { "Tokyo Standard Time", "Asia/Tokyo" })
{
    try
    {
        var tz = TimeZoneInfo.FindSystemTimeZoneById(id);
        var t = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz);
        Console.WriteLine($"TimeZoneInfo({id}):{t:yyyy/MM/dd HH:mm:ss}");
    }
    catch (TimeZoneNotFoundException)
    {
        Console.WriteLine($"TimeZoneInfo({id}):見つかりません");
    }
}


/// <summary>
/// 日本時間(UTC+9)の今。日本にはサマータイムが無いので、+9 時間で常に正しい。
/// Kind は Unspecified にしておく(データベースの「タイムゾーンなし」の列にそのまま入れられる)。
/// </summary>
static DateTime JstNow() =>
    DateTime.SpecifyKind(DateTime.UtcNow.AddHours(9), DateTimeKind.Unspecified);

/// <summary>日本時間の今日。</summary>
static DateOnly JstToday() => DateOnly.FromDateTime(JstNow());

実行結果

DateTime.Now    :2026/10/25 10:27:10(PC のタイムゾーンしだい)
DateTime.UtcNow :2026/10/25 01:27:10(世界標準時)
JST(UTC+9)    :2026/10/25 10:27:10
今日(JST)     :2026/10/25
Kind            :Unspecified
DateTimeOffset  :2026/10/25 10:27:10 +09:00
TimeZoneInfo(Tokyo Standard Time):2026/10/25 10:27:10
TimeZoneInfo(Asia/Tokyo):2026/10/25 10:27:10

このPCはタイムゾーンが日本なので、DateTime.Now とJSTが同じ時刻になっています。タイムゾーンがUTCのサーバで動かすと、DateTime.Now の行だけが9時間前の時刻になり、JSTの行は変わりません。

最近の.NET(.NET 6以降)は、WindowsでもLinuxのID(Asia/Tokyo)でタイムゾーンを探せるため、この実行結果では両方とも見つかっています。ただ、動かす環境しだいで結果が変わる書き方は、できるだけ避けています。

コードの説明

  • DateTime.UtcNow.AddHours(9) で、世界標準時に9時間を足して日本時間にしています。
  • DateTime には、その時刻がUTCなのか、PCのローカル時刻なのか、どちらでもないのかを表す Kind があります。UtcNow から作ると Kind は Utc のままなので、DateTime.SpecifyKind(..., DateTimeKind.Unspecified) で「タイムゾーンを持たない日本時間」にしています。
  • PostgreSQLの timestamp without time zone の列に Kind が Utc の値を入れようとすると、エラーになります(Npgsql 6以降)。Unspecified にしておけば、そのまま保存できます。
  • DateOnly.FromDateTime は、日付だけが欲しいとき(今日が何日か)に使います。

「今」を差し替えられるようにしておく

立替精算システムでは、時刻の取得をインターフェースにしています。

public interface IClock
{
    /// <summary>現在日時(JST)。</summary>
    DateTime Now { get; }

    /// <summary>今日の日付(JST)。</summary>
    DateOnly Today { get; }
}

public sealed class JstClock : IClock
{
    private const int JstOffsetHours = 9;

    public DateTime Now =>
        DateTime.SpecifyKind(DateTime.UtcNow.AddHours(JstOffsetHours), DateTimeKind.Unspecified);

    public DateOnly Today => DateOnly.FromDateTime(Now);
}

ASP.NET Coreでは、Program.cs で builder.Services.AddSingleton<IClock, JstClock>(); と登録し、画面や処理は DateTime.Now を直接使わずに IClock を受け取って時刻を取ります。

こうしておくと、試験のときに「今日は2026年9月30日」のように日付を固定した時計に差し替えられます。月末・月初の締め処理や、「今月は何月か」で動きが変わる処理を確かめるときに便利です。

注意点

  • PCの時計そのものがずれていると、正しい日本時間にはなりません。Windowsの「設定」→「時刻と言語」で、時刻の同期が有効になっているか確認してください。
  • 海外の拠点など、日本以外の時刻も扱うシステムでは、DateTimeOffset や TimeZoneInfo を使ってください。
  • 1つのシステムの中で DateTime.Now とJSTが混ざると、比べたときに9時間ずれます。時刻を取る場所を1か所(IClock)にまとめておくのが安全です。

最後に

以上が、C#で日本時間(JST)を取得する方法です。

DateTime.UtcNow.AddHours(9) の1行で済みますが、時刻を取る場所を1か所にまとめておくと、サーバの設定で時刻がずれる心配がなくなり、試験もしやすくなります。

この部品を使っている立替精算システムは、こちらで紹介しています。

Pythonでインターネットの時刻サーバ(NTP)から日本時間を取得する方法は、こちらで紹介しています。

コメント

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