DateTime vs DateTimeOffset

742

Atualmente, temos uma maneira padrão de lidar com o .NET de DateTimemaneira ciente do TimeZone: sempre que produzimos um DateTime, fazemos no UTC (por exemplo, usando DateTime.UtcNow), e sempre que exibimos um, convertemos novamente do UTC para o horário local do usuário. .

Isso funciona bem, mas eu tenho lido sobre DateTimeOffsete como ele captura a hora local e UTC no próprio objeto. Portanto, a pergunta é: quais seriam as vantagens de usar em DateTimeOffsetcomparação com o que já estamos fazendo?

David Reis
fonte
3
Existem ótimas respostas abaixo. Mas ainda me pergunto o que, se houver, poderia ter convencido você a começar a usar o DateTimeOffset.
HappyNomad
Quando se trata de armazenamento, o stackoverflow.com/questions/4715620/… também é interessante.
Dejan

Respostas:

1169

DateTimeOffseté uma representação do tempo instantâneo (também conhecido como tempo absoluto ). Com isso, quero dizer um momento no tempo que é universal para todos (sem levar em conta os segundos bissextos ou os efeitos relativísticos da dilatação do tempo ). Outra maneira de representar o tempo instantâneo é com um DateTimeonde .Kindestá DateTimeKind.Utc.

Isso é diferente do horário do calendário (também conhecido como horário civil ), que é uma posição no calendário de alguém, e existem muitos calendários diferentes em todo o mundo. Chamamos esses fusos horários de calendários . A hora do calendário é representada por um DateTimeonde .Kindé DateTimeKind.Unspecifiedou DateTimeKind.Local. E .Localsó é significativo nos cenários em que você tem uma compreensão implícita de onde o computador que está usando o resultado está posicionado. (Por exemplo, a estação de trabalho de um usuário)

Então, por que em DateTimeOffsetvez de um UTC DateTime? É tudo sobre perspectiva. Vamos usar uma analogia - fingiremos ser fotógrafos.

Imagine que você está na linha do tempo de um calendário, apontando uma câmera para uma pessoa na linha do tempo instantânea definida à sua frente. Você alinha sua câmera de acordo com as regras do seu fuso horário - que mudam periodicamente devido ao horário de verão ou devido a outras alterações na definição legal do seu fuso horário. (Você não tem uma mão firme, então sua câmera está trêmula.)

A pessoa parada na foto veria o ângulo em que sua câmera veio. Se outros estavam tirando fotos, eles poderiam ser de diferentes ângulos. Isto é o que a Offsetparte do DateTimeOffsetrepresenta.

Portanto, se você rotular sua câmera como "Hora do Leste", algumas vezes estará apontando para -5 e outras para -4. Existem câmeras em todo o mundo, todas rotuladas de coisas diferentes e todas apontando para a mesma linha do tempo instantânea de diferentes ângulos. Alguns deles estão próximos um do outro (ou por cima deles), portanto, apenas conhecer o deslocamento não é suficiente para determinar a qual fuso horário o horário está relacionado.

E o UTC? Bem, é a única câmera por aí que garante uma mão firme. Está em um tripé, firmemente ancorado no chão. Não vai a lugar nenhum. Chamamos seu ângulo de perspectiva de deslocamento zero.

Visualização de hora instantânea versus hora do calendário

Então - o que essa analogia nos diz? Ele fornece algumas diretrizes intuitivas -

  • Se você estiver representando um horário em relação a algum lugar em particular, represente-o no horário do calendário com a DateTime. Só não se esqueça de nunca confundir um calendário com outro. Unspecifieddeve ser sua suposição. Localsó é útil vindo DateTime.Now. Por exemplo, eu posso obtê DateTime.Now-lo e salvá-lo em um banco de dados - mas quando o recupero, devo assumir que é Unspecified. Não posso confiar que meu calendário local seja o mesmo de onde foi originalmente retirado.

  • Se você sempre deve ter certeza do momento, verifique se está representando um tempo instantâneo. Use DateTimeOffsetpara aplicá-lo ou use o UTC DateTimepor convenção.

  • Se você precisar acompanhar um momento instantâneo, mas também quiser saber "A que horas o usuário achou que estava no calendário local?" - então você deve usar a DateTimeOffset. Isso é muito importante para os sistemas de cronometragem, por exemplo - tanto por questões técnicas quanto jurídicas.

  • Se você precisar modificar um gravado anteriormente DateTimeOffset- você não possui informações suficientes apenas no deslocamento para garantir que o novo deslocamento ainda seja relevante para o usuário. Você também deve armazenar um identificador de fuso horário (pense: preciso do nome dessa câmera para poder tirar uma nova foto, mesmo que a posição tenha mudado).

    Também deve ser destacado que o Noda Time tem uma representação chamada ZonedDateTimepara isso, enquanto a biblioteca de classes base .Net não possui nada parecido. Você precisaria armazenar a DateTimeOffsete um TimeZoneInfo.Idvalor.

  • Ocasionalmente, você desejará representar um horário do calendário local para "quem estiver olhando para ele". Por exemplo, ao definir o que hoje significa. Hoje é sempre meia-noite a meia-noite, mas estes representam um número quase infinito de intervalos sobrepostos na linha do tempo instantânea. (Na prática, temos um número finito de fusos horários, mas você pode expressar compensações até o ponto). Portanto, nessas situações, certifique-se de entender como limitar o "quem está perguntando?" questione em um único fuso horário ou lembre-se de convertê-los novamente para o horário instantâneo, conforme apropriado.

Aqui estão alguns outros pequenos detalhes sobre DateTimeOffsetessa analogia e algumas dicas para mantê-lo reto:

  • Se você comparar dois DateTimeOffsetvalores, eles serão normalizados primeiro com o deslocamento zero antes da comparação. Em outras palavras, 2012-01-01T00:00:00+00:00e se 2012-01-01T02:00:00+02:00referem ao mesmo momento instantâneo e, portanto, são equivalentes.

  • Se você estiver fazendo qualquer teste de unidade e necessidade de ter certeza do offset, teste tanto o DateTimeOffsetvalor, ea .Offsetpropriedade separadamente.

  • Há uma conversão implícita unidirecional incorporada à estrutura .Net que permite passar a DateTimepara qualquer DateTimeOffsetparâmetro ou variável. Ao fazer isso, os .Kindassuntos . Se você passar um tipo UTC, ele será transferido com um deslocamento zero, mas se você passar um .Localou .Unspecified, será assumido como local . A estrutura está basicamente dizendo: "Bem, você me pediu para converter a hora do calendário em hora instantânea, mas não tenho ideia de onde isso veio, então vou usar o calendário local". Essa é uma grande dificuldade se você carregar um não especificado DateTimeem um computador com um fuso horário diferente. (IMHO - isso deve gerar uma exceção - mas não gera).

Plug Shameless:

Muitas pessoas compartilharam comigo que acham essa analogia extremamente valiosa, por isso a incluí no meu curso Pluralsight, Fundamentos de data e hora . Você encontrará uma explicação passo a passo da analogia da câmera no segundo módulo, "Assuntos de contexto", no clipe intitulado "Horário do calendário versus horário instantâneo".

Matt Johnson-Pint
fonte
4
@ZackJannsen Se você possui um DateTimeOffsetem C #, deve persistir em um DATETIMEOFFSETno SQL Server. DATETIME2ou apenas DATETIME(dependendo do intervalo necessário) são adequados para DateTimevalores regulares . Sim - você pode resolver uma hora local a partir de qualquer pareamento de fuso horário + dto ou utc. A diferença é: você sempre deseja calcular as regras a cada resolução ou deseja pré-calculá-las? Em muitos casos (às vezes por questões legais), um DTO é uma escolha melhor.
precisa
3
@ZackJannsen Para a segunda parte da sua pergunta, eu recomendaria fazer o máximo possível no lado do servidor. Javascript não é tão bom para o cálculo de fuso horário. Se você precisar fazer isso, use uma dessas bibliotecas . Mas o lado do servidor é o melhor. Se você tiver outras perguntas mais detalhadas, inicie uma nova pergunta SO para elas e eu responderei, se puder. Obrigado.
precisa
4
@ JoãoLeme - Depende de onde você o obteve. Você está certo de que, se você disser DateTimeOffset.Nowno servidor, obterá o deslocamento do servidor. O ponto é que o DateTimeOffsettipo pode reter esse deslocamento. Você poderia facilmente fazer isso no cliente, enviá-lo ao servidor e, em seguida, o servidor saberia o deslocamento do cliente.
Matt Johnson-Pint
8
Realmente amo a analogia da câmera.
Sachin Kainth
2
Sim esta correto. Exceto que o DTO é armazenado como o par (hora local, deslocamento), não o par (hora utc, deslocamento). Em outras palavras, o deslocamento do UTC já está refletido no horário local. Para converter novamente em utc, inverta o sinal do deslocamento e aplique-o à hora local.
Matt Johnson-Pint
328

Da Microsoft:

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

source: "Escolhendo entre DateTime, DateTimeOffset, TimeSpan e TimeZoneInfo" , MSDN

Usamos DateTimeOffsetpara quase tudo, pois nosso aplicativo lida com pontos específicos no tempo (por exemplo, quando um registro foi criado / atualizado). Como observação, também usamos DATETIMEOFFSETno SQL Server 2008.

Considero DateTimeútil quando você deseja lidar apenas com datas, horários apenas ou em um sentido genérico. Por exemplo, se você tem um alarme que deseja disparar todos os dias às 7 da manhã, pode armazená-lo em uma DateTimeutilização DateTimeKindde a Unspecifiedporque deseja que ele toque às 7 da manhã, independentemente do horário de verão. Mas se você quiser representar o histórico de ocorrências de alarme, use DateTimeOffset.

Tenha cuidado ao usar uma combinação de DateTimeOffsete DateTimeprincipalmente ao atribuir e comparar entre os tipos. Além disso, compare apenas DateTimeinstâncias iguais DateTimeKindporque DateTimeignora o deslocamento do fuso horário ao comparar.

Argila
fonte
146
A resposta aceita é excessivamente longa e a analogia é tensa; essa é uma resposta IMO muito melhor e mais concisa.
Nexus disse
10
Vou apenas dizer que também gosto dessa resposta e votada. Embora na última parte - mesmo garantindo que Kindsejam iguais, a comparação pode estar errada. Se ambos os lados o DateTimeKind.Unspecifiedconhecem, você realmente não sabe que eles vieram do mesmo fuso horário. Se os dois lados estiverem DateTimeKind.Local, a maioria das comparações será boa, mas você ainda pode ter erros, pois um lado é ambíguo no fuso horário local. Realmente, apenas as DateTimeKind.Utccomparações são infalíveis, e sim, DateTimeOffsetgeralmente é o preferido. (Felicidades!)
Matt Johnson-Pint
1
+1 eu acrescentaria a isso: o DataType que você escolher deve refletir sua intenção. Não use DateTimeOffset em todos os lugares, apenas causa. Se o deslocamento for importante para seus cálculos e leitura de / persistência para o banco de dados, use DateTimeOffset. Se isso não importa, use DateTime, para entender (apenas olhando para o DataType) que o Offset não deve ter influência e o Times deve permanecer relativo à localidade do servidor / máquina em que seu código C # está sendo executado.
MikeTeeVee 22/07/19
"Mas se você deseja representar o histórico de ocorrências de alarme, você usaria DateTimeOffset." Gostaria de explicar por que você acha que é isso? Eu poderia preencher isso lendo todas as informações desta página, mas talvez você também possa fornecer essas informações de uma maneira fácil de ler. Esta é uma resposta muito legível.
Barrosy 8/11/19
77

O DateTime é capaz de armazenar apenas dois horários distintos, o horário local e o UTC. A propriedade Kind indica qual.

O DateTimeOffset expande isso ao poder armazenar horários locais de qualquer lugar do mundo. Ele também armazena o deslocamento entre a hora local e o UTC. Observe como o DateTime não pode fazer isso, a menos que você adicione um membro extra à sua classe para armazenar esse deslocamento UTC. Ou apenas trabalhe com o UTC. O que por si só é uma ótima idéia.

Hans Passant
fonte
33

Existem alguns lugares onde DateTimeOffsetfaz sentido. Uma é quando você lida com eventos recorrentes e com horário de verão. Digamos que eu queira definir um alarme para tocar às 9h todos os dias. Se eu usar a regra "armazenar como UTC, exibir como hora local", o alarme disparará em um horário diferente quando o horário de verão estiver em vigor.

Provavelmente existem outros, mas o exemplo acima é um exemplo que já encontrei no passado (isso foi antes da adição DateTimeOffsetao BCL - minha solução na época era armazenar explicitamente o horário no fuso horário local e salvar informações sobre o fuso horário: basicamente o que DateTimeOffsetfaz internamente).

Dean Harding
fonte
12
DateTimeOffset não corrige o problema do horário de verão
JarrettV 15/01
2
O uso da classe TimeZoneInfo carrega regras para o horário de verão. se você estiver no .net 3.5 ou posterior, use as classes TimeZone ou TimeZoneInfo para lidar com datas que devem lidar com o horário de verão em conjunto com o deslocamento do fuso horário.
Zack Jannsen 29/03
1
Sim, é um bom exemplo de uma exceção (o aplicativo de alarme), mas quando a hora é mais importante que a data, você realmente deve armazenar esses itens separados na estrutura de dados de agendamento do aplicativo, ou seja, tipo de ocorrência = Diariamente e hora = 09:00. O ponto aqui é que o desenvolvedor precisa estar ciente de que tipo de data está gravando, calculando ou apresentando aos usuários. Especialmente, os aplicativos tendem a ser mais globais agora que temos a Internet como lojas padrão e grandes de aplicativos para escrever software. Como um nó lateral, eu também gostaria que a Microsoft adicionasse uma estrutura de Data e Hora separada.
Tony Muro
3
Resumindo os comentários de Jarrett e Zack: Parece que o DateTimeOffset sozinho não tratará do problema do horário de verão, mas o uso do DateTimeOffset em conjunto com o TimeZoneInfo o tratará. Isso não é diferente de DateTime, em que tipo é Utc. Nos dois casos, preciso conhecer o fuso horário (não apenas o deslocamento) do calendário no qual estou projetando o momento. (Eu posso armazenar isso no perfil de um usuário ou obtê-lo do cliente (por exemplo, Windows), se possível). Soa certo?
9608 Jeremy Cook
"Há alguns lugares onde o DateTimeOffset faz sentido." --- Indiscutivelmente, mais frequentemente faz sentido do que não.
Ronnie Overby
23

A distinção mais importante é que o DateTime não armazena informações de fuso horário, enquanto o DateTimeOffset faz.

Embora o DateTime faça distinção entre UTC e Local, não há absolutamente nenhum deslocamento explícito de fuso horário associado a ele. Se você fizer qualquer tipo de serialização ou conversão, o fuso horário do servidor será usado. Mesmo se você criar manualmente um horário local adicionando minutos para compensar um horário UTC, ainda poderá receber bits na etapa de serialização, porque (devido à falta de um deslocamento explícito no DateTime), ele usará o deslocamento de fuso horário do servidor.

Por exemplo, se você serializar um valor DateTime com Kind = Local usando Json.Net e um formato de data ISO, obterá uma string como 2015-08-05T07:00:00-04. Observe que a última parte (-04) não teve nada a ver com o seu DateTime ou qualquer deslocamento que você usou para calculá-lo ... é apenas o deslocamento do fuso horário do servidor.

Enquanto isso, DateTimeOffset inclui explicitamente o deslocamento. Pode não incluir o nome do fuso horário, mas pelo menos inclui o deslocamento, e se você o serializar, obterá o deslocamento incluído explicitamente no seu valor, em vez do horário local do servidor.

Triynko
fonte
14
com todas as respostas acima, eu me pergunto por que ninguém se preocupou em escrever a sua única frase que resume tudoThe most important distinction is that DateTime does not store time zone information, while DateTimeOffset does.
Korayem
9
DateTimeOffset NÃO armazena informações de fuso horário. O documento da Microsoft intitulado "Escolhendo entre DateTime, DateTimeOffset, TimeSpan e TimeZoneInfo" especifica esta afirmação: "Um valor DateTimeOffset não está vinculado a um fuso horário específico, mas pode se originar em vários fusos horários". Dito isto, DateTimeOffset É o fuso horário AWARE, contendo o deslocamento do UTC, o que faz toda a diferença e é por isso que é recomendado pela Microsoft a classe padrão ao lidar com o desenvolvimento de aplicativos que lida com informações de data. Se você realmente se preocupam com o qual fuso horário específico os dados vieram, você deve preservar essa separadamente
stonedauwg
1
Sim, mas como foi mostrado em muitos lugares, as horas + ou - não dizem nada sobre o fuso horário em que você estava e é inútil. Dependendo do que você precisa fazer, você também pode armazenar um datetime como Kind.Unspecified e, em seguida, armazenar o ID do fuso horário e acho que você está realmente melhor.
Arwin #
13

Este trecho de código da Microsoft explica tudo:

// Find difference between Date.Now and Date.UtcNow
  date1 = DateTime.Now;
  date2 = DateTime.UtcNow;
  difference = date1 - date2;
  Console.WriteLine("{0} - {1} = {2}", date1, date2, difference);

  // Find difference between Now and UtcNow using DateTimeOffset
  dateOffset1 = DateTimeOffset.Now;
  dateOffset2 = DateTimeOffset.UtcNow;
  difference = dateOffset1 - dateOffset2;
  Console.WriteLine("{0} - {1} = {2}", 
                    dateOffset1, dateOffset2, difference);
  // If run in the Pacific Standard time zone on 4/2/2007, the example
  // displays the following output to the console:
  //    4/2/2007 7:23:57 PM - 4/3/2007 2:23:57 AM = -07:00:00
  //    4/2/2007 7:23:57 PM -07:00 - 4/3/2007 2:23:57 AM +00:00 = 00:00:00
Mojtaba
fonte
9

A maioria das respostas é boa, mas pensei em adicionar mais alguns links do MSDN para obter mais informações

dekdev
fonte
7

Uma grande diferença é que ela DateTimeOffsetpode ser usada em conjunto com TimeZoneInfopara converter em horários locais em fusos horários diferentes do atual.

Isso é útil em um aplicativo de servidor (por exemplo, ASP.NET) que é acessado por usuários em fusos horários diferentes.

Joe
fonte
3
@Bugeo Bugeo é verdade, mas há um risco. Você pode comparar dois DateTimes chamando primeiro "ToUniversalTime" em cada um. Se você tiver exatamente um valor na comparação DateTimeKind = Não especificado, sua estratégia falhará. Esse potencial de falha é um motivo para considerar DateTimeOffset sobre DateTime quando são necessárias conversões para a hora local.
Zack Jannsen 29/03
Como acima, acho que nesse cenário é melhor você armazenar o TimeZoneId do que usar DateTimeOffset, o que, em última análise, não significa nada.
Arwin #
2

O único lado negativo do DateTimeOffset que vejo é que a Microsoft "esqueceu" (por design) o suporte na classe XmlSerializer. Mas, desde então, foi adicionado à classe de utilitário XmlConvert.

XmlConvert.ToDateTimeOffset

XmlConvert.ToString

Eu digo: vá em frente e use DateTimeOffset e TimeZoneInfo por causa de todos os benefícios, tenha cuidado ao criar entidades que serão ou poderão ser serializadas de ou para XML (todos os objetos de negócios).

Tony Wall
fonte