Выбор между DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnly и TimeZoneInfo
Приложения .NET могут использовать сведения о дате и времени несколькими способами. Ниже приведены наиболее распространенные способы использования сведений о дате и времени.
- для представления только даты; сведения о времени не имеют значения;
- для представления только времени; сведения о дате не имеют значения;
- Чтобы отразить абстрактную дату и время, которое не привязано к определенному времени и месту (например, большинство магазинов в международной сети открываются в будничных днях в 9:00 утра).
- Для получения сведений о дате и времени из источников за пределами .NET обычно сведения о дате и времени хранятся в простом типе данных.
- для однозначной идентификации конкретного момента времени: Для некоторых приложений требуется, чтобы дата и время были однозначно указать только в системе узла. Для других приложений требуется однозначное использование в разных системах (т. е. сериализованная дата в одной системе может быть значимо десериализирована и использована в другой системе в любом месте мира).
- Чтобы сохранить несколько связанных раз (например, локальное время запрашивающего и время получения сервера для веб-запроса).
- для выполнения арифметических операций с датой и временем, возможно, с результатом, однозначно определяющим момент времени.
.NET включает в себя DateTime, DateOnly, DateTimeOffset, TimeOnlyTimeSpanи типы, все из которых можно использовать для создания приложений, работающих с датами и TimeZoneInfo временем.
Примечание.
Эта статья не обсуждается TimeZone , так как ее функциональные возможности практически полностью включены в TimeZoneInfo класс. По возможности используйте TimeZoneInfo класс вместо TimeZone класса.
Структура DateTimeOffset
Структура DateTimeOffset представляет значение даты и времени, а также смещение, которое указывает, насколько это значение отличается от времени в формате UTC. Таким образом, значение всегда однозначно идентифицирует единственный момент времени.
Тип DateTimeOffset включает все функциональные возможности типа DateTime , а также сведения о часовом поясе. Это делает его подходящим для приложений, которые:
- однозначно идентифицируют единственный момент времени; тип DateTimeOffset можно использовать для однозначного определения текущего момента времени, для ведения журнала времени транзакций, ведения журнала времени событий системы или приложений, а также для записи времени создания и изменения файлов;
- выполняют общие арифметические операции с датами и временем;
- сохраняют несколько связанных значений времени, если они хранятся как два отдельных значения или два члена структуры.
Примечание.
Эти варианты использования значений 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("{0} could belong to the following time zones:",
offsetTime.ToString());
// 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(" {0}", 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
Выходные данные показывают, что все значения даты и времени в этом примере могут принадлежать по крайней мере трем разным часовым поясам. Значение DateTimeOffset 6.10.2007 показывает, что если значение даты и времени представляет собой летнее время, его смещение от UTC даже не обязательно соответствует базовому смещение часового пояса в формате UTC или смещение от UTC, найденному в отображаемом имени. Так как одно DateTimeOffset значение не тесно связано с часовыми поясами, оно не может отражать переход часового пояса на летнее время и из него. Это может быть проблематично, когда арифметика даты и времени используется для управления значением DateTimeOffset . Описание способов выполнения арифметических операций с датой и временем с учетом правил коррекции часового пояса см. в разделе Выполнение арифметических операций с датами и временем.
Структура DateTime
Значение DateTime определяет конкретную дату и время. Он включает свойство, которое предоставляет ограниченные сведения о часовом Kind поясе, к которому принадлежит эта дата и время. Значение DateTimeKind , возвращаемое свойством Kind , указывает, представляет ли значение DateTime местное время (DateTimeKind.Local), время в формате UTC (DateTimeKind.Utc) или неопределенное время (DateTimeKind.Unspecified).
Структура DateTime подходит для приложений с одной или несколькими следующими характеристиками:
- работают с абстрактными датами и временем;
- работают с датами и временем, для которых отсутствуют сведения о часовом поясе;
- работают только с датами и временем в формате UTC;
- выполняют арифметические операции с датой и временем, но учитывают только общие результаты. Например, в операции сложения, которая добавляет шесть месяцев к определенным дате и времени, часто не важно, корректируется ли результат с учетом перехода на летнее время.
Кроме случая, когда определенное значение DateTime представляет время в формате UTC, значение даты и времени часто является неоднозначным или ограниченным в плане возможности переноса. Например, если DateTime значение представляет локальное время, оно переносимо в пределах этого локального часового пояса (то есть, если значение десериализировано в другой системе в том же часовом поясе, это значение по-прежнему однозначно идентифицирует один момент времени). За пределами местного часового пояса значение DateTime может иметь несколько интерпретаций. Если свойство DateTimeKind.Unspecifiedзначения Kind имеет значение, оно еще менее переносимо: в настоящее время неоднозначно в пределах одного часового пояса и, возможно, даже в той же системе, где она была впервые сериализована. Значение DateTime однозначно идентифицирует момент времени независимо от времени системы или часового пояса, в котором оно используется, только если это значение представляет время в формате UTC.
Внимание
При сохранении или совместном использовании DateTime данных используйте utc и задайте DateTime для свойства DateTimeKind.Utcзначения Kind значение .
Структура DateOnly
Структура DateOnly представляет определенную дату без времени. Поскольку он не имеет компонента времени, он представляет дату с начала дня до конца дня. Эта структура идеально подходит для хранения конкретных дат, таких как дата рождения, дата годовщины, праздник или дата, связанная с бизнесом.
Несмотря на то, что вы можете использовать DateTime
при игнорировать компонент времени, существует несколько преимуществ использованияDateOnly
:DateTime
- Структура
DateTime
может свернуться в предыдущий или следующий день, если это смещение часового пояса.DateOnly
не может быть смещение по часовой поясу, и всегда представляет дату, заданную. - Сериализация
DateTime
структуры включает компонент времени, который может скрыть намерение данных. Кроме того,DateOnly
сериализует меньше данных. - При взаимодействии кода с базой данных, например SQL Server, все даты обычно хранятся как
date
тип данных, который не включает время.DateOnly
лучше соответствует типу базы данных.
Дополнительные сведения см. в DateOnly
разделе "Использование структур DateOnly и TimeOnly".
Внимание
DateOnly
недоступно в платформа .NET Framework.
Структура TimeSpan
Структура TimeSpan представляет интервал времени. Ее обычно используют двумя способами:
- для отражения интервала времени между двумя значениями даты и времени (например, при вычитании одного значения DateTime из другого возвращается значение TimeSpan );
- для измерения прошедшего времени. Например, Stopwatch.Elapsed свойство возвращает TimeSpan значение, которое отражает интервал времени, прошедший с момента вызова одного из Stopwatch методов, начинающих измерять истекшее время.
Значение TimeSpan также можно использовать в качестве замены DateTime значения, если это значение отражает время без ссылки на определенный день. Это использование аналогично DateTime.TimeOfDay свойствам и DateTimeOffset.TimeOfDay возвращает TimeSpan значение, представляющее время без ссылки на дату. Например, структуру TimeSpan можно использовать для представления ежедневного времени открытия или закрытия магазина или времени, в которое происходит любое регулярное событие.
В примере ниже определяется структура StoreInfo
, которая включает объекты TimeSpan , представляющие время закрытия и открытия магазина, а также объект TimeZoneInfo , представляющий часовой пояс магазина. Структура также включает два метода, IsOpenNow
и IsOpenAt
, указывающие, открыт ли магазин в то время, которое указал пользователь, предположительно находящийся в местном часовом поясе.
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 {0}: {1}",
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 {0}: {1}",
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,0000000 - 23:59:59.9999999999, определенное время дня.
TimeOnly
До введения типа программисты обычно используют DateTime тип или TimeSpan тип для представления определенного времени. Однако использование этих структур для имитации времени без даты может привести к некоторым проблемам, которые TimeOnly
решают:
TimeSpan
представляет истекшее время, например время, измеряемое с помощью стоп-часов. Верхний диапазон составляет более 29 000 лет, и его значение может быть отрицательным, чтобы указать, что перемещение назад во времени. ОтрицательныйTimeSpan
не указывает определенное время дня.- Если
TimeSpan
используется в качестве времени суток, существует риск того, что он может быть манипулировать значением за пределами 24-часового дня.TimeOnly
не имеет этого риска. Например, если смена работы сотрудника начинается с 18:00 и длится в течение 8 часов, добавление 8 часов вTimeOnly
структуру выполняется до 2:00. - Использование
DateTime
в течение дня требует, чтобы произвольные даты были связаны с временем, а затем игнорировались позже. Обычно рекомендуется выбратьDateTime.MinValue
(0001-01-01) в качестве даты, однако если часы вычитаются изDateTime
значения,OutOfRange
может возникнуть исключение.TimeOnly
не имеет этой проблемы, так как время откатывается вперед и назад вокруг 24-часового интервала времени. - Сериализация
DateTime
структуры включает компонент даты, который может скрыть намерение данных. Кроме того,TimeOnly
сериализует меньше данных.
Дополнительные сведения см. в TimeOnly
разделе "Использование структур DateOnly и TimeOnly".
Внимание
TimeOnly
недоступно в платформа .NET Framework.
Класс TimeZoneInfo
Класс TimeZoneInfo class represents any of the Earth's time zones, and enables the conversion of any date and time in one time zone to its equivalent in another time zone. Класс TimeZoneInfo позволяет работать со значениями даты и времени, обеспечивая однозначную идентификацию единственного момента времени с помощью любого значения даты и времени. Класс TimeZoneInfo также является расширяемым. Хотя он зависит от сведений о часовом поясе, предоставленных для систем Windows и определенных в реестре, он поддерживает создание настраиваемых часовых поясов. Кроме того, он поддерживает сериализацию и десериализацию сведений о часовом поясе.
В некоторых случаях использование всех преимуществ класса TimeZoneInfo может потребовать дальнейших усилий по разработке. Если значения даты и времени не тесно связаны с часовыми поясами, к которым они относятся, требуется дополнительная работа. Если приложение не предоставляет некоторые механизмы связывания даты и времени со связанным часовым поясом, это легко для определенного значения даты и времени, чтобы быть отсоединены от часового пояса. Одним из способов связывания этой информации является определение класса или структуры, содержащих значение даты и времени и связанный с ним часовой пояс.
Чтобы воспользоваться поддержкой часового пояса в .NET, необходимо знать часовой пояс, к которому относится значение даты и времени при создании экземпляра объекта даты и времени. Часовой пояс часто не известен, особенно в веб-приложениях или сетевых приложениях.