Atualmente, temos uma maneira padrão de lidar com o .NET de DateTime
maneira 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 DateTimeOffset
e como ele captura a hora local e UTC no próprio objeto. Portanto, a pergunta é: quais seriam as vantagens de usar em DateTimeOffset
comparação com o que já estamos fazendo?
Respostas:
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 umDateTime
onde.Kind
está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
DateTime
onde.Kind
éDateTimeKind.Unspecified
ouDateTimeKind.Local
. E.Local
só é 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
DateTimeOffset
vez de um UTCDateTime
? É 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
Offset
parte doDateTimeOffset
representa.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.
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.Unspecified
deve ser sua suposição.Local
só é útil vindoDateTime.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
DateTimeOffset
para aplicá-lo ou use o UTCDateTime
por 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
ZonedDateTime
para isso, enquanto a biblioteca de classes base .Net não possui nada parecido. Você precisaria armazenar aDateTimeOffset
e umTimeZoneInfo.Id
valor.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
DateTimeOffset
essa analogia e algumas dicas para mantê-lo reto:Se você comparar dois
DateTimeOffset
valores, eles serão normalizados primeiro com o deslocamento zero antes da comparação. Em outras palavras,2012-01-01T00:00:00+00:00
e se2012-01-01T02:00:00+02:00
referem 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
DateTimeOffset
valor, ea.Offset
propriedade separadamente.Há uma conversão implícita unidirecional incorporada à estrutura .Net que permite passar a
DateTime
para qualquerDateTimeOffset
parâmetro ou variável. Ao fazer isso, os.Kind
assuntos . Se você passar um tipo UTC, ele será transferido com um deslocamento zero, mas se você passar um.Local
ou.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 especificadoDateTime
em 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".
fonte
DateTimeOffset
em C #, deve persistir em umDATETIMEOFFSET
no SQL Server.DATETIME2
ou apenasDATETIME
(dependendo do intervalo necessário) são adequados paraDateTime
valores 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.DateTimeOffset.Now
no servidor, obterá o deslocamento do servidor. O ponto é que oDateTimeOffset
tipo 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.Da Microsoft:
source: "Escolhendo entre DateTime, DateTimeOffset, TimeSpan e TimeZoneInfo" , MSDN
Usamos
DateTimeOffset
para 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 usamosDATETIMEOFFSET
no 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 umaDateTime
utilizaçãoDateTimeKind
de aUnspecified
porque 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, useDateTimeOffset
.Tenha cuidado ao usar uma combinação de
DateTimeOffset
eDateTime
principalmente ao atribuir e comparar entre os tipos. Além disso, compare apenasDateTime
instâncias iguaisDateTimeKind
porqueDateTime
ignora o deslocamento do fuso horário ao comparar.fonte
Kind
sejam iguais, a comparação pode estar errada. Se ambos os lados oDateTimeKind.Unspecified
conhecem, você realmente não sabe que eles vieram do mesmo fuso horário. Se os dois lados estiveremDateTimeKind.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 asDateTimeKind.Utc
comparações são infalíveis, e sim,DateTimeOffset
geralmente é o preferido. (Felicidades!)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.
fonte
Existem alguns lugares onde
DateTimeOffset
faz 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
DateTimeOffset
ao 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 queDateTimeOffset
faz internamente).fonte
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.
fonte
The most important distinction is that DateTime does not store time zone information, while DateTimeOffset does.
Este trecho de código da Microsoft explica tudo:
fonte
A maioria das respostas é boa, mas pensei em adicionar mais alguns links do MSDN para obter mais informações
fonte
Uma grande diferença é que ela
DateTimeOffset
pode ser usada em conjunto comTimeZoneInfo
para 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.
fonte
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).
fonte