Escolher entre DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnly e TimeZoneInfo
Os aplicativos do .NET podem usar as informações de data e hora de várias maneiras. Os usos mais comuns das informações de data e hora incluem:
- Para refletir apenas uma data, de modo que as informações de hora se tornem irrelevantes.
- Para refletir somente um tempo, uma vez que a informação de data não é importante.
- Para refletir uma data abstrata e um tempo que não está vinculado a uma hora e local específicos (por exemplo, a maioria das lojas em um uma cadeia internacional abre em dias da semana às 9h).
- Para recuperar as informações de data e hora de fontes fora do .NET, normalmente nas quais as informações de data e hora são armazenadas em um tipo de dados simples.
- Para identificar um único ponto no tempo de maneira única e não ambígua. Alguns aplicativos exigem que a data e a hora sejam inequívocas somente no sistema host. Outros aplicativos exigem que seja inequívoco entre diferentes sistemas (ou seja, uma data serializada em um sistema pode ser desserializada de forma significativa e usada em outro sistema em qualquer lugar do mundo).
- Para preservar múltiplos horários relacionados (como o horário local do solicitante e o horário de recebimento do servidor em uma solicitação na Web).
- Para realizar a aritmética de data e hora, possivelmente com um resultado que identifica de maneira única e não ambígua um único ponto no tempo.
O .NET inclui os tipos DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnly e TimeZoneInfo. Todos eles podem ser usados para compilar aplicativos que funcionam com datas e horas.
Observação
Este artigo não discute o TimeZone, pois a funcionalidade foi quase totalmente incorporada à classe TimeZoneInfo. Sempre que possível, use a classe TimeZoneInfo, ao invés da classe TimeZone.
A estrutura DateTimeOffset
A estrutura DateTimeOffset representa um valor de data e hora, juntamente com um deslocamento que indica quanto o valor difere do UTC. Portanto, o valor sempre identifica sem ambiguidade um único ponto no tempo.
O tipo DateTimeOffset inclui toda a funcionalidade do tipo DateTime, juntamente com reconhecimento de fuso horário. Isso o torna adequado para aplicativos que:
- Identifique de maneira única e não ambígua um único ponto no tempo. O tipo DateTimeOffset pode ser usado para definir de forma inequívoca o significado de "agora", para registrar os tempos de transação, para registrar os tempos dos eventos de sistema ou de aplicativo e para registrar a criação de arquivos e os tempos de modificação.
- Realizar aritmética geral de data e hora.
- Preserve múltiplos horários relacionados, desde que esses horários sejam armazenados como dois valores separados ou como dois membros de uma estrutura.
Observação
Esses usos para os valores DateTimeOffset são muito mais comuns do que aqueles para os valores DateTime. Como resultado, considere o DateTimeOffset como o tipo de data e hora padrão para desenvolvimento de aplicativos.
Um valor DateTimeOffset não está vinculado a um fuso horário específico, mas pode ser proveniente de diversos fusos horários. O exemplo a seguir lista os fusos horários aos quais inúmeros valores DateTimeOffset (incluindo a Hora Padrão do Pacífico local) podem pertencer.
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
A saída mostra que cada valor de data e hora nesse exemplo pode pertencer a pelo menos três fusos horários diferentes. O valor DateTimeOffset de 10/06/2007 mostra que, se um valor de data e hora representa um horário de verão, a diferença UTC não corresponde necessariamente à diferença UTC de base do fuso horário de origem ou à diferença UTC encontrada no nome de exibição. Como um único valor DateTimeOffset não está rigorosamente acoplado ao fuso horário, ele não pode refletir a transição de um fuso horário para o horário de verão. Isso pode ser problemático quando a aritmética de data e hora é usada para manipular um valor DateTimeOffset. Para ver uma discussão sobre como realizar a aritmética de data e hora de uma forma que considere as regras de ajuste de um fuso horário, consulte Executando operações aritméticas com datas e horas.
A estrutura DateTime
Um valor DateTime define a data e a hora específicas. Isso inclui uma propriedade Kind que fornece informações limitadas sobre o fuso horário ao qual a data e a hora pertencem. O valor DateTimeKind retornado pela propriedade Kind indica se o valor DateTime representa a hora local (DateTimeKind.Local), UTC (Tempo Universal Coordenado) (DateTimeKind.Utc) ou uma hora não especificada (DateTimeKind.Unspecified).
A estrutura DateTime é adequada para aplicativos com uma ou mais das seguintes características:
- Trabalhar com datas e horas abstratas.
- Trabalhar com datas e horas para as quais as informações de fuso horário estão ausentes.
- Trabalhar apenas com datas e horas UTC.
- Realizar aritmética de data e hora, mas que há preocupação com resultados gerais. Por exemplo, em uma operação de adição que soma seis meses a uma data e hora determinada, geralmente não é importante se o resultado é ajustado para horário de verão.
A menos que um valor DateTime específico represente o UTC, geralmente a portabilidade do valor de data e hora é ambígua ou limitada. Por exemplo, se um valor DateTime representa a hora local, ele é portátil nesse fuso horário local (ou seja, se o valor for desserializado em outro sistema no mesmo fuso horário, esse valor ainda identificará de maneira inequívoca um único ponto no tempo). Fora do fuso horário local, esse valor DateTime pode ter várias interpretações. Se a propriedade Kind do valor for DateTimeKind.Unspecified, o valor será ainda menos portátil: agora ele é ambíguo no mesmo fuso horário e possivelmente até no mesmo sistema onde foi serializado pela primeira vez. Somente se um valor DateTime representar UTC, esse valor identificará inequivocamente um único ponto no tempo, independentemente do sistema ou fuso horário em que é usado.
Importante
Ao salvar ou compartilhar dados DateTime, use UTC e defina a propriedade Kind do valor DateTime como DateTimeKind.Utc.
A estrutura DateOnly
A estrutura DateOnly representa uma data específica, sem a hora. Como não tem nenhum componente de hora, ela representa uma data do início do dia até o final do dia. Essa estrutura é ideal para armazenar datas específicas, como uma data de nascimento, uma data de aniversário, um feriado ou uma data relacionada a negócios.
Embora você possa usar DateTime
enquanto ignora o componente de hora, há alguns benefícios em usar DateOnly
em vez de DateTime
:
- A estrutura
DateTime
poderá ser revertida para o dia anterior ou seguinte se for deslocada por um fuso horário.DateOnly
não pode ser deslocado por um fuso horário e sempre representa a data que foi definida. - Serializar uma estrutura
DateTime
inclui o componente de hora, que pode obscurecer a intenção dos dados. Além disso,DateOnly
serializa menos dados. - Quando o código interage com um banco de dados, como o SQL Server, datas inteiras geralmente são armazenadas como o tipo de dados
date
, que não inclui uma hora.DateOnly
corresponde melhor ao tipo de banco de dados.
Para obter mais informações sobre DateOnly
, consulte Como usar as estruturas DateOnly e TimeOnly.
Importante
DateOnly
não está disponível para o .NET Framework.
A estrutura TimeSpan
A estrutura TimeSpan representa um intervalo de tempo. Seus dois usos típicos são:
- Refletir o intervalo de tempo entre dois valores de data e hora. Por exemplo, subtrair um valor DateTime de outro retorna um valor TimeSpan.
- Calcular o tempo decorrido. Por exemplo, a propriedade Stopwatch.Elapsedretorna um valor TimeSpan, que reflete o intervalo de tempo decorrido desde a chamada para um dos métodos Stopwatch que começa a medir o tempo decorrido.
Um valor TimeSpan também pode ser usado como uma substituição para um valor DateTime, quando esse valor reflete um tempo sem referência a determinado dia. Esse uso é semelhante às propriedades DateTime.TimeOfDaye DateTimeOffset.TimeOfDay, que retornam um valor TimeSpan que representa o tempo sem referência a uma data. Por exemplo, a estrutura de TimeSpan pode ser usada para refletir a hora de abertura ou fechamento diária de uma loja, ou pode ser usada para representar o momento em que qualquer evento regular ocorre.
O exemplo a seguir define uma estrutura StoreInfo
que inclui objetos TimeSpan para a hora de abertura e de fechamento do repositório, bem como um objeto TimeZoneInfo que representa o fuso horário do repositório. A estrutura também inclui dois métodos, IsOpenNow
e IsOpenAt
, que indica se o repositório está aberto em um momento especificado pelo usuário, que se considera estar no fuso horário local.
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
A estrutura StoreInfo
pode ser usada pelo código do cliente como o exposto a seguir.
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
A estrutura TimeOnly
A estrutura TimeOnly representa um valor de hora do dia, como um despertador diário ou a hora em que você almoça todos os dias. TimeOnly
é limitado ao intervalo de 00:00:00.0000000 - 23:59:59.9999999, uma hora específica do dia.
Antes do tipo TimeOnly
ser introduzido, os programadores normalmente usavam o tipo DateTime ou o tipo TimeSpan para representar uma hora específica. No entanto, usar essas estruturas para simular uma hora sem uma data pode introduzir alguns problemas, o que TimeOnly
resolve:
-
TimeSpan
representa o tempo decorrido, como o tempo medido com um cronômetro. O intervalo superior é de mais de 29.000 anos, e seu valor pode ser negativo para indicar a movimentação para trás no tempo. UmTimeSpan
negativo não indica uma hora específica do dia. - Se
TimeSpan
for usado como uma hora do dia, há o risco de que possa ser manipulado para um valor fora do dia de 24 horas.TimeOnly
não tem esse risco. Por exemplo, se o turno de trabalho de um funcionário começar às 18:00 e durar 8 horas, a adição de 8 horas à estruturaTimeOnly
resulta em 2:00. - Usar
DateTime
para uma hora do dia requer que uma data arbitrária seja associada à hora e, em seguida, seja desconsiderada. É uma prática comum escolherDateTime.MinValue
(0001-01-01) como a data; no entanto, se horas forem subtraídas do valorDateTime
, uma exceçãoOutOfRange
poderá ocorrer.TimeOnly
não tem esse problema, pois o tempo decorre para frente e para trás em torno do período de 24 horas. - Serializar uma estrutura
DateTime
inclui o componente de data, que pode obscurecer a intenção dos dados. Além disso,TimeOnly
serializa menos dados.
Para obter mais informações sobre TimeOnly
, consulte Como usar as estruturas DateOnly e TimeOnly.
Importante
TimeOnly
não está disponível para o .NET Framework.
A classe TimeZoneInfo
A classe TimeZoneInfo representa qualquer um dos fusos horários da Terra e permite a conversão de qualquer data e hora em um fuso horário para o equivalente em outro fuso horário. A classe TimeZoneInfo possibilita trabalhar com datas e horas de modo que qualquer valor de data e hora identifique de forma inequívoca um único ponto no tempo. A classe TimeZoneInfo também é extensível. Embora dependa das informações de fuso horário fornecidas para sistemas Windows e definidas no registro, ela permite a criação de fusos horários personalizados. Ela também permite a serialização e desserialização das informações de fuso horário.
Em alguns casos, aproveitar ao máximo a classe TimeZoneInfo pode exigir um trabalho adicional de desenvolvimento. Se os valores de data e hora não forem rigorosamente acoplados aos fusos horários aos quais pertencem, um trabalho adicional será necessário. A menos que o aplicativo ofereça um mecanismo para vincular a data e hora ao fuso horário associado, é fácil para determinado valor de data e hora se desassociar do fuso horário. Um método de vinculação dessas informações é definir uma classe ou estrutura que contenha o valor de data e hora e seu objeto de fuso horário associado.
Para aproveitar o suporte a fuso horário no .NET, você deve saber o fuso horário a que um valor de data e hora pertence, quando esse objeto de data e hora for instanciado. Muitas vezes, o fuso horário não é conhecido, especialmente em aplicativos Web ou de rede.