Prática recomendada para armazenar o DateTime com base no fuso horário

16

Desenvolvimento de um aplicativo da web que permita ao usuário agendar compromissos com base no fuso horário. E estou armazenando o datetime do usuário agendado como datetime do servidor no campo do banco de dados. Ao mostrar as informações de agendamento, o valor do banco de dados foi recuperado e convertido no fuso horário do usuário. processamento na base de código Estou convertendo o DateTime com base no fuso horário do usuário. Sugira que esta é uma prática recomendada ou existe alguma maneira fácil?

user46506
fonte

Respostas:

33

Bem-vindo a um dos problemas mais difíceis da programação não computacional - representando corretamente datas e horas para os usuários finais.

Realisticamente, os carimbos de data e hora devem ser armazenados em uma única representação fixa, independentemente de como serão interpretados, porque, por mais que você tente, sempre terá casos ambíguos e não poderá resolvê-los sem uma representação fixa. E você escolheu um dos piores casos de uso - agendar um compromisso. O único pior caso de uso comum são as viagens aéreas, nas quais uma viagem pode começar em um fuso horário e terminar em outro, possível em um horário local anterior.

Sempre, sempre, SEMPRE armazene no UTC e exiba no fuso horário preferido ou especificado explicitamente pelo usuário. Se possível, faça com que o usuário diga em que fuso horário acredita que o carimbo de data / hora deve estar quando o inserir ( por exemplo , tenha um campo explícito de fuso horário e preencha-o previamente com sua zona preferida).

Ross Patterson
fonte
1
+1. E é flexível. Tendo um tempo de referência padrão universal, consistente e universal - qualquer "desreferenciamento" de zona local será realizada corretamente.
Radarbob
Na verdade, agendar um compromisso é uma das poucas vezes em que você não pode armazená-lo como UTC: se as regras do horário de verão mudarem (ou o deslocamento do UTC), o que acontece com o horário do compromisso? Na maioria dos casos, você deseja que o horário do compromisso permaneça o mesmo localmente, o que significa que o valor UTC é alterado. Você provavelmente deseja armazenar um valor representando "TimeInTimeZone + TimeZone", do qual você pode derivar o UTC facilmente. O agendamento de fuso horário (como as companhias aéreas) tem restrições que provavelmente significam que uma combinação de ambos é usada.
Clockwork-Muse
1
Oh, fica pior do que isso. Eu regularmente tenho reuniões com colegas de outros países cujas regras de mudança de horário diferem das minhas. Quando isso acontece, qual é a resposta certa? Na verdade, nenhum computador pode saber :-(
Ross Patterson
3

A maneira mais fácil de lidar com informações de data / hora depende dos requisitos do seu aplicativo.

Se a data e a hora forem inseridas pelo usuário e o servidor não processar essas informações de data / hora, exceto para armazená-las em um banco de dados e não houver expectativa de que a hora inserida seja adaptada ao local em que é exibida (por exemplo, , o usuário inseriu "20:00" e deve ser exibido como "20:00", independentemente do local em que for visualizado). A maneira mais fácil é armazenar o DateTime como horário local sem nenhuma informação de fuso horário.

Por outro lado, se o servidor precisar usar o DateTime inserido como um gatilho para fazer algo, ou se o horário deve ser ajustado corretamente para o fuso horário atual, sua melhor opção é armazenar o DateTime como horário UTC e ajustá-lo ao local hora (para o local atual do usuário) sempre que você o lê no banco de dados.

Bart van Ingen Schenau
fonte
-1 para "armazená-lo na hora local", +1 para "armazená-lo no UTC".
Ross Patterson
@ RossPatterson: Você pode explicar seu '-1 para "armazená-lo na hora local"'? Estou curioso para saber por que deve ser uma má ideia, dadas as condições que dei a ela.
Bart van Ingen Schenau
1
Por exemplo, o uso da hora local do usuário significa que todo valor é efetivamente um de várias dezenas de tipos de dados diferentes. O que significa que não é possível executar operações de banco de dados interessantes como "TIMESTAMP> '2013-08-27T12: 00: 00'". Isso significa que você não pode saber quando e se aplicar as conversões de horário de verão.
Ross Patterson
@ RossPatterson: Obrigado. Concordo que a hora local não é a solução certa na maioria dos casos.
Bart van Ingen Schenau
2

É importante que você não confunda dois conceitos relacionados.

Se você está manipulando um ponto no tempo real, ou seja, um horário específico em um dia específico, a única maneira de fazer isso é sempre usar o valor universal do horário UTC. Nunca tente armazenar isso como um valor relevante para o fuso horário. Ao exibir esse valor de hora para um usuário, sempre converta para o fuso horário do usuário naquele horário. (E não esqueça que a hora e a data dependem do fuso horário.)

O outro conceito é o de uma "expressão de tempo", como "20:00 toda terça-feira". Você mencionou especificamente permitir que as pessoas agendem compromissos e, se você tiver compromissos recorrentes, precisará de uma expressão como essa. Não conheço nenhuma linguagem de expressão padrão, mas, o que quer que você use, será relativo ao fuso horário da pessoa que a especificou. Seria melhor deixar isso explícito, por exemplo, "20:00 toda terça-feira no fuso horário do Pacífico". No caso de uma expressão de hora, você a armazena sempre como uma sequência e nunca envolve nenhum formato de hora do sistema.

Veja mais discussões sobre isso no meu blog

AgilePro
fonte
1

Muitas respostas aqui indicam o armazenamento como UTC. Mas tenha muito cuidado com isso. Por exemplo, se você agendar um compromisso às 12:00, mas o compromisso ocorrer após a alteração do horário de verão, o que acontecerá? O UTC não mantém nenhuma informação sobre se o dst estava ativo quando o compromisso foi armazenado. Muitos sistemas grandes e famosos cometeram esse erro, onde um usuário envia um email às 9h no verão e, no inverno, o horário enviado mostra 8h, porque o cálculo do UTC depende de quando você olha para a data e hora, não ligado quando a data e hora foram gravadas.

Muito melhor é assumir que seu usuário deseja ter os horários que escolheu sempre. Nenhuma conversão UTC, nenhuma conversão de tempo, nenhuma informação de fuso horário, nada. Marcar uma consulta das 08:00 às 12:00 de 21 de março de 2016 é exatamente isso. Não use a hora local ou a hora UTC, mas a hora não especificada (no json, isso não tem z nem +, basicamente, no .NET, ele tem DateTime.Kind = DateTimeKind.Unspecified).

Obviamente, se seu caso de uso é que você é uma empresa que realiza reuniões com alguém de fusos horários diferentes e deseja ver essas informações em, por exemplo, um calendário da empresa, mas permite que os usuários vejam a que horas é o fuso horário, fica mais complicado. O horário deve ser correto para pessoas diferentes em fusos horários diferentes (o fornecedor e o cliente).

Nesses casos, você pode até querer registrar quando o compromisso foi salvo no banco de dados, em que fuso horário e se isso incluía o dst ou não. Dessa forma, você sempre pode calcular a hora local para qualquer outra coisa, seja para o que seria agora ou para o que deveria ser historicamente. Como os fusos horários não são estáticos, tornando as coisas ainda mais complicadas.

Felizmente, é aqui que as bibliotecas como http://nodatime.org/ entram e são altamente recomendadas. Eles trabalham com datas de maneira muito mais consistente. Mesmo assim, eu recomendaria agrupar todas as suas variáveis ​​de data e hora e lógica em seus próprios wrappers, usando interfaces para que elas possam ser zombadas e, em seguida, você ainda poderá alternar a lógica posteriormente.

Arwin
fonte
Não concordo totalmente com as suas conclusões, mas uma reunião ao meio-dia marcada por uma mudança de horário de verão é um desafio especial, pois seria uma reunião semanal ao meio-dia marcada para vários anos no futuro.
Bent
" É claro que, se seu caso de uso é que você é uma empresa que realiza reuniões com alguém de fusos horários diferentes e deseja ver essas informações em, por exemplo, um calendário da empresa, mas permite que os usuários vejam a que horas é o fuso horário, fica mais complicado. "- isso é praticamente toda empresa. Pouquíssimas empresas fora da China e da Índia podem ignorar compromissos entre fusos horários.
Ross Patterson
" Dessa forma, você sempre pode calcular a hora local para qualquer outra coisa, seja o que seria agora ou como deveria ser historicamente. Como os fusos horários não são muito estáticos, tornando as coisas ainda mais complicadas " . você aceita que fará cálculos, precisa realmente escolher uma única representação para o tempo. UTC, EST (mas não EST5EDT), IST, qualquer que seja. Mas você simplesmente não pode ter valores baseados em vários fusos horários no mesmo campo e executar qualquer processamento agregado legítimo. Processamento de exibição, com certeza. Mas essa é a parte mais fácil.
Ross Patterson
Pelo menos você ainda pode converter corretamente para UTC. Você sempre pode criar uma tabela ou valor de cálculo a partir dessas informações em retrospecto. Mas você nunca pode seguir o outro caminho. Observe que o SQL 2016 começa a dar suporte a isso nativamente, embora, infelizmente, ainda dependa dos dados do registro - pelo menos historicamente - não muito corretos -, mas é mais uma melhoria.
Arwin