次の方法で共有


DateTime、DateOnly、DateTimeOffset、TimeSpan、TimeOnly、TimeZoneInfo から選択する

.NET アプリケーションでは、いくつかの方法で日付と時刻の情報を使用できます。 日付と時刻の情報のより一般的な用途は次のとおりです。

  • 時刻情報が重要でないように、日付のみを反映する。
  • 日付情報が重要でないように、時刻のみを反映する。
  • 特定の時刻と場所に関連付けられていない抽象的な日時を反映するため (たとえば、平日の午前 9 時に営業している国際チェーンのほとんどの店舗)。
  • .NET の外部のソースから日付と時刻の情報を取得するには、通常、日付と時刻の情報が単純なデータ型で格納されます。
  • 1 つの時点を一意かつ明確に識別するため。 一部のアプリケーションでは、ホスト システムでのみ日付と時刻を明確にする必要があります。 他のアプリでは、システム間で明確である必要があります (つまり、あるシステムでシリアル化された日付を意味のある方法で逆シリアル化し、世界中の別のシステムで使用できます)。
  • 複数の関連時刻 (要求元の現地時刻や Web 要求のサーバーの受信時刻など) を保持するため。
  • 日付と時刻の算術演算を実行するには、1 つの時点を一意かつ明確に識別する結果が含まれる可能性があります。

.NET には、 DateTimeDateOnlyDateTimeOffsetTimeSpanTimeOnly、および TimeZoneInfo の型が含まれています。これらはすべて、日付と時刻を操作するアプリケーションを構築するために使用できます。

この記事では、TimeZoneInfo クラスにほぼ完全に組み込まれているため、TimeZoneについては説明しません。 可能な限り、TimeZone クラスの代わりに TimeZoneInfo クラスを使用します。

DateTimeOffset 構造体

DateTimeOffset構造体は、日付と時刻の値と、その値が UTC とどの程度異なるかを示すオフセットを表します。 したがって、値は常に 1 つの時点を明確に識別します。

DateTimeOffset型には、タイム ゾーン認識と共にDateTime型のすべての機能が含まれます。 これにより、次のアプリケーションに適しています。

  • 1 つの時点を一意かつ明確に識別します。 DateTimeOffsetの種類を使用すると、"now" の意味を明確に定義したり、トランザクション時間をログに記録したり、システムまたはアプリケーション イベントの時刻をログに記録したり、ファイルの作成と変更の時間を記録したりできます。
  • 一般的な日付と時刻の算術演算を実行します。
  • これらの時間が 2 つの個別の値または構造体の 2 つのメンバーとして格納されている限り、複数の関連する時間を保持します。

これらの DateTimeOffset 値の使用は、 DateTime 値の場合よりもはるかに一般的です。 その結果、アプリケーション開発の既定の日付と時刻の種類として DateTimeOffset を検討してください。

DateTimeOffset値は特定のタイム ゾーンに関連付けられませんが、さまざまなタイム ゾーンから取得できます。 次の例では、複数の DateTimeOffset 値 (ローカルの太平洋標準時を含む) が属できるタイム ゾーンを一覧表示します。

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($"{offsetTime.ToString()} could belong to the following time zones:");
        // 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($"   {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
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

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

DateTime 構造体

DateTime値は、特定の日時を定義します。 これには、その日付と時刻が属するタイム ゾーンに関する限られた情報を提供する Kind プロパティが含まれています。 Kind プロパティによって返されるDateTimeKind値は、DateTime値が現地時刻 (DateTimeKind.Local)、協定世界時 (UTC) (DateTimeKind.Utc)、または指定されていない時刻 (DateTimeKind.Unspecified) を表すかどうかを示します。

DateTime構造は、次の 1 つ以上の特性を持つアプリケーションに適しています。

  • 抽象的な日付と時刻を操作します。
  • タイム ゾーン情報が不足している日付と時刻を操作します。
  • UTC の日付と時刻のみを操作します。
  • 日付と時刻の算術演算を実行しますが、一般的な結果には関係します。 たとえば、特定の日時に 6 か月を加算する加算操作では、夏時間に合わせて結果を調整するかどうかは重要ではないことがよくあります。

特定の DateTime 値が UTC を表さない限り、多くの場合、その日付と時刻の値はあいまいであるか、移植性に制限されます。 たとえば、 DateTime 値が現地時刻を表す場合、そのローカル タイム ゾーン内で移植可能です (つまり、値が同じタイム ゾーン内の別のシステムで逆シリアル化されている場合、その値は単一の時点を明確に識別します)。 ローカル タイム ゾーンの外部では、その DateTime 値に複数の解釈を含めることができます。 値の Kind プロパティが DateTimeKind.Unspecified場合、移植性はさらに低くなります。同じタイム ゾーン内であいまいになり、最初にシリアル化されたのと同じシステム上でもあいまいになる可能性があります。 DateTime値が UTC を表す場合にのみ、その値は、その値が使用されているシステムまたはタイム ゾーンに関係なく、1 つの時点を明確に識別します。

Von Bedeutung

DateTimeデータを保存または共有する場合は、UTC を使用し、DateTime値のKindプロパティをDateTimeKind.Utcに設定します。

DateOnly 構造体

DateOnly構造体は、時刻のない特定の日付を表します。 時刻コンポーネントがないため、その日の開始日から終了日までの日付を表します。 この構造は、生年月日、記念日、休日、ビジネス関連の日付など、特定の日付を格納するのに最適です。

時間コンポーネントを無視しながらDateTimeを使用することもできますが、DateTimeよりもDateOnlyを使用する利点がいくつかあります。

  • DateTime構造体は、タイム ゾーンによってオフセットされている場合、前または翌日にロールインする可能性があります。 DateOnly タイム ゾーンでオフセットすることはできません。また、常に設定された日付を表します。
  • DateTime構造体のシリアル化には、データの意図を隠す可能性がある時間コンポーネントが含まれます。 また、 DateOnly ではシリアル化されるデータが少なくなります。
  • コードが SQL Server などのデータベースとやり取りする場合、一般に、日付全体が date データ型として格納され、時刻は含まれません。 DateOnly は、データベースの種類に一致します。

DateOnlyの詳細については、「DateOnly 構造体と TimeOnly 構造体の使用方法」を参照してください。

Von Bedeutung

DateOnly は .NET Framework では使用できません。

TimeSpan 構造体

TimeSpan構造体は時間間隔を表します。 その 2 つの一般的な用途は次のとおりです。

  • 2 つの日付と時刻の値の間の時間間隔を反映しています。 たとえば、ある DateTime 値を別の値から減算すると、 TimeSpan 値が返されます。
  • 経過時間の測定。 たとえば、Stopwatch.Elapsed プロパティは、経過時間の測定を開始する Stopwatch メソッドの呼び出しから経過した時間間隔を反映するTimeSpan値を返します。

TimeSpan値は、その値が特定の日を参照しない時間を反映する場合に、DateTime値の代わりに使用することもできます。 この使用法は、 DateTime.TimeOfDay プロパティと DateTimeOffset.TimeOfDay プロパティに似ています。このプロパティは、日付を参照しない時刻を表す TimeSpan 値を返します。 たとえば、 TimeSpan 構造を使用して、店舗の毎日の開店または閉店時刻を反映したり、通常のイベントが発生した時刻を表すために使用できます。

次の例では、開店時刻と終了時刻のTimeSpan オブジェクトと、ストアのタイム ゾーンを表すTimeZoneInfo オブジェクトを含むStoreInfo構造体を定義します。 この構造体には、 IsOpenNowIsOpenAtの 2 つのメソッドも含まれています。このメソッドは、ユーザーが指定した時刻にストアを開いているかどうかを示します。このメソッドは、ローカル タイム ゾーンにあると見なされます。

using System;

public struct StoreInfo
{
   public String store;
   public TimeZoneInfo tz;
   public TimeSpan open;
   public TimeSpan close;

   public bool IsOpenNow()
   {
      return IsOpenAt(DateTime.Now.TimeOfDay);
   }

   public bool IsOpenAt(TimeSpan time)
   {
      TimeZoneInfo local = TimeZoneInfo.Local;
      TimeSpan offset = TimeZoneInfo.Local.BaseUtcOffset;

      // Is the store in the same time zone?
      if (tz.Equals(local)) {
         return time >= open & time <= close;
      }
      else {
         TimeSpan delta = TimeSpan.Zero;
         TimeSpan storeDelta = TimeSpan.Zero;

         // Is it daylight saving time in either time zone?
         if (local.IsDaylightSavingTime(DateTime.Now.Date + time))
            delta = local.GetAdjustmentRules()[local.GetAdjustmentRules().Length - 1].DaylightDelta;

         if (tz.IsDaylightSavingTime(TimeZoneInfo.ConvertTime(DateTime.Now.Date + time, local, tz)))
            storeDelta = tz.GetAdjustmentRules()[tz.GetAdjustmentRules().Length - 1].DaylightDelta;

         TimeSpan comparisonTime = time + (offset - tz.BaseUtcOffset).Negate() + (delta - storeDelta).Negate();
         return comparisonTime >= open && comparisonTime <= close;
      }
   }
}
Public Structure StoreInfo
    Dim store As String
    Dim tz As TimeZoneInfo
    Dim open As TimeSpan
    Dim close As TimeSpan

    Public Function IsOpenNow() As Boolean
        Return IsOpenAt(Date.Now.TimeOfDay)
    End Function

    Public Function IsOpenAt(time As TimeSpan) As Boolean
        Dim local As TimeZoneInfo = TimeZoneInfo.Local
        Dim offset As TimeSpan = TimeZoneInfo.Local.BaseUtcOffset

        ' Is the store in the same time zone?
        If tz.Equals(local) Then
            Return time >= open AndAlso time <= close
        Else
            Dim delta As TimeSpan = TimeSpan.Zero
            Dim storeDelta As TimeSpan = TimeSpan.Zero

            ' Is it daylight saving time in either time zone?
            If local.IsDaylightSavingTime(Date.Now.Date + time) Then
                delta = local.GetAdjustmentRules(local.GetAdjustmentRules().Length - 1).DaylightDelta
            End If
            If tz.IsDaylightSavingTime(TimeZoneInfo.ConvertTime(Date.Now.Date + time, local, tz))
                storeDelta = tz.GetAdjustmentRules(tz.GetAdjustmentRules().Length - 1).DaylightDelta
            End If
            Dim comparisonTime As TimeSpan = time + (offset - tz.BaseUtcOffset).Negate() + (delta - storeDelta).Negate
            Return (comparisonTime >= open AndAlso comparisonTime <= close)
        End If
    End Function
End Structure

StoreInfo構造体は、次のようなクライアント コードで使用できます。

public class Example
{
   public static void Main()
   {
      // Instantiate a StoreInfo object.
      var store103 = new StoreInfo();
      store103.store = "Store #103";
      store103.tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
      // Store opens at 8:00.
      store103.open = new TimeSpan(8, 0, 0);
      // Store closes at 9:30.
      store103.close = new TimeSpan(21, 30, 0);

      Console.WriteLine($"Store is open now at {DateTime.Now.TimeOfDay}: {store103.IsOpenNow()}");
      TimeSpan[] times = { new TimeSpan(8, 0, 0), new TimeSpan(21, 0, 0),
                           new TimeSpan(4, 59, 0), new TimeSpan(18, 31, 0) };
      foreach (var time in times)
         Console.WriteLine($"Store is open at {time}: {store103.IsOpenAt(time)}");
   }
}
// The example displays the following output:
//       Store is open now at 15:29:01.6129911: True
//       Store is open at 08:00:00: True
//       Store is open at 21:00:00: True
//       Store is open at 04:59:00: False
//       Store is open at 18:31:00: True
Module Example
    Public Sub Main()
        ' Instantiate a StoreInfo object.
        Dim store103 As New StoreInfo()
        store103.store = "Store #103"
        store103.tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")
        ' Store opens at 8:00.
        store103.open = new TimeSpan(8, 0, 0)
        ' Store closes at 9:30.
        store103.close = new TimeSpan(21, 30, 0)

        Console.WriteLine("Store is open now at {0}: {1}",
                          Date.Now.TimeOfDay, store103.IsOpenNow())
        Dim times() As TimeSpan = {New TimeSpan(8, 0, 0),
                                    New TimeSpan(21, 0, 0),
                                    New TimeSpan(4, 59, 0),
                                    New TimeSpan(18, 31, 0)}
        For Each time In times
            Console.WriteLine("Store is open at {0}: {1}",
                              time, store103.IsOpenAt(time))
        Next
    End Sub
End Module
' The example displays the following output:
'       Store is open now at 15:29:01.6129911: True
'       Store is open at 08:00:00: True
'       Store is open at 21:00:00: False
'       Store is open at 04:59:00: False
'       Store is open at 18:31:00: False

TimeOnly 構造体

TimeOnly構造体は、毎日の目覚まし時計や毎日の昼食を食べる時間など、時刻の値を表します。 TimeOnly は、特定の時刻である 00:00:00.00000000 - 23:59:59.99999999 の範囲に制限されます。

TimeOnly型が導入される前は、プログラマは通常、特定の時刻を表すためにDateTime型またはTimeSpan型を使用します。 ただし、これらの構造体を使用して日付のない時刻をシミュレートすると、いくつかの問題が発生する可能性があり、 TimeOnly 解決されます。

  • TimeSpan は、ストップウォッチで測定された時間などの経過時間を表します。 上位の範囲は 29,000 年を超え、その値は負の値を指定して、時間をさかのぼって移動することを示すことができます。 負の TimeSpan は、1 日の特定の時刻を示すわけではありません。
  • TimeSpanを時刻として使用すると、24 時間外の値に操作される可能性があります。 TimeOnly このリスクはありません。 たとえば、従業員の勤務シフトが 18:00 から 8 時間続く場合、 TimeOnly 構造に 8 時間を追加すると、2:00 にロールオーバーされます。
  • DateTimeを 1 日の時刻に使用するには、任意の日付を時刻に関連付け、後で無視する必要があります。 日付として DateTime.MinValue (0001-01-01) を選択するのが一般的ですが、 DateTime 値から時間を減算すると、 OutOfRange 例外が発生する可能性があります。 TimeOnly 時間が24時間の時間枠の周りに前後に転がり、この問題はありません。
  • DateTime構造体のシリアル化には、データの意図を隠す可能性がある日付コンポーネントが含まれます。 また、 TimeOnly ではシリアル化されるデータが少なくなります。

TimeOnlyの詳細については、「DateOnly 構造体と TimeOnly 構造体の使用方法」を参照してください。

Von Bedeutung

TimeOnly は .NET Framework では使用できません。

TimeZoneInfo クラス

TimeZoneInfo クラスは、地球のいずれかのタイム ゾーンを表し、あるタイム ゾーン内の任意の日付と時刻を別のタイム ゾーンで等価に変換できるようにします。 TimeZoneInfo クラスを使用すると、日付と時刻を操作して、任意の日付と時刻の値が 1 つの時点を明確に識別できるようにします。 TimeZoneInfo クラスも拡張可能です。 Windows システム用に提供され、レジストリで定義されているタイム ゾーン情報に依存しますが、カスタム タイム ゾーンの作成をサポートします。 また、タイム ゾーン情報のシリアル化と逆シリアル化もサポートしています。

場合によっては、 TimeZoneInfo クラスを最大限に活用するには、さらに開発作業が必要になる場合があります。 日付と時刻の値が属するタイム ゾーンと密接に結合されていない場合は、さらに作業が必要です。 アプリケーションで日付と時刻を関連付けられたタイム ゾーンにリンクするためのメカニズムがない限り、特定の日付と時刻の値がタイム ゾーンとの関連付けを解除するのは簡単です。 この情報をリンクする方法の 1 つは、日付と時刻の値とそれに関連付けられているタイム ゾーン オブジェクトの両方を含むクラスまたは構造体を定義することです。

.NET でタイム ゾーンのサポートを利用するには、その日付と時刻オブジェクトがインスタンス化されるときに、日付と時刻の値が属するタイム ゾーンを知っている必要があります。 タイム ゾーンは、特に Web アプリやネットワーク アプリでは、よく知られていません。

こちらも参照ください