C# Tips | 日付・時間処理:UTC変換

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

はじめに 「UTC変換」は“時間のものさしを世界共通にそろえる”作業

業務システムで日時を扱うとき、
「サーバーのタイムゾーンが違う」「海外リージョンにデプロイする」「他システムと連携する」
こういう場面が出てくると、ローカル時間だけで考えるのは限界がきます。

そこで出てくるキーワードが「UTC(協定世界時)」です。
UTC変換とは、ざっくり言うと「バラバラなローカル時間を、世界共通の基準時刻にそろえる」ことです。

C# では、DateTimeToUniversalTimeTimeZoneInfoDateTimeOffset などを使って、
UTCへの変換を安全に行えます。

ここでは、初心者向けに
UTCとは何か → DateTime のUTC変換 → タイムゾーンを指定した変換 → DateTimeOffset での変換
という流れでかみ砕いて説明します。


UTCとは何かをざっくり押さえる

ローカル時間とUTCの関係

日本時間(JST)は「UTC+9時間」です。
例えば、日本時間で「2026/02/10 20:00」は、UTCでは「2026/02/10 11:00」です。

ローカル時間は「場所によって変わる時間」、
UTCは「世界共通の一本の時間軸」です。

業務システムでは、
保存・比較・連携などの“内部処理”はUTCで、
画面表示など“人が見るところ”はローカル時間で、
という分担にすると、設計が安定しやすくなります。


DateTime の基本的なUTC変換

ToUniversalTime の挙動と DateTimeKind

DateTime には ToUniversalTime() というメソッドがあります。

DateTime local = DateTime.Now;          // ローカル時刻(Kind = Local)
DateTime utc   = local.ToUniversalTime();

Console.WriteLine(local); // 2026/02/10 20:00:00 (例)
Console.WriteLine(utc);   // 2026/02/10 11:00:00 (例)
C#

ここで重要なのが、DateTime.Kind です。
DateTime には次の3種類の Kind があります。

Unspecified(不明)
Local(ローカル時間)
Utc(UTC)

ToUniversalTime() は、Kind によって挙動が変わります。

Kind が Local の場合
ローカル → UTC に変換される(日本なら -9時間される)。

Kind が Utc の場合
そのまま返される(すでにUTCなので変換不要)。

Kind が Unspecified の場合
「ローカル時間だとみなして」UTCに変換される。

ここが落とし穴で、
「本当はUTCなのに Kind が Unspecified のまま」だと、
ToUniversalTime() で二重に変換されてしまいます。

Kind を意識したUTC変換ユーティリティ

安全側に寄せるなら、
「Kind を見てから変換する」ユーティリティを用意しておくとよいです。

public static class UtcUtil
{
    public static DateTime ToUtc(DateTime dt)
    {
        if (dt.Kind == DateTimeKind.Utc)
        {
            return dt;
        }

        if (dt.Kind == DateTimeKind.Local)
        {
            return dt.ToUniversalTime();
        }

        // Unspecified の場合は「ローカル時間として解釈する」方針
        return DateTime.SpecifyKind(dt, DateTimeKind.Local).ToUniversalTime();
    }
}
C#

使い方はこうです。

DateTime local = DateTime.Now;
DateTime utc1  = UtcUtil.ToUtc(local);

DateTime utcRaw = new DateTime(2026, 2, 10, 11, 0, 0, DateTimeKind.Utc);
DateTime utc2   = UtcUtil.ToUtc(utcRaw); // そのまま

DateTime unspecified = new DateTime(2026, 2, 10, 20, 0, 0); // Kind = Unspecified
DateTime utc3        = UtcUtil.ToUtc(unspecified);          // ローカルとみなしてUTCへ
C#

「Unspecified をどう扱うか」はプロジェクトの方針次第ですが、
ユーティリティ側でルールを固定しておくと、呼び出し側が迷わずに済みます。


任意のタイムゾーンからUTCへ変換する(TimeZoneInfo)

「日本時間として解釈してUTCにしたい」などのケース

外部システムから「2026-02-10 20:00」という文字列だけが来て、
「これは日本時間(Asia/Tokyo)だ」と分かっている場合など、
「どのタイムゾーンの時間か」を指定してUTCに変換したいことがあります。

そのときに使うのが TimeZoneInfo です。

using System;

public static class UtcUtil
{
    public static DateTime ToUtcFromTimeZone(DateTime localTime, string timeZoneId)
    {
        var tz = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
        return TimeZoneInfo.ConvertTimeToUtc(localTime, tz);
    }
}
C#

日本時間(JST)として解釈してUTCに変換する例です。

DateTime jstTime = new DateTime(2026, 2, 10, 20, 0, 0, DateTimeKind.Unspecified);

DateTime utc = UtcUtil.ToUtcFromTimeZone(jstTime, "Tokyo Standard Time");

Console.WriteLine(utc); // 2026/02/10 11:00:00 (例)
C#

ポイントは、jstTime の Kind を Unspecified にしておくことです。
ConvertTimeToUtc は、「指定したタイムゾーンのローカル時間」として解釈してくれます。

サーバーのローカルタイムゾーンに依存せず、
「これは日本時間」「これはアメリカ東部時間」と明示して変換できるので、
多拠点システムでは特に重要なパターンです。


DateTimeOffset でのUTC変換

オフセット付き日時からUTCへ

DateTimeOffset は「日時+UTCからのオフセット」を持つ型なので、
UTCへの変換はとてもシンプルです。

DateTimeOffset dto = DateTimeOffset.Now; // 例: 2026-02-10T20:00:00+09:00

DateTime utc = dto.UtcDateTime;

Console.WriteLine(utc); // 2026/02/10 11:00:00
C#

UtcDateTime プロパティは、
「オフセットを考慮してUTCに変換した DateTime(Kind = Utc)」を返します。

逆に、UTCから任意のオフセットに変換したい場合は、
ToOffset を使います。

DateTimeOffset utcNow = DateTimeOffset.UtcNow;          // +00:00
DateTimeOffset jst    = utcNow.ToOffset(TimeSpan.FromHours(9)); // +09:00
C#

DateTime よりも「どのタイムゾーンの時間か」が明確なので、
時差やサマータイムをまたぐシステムでは、
DateTimeOffset を基準に設計するのがかなりおすすめです。


実務ユーティリティとしてのまとめ方

「UTCで保存する」ための入口を一本化する

例えば、DBに保存する日時はすべてUTCにしたい、という方針にするなら、
「保存前に必ず通すUTC変換ユーティリティ」を用意しておくと安全です。

public static class UtcClock
{
    public static DateTime NowUtc() => DateTime.UtcNow;

    public static DateTime NormalizeToUtc(DateTime dt) => UtcUtil.ToUtc(dt);

    public static DateTime FromLocalJstToUtc(DateTime jstUnspecified)
        => UtcUtil.ToUtcFromTimeZone(jstUnspecified, "Tokyo Standard Time");
}
C#

使い方のイメージはこうです。

// 現在のUTCをそのまま保存
var createdAt = UtcClock.NowUtc();

// ユーザー入力(ローカル時間)をUTCに正規化して保存
var deadlineUtc = UtcClock.NormalizeToUtc(userInputDateTime);

// 「これはJSTだ」と分かっている日時をUTCに変換して保存
var eventUtc = UtcClock.FromLocalJstToUtc(jstDateTime);
C#

「UTCにしてから保存する」というルールを、
コード上でもはっきり見える形にしておくと、
チーム開発でもブレにくくなります。


まとめ 「UTC変換ユーティリティ」は“時間のズレ事故を防ぐための保険”

UTC変換は、
「どのサーバーで動かしても、どのタイムゾーンからアクセスしても、
同じ“時間のものさし”で話ができるようにする」ための仕組みです。

押さえておきたいポイントは次の通りです。

DateTime.ToUniversalTime()Kind によって挙動が変わるので、DateTimeKind を意識すること。
TimeZoneInfo.ConvertTimeToUtc を使うと、「この日時は〇〇タイムゾーンだ」と明示してUTCに変換できること。
DateTimeOffset は「日時+オフセット」を持つので、UtcDateTime で安全にUTCに変換できること。
DB保存やシステム間連携などの“内部処理”はUTCで統一し、表示時にローカル時間に変換する設計が安定しやすいこと。
UTC変換の窓口(ユーティリティ)を1か所にまとめておくと、方針変更やテストが圧倒的に楽になること。

ここをきちんと押さえておくと、
「なんとなく日時を扱っている」状態から一歩進んで、
“時間のズレ事故を起こしにくい、実務で信頼できる日時処理”を組み立てられるようになります。

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