Выполнение арифметических операций с датами и временем
Несмотря на то, что структуры DateTime и DateTimeOffset предоставляют члены, выполняющие арифметические действия над их значениями, результаты арифметических операций очень различаются. В данном разделе эти различия описываются в контексте различных уровней представленности сведений о часовом поясе во временных данных, а также рассматриваются способы выполнения операций над датами и временем с полным учетом сведений о часовых поясах.
Сравнения и арифметические операции со значениями даты и времени
Начиная с платформы .NET Framework версии 2.0, в значениях DateTime присутствуют ограниченные сведения о часовом поясе. Свойство DateTime.Kind позволяет присваивать значению даты и времени значение DateTimeKind, чтобы указать, представляет ли оно местное время, время в формате UTC или время в неопределенном часовом поясе. Тем не менее, эти ограниченные сведения о часовом поясе игнорируются при сравнении или выполнении арифметических операций со значениями 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 и местным временем для тихоокеанского стандартного часового пояса США составляет семь часов. Тем не менее, поскольку эти значения дают различные представления одного и того же момента времени, в данном случае очевидно, что причина этого различия целиком кроется в смещении местного часового пояса относительно времени в формате UTC.
В общем случае свойство DateTime.Kind не влияет на результаты, возвращаемые методами сравнения и арифметическими операциями (как показывает результат сравнения двух идентичных моментов времени) с DateTime, хотя оно может повлиять на интерпретацию этих результатов. Пример:
Результат любой арифметической операции над парой значений даты и времени, свойства DateTime.Kind которых имеют значение Utc, отражает фактический интервал времени между этими двумя значениями. Аналогичным образом результат сравнения двух таких значений даты и времени будет точно отражать соотношение между временами.
Результат любой арифметической операции или операции сравнения над двумя значениями даты и времени, свойства DateTime.Kind которых имеют значение Local, или над двумя значениями даты и времени с различными значениями свойства DateTime.Kind будет отражать временную разницу между этими значениями.
Арифметические операции или операции сравнения, выполняемые над местными значениями даты и времени, не учитывают допустимость или недопустимость конкретных значений; не учитывают они и какие-либо правила коррекции, необходимые при переводе местного часового пояса на зимнее или летнее время.
В результат любой операции, сравнивающей или вычисляющей разницу между временем в формате UTC и местным временем, входит временной интервал, равный смещению местного часового пояса относительно времени в формате UTC.
Любая операция, которая сравнивает или вычисляет разницу между неуказанным временем и временем в формате UTC или местным временем, соответствует простому времени. Различия между часовыми поясами не учитываются, и в результате не отражено применение правил коррекции часовых поясов.
Любая операция, которая сравнивает или вычисляет разницу между двумя неуказанными значениями времени, может включать неизвестный интервал, который отражает временную разницу между двумя различными часовыми поясами.
Существует множество сценариев, в которых различие часовых поясов не влияет на вычисления с датами и временем (обзор некоторых из них см. в разделе Выбор между типами DateTime, DateTimeOffset и TimeZoneInfo), или же контекст данных даты и времени определяет суть операций сравнения или арифметических операций.
Сравнения и арифметические операции со значениями DateTimeOffset
Значение DateTimeOffset содержит не только дату и время, но также и смещение, которое однозначно задает его соотношение с временем в формате UTC. Это дает возможность определить равенство несколько иначе, чем для значений DateTime. Если значения DateTime считаются равными в случае, когда они имеют одинаковое значение даты и времени, то значения DateTimeOffset считаются равными тогда, когда они ссылаются на один момент времени. Это делает значение DateTimeOffset более точным и менее контекстно-зависимым при использовании в сравнении и в большинстве арифметических операций, которые определяют интервал между двумя значениями даты и времени. Различия в поведении DateTimeOffset демонстрирует следующий пример, который является аналогом предыдущего примера, в котором сравнивались значения DateTime для местного времени и времени в формате 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)
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 показывает, что разница между двумя значениями времени равна TimeSpan.Zero.
Главным препятствием для использования значений DateTimeOffset в арифметике дат и времени является то, что значения DateTimeOffset отражают сведения о часовых поясах не полностью, хоть и содержат некоторую информацию о часовом поясе. Несмотря на то, что при первом присваивании значения переменной типа DateTimeOffset смещение DateTimeOffset соответствует смещению часового пояса относительно времени в формате UTC, впоследствии оно рассогласовывается с часовым поясом. Поскольку оно больше не связано напрямую с распознаваемым временем, правила коррекции часовых поясов больше не будут учитываться при сложении и вычитании интервалов даты и времени.
Пример: переход на летнее время в зоне центрального стандартного времени США происходит в 2:00. 9 марта, 2008. Это означает, что при прибавлении двух с половиной часов к 1:30 9 марта 2008 г. центрального стандартного времени США должно получиться 5:00. 9 марта, 2008. Однако, как показано в следующем примере, результатом сложения является 4:00. 9 марта, 2008. Обратите внимание, что такой результат данной операции представляет правильный момент времени, хотя и не является временем в искомом часовом поясе (в результате не содержится ожидаемое смещение часового пояса).
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 содержит несколько методов преобразования, которые автоматически применяют правила коррекции при переводе значения времени из одного часового пояса в другой. В их числе:
методы ConvertTime и ConvertTimeBySystemTimeZoneId, которые преобразуют значения времени между любыми двумя часовыми поясами;
методы ConvertTimeFromUtc и ConvertTimeToUtc, которые преобразуют время определенного часового пояса во время в формате UTC или время в формате UTC во время определенного часового пояса.
Дополнительные сведения см. в разделе Преобразование времени из одного часового пояса в другой.
В классе TimeZoneInfo отсутствуют методы, которые бы автоматически применяли правила коррекции при выполнении арифметических действий с датами и временем. Тем не менее, этого можно добиться, преобразовав время в часовом поясе во время в формате UTC, выполнив арифметическую операцию и преобразовав время UTC обратно во время в часовом поясе. Дополнительные сведения см. в разделе Практическое руководство. Использование часовых поясов в арифметических операциях с датами и временем.
Например, следующий код аналогичен предыдущему коду, в котором 2,5 часа добавлялись к 2:00 9 марта, 2008. Тем не менее, поскольку в этом примере перед выполнением арифметических действий над датой и временем центральное стандартное время преобразуется во время в формате 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