Partilhar via


Escolha entre DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnly e TimeZoneInfo

Os aplicativos .NET podem usar informações de data e hora de várias maneiras. Os usos mais comuns de informações de data e hora incluem:

  • Para refletir apenas uma data, para que as informações de hora não sejam importantes.
  • Para refletir apenas uma hora, para que as informações de data não sejam importantes.
  • Para refletir uma data e hora abstrata que não esteja vinculada a uma hora e local específicos (por exemplo, a maioria das lojas de uma cadeia internacional abre nos dias úteis às 9h00).
  • Para recuperar informações de data e hora de fontes fora do .NET, normalmente onde as informações de data e hora são armazenadas em um tipo de dados simples.
  • Identificar única e inequivocamente um único ponto no tempo. Alguns aplicativos exigem que uma data e hora sejam inequívocas apenas no sistema host. Outros aplicativos exigem que ele seja inequívoco entre sistemas (ou seja, uma data serializada em um sistema pode ser significativamente desserializada e usada em outro sistema em qualquer lugar do mundo).
  • Para preservar vários horários relacionados (como a hora local do solicitante e a hora de recebimento de uma solicitação da Web do servidor).
  • Realizar aritmética de data e hora, possivelmente com um resultado que identifique única e inequivocamente um único ponto no tempo.

O .NET inclui os DateTimetipos , DateOnly, DateTimeOffset, TimeSpanTimeOnly, e , todos TimeZoneInfo os quais podem ser usados para criar aplicativos que funcionam com datas e horas.

Nota

Este artigo não discute TimeZone porque sua funcionalidade é quase inteiramente incorporada na TimeZoneInfo classe. Sempre que possível, use a TimeZoneInfo classe em vez da TimeZone classe.

A estrutura DateTimeOffset

A DateTimeOffset estrutura representa um valor de data e hora, juntamente com um deslocamento que indica o quanto esse valor difere do UTC. Assim, o valor identifica sempre de forma inequívoca um único ponto no tempo.

O DateTimeOffset tipo inclui todas as funcionalidades do tipo, juntamente com o reconhecimento de fuso DateTime horário. Isso o torna adequado para aplicações que:

  • Identifique de forma única e inequívoca um único ponto no tempo. O DateTimeOffset tipo pode ser usado para definir inequivocamente o significado de "agora", para registrar tempos de transação, para registrar os tempos de eventos do sistema ou aplicativo e para registrar os tempos de criação e modificação de arquivos.
  • Execute aritmética geral de data e hora.
  • Preserve vários tempos relacionados, desde que esses tempos sejam armazenados como dois valores separados ou como dois membros de uma estrutura.

Nota

Esses usos para DateTimeOffset valores são muito mais comuns do que aqueles para DateTime valores. Como resultado, considere DateTimeOffset como o tipo de data e hora padrão para o desenvolvimento de aplicativos.

Um DateTimeOffset valor não está vinculado a um fuso horário específico, mas pode ter origem em uma variedade de fusos horários. O exemplo a seguir lista os fusos horários aos quais vários valores (incluindo uma hora padrão do DateTimeOffset 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 neste exemplo pode pertencer a pelo menos três fusos horários diferentes. O DateTimeOffset valor de 6/10/2007 mostra que, se um valor de data e hora representa um horário de verão, seu deslocamento do UTC nem sequer corresponde necessariamente ao deslocamento UTC base do fuso horário de origem ou ao deslocamento do UTC encontrado em seu nome de exibição. Como um único DateTimeOffset valor não está firmemente acoplado ao seu fuso horário, ele não pode refletir a transição de um fuso horário de e para o horário de verão. Isso pode ser problemático quando a aritmética de data e hora é usada para manipular um DateTimeOffset valor. Para obter uma discussão sobre como executar a aritmética de data e hora de uma forma que leve em conta as regras de ajuste de um fuso horário, consulte Executando operações aritméticas com datas e horas.

A estrutura DateTime

Um DateTime valor define uma data e hora específicas. Inclui uma Kind propriedade que fornece informações limitadas sobre o fuso horário ao qual essa data e hora pertencem. O DateTimeKind valor retornado pela Kind propriedade indica se o DateTime valor representa a hora local (DateTimeKind.Local), Tempo Universal Coordenado (UTC) (DateTimeKind.Utc), ou uma hora não especificada (DateTimeKind.Unspecified).

A DateTime estrutura é adequada para aplicações com uma ou mais das seguintes características:

  • Trabalhe com datas e horários abstratos.
  • Trabalhe com datas e horas para as quais faltam informações de fuso horário.
  • Trabalhe apenas com datas e horas UTC.
  • Realizam aritmética de data e hora, mas preocupam-se com resultados gerais. Por exemplo, em uma operação adicional que adiciona seis meses a uma determinada data e hora, muitas vezes não é importante se o resultado é ajustado para o horário de verão.

A menos que um determinado DateTime valor represente UTC, esse valor de data e hora é muitas vezes ambíguo ou limitado em sua portabilidade. Por exemplo, se um DateTime valor representa a hora local, ele é portátil dentro desse fuso horário local (ou seja, se o valor é desserializado em outro sistema no mesmo fuso horário, esse valor ainda identifica inequivocamente um único ponto no tempo). Fora do fuso horário local, esse DateTime valor pode ter várias interpretações. Se a propriedade do Kind valor é DateTimeKind.Unspecified, é ainda menos portátil: agora é ambíguo dentro do mesmo fuso horário e, possivelmente, até mesmo no mesmo sistema onde foi serializado pela primeira vez. Somente se um DateTime valor representar UTC é que esse valor identifica inequivocamente um único ponto no tempo, independentemente do sistema ou fuso horário em que o valor é usado.

Importante

Ao salvar ou compartilhar DateTime dados, use UTC e defina a DateTime propriedade do Kind valor como DateTimeKind.Utc.

A estrutura DateOnly

A DateOnly estrutura representa uma data específica, sem tempo. Uma vez que não tem componente de tempo, representa uma data desde o início do dia até ao 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 ao negócio.

Embora você possa usar DateTime ignorando o componente de tempo, há alguns benefícios em usar DateOnly sobre DateTime:

  • A DateTime estrutura pode rolar para o dia anterior ou seguinte se for compensada por um fuso horário. DateOnly não pode ser deslocado por um fuso horário e sempre representa a data que foi definida.
  • A serialização de uma DateTime estrutura inclui o componente de tempo, 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 date tipo de dados, que não inclui uma hora. DateOnly corresponde melhor ao tipo de banco de dados.

Para obter mais informações sobre DateOnlyo , consulte Como usar as estruturas DateOnly e TimeOnly.

Importante

DateOnly não está disponível no .NET Framework.

A estrutura TimeSpan

A TimeSpan estrutura representa um intervalo de tempo. As suas duas utilizações típicas são:

  • Refletindo o intervalo de tempo entre dois valores de data e hora. Por exemplo, subtrair um DateTime valor de outro retorna um TimeSpan valor.
  • Medição do tempo decorrido. Por exemplo, a Stopwatch.Elapsed propriedade retorna um TimeSpan valor que reflete o intervalo de tempo decorrido desde a chamada para um dos métodos que começa a medir o Stopwatch tempo decorrido.

Um TimeSpan valor também pode ser usado como um substituto para um DateTime valor quando esse valor reflete uma hora sem referência a um dia específico. Esse uso é semelhante às DateTime.TimeOfDay propriedades e DateTimeOffset.TimeOfDay , que retornam um TimeSpan valor que representa a hora sem referência a uma data. Por exemplo, a TimeSpan estrutura pode ser usada para refletir o horário diário de abertura ou fechamento de uma loja, ou pode ser usada para representar o momento em que qualquer evento regular ocorre.

O exemplo a seguir define uma StoreInfo estrutura que inclui TimeSpan objetos para horários de abertura e fechamento da loja, bem como um TimeZoneInfo objeto que representa o fuso horário da loja. A estrutura também inclui dois métodos, IsOpenNow e IsOpenAt, que indica se a loja está aberta em um horário especificado pelo usuário, que se presume 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 StoreInfo estrutura pode então ser usada pelo código do cliente como o seguinte.

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 TimeOnly estrutura representa um valor de hora do dia, como um despertador diário ou a que horas você almoça todos os dias. TimeOnlyestá limitado ao intervalo de 00:00:00.0000000 - 23:59:59.9999999, uma hora específica do dia.

Antes do TimeOnly tipo ser introduzido, os programadores normalmente usavam o DateTime tipo ou o TimeSpan tipo para representar um tempo específico. No entanto, utilizar estas estruturas para simular uma hora sem 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 retrocesso no tempo. Um negativo TimeSpan 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ça às 18:00 e dura 8 horas, adicionar TimeOnly 8 horas à estrutura passa para 2:00.
  • Usar DateTime para uma hora do dia requer que uma data arbitrária seja associada à hora e, posteriormente, desconsiderada. É prática comum escolher DateTime.MinValue (0001-01-01) como a data, no entanto, se as horas forem subtraídas do DateTime valor, uma OutOfRange exceção pode ocorrer. TimeOnly não tem esse problema, pois o tempo rola para frente e para trás em torno do período de 24 horas.
  • A serialização de uma DateTime estrutura 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 TimeOnlyo , consulte Como usar as estruturas DateOnly e TimeOnly.

Importante

TimeOnly não está disponível no .NET Framework.

A classe TimeZoneInfo

A TimeZoneInfo classe 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 seu equivalente em outro fuso horário. A TimeZoneInfo classe torna possível trabalhar com datas e horas para que qualquer valor de data e hora identifique inequivocamente um único ponto no tempo. A TimeZoneInfo classe também é extensível. Embora dependa das informações de fuso horário fornecidas para sistemas Windows e definidas no registro, ele suporta a criação de fusos horários personalizados. Ele também suporta a serialização e desserialização de informações de fuso horário.

Em alguns casos, tirar o TimeZoneInfo máximo proveito da aula pode exigir mais trabalho de desenvolvimento. Se os valores de data e hora não estiverem estreitamente ligados aos fusos horários a que pertencem, é necessário trabalhar mais. A menos que seu aplicativo forneça algum mecanismo para vincular uma data e hora com seu fuso horário associado, é fácil que um determinado valor de data e hora seja desassociado de seu fuso horário. Um método de vincular essas 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 de fuso horário no .NET, você deve saber o fuso horário ao qual um valor de data e hora pertence quando esse objeto de data e hora é instanciado. O fuso horário muitas vezes não é conhecido, particularmente em aplicativos Web ou de rede.

Consulte também