次の方法で共有


日付と時刻を使用した算術演算の実行

DateTime構造体と DateTimeOffset 構造体の両方で、値に対して算術演算を実行するメンバーが提供されますが、算術演算の結果は大きく異なります。 この記事では、これらの違いを調べ、日付と時刻データのタイム ゾーン認識度に関連付け、日付と時刻データを使用して完全なタイム ゾーン対応操作を実行する方法について説明します。

DateTime 値を使用した比較と算術演算

DateTime.Kind プロパティを使用すると、DateTimeKind値を日付と時刻に割り当てて、ローカル時刻、世界協定時刻 (UTC)、または指定されていないタイム ゾーン内の時刻を表すかどうかを示すことができます。 ただし、この制限付きタイム ゾーン情報は、 DateTimeKind 値に対して日付と時刻の算術演算を比較または実行する場合は無視されます。 次の例では、現在の現地時刻と現在の UTC 時刻を比較し、タイム ゾーン情報を無視する方法を示します。

using System;

public enum TimeComparison2
{
    EarlierThan = -1,
    TheSameAs = 0,
    LaterThan = 1
}

public class DateManipulation
{
    public static void Main()
    {
        DateTime localTime = DateTime.Now;
        DateTime utcTime = DateTime.UtcNow;

        Console.WriteLine("Difference between {0} and {1} time: {2}:{3} hours",
                          localTime.Kind,
                          utcTime.Kind,
                          (localTime - utcTime).Hours,
                          (localTime - utcTime).Minutes);
        Console.WriteLine("The {0} time is {1} the {2} time.",
                          localTime.Kind,
                          Enum.GetName(typeof(TimeComparison2), localTime.CompareTo(utcTime)),
                          utcTime.Kind);
    }
}
// If run in the U.S. Pacific Standard Time zone, the example displays
// the following output to the console:
//    Difference between Local and Utc time: -7:0 hours
//    The Local time is EarlierThan the Utc time.
Public Enum TimeComparison As Integer
    EarlierThan = -1
    TheSameAs = 0
    LaterThan = 1
End Enum

Module DateManipulation
    Public Sub Main()
        Dim localTime As Date = Date.Now
        Dim utcTime As Date = Date.UtcNow

        Console.WriteLine("Difference between {0} and {1} time: {2}:{3} hours", _
                          localTime.Kind.ToString(), _
                          utcTime.Kind.ToString(), _
                          (localTime - utcTime).Hours, _
                          (localTime - utcTime).Minutes)
        Console.WriteLine("The {0} time is {1} the {2} time.", _
                          localTime.Kind.ToString(), _
                          [Enum].GetName(GetType(TimeComparison), localTime.CompareTo(utcTime)), _
                          utcTime.Kind.ToString())
        ' If run in the U.S. Pacific Standard Time zone, the example displays 
        ' the following output to the console:
        '    Difference between Local and Utc time: -7:0 hours
        '    The Local time is EarlierThan the Utc time.                                                    
    End Sub
End Module

CompareTo(DateTime)メソッドは、現地時刻が UTC 時刻より前 (またはそれより小さい) ことを報告し、減算演算は、UTC と米国太平洋標準時ゾーンのシステムの現地時刻の差が 7 時間であることを示します。 ただし、この 2 つの値は単一の時点の異なる表現を提供するため、この場合、時間間隔が UTC からのローカル タイム ゾーンのオフセットに完全に起因することは明らかです。

より一般的には、 DateTime.Kind プロパティは、 Kind 比較メソッドと算術メソッドによって返される結果には影響しません (2 つの同じ時点の比較が示すように)。ただし、これらの結果の解釈に影響を与える可能性があります。 例えば次が挙げられます。

  • DateTime.Kindプロパティが両方ともDateTimeKind等しい 2 つの日付と時刻の値に対して実行された算術演算の結果には、2 つの値の間の実際の時間間隔が反映されます。 同様に、このような 2 つの日付と時刻の値を比較すると、時刻間の関係が正確に反映されます。

  • 2 つの日付と時刻の値に対して実行された算術演算または比較演算の結果で、 DateTime.Kind プロパティが両方とも DateTimeKind 等しいか、 DateTime.Kind プロパティ値が異なる 2 つの日付と時刻の値に対して実行された結果は、2 つの値間のクロック時間の差を反映します。

  • ローカルの日付と時刻の値に対する算術演算または比較操作では、特定の値があいまいか無効かは考慮されません。また、ローカル タイム ゾーンの夏時間への切り替えまたは夏時間からの切り替えによる調整規則の影響も考慮されません。

  • UTC と現地時刻の差を比較または計算する操作には、結果の UTC からのローカル タイム ゾーンのオフセットと等しい時間間隔が含まれます。

  • 指定されていない時刻と UTC またはローカル時刻の差を比較または計算する操作には、単純なクロック時間が反映されます。 タイム ゾーンの違いは考慮されず、結果にはタイム ゾーン調整ルールの適用は反映されません。

  • 指定されていない 2 つの時刻の差を比較または計算する操作には、2 つの異なるタイム ゾーンの時間の差を反映する不明な間隔が含まれる場合があります。

タイム ゾーンの違いが日付と時刻の計算に影響しないシナリオは多数あります (これらのシナリオの一部については、「 DateTime、DateTimeOffset、TimeSpan、TimeZoneInfo の選択」を参照してください)。また、日付と時刻データのコンテキストによって比較演算または算術演算の意味が定義されます。

DateTimeOffset 値を使用した比較と算術演算

DateTimeOffset値には、日付と時刻だけでなく、UTC を基準にしてその日付と時刻を明確に定義するオフセットも含まれます。 このオフセットにより、 DateTime 値の場合とは異なる方法で等値を定義できます。 DateTime値が同じ日付と時刻の値を持つ場合は等しくなりますが、DateTimeOffset値は両方とも同じ時点を参照している場合は等しくなります。 比較や、2 つの日付と時刻の間隔を決定するほとんどの算術演算で使用する場合、 DateTimeOffset 値の方が正確になり、解釈が必要なくなります。 次の例は、ローカル値と UTC DateTimeOffset値を比較した前の例と同等のDateTimeOffsetであり、この動作の違いを示しています。

using System;

public enum TimeComparison
{
    EarlierThan = -1,
    TheSameAs = 0,
    LaterThan = 1
}

public class DateTimeOffsetManipulation
{
    public static void Main()
    {
        DateTimeOffset localTime = DateTimeOffset.Now;
        DateTimeOffset utcTime = DateTimeOffset.UtcNow;

        Console.WriteLine($"Difference between local time and UTC: {(localTime - utcTime).Hours}:{(localTime - utcTime).Minutes:D2} hours");
        Console.WriteLine($"The local time is {Enum.GetName(typeof(TimeComparison), localTime.CompareTo(utcTime))} UTC.");
    }
}
// Regardless of the local time zone, the example displays
// the following output to the console:
//    Difference between local time and UTC: 0:00 hours.
//    The local time is TheSameAs UTC.
Public Enum TimeComparison As Integer
    EarlierThan = -1
    TheSameAs = 0
    LaterThan = 1
End Enum

Module DateTimeOffsetManipulation
    Public Sub Main()
        Dim localTime As DateTimeOffset = DateTimeOffset.Now
        Dim utcTime As DateTimeOffset = DateTimeOffset.UtcNow

        Console.WriteLine("Difference between local time and UTC: {0}:{1:D2} hours.", _
                          (localTime - utcTime).Hours, _
                          (localTime - utcTime).Minutes)
        Console.WriteLine("The local time is {0} UTC.", _
                          [Enum].GetName(GetType(TimeComparison), localTime.CompareTo(utcTime)))
    End Sub
End Module
' Regardless of the local time zone, the example displays 
' the following output to the console:
'    Difference between local time and UTC: 0:00 hours.
'    The local time is TheSameAs UTC.
'          Console.WriteLine(e.GetType().Name)

この例では、 CompareTo メソッドは現在の現地時刻と現在の UTC 時刻が等しいことを示し、 CompareTo(DateTimeOffset) 値の減算は、2 つの時刻の差が TimeSpan.Zeroであることを示します。

日付と時刻の算術演算で DateTimeOffset 値を使用する主な制限は、 DateTimeOffset 値にはタイム ゾーン認識はあるものの、完全にはタイム ゾーンに対応していないということです。 DateTimeOffset値のオフセットは、DateTimeOffset変数に最初に値が割り当てられた場合の UTC からのタイム ゾーンのオフセットを反映しますが、その後、タイム ゾーンとの関連付けが解除されます。 識別可能な時刻に直接関連付けられていないため、日付と時間間隔の加算と減算では、タイム ゾーンの調整規則は考慮されません。

たとえば、米国中部標準時ゾーンの夏時間への移行は、2008 年 3 月 9 日の午前 2 時に行われます。 そのことを念頭に置いて、2008 年 3 月 9 日午前 1 時 30 分の中央標準時刻に 2 時間 30 分間隔を追加すると、2008 年 3 月 9 日の午前 5 時の日付と時刻が生成されます。 ただし、次の例に示すように、加算の結果は 2008 年 3 月 9 日午前 4 時です。 この操作の結果は正しい時点を表しますが、関心のあるタイム ゾーンの時刻ではありません (つまり、予想されるタイム ゾーン オフセットがありません)。

using System;

public class IntervalArithmetic
{
    public static void Main()
    {
        DateTime generalTime = new DateTime(2008, 3, 9, 1, 30, 0);
        const string tzName = "Central Standard Time";
        TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);

        // Instantiate DateTimeOffset value to have correct CST offset
        try
        {
            DateTimeOffset centralTime1 = new DateTimeOffset(generalTime,
                       TimeZoneInfo.FindSystemTimeZoneById(tzName).GetUtcOffset(generalTime));

            // Add two and a half hours
            DateTimeOffset centralTime2 = centralTime1.Add(twoAndAHalfHours);
            // Display result
            Console.WriteLine($"{centralTime1} + {twoAndAHalfHours.ToString()} hours = {centralTime2}");
        }
        catch (TimeZoneNotFoundException)
        {
            Console.WriteLine("Unable to retrieve Central Standard Time zone information.");
        }
    }
}
// The example displays the following output to the console:
//    3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 4:00:00 AM -06:00
Module IntervalArithmetic
    Public Sub Main()
        Dim generalTime As Date = #03/09/2008 1:30AM#
        Const tzName As String = "Central Standard Time"
        Dim twoAndAHalfHours As New TimeSpan(2, 30, 0)

        ' Instantiate DateTimeOffset value to have correct CST offset
        Try
            Dim centralTime1 As New DateTimeOffset(generalTime, _
                       TimeZoneInfo.FindSystemTimeZoneById(tzName).GetUtcOffset(generalTime))

            ' Add two and a half hours      
            Dim centralTime2 As DateTimeOffset = centralTime1.Add(twoAndAHalfHours)
            ' Display result
            Console.WriteLine("{0} + {1} hours = {2}", centralTime1, _
                                                       twoAndAHalfHours.ToString(), _
                                                       centralTime2)
        Catch e As TimeZoneNotFoundException
            Console.WriteLine("Unable to retrieve Central Standard Time zone information.")
        End Try
    End Sub
End Module
' The example displays the following output to the console:
'    3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 4:00:00 AM -06:00

タイム ゾーン内の時刻を含む算術演算

TimeZoneInfo クラスには、あるタイム ゾーンから別のタイム ゾーンに時刻を変換するときに調整を自動的に適用する変換メソッドが含まれています。 これらの変換方法は次のとおりです。

詳細については、「 タイム ゾーン間の時刻の変換」を参照してください。

TimeZoneInfo クラスは、日付と時刻の算術演算を実行するときに調整規則を自動的に適用するメソッドを提供しません。 ただし、調整ルールを適用するには、タイム ゾーンの時刻を UTC に変換し、算術演算を実行してから、UTC からタイム ゾーンの時刻に変換します。 詳細については、「 方法: 日付と時刻の算術演算でタイム ゾーンを使用する」を参照してください。

たとえば、次のコードは、2008 年 3 月 9 日の午前 2 時に 2 時間 30 分を追加した前のコードと似ています。 ただし、日付と時刻の算術演算を実行する前に中央標準時を UTC に変換し、その結果を UTC から中央標準時刻に変換するため、結果の時刻には、中央標準時の夏時間への移行が反映されます。

using System;

public class TimeZoneAwareArithmetic
{
    public static void Main()
    {
        const string tzName = "Central Standard Time";

        DateTime generalTime = new DateTime(2008, 3, 9, 1, 30, 0);
        TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById(tzName);
        TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);

        // Instantiate DateTimeOffset value to have correct CST offset
        try
        {
            DateTimeOffset centralTime1 = new DateTimeOffset(generalTime,
                                          cst.GetUtcOffset(generalTime));

            // Add two and a half hours
            DateTimeOffset utcTime = centralTime1.ToUniversalTime();
            utcTime += twoAndAHalfHours;

            DateTimeOffset centralTime2 = TimeZoneInfo.ConvertTime(utcTime, cst);
            // Display result
            Console.WriteLine($"{centralTime1} + {twoAndAHalfHours.ToString()} hours = {centralTime2}");
        }
        catch (TimeZoneNotFoundException)
        {
            Console.WriteLine("Unable to retrieve Central Standard Time zone information.");
        }
    }
}
// The example displays the following output to the console:
//    3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 5:00:00 AM -05:00
Module TimeZoneAwareArithmetic
    Public Sub Main()
        Const tzName As String = "Central Standard Time"

        Dim generalTime As Date = #03/09/2008 1:30AM#
        Dim cst As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(tzName)
        Dim twoAndAHalfHours As New TimeSpan(2, 30, 0)

        ' Instantiate DateTimeOffset value to have correct CST offset
        Try
            Dim centralTime1 As New DateTimeOffset(generalTime, _
                       cst.GetUtcOffset(generalTime))

            ' Add two and a half hours 
            Dim utcTime As DateTimeOffset = centralTime1.ToUniversalTime()
            utcTime += twoAndAHalfHours

            Dim centralTime2 As DateTimeOffset = TimeZoneInfo.ConvertTime(utcTime, cst)
            ' Display result
            Console.WriteLine("{0} + {1} hours = {2}", centralTime1, _
                                                       twoAndAHalfHours.ToString(), _
                                                       centralTime2)
        Catch e As TimeZoneNotFoundException
            Console.WriteLine("Unable to retrieve Central Standard Time zone information.")
        End Try
    End Sub
End Module
' The example displays the following output to the console:
'    3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 5:00:00 AM -05:00

こちらもご覧ください