DateTimeとDateTimeOffsetはどちらも日時を扱う方ですが、時刻の種類の扱いが異なります。 DateTimeでは時刻がローカル時刻とUTCのどちらを表すかという2種類で時刻の種類を扱うのに対し、DateTimeOffsetでは時刻のオフセット値を用いることでローカル時刻・UTCだけでなくその他の地域の時刻を扱うことができます。
ここでは時刻の種類の扱い方の違いや、時刻をローカル時刻・UTCに変換する方法、その際の注意点などについて見ていきます。 また、オフセット値だけでなくタイムゾーン情報を扱うTimeZoneInfoクラスについても見ていきます。
なお、本文中にあるいくつかのサンプルコードについて、実行環境に設定されているタイムゾーン・言語・書式等によって実行結果・出力内容が変わるものもが存在します。 特に明記していない場合は、日本標準時(UTC+9)・日本語の環境での実行結果となります。
時刻の種類・オフセット
DateTime.Kind
DateTimeには、時刻がローカル時刻とUTCのどちらを表すのかを判別するためのプロパティKindが用意されています。
DateTime.Kindに設定される値とその意味は次のようになります。
DateTimeKind | 意味 |
---|---|
DateTimeKind.Local | DateTimeの時刻はローカル時刻を表す |
DateTimeKind.Utc | DateTimeの時刻はUTCでの時刻を表す |
DateTimeKind.Unspecified | 時刻の種類が特に指定されていない、あるいはどちらでもない |
DateTimeで時刻の変換を行う際にはこのプロパティの値が参照され、例えばローカル時刻またはUTCへの変換を行うToUniversalTime・ToLocalTimeメソッドでは、このKindプロパティの値に応じて適切な時刻に値が変換されます。
DateTimeのコンストラクタでは、時刻がローカル時刻・UTCのどちらを表すのか明示するためにDateTimeKindを指定することが出来ます。 また、DateTime.Nowプロパティで取得できる現在日時はDateTimeKind.Local、DateTime.UtcNowプロパティではDateTimeKind.Utcとなります。
DateTime.Parseメソッドで文字列から日時を読み取りDateTimeを作成する場合にも、文字列に指定されているタイムゾーン部分を判別して適当なDateTimeKindが設定されます。
Parseメソッドで文字列からDateTimeに変換する場合、デフォルトではローカル時刻(オフセット表記のある場合)またはUnspecified(オフセット表記のない場合)に変換されますが、オプションで変換の際に時刻の種類をどう扱うかを変更することができます。 詳しくは日時・文字列の変換と書式 §.変換時のオプション (DateTimeStyles)で解説します。
時刻の種類の変更
時刻の種類(Kindプロパティの値)はインスタンス作成時に設定された後は一切変更することができません。 ですが、DateTime.SpecifyKindメソッドを使うと日時部分はそのままで、Kindプロパティの値だけを指定した値に変更したDateTimeを取得することが出来ます。
このメソッドはDateTimeの日時の部分は変更せず、Kindプロパティの値のみを変更します。 つまり、変更に際してUTCとローカル時刻間の時差は考慮されません。 そのため、このメソッドの主な目的は、DateTimeから時刻の種類の情報を切り離したい場合、あるいは時刻の種類が設定されていない(Unspecifiedな)状態のDateTimeに時刻の種類を設定することになります。
一方、UTCとローカル時刻間での時差を考慮した上で変更したい場合はToUniversalTime・ToLocalTimeメソッドを使います。
DateTimeOffset.Offset
DateTimeOffsetでは、時刻は常にいずれかのタイムゾーンでの時刻を表す(UTCからの時差を持つ)ものとして扱われるため、時刻の種類という概念はありません。 そのかわり、タイムゾーンに対応するオフセット値を表すプロパティOffsetが用意されています。
このプロパティはUTCからの時差をTimeSpanとして表し、例えばDateTimeOffsetの表す日時がUTCの場合、Offsetプロパティの値は +00:00:00 (TimeSpan.Zero)になります。 また、実行環境の時刻が日本標準時(UTC+9)に設定されている環境の場合、DateTimeOffset.NowプロパティはOffsetに +09:00:00 が設定された値を返します。
なお、DateTimeOffset.DateTimeプロパティを参照すると、DateTimeOffsetからオフセット値部分を除いて日時部分だけにしたDateTimeを取得できます。 このプロパティで取得できるDateTimeのKindプロパティの値は、DateTimeKind.Unspecifiedとなります。
DateTimeOffset.Parseメソッドで文字列から日時を読み取りDateTimeOffsetを作成する場合、文字列中にオフセット値が指定されていれば、その値がOffsetプロパティの値となります。
Parseメソッドで文字列からDateTimeOffsetに変換する場合、オフセット表記がない場合はデフォルトではローカル時刻であるものとして変換されますが、オプションで変換の際にオフセット値をどう扱うかを変更することができます。 詳しくは日時・文字列の変換と書式 §.変換時のオプション (DateTimeStyles)で解説します。
ローカル時刻・UTCへの変換
DateTime・DateTimeOffsetの表す時刻をローカル時刻に変換するにはToLocalTimeメソッド、UTCに変換するにはToUniversalTimeメソッドを使うことが出来ます。 このメソッドは、インスタンスの表す時刻をローカル時刻・UTCに変換した結果を返します(元のインスタンスの状態は変化しません)。 DateTime・DateTimeOffsetではローカル時刻・UTCの表現に違いがあるため、時刻の変換を行う場合にもその動作に違いがあります。
DateTimeOffsetではローカル時刻・UTCへの変換の他に、特定のオフセット値での時刻に変換した値を取得するメソッドToOffsetを使うこともできます。
DateTime
DateTimeの表す時刻をToLocalTime・ToUniversalTimeメソッドで変換する場合、変換結果はKindプロパティの値によって変わります。 例えば、既にローカル時刻であるとされている値に対してToLocalTimeメソッドを呼び出しても得られる値は元の時刻と変わりませんが、ToUniversalTimeメソッドを呼び出せばUTCに変換された時刻が得られます。 DateTime.Kindの値とToLocalTime・ToUniversalTimeメソッドの結果は次のようになります。
DateTime.Kindの値 | ToLocalTimeの結果 | ToUniversalTimeの結果 |
---|---|---|
DateTimeKind.Local | 元の値と同じ | UTCに変換された値 |
DateTimeKind.Utc | ローカル時刻に変換された値 | 元の値と同じ |
DateTimeKind.Unspecified | (時刻の種類をUTCと仮定した上で) ローカル時刻に変換された値 |
(時刻の種類をローカル時刻と仮定した上で) UTCに変換された値 |
なお、ToLocalTimeメソッドが返すDateTimeのKindプロパティは当然DateTimeKind.Localとなります。 同様にToUniversalTimeはKindがDateTimeKind.UtcのDateTimeを返します。
以下の例はDateTimeKindが異なる日時のDateTimeに対してToLocalTime・ToUniversalTimeメソッドを呼び出した結果の違いを表示するものです。 なお、実行結果はタイムゾーンにUTC+9(日本標準時)が設定されている環境でのものです。
日時の変換を行わずにKindプロパティの設定値のみを変更したい場合は、SpecifyKindメソッドを使います。
DateTimeOffset
DateTimeOffsetでは、LocalDateTimeおよびUtcDateTimeプロパティを参照することでローカル時刻・UTCでの値を取得することが出来ます。 ただし、このプロパティではDateTimeでの値が返されるため、オフセット情報が消失した値となります。 LocalDateTimeプロパティではDateTime.KindプロパティにDateTimeKind.Local、UtcDateTimeプロパティではDateTimeKind.UtcがセットされたDateTimeが返されます。
DateTimeOffsetでも、DateTimeの場合と同様にToLocalTime・ToUniversalTimeメソッドで時刻を変換することができます。 さらに、ToOffsetメソッドで任意のオフセット値に変更した時刻に変換することも出来ます。
ToLocalTimeメソッドが返すDateTimeOffsetのOffsetプロパティは当然ローカル時刻のオフセット値と同じになります。 同様にToUniversalTimeはOffsetが 00:00:00 のDateTimeOffsetを返し、ToOffsetは指定したオフセット値が設定されたDateTimeOffsetを返します。
以下の例は、オフセット値が異なる日時のDateTimeOffsetに対してToLocalTime・ToUniversalTime・ToOffsetの各メソッドを呼び出した結果の違いを表示するものです。 なお、実行結果はタイムゾーンにUTC+9(日本標準時)が設定されている環境でのものです。
ToLocalTimeメソッドでは、実行環境に設定されているタイムゾーンに従って夏時間等の時間調整を考慮した変換が行われます。 一方、ToOffsetメソッドでは、単に指定されたオフセット値への変換のみが行われ、夏時間等の考慮はされません。 夏時間等の調整が必要なタイムゾーンへの変換を行う場合は、TimeZoneInfo.ConvertTimeメソッドを使う必要があります。
TimeZoneInfo
TimeZoneInfoクラスは、.NET Framework 3.5から導入されたクラスです。 TimeZoneInfoでは特定のタイムゾーンに関する情報を扱い、日時をそのタイムゾーンでのものに変換するメソッドなどが用意されています。
タイムゾーンとその情報の取得
TimeZoneInfoクラスでは、タイムゾーンの名称(StandardNameプロパティ)、タイムゾーンにおける標準時とUTCとの差(オフセット値、BaseUtcOffsetプロパティ)、タイムゾーンに夏時間が存在するかどうか(SupportsDaylightSavingTimeプロパティ)、といった情報を参照することが出来ます。
次の例では、実行環境に設定されているタイムゾーンをLocalプロパティで取得し、その情報を表示しています。
ローカルタイムゾーン以外を表すTimeZoneInfoを取得したい場合は、FindSystemTimeZoneByIdメソッドに目的のタイムゾーンのIDを指定して取得します。 Windowsでは、レジストリに格納されている情報を元にTimeZoneInfoが取得されます。 FindSystemTimeZoneByIdメソッドでは、該当するタイムゾーンが見つからない場合、例外TimeZoneNotFoundExceptionがスローされます。
このほか、Localプロパティと同様、Utcプロパティを参照すれば、UTCのタイムゾーンを表すTimeZoneInfoを取得することが出来ます。
FindSystemTimeZoneByIdメソッドでは、システムに保存されているタイムゾーン情報の形式の違いにより、WindowsではJST
やTokyo Standard Time
といったタイムゾーン名を、Linux等ではAsia/Tokyo
といった地域/都市名の表記を指定する必要があります。
システムで使用可能なすべてのタイムゾーンを取得するには、GetSystemTimeZonesメソッドを使います。
日時の変換
ConvertTimeメソッドを使うと、DateTime・DateTimeOffsetの値を異なるタイムゾーンでの日時に変換することができます。
DateTimeOffset.Offsetメソッドではオフセット値の変更はできますが、この際、夏時間などタイムゾーン内での時間調整は一切行われません。 一方TimeZoneInfo.ConvertTimeメソッドでは、変換に際してそのタイムゾーン内での時刻の変換規則に基づいた時間調整が行われます。 DateTimeOffsetだけでなく、DateTimeに対しても同様に時間調整が行われます。
あるタイムゾーンにて日時が夏時間の期間中かどうかを調べるには、IsDaylightSavingTimeメソッドが使えます。
さらに、ConvertTimeBySystemTimeZoneIdメソッドを使えば、目的のタイムゾーンを表すTimeZoneInfoの取得を省略することが出来ます。 このメソッドは、FindSystemTimeZoneByIdメソッドとConvertTimeメソッドの組み合わせと同等であるため、呼び出しが1度であるならこちらのメソッドを使った方が記述が少なくなります。