夏時間を考慮して日時を変換するには?[Win 8/WP 8]:WinRT/Metro TIPS
海外の時刻を扱うときにやっかいなのが夏時間。Windows ストア・アプリとWindows Phone 8で、夏時間を含むタイムゾーン情報を取得する方法を紹介する。
powered by Insider.NET
海外の時刻を扱うときにやっかいなのが、夏時間(サマータイム)だ。単純に時差を考慮して変換するだけなら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)
例として、11月3日の日本時間で16時から19時までをPSTに変換してみると、次の画像のようになる。PSTでの夏時刻の終了をまたいでいるので、PSTの1時が2回続けて出てくるのだ。
ところが、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で以下のように作業する。
- ソリューション・エクスプローラで、導入したいプロジェクトを選択する。
- メニュー[プロジェクト]−[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)
●タイムゾーンIDを列挙するには?
上記のコードのようにして時刻を変換するには、タイムゾーンID(上の例では「Pacific Standard Time」)が分かっていないといけない。これは次のようにしてリストを取得できる。
IReadOnlyList<string> tzList
= TimeZones.TimeZoneService.SystemTimeZoneIds;
Dim tzList As IReadOnlyList(Of String) _
= TimeZones.TimeZoneService.SystemTimeZoneIds
●タイムゾーンの名称を取得するには?
上で取得したタイムゾーン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 '"太平洋夏時間"
●夏時間かどうかを判定するには?
特定の日時がそのタイムゾーンでの夏時間に該当しているかどうかは、次のようなコードで判定できる。
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)
●実行結果
以上を応用して、冒頭に示したWPFと同様なアプリを作ってみた。さらに、タイムゾーンIDと名前の一覧も表示してみた。
●まとめ
WinRTTimeZonesライブラリを利用することで、Windowsストア・アプリでもWP 8でも、夏時間を考慮した時刻の変換ができた。
このライブラリを公開してくれたOren Novotny氏に感謝したい。公開時のブログ記事は次で読める(英文)。
Copyright© Digital Advantage Corp. All Rights Reserved.