DateTime、DateTimeOffset、および TimeZoneInfo の使い分け

更新 : 2007 年 11 月

日付と時刻の情報を使用する .NET Framework アプリケーションには多様性があり、さまざまな方法でその情報を使用できます。日付と時刻の情報は、通常、次のような方法で使用されます。

  • 日付のみを反映します。この場合、時刻の情報は重要ではありません。

  • 時刻のみを反映します。この場合、日付の情報は重要ではありません。

  • 特定の時刻や場所に拘束されない概念上の日付と時刻を示します (国際的にチェーン展開している店舗の大部分が平日の午前 9 時に開店する、というような例が挙げられます)。

  • .NET Framework の外部にあるソースから日付と時刻の情報を取得します。この場合、日付と時刻の情報は単純データ型で格納されることが一般的です。

  • ある時刻を一意的かつ明確に特定します。アプリケーションの中には、ホスト システムでのみ明確な日時情報を必要とするものもありますし、複数のシステム間で明確な日時情報を必要とするものもあります (あるシステムでシリアル化された日付は、有意に逆シリアル化でき、世界中のどのシステムでも使用できます)。

  • 関連する複数の時刻を保存します (Web 要求に関する、要求元の現地時刻とサーバー側での受信時刻など)。

  • 日付と時刻の演算を実行します。ある時刻が一意的かつ明確に特定されることがあります。

.NET Framework には、DateTime 型、DateTimeOffset 型、および TimeZoneInfo 型が用意されています。これらの型は、日付や時刻を操作するアプリケーションのビルドする際に使用できます。

メモ :

このトピックでは、4 番目の型である TimeZone については説明しません。この型の大部分の機能は、TimeZoneInfo クラスに組み込まれています。可能な限り、TimeZone クラスの代わりに TimeZoneInfo クラスを使用してください。

DateTime 構造体

DateTime の値は、特定の日時を定義します。.NET Framework Version 2.0 以降では、その日付と時刻が属するタイム ゾーンについての制限された情報を提供する Kind プロパティが含まれるようになりました。Kind プロパティによって返される DateTimeKind 値は、DateTime 値が現地時刻 (DateTimeKind.Local)、世界協定時刻 (UTC: Coordinated Universal Time) (DateTimeKind.Utc)、タイム ゾーンが指定されていない時刻 (DateTimeKind.Unspecified) のどの時刻を指すかを示します。

DateTime 構造体は、次のような処理を実行するアプリケーションに適しています。

  • 日付のみを操作するアプリケーション。

  • 時刻のみを操作するアプリケーション。

  • 概念上の日付と時刻を操作するアプリケーション。

  • .NET Framework の外部にあるソース (たとえば、SQL データベースなど) から日付と時刻の情報を取得するアプリケーション。一般に、これらのソースでは、DateTime 構造体と互換性のある単純な形式で日付と時刻の情報が格納されています。

  • 一般的な結果に関連する、日付と時刻の演算を実行するアプリケーション。たとえば、特定の日時に 6 か月を追加する加算では、多くの場合、求められた結果を夏時間に調整するかどうかは重要ではありません。

特定の DateTime 値が UTC を表す場合を除き、多くの場合、日付と時刻の値は不明確であるか、その移植性が限定的です。たとえば、DateTime 値が現地時刻を表す場合、ローカル タイム ゾーン内では移植性があります (同じタイム ゾーン内の別のシステムで値を逆シリアル化しても、その値はある時刻を明確に特定します)。ローカル タイム ゾーンの範囲外では、その DateTime 値は多様に解釈できます。値の Kind プロパティが DateTimeKind.Unspecified である場合、その移植性は低くなります。同じタイム ゾーン内であっても、また同じシステム上であっても、最初にシリアル化された時点で不明確となります。DateTime 値が UTC であることを表す場合のみ、使用されるシステムやタイム ゾーンに関係なく、その値はある時刻を明確に特定します。

重要 :

DateTime データを保存または共有するときは、UTC を使用し、DateTime 値の Kind プロパティを DateTimeKindUTC() に設定する必要があります。

DateTimeOffset 構造体

DateTimeOffset 構造体は、UTC との値の差異を示すオフセットを使用して、日付と時刻の値を表します。したがって、この値は常にある時刻を明確に特定します。

DateTimeOffset 型には DateTime 型の大部分の機能が含まれますが、アプリケーション開発において DateTime 型の代わりに使用することを意図されたものではありません。これは、次のような処理を実行するアプリケーションに適しています。

  • ある時刻を一意的かつ明確に特定するアプリケーション。DateTimeOffset 型を使用して、"現在" の意味の明確な定義、トランザクション時刻のログ記録、システム イベント時刻やアプリケーション イベント時刻のログ記録、ファイルの作成時刻や変更時刻の記録などを行うことができます。

  • 一般的な日付と時刻の演算を実行するアプリケーション。

  • 関連する複数の時刻を保存するアプリケーション。2 つの別個の値として、または構造体の 2 つのメンバとして時刻が格納されている場合に限り保存されます。

メモ :

このように DateTimeOffset 値を使用するケースの方が、DateTime 値を使用するよりもはるかに一般的です。このことから、アプリケーション開発における既定の日付時刻型は DateTimeOffset であると見なすことができます。

DateTimeOffset 値は特定のタイム ゾーンには拘束されず、さまざまなタイム ゾーンから作成できます。例として、以下に、多くの DateTimeOffset 値 (ローカルな太平洋標準時を含む) が属することのできるタイム ゾーンを一覧します。

Imports System.Collections.ObjectModel

Module TimeOffsets
   Public Sub Main()
      Dim thisTime As DateTimeOffset 

      thisTime = New DateTimeOffset(#06/10/2007#, New TimeSpan(-7, 0, 0))
      ShowPossibleTimeZones(thisTime) 

      thisTime = New DateTimeOffset(#03/10/2007#, New TimeSpan(-6, 0, 0))  
      ShowPossibleTimeZones(thisTime)

      thisTime = New DateTimeOffset(#03/10/2007#, New TimeSpan(+1, 0, 0))
      ShowPossibleTimeZones(thisTime)
   End Sub

   Private Sub ShowPossibleTimeZones(offsetTime As DateTimeOffset)
      Dim offset As TimeSpan = offsetTime.Offset
      Dim timeZones As ReadOnlyCollection(Of TimeZoneInfo)

      Console.WriteLine("{0} could belong to the following time zones:", _
                        offsetTime.ToString())
      ' Get all time zones defined on local system
      timeZones = TimeZoneInfo.GetSystemTimeZones()     
      ' Iterate time zones
      For Each timeZone As TimeZoneInfo In timeZones
         ' Compare offset with offset for that date in that time zone
         If timeZone.GetUtcOffset(offsetTime.DateTime).Equals(offset) Then
            Console.WriteLine("   {0}", timeZone.DisplayName)
         End If   
      Next
      Console.WriteLine()
   End Sub
End Module
' This example displays the following output to the console:
'       6/10/2007 12:00:00 AM -07:00 could belong to the following time zones:
'          (GMT-07:00) Arizona
'          (GMT-08:00) Pacific Time (US & Canada)
'          (GMT-08:00) Tijuana, Baja California
'       
'       3/10/2007 12:00:00 AM -06:00 could belong to the following time zones:
'          (GMT-06:00) Central America
'          (GMT-06:00) Central Time (US & Canada)
'          (GMT-06:00) Guadalajara, Mexico City, Monterrey - New
'          (GMT-06:00) Guadalajara, Mexico City, Monterrey - Old
'          (GMT-06:00) Saskatchewan
'       
'       3/10/2007 12:00:00 AM +01:00 could belong to the following time zones:
'          (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
'          (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
'          (GMT+01:00) Brussels, Copenhagen, Madrid, Paris
'          (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb
'          (GMT+01:00) West Central Africa
using System;
using System.Collections.ObjectModel;

public class TimeOffsets
{
   public static void Main()
   {
      DateTime thisDate = new DateTime(2007, 3, 10, 0, 0, 0);
      DateTime dstDate = new DateTime(2007, 6, 10, 0, 0, 0);
      DateTimeOffset thisTime;

      thisTime = new DateTimeOffset(dstDate, new TimeSpan(-7, 0, 0));
      ShowPossibleTimeZones(thisTime);

      thisTime = new DateTimeOffset(thisDate, new TimeSpan(-6, 0, 0));  
      ShowPossibleTimeZones(thisTime);

      thisTime = new DateTimeOffset(thisDate, new TimeSpan(+1, 0, 0));
      ShowPossibleTimeZones(thisTime);
   }

   private static void ShowPossibleTimeZones(DateTimeOffset offsetTime)
   {
      TimeSpan offset = offsetTime.Offset;
      ReadOnlyCollection<TimeZoneInfo> timeZones;

      Console.WriteLine("{0} could belong to the following time zones:", 
                        offsetTime.ToString());
      // Get all time zones defined on local system
      timeZones = TimeZoneInfo.GetSystemTimeZones();     
      // Iterate time zones 
      foreach (TimeZoneInfo timeZone in timeZones)
      {
         // Compare offset with offset for that date in that time zone
         if (timeZone.GetUtcOffset(offsetTime.DateTime).Equals(offset))
            Console.WriteLine("   {0}", timeZone.DisplayName);
      }
      Console.WriteLine();
   } 
}
// This example displays the following output to the console:
//       6/10/2007 12:00:00 AM -07:00 could belong to the following time zones:
//          (GMT-07:00) Arizona
//          (GMT-08:00) Pacific Time (US & Canada)
//          (GMT-08:00) Tijuana, Baja California
//       
//       3/10/2007 12:00:00 AM -06:00 could belong to the following time zones:
//          (GMT-06:00) Central America
//          (GMT-06:00) Central Time (US & Canada)
//          (GMT-06:00) Guadalajara, Mexico City, Monterrey - New
//          (GMT-06:00) Guadalajara, Mexico City, Monterrey - Old
//          (GMT-06:00) Saskatchewan
//       
//       3/10/2007 12:00:00 AM +01:00 could belong to the following time zones:
//          (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
//          (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
//          (GMT+01:00) Brussels, Copenhagen, Madrid, Paris
//          (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb
//          (GMT+01:00) West Central Africa

出力では、この例の日付と時刻の値は少なくとも 3 つの異なるタイム ゾーンに属することができることを示しています。6/10/2007 という DateTimeOffset 値は、日付と時刻の値が夏時間を表す場合に、UTC からのオフセットが元のタイム ゾーンのベース UTC オフセットや表示名にある UTC からのオフセットに必ずしも対応しないことを示します。つまり、単一の DateTimeOffset 値がタイム ゾーンと密に結合されているわけではないため、タイム ゾーンの夏時間への移行と夏時間からの移行を反映できません。これは、日付と時刻の演算が DateTimeOffset 値の操作に使用される場合に、特に問題になる可能性があります (タイム ゾーンの調整規則を考慮して日付と時刻の演算を実行する方法については、「日付と時刻を使用した算術演算の実行」を参照してください)。

TimeZoneInfo クラス

TimeZoneInfo クラスは、地球上のすべてのタイム ゾーンを表し、あるタイム ゾーンの日付と時刻を別のタイム ゾーンの日付と時刻に変換することができます。TimeZoneInfo クラスは、ある時刻を明確に特定するように、日付と時刻を操作できます。TimeZoneInfo クラスも拡張可能です。Windows システムで提供され、レジストリで定義されているタイム ゾーン情報に依存しますが、カスタム タイム ゾーンの作成をサポートしています。タイム ゾーン情報のシリアル化と逆シリアル化もサポートしています。

TimeZoneInfo クラスを最大限に活用すると、開発作業の負荷を高めることになる場合があります。まず、日付と時刻の値は、それらの値が属するタイム ゾーンと密に結合されていません。結果的に、日付と時刻をその関連付けられたタイム ゾーンにリンクするメカニズムを提供するアプリケーションを使用しない限り、特定の日付と時刻の値とタイム ゾーンの関連付けは、容易に解除されます (この情報をリンクするための 1 つの方法として、日付と時刻の両方の値および関連付けられたタイム ゾーン オブジェクトを含む、クラスまたは構造体を定義する方法があります)。次に、Windows XP およびそれ以前のバージョンの Windows では、過去のタイム ゾーンの履歴情報が実質的にサポートされていません。Windows Vista では、限定的なサポートのみ提供されています。過去のタイム ゾーンの履歴情報を扱うように設計されているアプリケーションでは、カスタム タイム ゾーンを十分に利用する必要があります。

.NET Framework でのタイム ゾーン サポートは、日付時刻オブジェクトをインスタンス化するときに日付と時刻の値が属するタイム ゾーンが明確である場合にのみ活用できます。多くの場合 (特に Web アプリケーションやネットワーク アプリケーションでは)、この条件は該当しません。

参照

その他の技術情報

時刻とタイム ゾーン