夏時間を考慮して日時を変換するには?[Win 8/WP 8]WinRT/Metro TIPS

海外の時刻を扱うときにやっかいなのが夏時間。Windows ストア・アプリとWindows Phone 8で、夏時間を含むタイムゾーン情報を取得する方法を紹介する。

» 2013年02月21日 13時08分 公開
[山本康彦BluewaterSoft]
WinRT/Metro TIPS
業務アプリInsider/Insider.NET

powered by Insider.NET

「WinRT/Metro TIPS」のインデックス

連載目次

 海外の時刻を扱うときにやっかいなのが、夏時間(サマータイム)だ。単純に時差を考慮して変換するだけならDateTimeOffsetクラスがサポートしているものの、タイムゾーンの概念は持っていない。つまり、タイムゾーンによって異なる夏時間は扱えないのだ。例えば「世界時計」を作ろうと思ったときに、これでは困ってしまう。

 Windows ストア・アプリとWindows Phone 8(以降、WP 8)に用意されているAPIでは、夏時間を含むタイムゾーン情報がサポートされていない。しかし、Win32 APIを使ってレジストリのタイムゾーン情報を参照することは可能だ。本稿では、それを実現したライブラリの使い方を紹介する。本稿のサンプルは「Windows Store app samples:MetroTips #25(Windows 8版)」と「Windows Store app samples:MetroTips #25(WP 8版)」からダウンロードできる。

事前準備

 Windows 8(以降、Win8)向けのWindowsストア・アプリを開発するには、Win 8とVisual Studio 2012(以降、VS 2012)が必要である。これらを準備するには、第1回のTIPSを参考にしてほしい。本稿では64bit版Win 8 ProとVS 2012 Express for Windows 8を使用している。

 WP 8向けのアプリを開発するには、SLAT対応CPUを搭載したPC上の64bit版Win 8 Pro以上Windows Phone SDK 8.0(無償)が必要となる。

デスクトップ・アプリで夏時間を扱うには?

 最初に、ちょっと復習しておこう。

 従来のデスクトップ・アプリなどでは、System名前空間のTimeZoneInfoクラスを使うことで夏時間対応の時刻変換が可能だった。例えば、UTC(世界標準時)からPST(太平洋標準時)へ変換するには次のようなコードを書いていた。

var tzPst = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
var utc = new DateTimeOffset(2013, 11, 3, 7, 0, 0, TimeSpan.Zero).UtcDateTime;
var pst = TimeZoneInfo.ConvertTimeFromUtc(utc, tzPst);

Dim tzPst = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time")
Dim utc = new DateTimeOffset(2013, 11, 3, 7, 0, 0, TimeSpan.Zero).UtcDateTime
Dim pst = TimeZoneInfo.ConvertTimeFromUtc(utc, tzPst)

UTCからPSTへ変換するコード(デスクトップ・アプリ用)(上:C#、下:VB)

 例として、11月3日の日本時間で16時から19時までをPSTに変換してみると、次の画像のようになる。PSTでの夏時刻の終了をまたいでいるので、PSTの1時が2回続けて出てくるのだ。

JSTからPSTへ変換した結果の画面(WPF)

 ところが、Windowsストア・アプリとWP 8には、このTimeZoneInfoクラスが用意されていないのだ。どうしたらよいだろう?

レジストリをのぞく

 タイムゾーンの情報は、レジストリの次のキーのサブ・キーとして格納されている。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones

 そこには次の画像のように、タイムゾーンIDごとにUTCからの時差と、夏時間の開始/終了日時(バイナリデータになっている)などが記録されているのだ。ローカライズされたタイムゾーンの名前も入っている。

レジストリ内のタイムゾーンの定義(レジストリ・エディタ)

 これらのサブ・キーとその内容を列挙するには、Win32 APIが必要だ。しかし、C#/VBから直接呼び出せるように作られた「WinRTTimeZonesライブラリ」がNuGetに公開されているので、それを使えばよい。

NuGetからWinRTTimeZonesライブラリを導入するには?

 VS 2012で以下のように作業する。

  1. ソリューション・エクスプローラで、導入したいプロジェクトを選択する。
  2. メニュー[プロジェクト]−[NuGet パッケージの管理]を選ぶ。次の画像のようなダイアログが出るので、右上の「オンライン の検索」と表示されているテキスト・ボックスに「WinRTTimeZones」と入力する。
「NuGet パッケージの管理」ダイアログ
オンラインで「WinRTTimeZones」を検索したところ

 3. 見つかったライブラリの[インストール]ボタンをクリックすれば、完了だ。自動的にライブラリのパッケージがダウンロードされてプロジェクトに組み込まれるとともに、プロジェクトに必要な参照設定も追加される*1

*1 筆者の環境では、WP 8のプロジェクトにはNuGetからのインストールに失敗した。Windowsストア・アプリのプロジェクトにインストールされたパッケージの「WinRTTimeZones.0.7.4\lib\wp8」サブフォルダに入っている「TimeZones.dll」と「TimeZones.WP8.dll」に対して、WP 8のプロジェクトに参照を手動で追加することで、動作させることができた。
  なお、このライブラリはWP 8用としてWinPRTコンポーネントを含んでいる。そのため、「シフトJISのデータを読み取るには?[WP 8]」で説明したように、WP 8のプロジェクトのビルド対象プラットフォームを「Any CPU」から「x86」(エミュレータ用)または「ARM」(実機用)に変更する必要がある。


時刻を変換するには?

 WinRTTimeZonesライブラリ中のTimeZones名前空間にあるTimeZoneServiceクラスを使う。時刻とタイムゾーンIDを渡してConvertTimeBySystemTimeZoneIdメソッドを呼び出せばよい。UTCからPSTへの変換は次のようなコードになる。

const string TimeZoneID_PST = "Pacific Standard Time"; // タイムゾーンID
DateTimeOffset utc = new DateTimeOffset(2013, 11, 3, 7, 0, 0, TimeSpan.Zero);
DateTimeOffset pst
      = TimeZones.TimeZoneService
        .ConvertTimeBySystemTimeZoneId(t, TimeZoneID_PST);

Const TimeZoneID_PST As String = "Pacific Standard Time" ' タイムゾーンID
Dim utc As DateTimeOffset _
      = New DateTimeOffset(2013, 11, 3, 7, 0, 0, TimeSpan.Zero)
Dim pst As DateTimeOffset _
      = TimeZones.TimeZoneService _
        .ConvertTimeBySystemTimeZoneId(t, TimeZoneID_PST)

UTCからPSTへ変換するコード(WinRTTimeZonesライブラリ使用)(上:C#、下:VB)

タイムゾーンIDを列挙するには?

 上記のコードのようにして時刻を変換するには、タイムゾーンID(上の例では「Pacific Standard Time」)が分かっていないといけない。これは次のようにしてリストを取得できる。

IReadOnlyList<string> tzList
    = TimeZones.TimeZoneService.SystemTimeZoneIds;

Dim tzList As IReadOnlyList(Of String) _
    = TimeZones.TimeZoneService.SystemTimeZoneIds

タイムゾーンIDのリストを取得するコード(WinRTTimeZonesライブラリ使用)(上:C#、下:VB)

タイムゾーンの名称を取得するには?

 上で取得したタイムゾーンIDは、ユーザーに提示して選択してもらうには不適切だ。次のようにして、ローカライズされた分かりやすい名前を取得できる。

TimeZones.ITimeZoneEx tzPst
  = TimeZones.TimeZoneService
    .FindSystemTimeZoneById("Pacific Standard Time");
string stdName = tzPst.StandardName// "太平洋標準時"
string dstName = tzPst.DaylightName// "太平洋夏時間"

Dim tzPst As TimeZones.ITimeZoneEx _
  = TimeZones.TimeZoneService _
    .FindSystemTimeZoneById("Pacific Standard Time")
Dim stdName As String = tzPst.StandardName '"太平洋標準時"
Dim dstName As String = tzPst.DaylightName '"太平洋夏時間"

タイムゾーンの名前を取得するコード(WinRTTimeZonesライブラリ使用)(上:C#、下:VB)

夏時間かどうかを判定するには?

 特定の日時がそのタイムゾーンでの夏時間に該当しているかどうかは、次のようなコードで判定できる。

TimeZones.ITimeZoneEx tzPst
  = TimeZones.TimeZoneService
    .FindSystemTimeZoneById("Pacific Standard Time");
DateTimeOffset pst = ……(判定したい日時)
bool isDst = tzPst.IsDaylightSavingTime(pst);

Dim tzPst As TimeZones.ITimeZoneEx _
  = TimeZones.TimeZoneService _
    .FindSystemTimeZoneById("Pacific Standard Time")
Dim pst As DateTimeOffset = ……(判定したい日時)
Dim isDst As Boolean = tzPst.IsDaylightSavingTime(pst)

夏時間かどうかを判定するコード(WinRTTimeZonesライブラリ使用)(上:C#、下:VB)

実行結果

 以上を応用して、冒頭に示したWPFと同様なアプリを作ってみた。さらに、タイムゾーンIDと名前の一覧も表示してみた。

実行結果(上:Win 8、下:WP 8)

まとめ

 WinRTTimeZonesライブラリを利用することで、Windowsストア・アプリでもWP 8でも、夏時間を考慮した時刻の変換ができた。

 このライブラリを公開してくれたOren Novotny氏に感謝したい。公開時のブログ記事は次で読める(英文)。

「WinRT/Metro TIPS」のインデックス

WinRT/Metro TIPS

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。