C# Tips | 日付・時間処理:タイムゾーン変換

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

はじめに 「タイムゾーン変換」は“世界とつながる日時処理の要”

日本だけで完結しているうちはあまり意識しませんが、
クラウド、海外拠点、スマホアプリなどが絡み始めると、
「この日時はどこの国の時間なのか?」という問題が一気に重要になります。

C# では、DateTimeTimeZoneInfo を組み合わせることで、
UTC と各国ローカル時間の相互変換ができます。
ここをきちんと押さえておくと、「日付が1日ズレた」「9時のはずが8時になった」といった事故をかなり防げます。


基本の考え方:UTC とローカル時間を分けて考える

UTC とローカル時間とは何か

UTC(協定世界時)は、世界共通の“基準の時間”です。
日本時間(JST)は「UTC+9時間」、ニューヨークは「UTC-5時間(サマータイム時は-4)」のように、
各国のローカル時間は「UTCにオフセットを足したもの」として表現できます。

実務では、
「保存や計算は UTC で行い、表示するときにローカル時間に変換する」
というパターンがとても多いです。

C# では、UTC を表す DateTime と、ローカル時間を表す DateTime
TimeZoneInfo を使って相互に変換できます。


DateTimeKind を理解する:その日時は“どこの時間”か

Kind プロパティの意味

DateTime には Kind というプロパティがあり、
DateTimeKind.UtcDateTimeKind.LocalDateTimeKind.Unspecified のいずれかが入ります。

DateTime.UtcNow は Kind が Utc
DateTime.Now は Kind が Local
new DateTime(2026, 2, 10, 9, 0, 0) のように単純に new した場合は Unspecified です。

この Kind によって、TimeZoneInfo で変換するときの解釈が変わります。
「この日時は UTC なのか、ローカルなのか、よく分からないのか」を
コード側でちゃんと意識することが、タイムゾーン変換の第一歩です。


UTC → 日本時間(JST)への変換

TimeZoneInfo を使った基本パターン

まずは、UTC の日時を日本時間に変換する例から見てみます。

using System;

DateTime utc = new DateTime(2026, 2, 10, 0, 0, 0, DateTimeKind.Utc);

TimeZoneInfo jst = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");

DateTime jstTime = TimeZoneInfo.ConvertTimeFromUtc(utc, jst);

Console.WriteLine(utc);     // 2026/02/10 0:00:00 (UTC)
Console.WriteLine(jstTime); // 2026/02/10 9:00:00 (JST)
C#

ここで重要なのは、UTC の DateTime を作るときに
必ず DateTimeKind.Utc を指定していることです。

new DateTime(2026, 2, 10, 0, 0, 0, DateTimeKind.Utc) のように Kind を明示しておくと、
ConvertTimeFromUtc が正しく解釈してくれます。

FindSystemTimeZoneById の引数 "Tokyo Standard Time" は、
Windows のタイムゾーン ID です。
Linux やコンテナ環境では "Asia/Tokyo" など別の ID を使う場合もあるので、
環境ごとの違いは覚えておくとよいです。


日本時間(JST)→ UTC への変換

ローカル時間を UTC に変換する

今度は逆に、日本時間の日時を UTC に変換してみます。

using System;

TimeZoneInfo jst = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");

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

DateTime utc = TimeZoneInfo.ConvertTimeToUtc(jstTime, jst);

Console.WriteLine(jstTime); // 2026/02/10 9:00:00
Console.WriteLine(utc);     // 2026/02/10 0:00:00
C#

ここでは、jstTime の Kind を Unspecified にしています。
「これは JST だよ」と ConvertTimeToUtc に教えるために、
第二引数で jst を渡しています。

もし DateTimeKind.Local の値を渡す場合は、
ConvertTimeToUtc(localTime) のように、
タイムゾーンを省略してローカル→UTC 変換を行うこともできます。

実務では、
「画面で入力された日本時間を UTC に変換して DB に保存する」
という流れでよく使います。


任意のタイムゾーン同士の変換(例:日本時間 ↔ ニューヨーク時間)

UTC を経由して変換するイメージ

日本時間からニューヨーク時間に変換したい場合、
イメージとしては「JST → UTC → America/New_York」という流れになります。

using System;

TimeZoneInfo jst  = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
TimeZoneInfo ny   = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");

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

DateTime nyTime = TimeZoneInfo.ConvertTime(jstTime, jst, ny);

Console.WriteLine(jstTime); // 2026/02/10 9:00:00 (JST)
Console.WriteLine(nyTime);  // 2026/02/09 19:00:00 (NY, サマータイムでなければ)
C#

ConvertTime は、
「あるタイムゾーンの日時を、別のタイムゾーンの日時に変換する」メソッドです。

ここでの重要ポイントは、
「この DateTime はどのタイムゾーンのものか」を
第二引数で明示していることです。


実務での設計パターン:保存は UTC、表示はローカル

なぜ UTC 保存が好まれるのか

複数の国・拠点・サーバーが絡むシステムでは、
「DB には UTC で保存する」という方針がよく採用されます。

理由はシンプルで、
UTC なら世界中どこから見ても同じ瞬間を表せるからです。

例えば、DB に 2026-02-10T00:00:00Z と保存しておけば、
日本では 9:00、ニューヨークでは前日の 19:00 として表示できます。

C# での典型的な流れはこうなります。

入力(ローカル時間) → タイムゾーンを指定して UTC に変換 → DB に保存
DB から取得した UTC → ユーザーのタイムゾーンに変換して画面表示

このとき、
「どのタイムゾーンのユーザーか」を
ユーザー設定やテナント設定として持っておくと、
世界中のユーザーに対して自然な時間表示ができます。


タイムゾーン変換ユーティリティを作る

よく使う変換をメソッドにまとめる

毎回 FindSystemTimeZoneByIdConvertTimeFromUtc を書くのは面倒なので、
ユーティリティクラスにまとめておくと便利です。

using System;

public static class TimeZoneUtil
{
    private static readonly TimeZoneInfo Tokyo =
        TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");

    public static DateTime UtcToJst(DateTime utc)
    {
        if (utc.Kind != DateTimeKind.Utc)
        {
            utc = DateTime.SpecifyKind(utc, DateTimeKind.Utc);
        }

        return TimeZoneInfo.ConvertTimeFromUtc(utc, Tokyo);
    }

    public static DateTime JstToUtc(DateTime jst)
    {
        DateTime unspecified = DateTime.SpecifyKind(jst, DateTimeKind.Unspecified);
        return TimeZoneInfo.ConvertTimeToUtc(unspecified, Tokyo);
    }
}
C#

使い方の例です。

DateTime utcNow = DateTime.UtcNow;

DateTime jstNow = TimeZoneUtil.UtcToJst(utcNow);

Console.WriteLine(utcNow);
Console.WriteLine(jstNow);
C#

ここでのポイントは、
SpecifyKind を使って Kind を明示的に指定していることです。
「この値は UTC として扱う」「これは JST として扱う」という意図を
コードに刻んでおくと、後から読んだときに迷いません。


実務での注意点:サマータイムと“存在しない時間”

サマータイムのある国では「飛ぶ時間」「重なる時間」がある

日本にはサマータイムがありませんが、
アメリカやヨーロッパなどでは、
年に2回「時計を1時間進める/戻す」タイミングがあります。

その瞬間には、
「存在しない時間帯」や「同じ時刻が2回ある時間帯」が発生します。

TimeZoneInfo は、
そのタイムゾーンのサマータイムルールを内部に持っていて、
ConvertTime などで自動的に考慮してくれます。

ただし、
「ユーザーが入力したローカル時刻が、実は存在しない時間だった」
というケースもあり得ます。

例えば、サマータイム開始の日に、
時計を 1:59 → 3:00 に進める国では、
2:30 という時刻は存在しません。

こうしたケースをどう扱うかは、
システムの要件次第です。
「エラーにする」「前後に丸める」など、
ビジネスルールとして決めておく必要があります。


まとめ 「タイムゾーン変換ユーティリティ」は“時間の意味をコードに刻むもの”

タイムゾーン変換は、
単に「時間を足し引きする」話ではなく、
「この日時はどこの国の、どの時間軸のものか」を
コードの中で明確にする作業です。

押さえておきたいポイントを整理すると、こうなります。

UTC とローカル時間を分けて考え、保存や計算は UTC、表示はローカルという方針にすると設計が安定する。
DateTime.Kind(Utc / Local / Unspecified)を意識し、「この日時はどの時間として扱うのか」を明示する。
TimeZoneInfoConvertTimeFromUtc / ConvertTimeToUtc / ConvertTime を使って、UTC ↔ 各国ローカル、ローカル同士の変換を行う。
よく使うタイムゾーン(例: 日本時間)はユーティリティクラスにまとめ、ID や Kind の扱いを一箇所に閉じ込める。
サマータイムのある国では「存在しない時間」「重なる時間」があることを理解し、入力時の扱いを業務ルールとして決めておく。

ここを押さえておけば、
「なんとなく Now をそのまま保存している」状態から一歩進んで、
“世界とつながる前提で設計された、実務で使えるタイムゾーン変換ユーティリティ”を
自分の C# コードの中にしっかり組み込めるようになります。

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