Выполнение арифметических операций с датами и временем

Хотя обе DateTimeDateTimeOffset структуры предоставляют элементы, выполняющие арифметические операции по их значениям, результаты арифметических операций очень отличаются. В данном разделе эти различия описываются в контексте различных уровней представленности сведений о часовом поясе во временных данных, а также рассматриваются способы выполнения операций над датами и временем с полным учетом сведений о часовых поясах.

Сравнения и арифметические операции со значениями DateTime

Свойство DateTime.Kind позволяет DateTimeKind назначать значение дате и времени, указывая, представляет ли оно местное время, согласованное универсальное время (UTC) или время в неопределенном часовом поясе. Однако эта ограниченная информация часового пояса игнорируется при сравнении или выполнении арифметики даты и времени для DateTimeKind значений. В следующем примере, который сравнивает текущее локальное время с текущим временем UTC, показано, как данные часового пояса игнорируются.

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,
                        utcTime.Kind,
                        (localTime - utcTime).Hours,
                        (localTime - utcTime).Minutes);
      Console.WriteLine("The {0} time is {1} the {2} time.",
                        localTime.Kind,
                        Enum.GetName(typeof(TimeComparison), 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 и местным временем для систем в тихоокеанском стандартном часовом поясе США составляет семь часов. Но поскольку эти два значения предоставляют разные представления одного точки во времени, ясно в этом случае, что интервал времени полностью связан с смещением местного часового пояса из UTC.

Как правило, свойство не влияет на результаты, DateTime.Kind возвращаемые Kind сравнением и арифметическими методами (как сравнение двух идентичных точек во времени указывает), хотя это может повлиять на интерпретацию этих результатов. Например:

  • Результат любой арифметической операции, выполняемой в двух значениях даты и времени, DateTime.Kind свойства которых оба равны, DateTimeKind отражают фактический интервал времени между двумя значениями. Аналогичным образом результат сравнения двух таких значений даты и времени будет точно отражать соотношение между временами.

  • Результат любой арифметической или сравнения операции, выполняемой в двух значениях даты и времени, DateTime.Kind свойства которых равны DateTimeKind или по двум значениям даты и времени с различными DateTime.Kind значениями свойств отражают разницу во времени между двумя значениями.

  • Арифметические операции или операции сравнения, выполняемые над местными значениями даты и времени, не учитывают допустимость или недопустимость конкретных значений; не учитывают они и какие-либо правила коррекции, необходимые при переводе местного часового пояса на зимнее или летнее время.

  • В результат любой операции, сравнивающей или вычисляющей разницу между временем в формате UTC и местным временем, входит временной интервал, равный смещению местного часового пояса относительно времени в формате UTC.

  • Любая операция, которая сравнивает или вычисляет разницу между неуказанным временем и временем в формате UTC или местным временем, соответствует простому времени. Различия между часовыми поясами не учитываются, и в результате не отражено применение правил коррекции часовых поясов.

  • Любая операция, которая сравнивает или вычисляет разницу между двумя неуказанными значениями времени, может включать неизвестный интервал, который отражает временную разницу между двумя различными часовыми поясами.

Существует множество сценариев, в которых различия часового пояса не влияют на вычисления даты и времени (для обсуждения некоторых из этих сценариев см . статью "Выбор между dateTime, DateTimeOffset, TimeSpan и TimeZoneInfo") или в контексте данных даты и времени, определяющих смысл операций сравнения или арифметических операций.

Сравнения и арифметические операции со значениями DateTimeOffset

Значение DateTimeOffset включает не только дату и время, но и смещение, которое однозначно определяет эту дату и время относительно UTC. Это смещение позволяет определить равенство по-разному, чем для DateTime значений. DateTime В то время как значения равны, если они имеют одно и то же значение даты и времени, значения равны, DateTimeOffset если они оба ссылаются на одну точку во времени. При использовании в сравнениях и в большинстве арифметических операций, определяющих интервал между двумя датами и временем, DateTimeOffset значение является более точным и менее нуждающимся в интерпретации. Следующий пример, эквивалентный предыдущему примеру, DateTimeOffset который сравнивал локальные и UTC-значения 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: {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.
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) значений указывает, что разница между двумя разами TimeSpan.Zero.

Основное ограничение использования DateTimeOffset значений в арифметике даты и времени заключается в том, что хотя DateTimeOffset значения имеют некоторое представление часового пояса, они не полностью осведомлены о часовом поясе. DateTimeOffset Хотя смещение значения отражает смещение часового пояса из UTC при DateTimeOffset первом назначении переменной значения, оно будет отсоединяется от часового пояса после этого. Поскольку оно больше не связано напрямую с распознаваемым временем, правила коррекции часовых поясов больше не будут учитываться при сложении и вычитании интервалов даты и времени.

Чтобы проиллюстрировать, переход на летнее время в центрально-стандартном часовом поясе США происходит в 2:00 утра 9 марта 2008 года. Учитывая это, добавив два с половиной часа интервала в центральное стандартное время 1:30 утра 9 марта 2008 года, должно создать дату и время 5:00 утра 9 марта 2008 года. Однако, как показано в следующем примере, результатом добавления является 4:00 утра 9 марта 2008 года. Результат этой операции представляет правильный момент времени, хотя это не время в часовом поясе, в котором мы заинтересованы (т. е. не имеет ожидаемого смещения часового пояса).

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
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 включает методы преобразования, которые автоматически применяют корректировки при преобразовании времени из одного часового пояса в другой. К этим методам преобразования относятся:

  • Методы ConvertTime и ConvertTimeBySystemTimeZoneId методы, которые преобразуют время между двумя часовых поясами.

  • Методы ConvertTimeFromUtc и ConvertTimeToUtc методы, которые преобразуют время в определенном часовом поясе в формате UTC или преобразуют время в определенный часовой пояс.

Дополнительные сведения см. в разделе "Преобразование времени между часовых поясов".

Класс TimeZoneInfo не предоставляет никаких методов, которые автоматически применяют правила корректировки при выполнении арифметики даты и времени. Однако правила корректировки можно применить, преобразовав время в часовой пояс в формате UTC, выполнив арифметическую операцию, а затем преобразовав из UTC обратно в время часового пояса. Дополнительные сведения см. в статье "Практическое руководство. Использование часовых поясов в арифметике даты и времени".

Например, следующий код аналогичен предыдущему коду, который добавил два с половиной часа до 2:00 утра 9 марта 2008 года. Тем не менее, поскольку в этом примере перед выполнением арифметических действий над датой и временем центральное стандартное время преобразуется во время в формате 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("{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
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

См. также