1. はじめに
タイムゾーンって「何時間か足す/引く」だけだと思ってるなら、すぐに世界中のプログラマーが感じてる痛みを味わうことになるよ。タイムゾーン、夏時間/冬時間の切り替え、他の地域に「時間を変換」しようとした時の予期しない例外…全部バグの元で、見つけるのも説明するのも超大変。
一番ヤバいのは、「世界共通の時間の考え方」なんてものは存在しないってこと。例えば、隣同士の国でも夏時間の扱いが違ったり、年の途中で突然ルールが変わったり(マジである!)。例:2011年にサモアは日付変更線をまたいで丸一日飛ばしたんだ。
でもC#と.NETには、なんとか戦うための強力なツールがある。今日は救世主TimeZoneInfo型とその機能を紹介するよ。これで君と君のアプリも助かるはず!
タイムゾーン対応が必要なとき
- ユーザーやサーバーが世界中のいろんな地域にいるとき
- DBに日付と時間をUTCで保存して、ユーザーにはローカル時間で見せたいとき
- スケジュール、リマインダー、オンラインイベントの計算(「ウェビナーはベルリン時間19:00開始―君の地域だと何時?」)
- グローバル/企業向けシステム開発時(例:国際ビジネス向け会計やCRMなど)
TimeZoneInfo型のざっくり紹介
TimeZoneInfoは.NETで用意された、タイムゾーン情報(UTCからのオフセット、名前、夏時間対応など)を表すためのクラスだよ。
古いTimeZoneクラスは「ローカル」と「UTC」しか知らないけど、TimeZoneInfoならシステムに登録されてる全タイムゾーン(手動で作ったやつも!)にちゃんとアクセスできる。
2. タイムゾーン情報の取得
ローカルとUTCのタイムゾーン
ローカルタイムゾーンの情報を取得するのは超カンタン:
// ローカル(つまり今プログラムが動いてる場所)
TimeZoneInfo local = TimeZoneInfo.Local;
// いつでもUTC
TimeZoneInfo utc = TimeZoneInfo.Utc;
Console.WriteLine(local.DisplayName); // 例: (UTC+01:00) Berlin, Vienna
Console.WriteLine(utc.DisplayName); // (UTC) Coordinated Universal Time
利用可能な全タイムゾーン
ユーザーに全タイムゾーン一覧を見せたい時(例:サービス登録時)によく使う:
foreach (var tz in TimeZoneInfo.GetSystemTimeZones())
{
Console.WriteLine($"{tz.Id} | {tz.DisplayName}");
}
こんな感じでリストが出る:
- Central European Standard Time | (UTC+01:00) Berlin, Vienna
- Pacific Standard Time | (UTC-08:00) Pacific Time (US & Canada)
- などなど
ビジュアル:TimeZoneInfoの主なプロパティ
| プロパティ | 説明 |
|---|---|
|
タイムゾーンのシステムID(検索時に使う) |
|
ユーザー向けの説明(時差や都市名付き) |
|
「標準時間」の名前 |
|
夏時間用の名前 |
|
UTCからのオフセット(例:ベルリンなら+01:00) |
|
夏時間/冬時間の切り替えに対応してるならTrue |
3. タイムゾーン間の時間変換
ここが本題!やりたいことは:
- あるゾーンの時間を別のゾーンに変換(例:サーバーはUTCで記録、ユーザーにはローカルで表示)
- 夏時間/冬時間の切り替えもちゃんと考慮
基本の書き方
DateTime utcNow = DateTime.UtcNow;
// 例えばこれを中央ヨーロッパ時間に変換
TimeZoneInfo europeZone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
DateTime europeTime = TimeZoneInfo.ConvertTimeFromUtc(utcNow, europeZone);
Console.WriteLine($"UTC now: {utcNow}"); // 2024-06-20 10:30:00
Console.WriteLine($"Europe now: {europeTime}"); // 2024-06-20 11:30:00
重要:内部計算や保存には必ずUTCを使って、「出力時」だけ必要なゾーンに変換しよう!
タイムゾーンIDの調べ方
WindowsだとタイムゾーンのID(Id)は独自(例:"Central European Standard Time")、Linux/UnixだとIANA/Olson ID("Europe/Berlin", "America/New_York")が多いよ。
システム内のID一覧はTimeZoneInfo.GetSystemTimeZones()で取得できる。
4. 任意のゾーン間での変換
例えばロンドン(GMT/UTC+0)の時間を東京(UTC+9)に変換したい場合:
DateTime londonTime = new DateTime(2024, 6, 20, 12, 0, 0, DateTimeKind.Unspecified);
TimeZoneInfo londonZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
TimeZoneInfo tokyoZone = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
// まずローカル時間をUTCに変換
DateTime utc = TimeZoneInfo.ConvertTimeToUtc(londonTime, londonZone);
// 次にUTCを東京時間に変換
DateTime tokyoTime = TimeZoneInfo.ConvertTimeFromUtc(utc, tokyoZone);
Console.WriteLine($"London: {londonTime} | UTC: {utc} | Tokyo: {tokyoTime}");
夏時間・冬時間の切り替え対応
TimeZoneInfoは、必要なゾーンが対応していれば夏時間/冬時間の切り替えもちゃんと考慮してくれる。
TimeZoneInfo eastern = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTime dateWinter = new DateTime(2024, 1, 1, 12, 0, 0, DateTimeKind.Unspecified);
DateTime dateSummer = new DateTime(2024, 7, 1, 12, 0, 0, DateTimeKind.Unspecified);
Console.WriteLine( TimeZoneInfo.ConvertTimeToUtc(dateWinter, eastern)); // UTC-5のオフセット
Console.WriteLine( TimeZoneInfo.ConvertTimeToUtc(dateSummer, eastern)); // UTC-4のオフセット―夏時間!
ちなみに、もしアメリカ(上の例)が夏時間を廃止しても(毎年そんな話が出る!)、OSのタイムゾーンDBはちゃんと更新されるから、君のコードも正しく動くよ。
5. 実践アドバイス&よくあるミス
例外と落とし穴
- 夏時間/冬時間の切り替え時(例:切り替え日の夜2:30)は「存在しない」または「重複する」時間が発生することがある:
例えば「戻す」日の2:30は2回発生して、どっちも「違う」2:30になる。 - タイムゾーン一覧はOSのアップデートで変わることがあるので注意!例:ある国が夏時間を廃止/導入した場合など。
GO TO FULL VERSION