DateTimeDateTimeOffsetはどちらも日時を扱う方ですが、時刻の種類の扱いが異なります。 DateTimeでは時刻がローカル時刻とUTCのどちらを表すかという2種類で時刻の種類を扱うのに対し、DateTimeOffsetでは時刻のオフセット値を用いることでローカル時刻・UTCだけでなくその他の地域の時刻を扱うことができます。

ここでは時刻の種類の扱い方の違いや、時刻をローカル時刻・UTCに変換する方法、その際の注意点などについて見ていきます。 また、オフセット値だけでなくタイムゾーン情報を扱うTimeZoneInfoクラスについても見ていきます。

なお、本文中にあるいくつかのサンプルコードについて、実行環境に設定されているタイムゾーン・言語・書式等によって実行結果・出力内容が変わるものもが存在します。 特に明記していない場合は、日本標準時(UTC+9)・日本語の環境での実行結果となります。

時刻の種類・オフセット

DateTime.Kind

DateTimeには、時刻がローカル時刻とUTCのどちらを表すのかを判別するためのプロパティKindが用意されています。

DateTime.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となります。

DateTimeKindを指定してDateTimeインスタンスを作成する
using System;

class Sample {
  static void Main()
  {
    var a = new DateTime(2013, 4, 1, 15, 0, 30, 123);                      // 日時の種類を指定しない
    var b = new DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Local);  // ローカル時刻として日時を指定
    var c = new DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Utc);    // UTCとして日時を指定
    var d = DateTime.Now;    // ローカル時刻での現在日時を取得
    var e = DateTime.UtcNow; // UTCでの現在日時を取得

    foreach (var dt in new[] {a, b, c, d, e}) {
      Console.WriteLine("{0} {1}", dt, dt.Kind);
    }
  }
}
DateTimeKindを指定してDateTimeインスタンスを作成する
Imports System

Class Sample
  Shared Sub Main()
    Dim a As New DateTime(2013, 4, 1, 15, 0, 30, 123)                      ' 日時の種類を指定しない
    Dim b As New DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Local)  ' ローカル時刻として日時を指定
    Dim c As New DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Utc)    ' UTCとして日時を指定
    Dim d As DateTime = DateTime.Now    ' ローカル時刻での現在日時を取得
    Dim e As DateTime = DateTime.UtcNow ' UTCでの現在日時を取得

    For Each dt As DateTime In New DateTime() {a, b, c, d, e}
      Console.WriteLine("{0} {1}", dt, dt.Kind)
    Next
  End Sub
End Class
実行結果例
2013/04/01 15:00:30 Unspecified
2013/04/01 15:00:30 Local
2013/04/01 15:00:30 Utc
2013/04/01 15:30:05 Local
2013/04/01 6:30:05 Utc

DateTime.Parseメソッドで文字列から日時を読み取りDateTimeを作成する場合にも、文字列に指定されているタイムゾーン部分を判別して適当なDateTimeKindが設定されます。

DateTime.Parseメソッドでオフセット表記のある/ない文字列からDateTimeに変換する
using System;

class Sample {
  static void Main()
  {
    var a = "2013-04-01T15:00:30";       // オフセット表記の無い日時
    var b = "2013-04-01T15:00:30-05:00"; // オフセット表記のある日時
    var c = "2013-04-01T15:00:30Z";      // UTCでの日時

    foreach (var s in new[] {a, b, c}) {
      // 文字列からDateTimeに変換
      var dt = DateTime.Parse(s);

      Console.WriteLine("{0} {1}", dt, dt.Kind);
    }
    Console.WriteLine();
  }
}
DateTime.Parseメソッドでオフセット表記のある/ない文字列からDateTimeに変換する
Imports System

Class Sample
  Shared Sub Main()
    Dim a As String = "2013-04-01T15:00:30"       ' オフセット表記の無い日時
    Dim b As String = "2013-04-01T15:00:30-05:00" ' オフセット表記のある日時
    Dim c As String = "2013-04-01T15:00:30Z"      ' UTCでの日時

    For Each s As String In New String() {a, b, c}
      ' 文字列からDateTimeに変換
      Dim dt As DateTime = DateTime.Parse(s)

      Console.WriteLine("{0} {1}", dt, dt.Kind)
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
2013/04/01 15:00:30 Unspecified
2013/04/02 5:00:30 Local
2013/04/02 0:00:30 Local

Parseメソッドで文字列からDateTimeに変換する場合、デフォルトではローカル時刻(オフセット表記のある場合)またはUnspecified(オフセット表記のない場合)に変換されますが、オプションで変換の際に時刻の種類をどう扱うかを変更することができます。 詳しくは日時・文字列の変換と書式 §.変換時のオプション (DateTimeStyles)で解説します。

時刻の種類の変更

時刻の種類(Kindプロパティの値)はインスタンス作成時に設定された後は一切変更することができません。 ですが、DateTime.SpecifyKindメソッドを使うと日時部分はそのままで、Kindプロパティの値だけを指定した値に変更したDateTimeを取得することが出来ます。

DateTime.SpecifyKindメソッドを使って時刻の種類を変更する
using System;

class Sample {
  static void Main()
  {
    var a = new DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Local);
    var b = new DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Utc);
    var c = new DateTime(2013, 4, 1, 15, 0, 30, 123); // DateTimeKind.Unspecified

    var kinds = new[] {
      DateTimeKind.Local,
      DateTimeKind.Utc,
      DateTimeKind.Unspecified,
    };

    foreach (var kind in kinds) {
      Console.WriteLine("SpecifyKind({0})", kind);

      foreach (DateTime dt in new DateTime[] {a, b, c}) {
        // SpecifyKindメソッドでKindの値を変更したDateTimeを取得する
        DateTime dtNew = DateTime.SpecifyKind(dt, kind);

        Console.WriteLine("{0} {1,-12} -> {2} {3}", dt, dt.Kind, dtNew, dtNew.Kind);
      }

      Console.WriteLine();
    }
  }
}
DateTime.SpecifyKindメソッドを使って時刻の種類を変更する
Imports System

Class Sample
  Shared Sub Main()
    Dim a As New DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Local)
    Dim b As New DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Utc)
    Dim c As New DateTime(2013, 4, 1, 15, 0, 30, 123) ' DateTimeKind.Unspecified

    Dim kinds() As DateTimeKind = New DateTimeKind() { _
      DateTimeKind.Local, _
      DateTimeKind.Utc, _
      DateTimeKind.Unspecified _
    }

    For Each kind As DateTimeKind In kinds
      Console.WriteLine("SpecifyKind({0})", kind)

      For Each dt As DateTime In New DateTime() {a, b, c}
        ' SpecifyKindメソッドでKindの値を変更したDateTimeを取得する
        Dim dtNew As DateTime = DateTime.SpecifyKind(dt, kind)

        Console.WriteLine("{0} {1,-12} -> {2} {3}", dt, dt.Kind, dtNew, dtNew.Kind)
      Next

      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
SpecifyKind(Local)
2013/04/01 15:00:30 Local        -> 2013/04/01 15:00:30 Local
2013/04/01 15:00:30 Utc          -> 2013/04/01 15:00:30 Local
2013/04/01 15:00:30 Unspecified  -> 2013/04/01 15:00:30 Local

SpecifyKind(Utc)
2013/04/01 15:00:30 Local        -> 2013/04/01 15:00:30 Utc
2013/04/01 15:00:30 Utc          -> 2013/04/01 15:00:30 Utc
2013/04/01 15:00:30 Unspecified  -> 2013/04/01 15:00:30 Utc

SpecifyKind(Unspecified)
2013/04/01 15:00:30 Local        -> 2013/04/01 15:00:30 Unspecified
2013/04/01 15:00:30 Utc          -> 2013/04/01 15:00:30 Unspecified
2013/04/01 15:00:30 Unspecified  -> 2013/04/01 15:00:30 Unspecified

このメソッドは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インスタンスを作成する
using System;

class Sample {
  static void Main()
  {
    var dt = new DateTime(2013, 4, 1, 15, 0, 30, 123);
    var a = new DateTimeOffset(dt, TimeSpan.Zero);   // UTC+0の日時を指定
    var b = new DateTimeOffset(dt, new TimeSpan(-5, 0, 0));  // UTC-5(東部標準時)の日時を指定
    var c = new DateTimeOffset(dt, new TimeSpan(+9, 0, 0));  // UTC+9(日本標準時)の日時を指定
    var d = DateTimeOffset.Now;     // ローカル時刻での現在日時を取得
    var e = DateTimeOffset.UtcNow;  // UTCでの現在日時を取得

    foreach (var dto in new[] {a, b, c, d, e}) {
      Console.WriteLine("{0}, {1}", dto.DateTime, dto.Offset);
    }
  }
}
オフセット値を指定してDateTimeOffsetインスタンスを作成する
Imports System

Class Sample
  Shared Sub Main()
    Dim dt As New DateTime(2013, 4, 1, 15, 0, 30, 123)
    Dim a As New DateTimeOffset(dt, TimeSpan.Zero)   ' UTC+0の日時を指定
    Dim b As New DateTimeOffset(dt, new TimeSpan(-5, 0, 0))  ' UTC-5(東部標準時)の日時を指定
    Dim c As New DateTimeOffset(dt, new TimeSpan(+9, 0, 0))  ' UTC+9(日本標準時)の日時を指定
    Dim d As DateTimeOffset = DateTimeOffset.Now    ' ローカル時刻での現在日時を取得
    Dim e As DateTimeOffset = DateTimeOffset.UtcNow ' UTCでの現在日時を取得

    For Each dto As DateTimeOffset In New DateTimeOffset() {a, b, c, d, e}
      Console.WriteLine("{0}, {1}", dto.DateTime, dto.Offset)
    Next
  End Sub
End Class
実行結果例
2013/04/01 15:00:30, 00:00:00
2013/04/01 15:00:30, -05:00:00
2013/04/01 15:00:30, 09:00:00
2013/04/01 15:30:05, 09:00:00
2013/04/01 6:30:05, 00:00:00

DateTimeOffset.Parseメソッドで文字列から日時を読み取りDateTimeOffsetを作成する場合、文字列中にオフセット値が指定されていれば、その値がOffsetプロパティの値となります。

DateTimeOffset.Parseメソッドでオフセット表記のある/ない文字列からDateTimeOffsetに変換する
using System;

class Sample {
  static void Main()
  {
    var a = "2013-04-01T15:00:30";       // オフセット表記の無い日時
    var b = "2013-04-01T15:00:30-05:00"; // オフセット表記のある日時
    var c = "2013-04-01T15:00:30Z";      // UTCでの日時

    foreach (var s in new string[] {a, b, c}) {
      // 文字列からDateTimeOffsetに変換
      DateTimeOffset dto = DateTimeOffset.Parse(s);

      Console.WriteLine("{0}, {1}", dto.DateTime, dto.Offset);
    }
    Console.WriteLine();
  }
}
DateTimeOffset.Parseメソッドでオフセット表記のある/ない文字列からDateTimeOffsetに変換する
Imports System

Class Sample
  Shared Sub Main()
    Dim a As String = "2013-04-01T15:00:30"       ' オフセット表記の無い日時
    Dim b As String = "2013-04-01T15:00:30-05:00" ' オフセット表記のある日時
    Dim c As String = "2013-04-01T15:00:30Z"      ' UTCでの日時

    For Each s As String In New String() {a,b, c}
      ' 文字列からDateTimeOffsetに変換
      Dim dto As DateTimeOffset = DateTimeOffset.Parse(s)

      Console.WriteLine("{0}, {1}", dto.DateTime, dto.Offset)
    Next
    Console.WriteLine()
  End Sub
End Class
実行結果
2013/04/01 15:00:30, 09:00:00
2013/04/01 15:00:30, -05:00:00
2013/04/01 15:00:30, 09:00:00

Parseメソッドで文字列からDateTimeOffsetに変換する場合、オフセット表記がない場合はデフォルトではローカル時刻であるものとして変換されますが、オプションで変換の際にオフセット値をどう扱うかを変更することができます。 詳しくは日時・文字列の変換と書式 §.変換時のオプション (DateTimeStyles)で解説します。

ローカル時刻・UTCへの変換

DateTime・DateTimeOffsetの表す時刻をローカル時刻に変換するにはToLocalTimeメソッド、UTCに変換するにはToUniversalTimeメソッドを使うことが出来ます。 このメソッドは、インスタンスの表す時刻をローカル時刻・UTCに変換した結果を返します(元のインスタンスの状態は変化しません)。 DateTime・DateTimeOffsetではローカル時刻・UTCの表現に違いがあるため、時刻の変換を行う場合にもその動作に違いがあります。

DateTimeOffsetではローカル時刻・UTCへの変換の他に、特定のオフセット値での時刻に変換した値を取得するメソッドToOffsetを使うこともできます。

DateTime

DateTimeの表す時刻をToLocalTimeToUniversalTimeメソッドで変換する場合、変換結果はKindプロパティの値によって変わります。 例えば、既にローカル時刻であるとされている値に対してToLocalTimeメソッドを呼び出しても得られる値は元の時刻と変わりませんが、ToUniversalTimeメソッドを呼び出せばUTCに変換された時刻が得られます。 DateTime.Kindの値とToLocalTime・ToUniversalTimeメソッドの結果は次のようになります。

DateTime.Kindの値とToLocalTime・ToUniversalメソッドの呼び出し結果
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(日本標準時)が設定されている環境でのものです。

DateTime.ToLocalTime/ToUniversalTimeメソッドで時刻をローカル時刻・UTCに変換する
using System;

class Sample {
  static void Main()
  {
    var a = new DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Local);
    var b = new DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Utc);
    var c = new DateTime(2013, 4, 1, 15, 0, 30, 123); // DateTimeKind.Unspecified

    Console.WriteLine("[ToLocalTime]");
    foreach (var dt in new[] {a, b, c}) {
      var local = dt.ToLocalTime(); // 時刻をローカル時刻に変換

      Console.WriteLine("{0} {1,-12} -> {2} {3}", dt, dt.Kind, local, local.Kind);
    }
    Console.WriteLine();

    Console.WriteLine("[ToUniversalTime]");
    foreach (var dt in new[] {a, b, c}) {
      var utc = dt.ToUniversalTime(); // 時刻をUTCに変換

      Console.WriteLine("{0} {1,-12} -> {2} {3}", dt, dt.Kind, utc, utc.Kind);
    }
  }
}
DateTime.ToLocalTime/ToUniversalTimeメソッドで時刻をローカル時刻・UTCに変換する
Imports System

Class Sample
  Shared Sub Main()
    Dim a As New DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Local)
    Dim b As New DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Utc)
    Dim c As New DateTime(2013, 4, 1, 15, 0, 30, 123) ' DateTimeKind.Unspecified

    Console.WriteLine("[ToLocalTime]")
    For Each dt As DateTime In New DateTime() {a, b, c}
      Dim local As DateTime = dt.ToLocalTime() ' 時刻をローカル時刻に変換

      Console.WriteLine("{0} {1,-12} -> {2} {3}", dt, dt.Kind, local, local.Kind)
    Next
    Console.WriteLine()

    Console.WriteLine("[ToUniversalTime]")
    For Each dt As DateTime In New DateTime() {a, b, c}
      Dim utc As DateTime = dt.ToUniversalTime() ' 時刻をUTCに変換

      Console.WriteLine("{0} {1,-12} -> {2} {3}", dt, dt.Kind, utc, utc.Kind)
    Next
  End Sub
End Class
UTC+9での実行結果
[ToLocalTime]
2013/04/01 15:00:30 Local        -> 2013/04/01 15:00:30 Local
2013/04/01 15:00:30 Utc          -> 2013/04/02 0:00:30 Local
2013/04/01 15:00:30 Unspecified  -> 2013/04/02 0:00:30 Local

[ToUniversalTime]
2013/04/01 15:00:30 Local        -> 2013/04/01 6:00:30 Utc
2013/04/01 15:00:30 Utc          -> 2013/04/01 15:00:30 Utc
2013/04/01 15:00:30 Unspecified  -> 2013/04/01 6:00:30 Utc

日時の変換を行わずにKindプロパティの設定値のみを変更したい場合は、SpecifyKindメソッドを使います。

DateTimeOffset

DateTimeOffsetでは、LocalDateTimeおよびUtcDateTimeプロパティを参照することでローカル時刻・UTCでの値を取得することが出来ます。 ただし、このプロパティではDateTimeでの値が返されるため、オフセット情報が消失した値となります。 LocalDateTimeプロパティではDateTime.KindプロパティにDateTimeKind.Local、UtcDateTimeプロパティではDateTimeKind.UtcがセットされたDateTimeが返されます。

DateTimeOffset.LocalDateTime/UtcDateTimeプロパティでローカル時刻・UTCに変換されたDateTimeを取得する
using System;

class Sample {
  static void Main()
  {
    var dto = new DateTimeOffset(2013, 4, 1, 15, 0, 30, 123, new TimeSpan(-5, 0, 0)); // UTC-5(EST、東部標準時)での時刻

    Console.WriteLine(dto);
    Console.WriteLine(dto.LocalDateTime);
    Console.WriteLine(dto.UtcDateTime);
  }
}
DateTimeOffset.LocalDateTime/UtcDateTimeプロパティでローカル時刻・UTCに変換されたDateTimeを取得する
Imports System

Class Sample
  Shared Sub Main()
    Dim dto As New DateTimeOffset(2013, 4, 1, 15, 0, 30, 123, new TimeSpan(-5, 0, 0)) ' UTC-5(EST、東部標準時)での時刻

    Console.WriteLine(dto)
    Console.WriteLine(dto.LocalDateTime)
    Console.WriteLine(dto.UtcDateTime)
  End Sub
End Class
UTC+9での実行結果
2013/04/01 15:00:30 -05:00
2013/04/02 5:00:30
2013/04/01 20:00:30

DateTimeOffsetでも、DateTimeの場合と同様にToLocalTimeToUniversalTimeメソッドで時刻を変換することができます。 さらに、ToOffsetメソッドで任意のオフセット値に変更した時刻に変換することも出来ます。

ToLocalTimeメソッドが返すDateTimeOffsetのOffsetプロパティは当然ローカル時刻のオフセット値と同じになります。 同様にToUniversalTimeはOffsetが 00:00:00 のDateTimeOffsetを返し、ToOffsetは指定したオフセット値が設定されたDateTimeOffsetを返します。

以下の例は、オフセット値が異なる日時のDateTimeOffsetに対してToLocalTime・ToUniversalTime・ToOffsetの各メソッドを呼び出した結果の違いを表示するものです。 なお、実行結果はタイムゾーンにUTC+9(日本標準時)が設定されている環境でのものです。

DateTimeOffset.ToLocalTime/ToUniversalTime/ToOffsetメソッドでローカル時刻・UTC・指定したオフセット値に変換されたDateTimeOffsetを取得する
using System;

class Sample {
  static void Main()
  {
    var a = new DateTimeOffset(2013, 4, 1, 15, 0, 30, 123, new TimeSpan(+9, 0, 0));
    var b = new DateTimeOffset(2013, 4, 1, 15, 0, 30, 123, new TimeSpan(-5, 0, 0));
    var c = new DateTimeOffset(2013, 4, 1, 15, 0, 30, 123, TimeSpan.Zero);

    Console.WriteLine("[ToLocalTime]");
    foreach (var dto in new[] {a, b, c}) {
      var local = dto.ToLocalTime(); // 時刻をローカル時刻に変換

      Console.WriteLine("{0} -> {1}", dto, local);
    }
    Console.WriteLine();

    Console.WriteLine("[ToUniversalTime]");
    foreach (var dto in new[] {a, b, c}) {
      var utc = dto.ToUniversalTime();

      Console.WriteLine("{0} -> {1}", dto, utc); // 時刻をUTCに変換
    }
    Console.WriteLine();

    Console.WriteLine("[ToOffset]");
    foreach (var dto in new[] {a, b, c}) {
      var est = dto.ToOffset(new TimeSpan(-5, 0, 0)); // 時刻をUTC-5(EST、東部標準時)に変換

      Console.WriteLine("{0} -> {1}", dto, est);
    }
  }
}
DateTimeOffset.ToLocalTime/ToUniversalTime/ToOffsetメソッドでローカル時刻・UTC・指定したオフセット値に変換されたDateTimeOffsetを取得する
Imports System

Class Sample
  Shared Sub Main()
    Dim a As New DateTimeOffset(2013, 4, 1, 15, 0, 30, 123, new TimeSpan(+9, 0, 0))
    Dim b As New DateTimeOffset(2013, 4, 1, 15, 0, 30, 123, new TimeSpan(-5, 0, 0))
    Dim c As New DateTimeOffset(2013, 4, 1, 15, 0, 30, 123, TimeSpan.Zero)

    Console.WriteLine("[ToLocalTime]")
    For Each dto As DateTimeOffset In New DateTimeOffset() {a, b, c}
      Dim local As DateTimeOffset = dto.ToLocalTime() ' 時刻をローカル時刻に変換

      Console.WriteLine("{0} -> {1}", dto, local)
    Next
    Console.WriteLine()

    Console.WriteLine("[ToUniversalTime]")
    For Each dto As DateTimeOffset In New DateTimeOffset() {a, b, c}
      Dim utc As DateTimeOffset = dto.ToUniversalTime()

      Console.WriteLine("{0} -> {1}", dto, utc) ' 時刻をUTCに変換
    Next
    Console.WriteLine()

    Console.WriteLine("[ToOffset]")
    For Each dto As DateTimeOffset In New DateTimeOffset() {a, b, c}
      Dim est As DateTimeOffset = dto.ToOffset(new TimeSpan(-5, 0, 0)) ' 時刻をUTC-5(EST、東部標準時)に変換

      Console.WriteLine("{0} -> {1}", dto, est)
    Next
  End Sub
End Class
UTC+9での実行結果
[ToLocalTime]
2013/04/01 15:00:30 +09:00 -> 2013/04/01 15:00:30 +09:00
2013/04/01 15:00:30 -05:00 -> 2013/04/02 5:00:30 +09:00
2013/04/01 15:00:30 +00:00 -> 2013/04/02 0:00:30 +09:00

[ToUniversalTime]
2013/04/01 15:00:30 +09:00 -> 2013/04/01 6:00:30 +00:00
2013/04/01 15:00:30 -05:00 -> 2013/04/01 20:00:30 +00:00
2013/04/01 15:00:30 +00:00 -> 2013/04/01 15:00:30 +00:00

[ToOffset]
2013/04/01 15:00:30 +09:00 -> 2013/04/01 1:00:30 -05:00
2013/04/01 15:00:30 -05:00 -> 2013/04/01 15:00:30 -05:00
2013/04/01 15:00:30 +00:00 -> 2013/04/01 10:00:30 -05:00

ToLocalTimeメソッドでは、実行環境に設定されているタイムゾーンに従って夏時間等の時間調整を考慮した変換が行われます。 一方、ToOffsetメソッドでは、単に指定されたオフセット値への変換のみが行われ、夏時間等の考慮はされません。 夏時間等の調整が必要なタイムゾーンへの変換を行う場合は、TimeZoneInfo.ConvertTimeメソッドを使う必要があります。

TimeZoneInfo

TimeZoneInfoクラスは、.NET Framework 3.5から導入されたクラスです。 TimeZoneInfoでは特定のタイムゾーンに関する情報を扱い、日時をそのタイムゾーンでのものに変換するメソッドなどが用意されています。

タイムゾーンとその情報の取得

TimeZoneInfoクラスでは、タイムゾーンの名称(StandardNameプロパティ)、タイムゾーンにおける標準時とUTCとの差(オフセット値、BaseUtcOffsetプロパティ)、タイムゾーンに夏時間が存在するかどうか(SupportsDaylightSavingTimeプロパティ)、といった情報を参照することが出来ます。

次の例では、実行環境に設定されているタイムゾーンをLocalプロパティで取得し、その情報を表示しています。

TimeZoneInfo.Localプロパティでローカルタイムゾーンに関する情報を取得する
using System;

class Sample {
  static void Main()
  {
    // ローカルタイムゾーンの情報を取得
    var local = TimeZoneInfo.Local;

    Console.WriteLine("StandardName: {0}", local.StandardName);
    Console.WriteLine("DisplayName: {0}", local.DisplayName);
    Console.WriteLine("BaseUtcOffset: {0}", local.BaseUtcOffset);
    Console.WriteLine("SupportsDaylightSavingTime: {0}", local.SupportsDaylightSavingTime);
  }
}
TimeZoneInfo.Localプロパティでローカルタイムゾーンに関する情報を取得する
Imports System

Class Sample
  Shared Sub Main()
    ' ローカルタイムゾーンの情報を取得
    Dim local As TimeZoneInfo = TimeZoneInfo.Local

    Console.WriteLine("StandardName: {0}", local.StandardName)
    Console.WriteLine("DisplayName: {0}", local.DisplayName)
    Console.WriteLine("BaseUtcOffset: {0}", local.BaseUtcOffset)
    Console.WriteLine("SupportsDaylightSavingTime: {0}", local.SupportsDaylightSavingTime)
  End Sub
End Class
タイムゾーンが「大阪、札幌、東京」での実行結果例
StandardName: 東京 (標準時)
DisplayName: (UTC+09:00) 大阪、札幌、東京
BaseUtcOffset: 09:00:00
SupportsDaylightSavingTime: False
Asia/Tokyoでの実行結果例
StandardName: 日本標準時
DisplayName: 日本標準時
BaseUtcOffset: 09:00:00
SupportsDaylightSavingTime: True
Asia/Tokyoでの実行結果例
StandardName: JST
DisplayName: JST
BaseUtcOffset: 09:00:00
SupportsDaylightSavingTime: True

ローカルタイムゾーン以外を表すTimeZoneInfoを取得したい場合は、FindSystemTimeZoneByIdメソッドに目的のタイムゾーンのIDを指定して取得します。 Windowsでは、レジストリに格納されている情報を元にTimeZoneInfoが取得されます。 FindSystemTimeZoneByIdメソッドでは、該当するタイムゾーンが見つからない場合、例外TimeZoneNotFoundExceptionがスローされます。

このほか、Localプロパティと同様、Utcプロパティを参照すれば、UTCのタイムゾーンを表すTimeZoneInfoを取得することが出来ます。

TimeZoneInfo.FindSystemTimeZoneByIdメソッドで特定のタイムゾーンに関する情報を取得する
using System;

class Sample {
  static void Main()
  {
    var timezones = new[] {
      TimeZoneInfo.Local,
      TimeZoneInfo.Utc,
    };

    foreach (var tz in timezones) {
      Console.WriteLine("{0} ({1}) {2}", tz.StandardName, tz.BaseUtcOffset, tz.DisplayName);
    }
    Console.WriteLine();

    // IDからTimeZoneInfoを取得
    var ids = new[] {
      "JST", "Tokyo Standard Time", "Asia/Tokyo",
      "EST", "Eastern Standard Time", "America/New_York",
      "GMT", "GMT Standard Time", "Europe/London",
    };

    foreach (var id in ids) {
      Console.Write("{0,-25} -> ", id);

      try {
        var tz = TimeZoneInfo.FindSystemTimeZoneById(id);

        Console.WriteLine("{0} ({1}) {2}", tz.StandardName, tz.BaseUtcOffset, tz.DisplayName);
      }
      catch (TimeZoneNotFoundException) {
        Console.WriteLine("(time zone not found)");
      }
    }
  }
}
TimeZoneInfo.FindSystemTimeZoneByIdメソッドで特定のタイムゾーンに関する情報を取得する
Imports System

Class Sample
  Shared Sub Main()
    Dim timezones() As TimeZoneInfo = New TimeZoneInfo() { _
      TimeZoneInfo.Local, _
      TimeZoneInfo.Utc _
    }

    For Each tz In timezones
      Console.WriteLine("{0} ({1}) {2}", tz.StandardName, tz.BaseUtcOffset, tz.DisplayName)
    Next
    Console.WriteLine()

    ' IDからTimeZoneInfoを取得
    Dim ids() As String = New String() { _
      "JST", "Tokyo Standard Time", "Asia/Tokyo", _
      "EST", "Eastern Standard Time", "America/New_York", _
      "GMT", "GMT Standard Time", "Europe/London" _
    }

    For Each id As String In ids
      Console.Write("{0,-25} -> ", id)

      Try
        Dim tz As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(id)

        Console.WriteLine("{0} ({1}) {2}", tz.StandardName, tz.BaseUtcOffset, tz.DisplayName)
      Catch ex As TimeZoneNotFoundException
        Console.WriteLine("(time zone not found)")
      End Try
    Next
  End Sub
End Class
タイムゾーンが「大阪、札幌、東京」での実行結果例
東京 (標準時) (09:00:00) (UTC+09:00) 大阪、札幌、東京
UTC (00:00:00) UTC

JST                       -> (time zone not found)
Tokyo Standard Time       -> 東京 (標準時) (09:00:00) (UTC+09:00) 大阪、札幌、東京
Asia/Tokyo                -> (time zone not found)
EST                       -> (time zone not found)
Eastern Standard Time     -> 東部標準時 (-05:00:00) (UTC-05:00) 東部標準時 (米国およびカナダ)
America/New_York          -> (time zone not found)
GMT                       -> (time zone not found)
GMT Standard Time         -> GMT 標準時 (00:00:00) (UTC+00:00) ダブリン、エジンバラ、リスボン、ロンドン
Europe/London             -> (time zone not found)
Asia/Tokyoでの実行結果例
日本標準時 (09:00:00) 日本標準時
UTC (00:00:00) UTC

JST                       -> (time zone not found)
Tokyo Standard Time       -> (time zone not found)
Asia/Tokyo                -> 日本標準時 (09:00:00) 日本標準時
EST                       -> GMT-05:00 (-05:00:00) GMT-05:00
Eastern Standard Time     -> (time zone not found)
America/New_York          -> アメリカ東部標準時 (-05:00:00) アメリカ東部標準時
GMT                       -> グリニッジ標準時 (00:00:00) グリニッジ標準時
GMT Standard Time         -> (time zone not found)
Europe/London             -> グリニッジ標準時 (00:00:00) グリニッジ標準時
Asia/Tokyoでの実行結果例
JST (09:00:00) JST
UTC (00:00:00) UTC

JST                       -> (time zone not found)
Tokyo Standard Time       -> (time zone not found)
Asia/Tokyo                -> JST (09:00:00) JST
EST                       -> EST (-05:00:00) EST
Eastern Standard Time     -> (time zone not found)
America/New_York          -> EST (-05:00:00) EST
GMT                       -> GMT (00:00:00) GMT
GMT Standard Time         -> (time zone not found)
Europe/London             -> GMT (00:00:00) GMT

FindSystemTimeZoneByIdメソッドでは、システムに保存されているタイムゾーン情報の形式の違いにより、WindowsではJSTTokyo Standard Timeといったタイムゾーン名を、Linux等ではAsia/Tokyoといった地域/都市名の表記を指定する必要があります。

システムで使用可能なすべてのタイムゾーンを取得するには、GetSystemTimeZonesメソッドを使います。

日時の変換

ConvertTimeメソッドを使うと、DateTime・DateTimeOffsetの値を異なるタイムゾーンでの日時に変換することができます。

DateTimeOffset.Offsetメソッドではオフセット値の変更はできますが、この際、夏時間などタイムゾーン内での時間調整は一切行われません。 一方TimeZoneInfo.ConvertTimeメソッドでは、変換に際してそのタイムゾーン内での時刻の変換規則に基づいた時間調整が行われます。 DateTimeOffsetだけでなく、DateTimeに対しても同様に時間調整が行われます。

TimeZoneInfo.ConvertTimeメソッドでDateTime・DateTimeOffsetを他のタームゾーンでの日時に変換する
using System;

class Sample {
  static void Main()
  {
    // 東部標準時のTimeZoneInfoを取得
    var est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");

    // ローカルでの冬期・夏期の日付 (DateTime)
    var dtWinter = new DateTime(2013, 1, 1, 12, 0, 0, DateTimeKind.Local);
    var dtSummer = new DateTime(2013, 7, 1, 12, 0, 0, DateTimeKind.Local);

    Console.WriteLine("{0} -> {1} {2}",
                      dtWinter,
                      est.IsDaylightSavingTime(dtWinter) ? est.DaylightName : est.StandardName,
                      TimeZoneInfo.ConvertTime(dtWinter, est));
    Console.WriteLine("{0} -> {1} {2}",
                      dtSummer,
                      est.IsDaylightSavingTime(dtSummer) ? est.DaylightName : est.StandardName,
                      TimeZoneInfo.ConvertTime(dtSummer, est));
    Console.WriteLine();

    // ローカルでの冬期・夏期の日付 (DateTimeOffset)
    var dtoWinter = new DateTimeOffset(2013, 1, 1, 12, 0, 0, TimeZoneInfo.Local.BaseUtcOffset);
    var dtoSummer = new DateTimeOffset(2013, 7, 1, 12, 0, 0, TimeZoneInfo.Local.BaseUtcOffset);

    Console.WriteLine("{0} -> {1} {2}",
                      dtoWinter,
                      est.IsDaylightSavingTime(dtoWinter) ? est.DaylightName : est.StandardName,
                      TimeZoneInfo.ConvertTime(dtoWinter, est));
    Console.WriteLine("{0} -> {1} {2}",
                      dtoSummer,
                      est.IsDaylightSavingTime(dtoSummer) ? est.DaylightName : est.StandardName,
                      TimeZoneInfo.ConvertTime(dtoSummer, est));
  }
}
TimeZoneInfo.ConvertTimeメソッドでDateTime・DateTimeOffsetを他のタームゾーンでの日時に変換する
Imports System

Class Sample
  Shared Sub Main()
    ' 東部標準時のTimeZoneInfoを取得
    Dim est As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")

    ' ローカルでの冬期・夏期の日付 (DateTime)
    Dim dtWinter As New DateTime(2013, 1, 1, 12, 0, 0, DateTimeKind.Local)
    Dim dtSummer As New DateTime(2013, 7, 1, 12, 0, 0, DateTimeKind.Local)

    Console.WriteLine("{0} -> {1} {2}", _
                      dtWinter, _
                      IIf(est.IsDaylightSavingTime(dtWinter), est.DaylightName, est.StandardName), _
                      TimeZoneInfo.ConvertTime(dtWinter, est))
    Console.WriteLine("{0} -> {1} {2}", _
                      dtSummer, _
                      IIf(est.IsDaylightSavingTime(dtSummer), est.DaylightName, est.StandardName), _
                      TimeZoneInfo.ConvertTime(dtSummer, est))
    Console.WriteLine()

    ' ローカルでの冬期・夏期の日付 (DateTimeOffset)
    Dim dtoWinter As New DateTimeOffset(2013, 1, 1, 12, 0, 0, TimeZoneInfo.Local.BaseUtcOffset)
    Dim dtoSummer As New DateTimeOffset(2013, 7, 1, 12, 0, 0, TimeZoneInfo.Local.BaseUtcOffset)

    Console.WriteLine("{0} -> {1} {2}", _
                      dtoWinter, _
                      IIf(est.IsDaylightSavingTime(dtoWinter), est.DaylightName, est.StandardName), _
                      TimeZoneInfo.ConvertTime(dtoWinter, est))
    Console.WriteLine("{0} -> {1} {2}", _
                      dtoSummer, _
                      IIf(est.IsDaylightSavingTime(dtoSummer), est.DaylightName, est.StandardName), _
                      TimeZoneInfo.ConvertTime(dtoSummer, est))
  End Sub
End Class
日本標準時が設定されている環境での実行結果
2013/01/01 12:00:00 -> 東部標準時 2012/12/31 22:00:00
2013/07/01 12:00:00 -> 東部夏時間 2013/06/30 23:00:00

2013/01/01 12:00:00 +09:00 -> 東部標準時 2012/12/31 22:00:00 -05:00
2013/07/01 12:00:00 +09:00 -> 東部夏時間 2013/06/30 23:00:00 -04:00

あるタイムゾーンにて日時が夏時間の期間中かどうかを調べるには、IsDaylightSavingTimeメソッドが使えます。


さらに、ConvertTimeBySystemTimeZoneIdメソッドを使えば、目的のタイムゾーンを表すTimeZoneInfoの取得を省略することが出来ます。 このメソッドは、FindSystemTimeZoneByIdメソッドとConvertTimeメソッドの組み合わせと同等であるため、呼び出しが1度であるならこちらのメソッドを使った方が記述が少なくなります。

TimeZoneInfo.ConvertTimeBySystemTimeZoneIdメソッドでDateTime・DateTimeOffsetを他のタームゾーンでの日時に変換する
using System;

class Sample {
  static void Main()
  {
    // 東部標準時のID
    const string est = "Eastern Standard Time";

    // ローカルでの冬期・夏期の日付 (DateTime)
    var dtWinter = new DateTime(2013, 1, 1, 12, 0, 0, DateTimeKind.Local);
    var dtSummer = new DateTime(2013, 7, 1, 12, 0, 0, DateTimeKind.Local);

    Console.WriteLine("{0} -> {1}",
                      dtWinter,
                      TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dtWinter, est));
    Console.WriteLine("{0} -> {1}",
                      dtSummer,
                      TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dtSummer, est));
    Console.WriteLine();

    // ローカルでの冬期・夏期の日付 (DateTimeOffset)
    var dtoWinter = new DateTimeOffset(2013, 1, 1, 12, 0, 0, TimeZoneInfo.Local.BaseUtcOffset);
    var dtoSummer = new DateTimeOffset(2013, 7, 1, 12, 0, 0, TimeZoneInfo.Local.BaseUtcOffset);

    Console.WriteLine("{0} -> {1}",
                      dtoWinter,
                      TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dtoWinter, est));
    Console.WriteLine("{0} -> {1}",
                      dtoSummer,
                      TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dtoSummer, est));
  }
}
TimeZoneInfo.ConvertTimeBySystemTimeZoneIdメソッドでDateTime・DateTimeOffsetを他のタームゾーンでの日時に変換する
Imports System

Class Sample
  Shared Sub Main()
    ' 東部標準時のID
    Const est As String = "Eastern Standard Time"

    ' ローカルでの冬期・夏期の日付 (DateTime)
    Dim dtWinter As New DateTime(2013, 1, 1, 12, 0, 0, DateTimeKind.Local)
    Dim dtSummer As New DateTime(2013, 7, 1, 12, 0, 0, DateTimeKind.Local)

    Console.WriteLine("{0} -> {1}", _
                      dtWinter, _
                      TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dtWinter, est))
    Console.WriteLine("{0} -> {1}", _
                      dtSummer, _
                      TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dtSummer, est))
    Console.WriteLine()

    ' ローカルでの冬期・夏期の日付 (DateTimeOffset)
    Dim dtoWinter As New DateTimeOffset(2013, 1, 1, 12, 0, 0, TimeZoneInfo.Local.BaseUtcOffset)
    Dim dtoSummer As New DateTimeOffset(2013, 7, 1, 12, 0, 0, TimeZoneInfo.Local.BaseUtcOffset)

    Console.WriteLine("{0} -> {1}", _
                      dtoWinter, _
                      TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dtoWinter, est))
    Console.WriteLine("{0} -> {1}", _
                      dtoSummer, _
                      TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dtoSummer, est))
  End Sub
End Class
日本標準時が設定されている環境での実行結果
2013/01/01 12:00:00 -> 2012/12/31 22:00:00
2013/07/01 12:00:00 -> 2013/06/30 23:00:00

2013/01/01 12:00:00 +09:00 -> 2012/12/31 22:00:00 -05:00
2013/07/01 12:00:00 +09:00 -> 2013/06/30 23:00:00 -04:00