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

DateTime 構造体と DateTimeOffset 構造体はいずれも、構造体の値に対して算術演算を実行するメンバーを提供しますが、演算の結果は大きく異なります。 ここでは、その相違点について考察し、日付と時刻のデータにおけるタイム ゾーンへの対応の程度について詳述します。さらに、日付と時刻のデータを使用して、タイム ゾーンに完全に対応した操作を実行する方法について解説します。

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

.NET Framework Version 2.0 以降では、DateTime 値でのタイム ゾーンへの対応は限られたものになっています。 DateTime.Kind プロパティを使用すると、DateTimeKind 値を日付と時刻に割り当てて、それが表す内容が現地時刻、世界協定時刻 (UTC: Coordinated Universal Time)、タイム ゾーンが指定されていない時刻のいずれであるかを示すことができます。 ただし、DateTime 値で日付と時刻の比較演算や算術演算が実行されるときには、このタイム ゾーンについての制限された情報は無視されます。 この例を次に示します。例では、現在の現地時刻を現在の UTC 時刻と比較します。

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
using System;

public enum TimeComparison
{
   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.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(typeof(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.                                                    

CompareTo(DateTime) メソッドでは、現地時刻が UTC 時刻よりも早い (小さい) ことが報告されます。また、減算により、UTC と、米国の太平洋標準時ゾーンにあるシステムの現地時刻との間の差異が 7 時間であることが示されます。 しかし、これらの 2 つの値では時刻の表現方法が異なるため、この例では明らかに、時間間隔は UTC からのローカル タイム ゾーンのオフセットに完全に帰属します。

より一般的な例としては、DateTime.Kind プロパティは DateTime 比較演算および算術演算によって返される結果には影響しないことが挙げられます (同じ 2 つの時刻の比較が行われるため)。ただし、結果の解釈には影響することがあります。 次に例を示します。

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

  • 2 つの日付と時刻の値 (DateTime.Kind プロパティがいずれも Local に設定されているか、2 つの日付と時刻の値にはそれぞれ異なる DateTime.Kind プロパティ値が設定されている) に対して実行した算術演算または比較演算の結果には、2 つの値の間のクロック時間の差異が反映されます。

  • ローカルの日付と時刻の値に対して実行される算術演算と比較演算では、特定の値が不明確または無効であるかは考慮されません。また、ローカル タイム ゾーンの夏時間への移行または夏時間からの移行によって生じる調整規則の影響についても考慮されません。

  • UTC 時刻と現地時刻との差異を比較または算出する演算には、時間間隔が含まれます。この時間間隔は、結果として求められる UTC からのローカル タイム ゾーンのオフセットに等しくなります。

  • タイム ゾーンが指定されていない時刻と UTC 時刻または現地時刻との差異を比較または算出する演算には、単にクロック時間が反映されます。 タイム ゾーン間の差異は考慮されず、結果にはタイム ゾーン調整規則のアプリケーションは反映されません。

  • タイム ゾーンが指定されていない 2 つの時刻間での差異を比較または算出する演算には、未知の間隔が含まれます。この時間間隔には、2 つの異なるタイム ゾーンにおける時差が反映されます。

タイム ゾーンの相違が日付と時刻の計算に影響しないシナリオ (詳細については、「DateTime、DateTimeOffset、および TimeZoneInfo の使い分け」を参照) や、日付と時刻のデータのコンテキストによって比較演算と算術演算の意味を定義するシナリオなど、さまざまなシナリオがあります。

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

DateTimeOffset 値には、日付と時刻だけでなく、UTC に対する日付と時刻を明確に定義するオフセットも定義されます。 これにより、DateTime 値とはいくぶん異なる等値性を定義できるようになります。 DateTime 値は日付と時刻の値が同じである場合に等しくなりますが、DateTimeOffset 値は同じ時刻を参照している場合に等しくなります。 これによって DateTimeOffset 値の精度が高まり、2 つの日付と時刻の間隔を判定する比較演算や大部分の算術演算で使用するときに解釈を行う必要性が低くなります。 次の例では、現地時刻および UTC 時刻の DateTime 値を比較した前述の例と同じ DateTimeOffset を使用して、その動作の違いを示します。

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)
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: {0}:{1:D2} hours", 
                        (localTime - utcTime).Hours, 
                        (localTime - utcTime).Minutes);
      Console.WriteLine("The local time is {0} UTC.", 
                        Enum.GetName(typeof(TimeComparison), localTime.CompareTo(utcTime)));  
   }
}
// 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.

この例では、CompareTo メソッドにより、現在の現地時刻と現在の UTC 時刻が等しいことが示されます。また、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 時です。 この時刻は目的のタイム ゾーンの時刻ではありませんが (予期したタイム ゾーン オフセットではありませんが)、演算の結果は正しい時刻を表していることに留意してください。

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
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("{0} + {1} hours = {2}", centralTime1, 
                                                    twoAndAHalfHours.ToString(), 
                                                    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

タイム ゾーンの時刻を使用した算術演算

TimeZoneInfo クラスには、タイム ゾーン間で時刻を変換するときに自動的に調整を適用する変換メソッドが数多く含まれています。 次に例を示します。

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

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

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

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
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("{0} + {1} hours = {2}", centralTime1, 
                                                    twoAndAHalfHours.ToString(), 
                                                    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

参照

処理手順

方法 : 日付と時刻の演算でタイム ゾーンを使用する

その他の技術情報

日付、時刻、およびタイム ゾーン