Como lidar com o TimeZone corretamente no SQL Server?

34

Meu servidor de desenvolvimento local está no Oriente Médio, mas meu servidor de produção está no Reino Unido.

Preciso mostrar a data para o usuário no fuso horário. Por exemplo, se um usuário estiver na Arábia Saudita, preciso mostrar a hora de acordo com o formato da Arábia Saudita.

Devo criar uma nova tabela de banco de dados chamada TimeZone e salvar a Hora no UTC?

user960567
fonte
Convertendo efetivamente datas entre o UTC e o horário local (ou seja, PST) no SQL 2005 http://stackoverflow.com/questions/24797/effectively-converting-dates-between-utc-and-local-ie-pst-time-in- sql-2005
mehdi
É um aplicativo da Web, desktop ou ...? A maneira como isso é implementado varia muito pelo mecanismo de entrega do cliente, porque o que você precisa depende do lado do cliente.
21813 Jon Seigel
Esta web e também o celular nativo. Sim, esse efeito é diferente para clientes diferentes.
user960567
1
Parece que sua pergunta não é apenas sobre o banco de dados, mas também envolve o desenvolvimento de aplicativos. Esta questão Stack Overflow é uma leitura obrigatória para você, então: melhores práticas de horário de verão e fuso horário
Gan

Respostas:

48

Infelizmente, não há solução rápida para isso. A internacionalização de um aplicativo deve fazer parte das primeiras discussões de design, pois realmente se concentra em várias áreas diferentes, incluindo comparações de data / hora e formatação de saída.

De qualquer forma, para seguir o caminho certo, é essencial armazenar as informações de fuso horário com o tempo . Em outras palavras, perceber que a data / hora não20130407 14:50 tem sentido sem (a) incluir o deslocamento UTC atual do fuso horário (nota 1) ou (b) garantir que toda lógica que insere esses valores primeiro converta em um determinado deslocamento fixo ( provavelmente 0). Sem uma dessas coisas, dois valores de tempo são incomparáveis e os dados estão corrompidos . (O último método é brincar com fogo (nota 2) , a propósito; não faça isso.)

No SQL Server 2008+, você pode armazenar o deslocamento diretamente com o horário, usando o datetimeoffsettipo de dados. (Para completar, em 2005 e antes, eu adicionaria uma segunda coluna para armazenar o valor de deslocamento UTC então atual (em minutos).)

Isso facilita para um aplicativo do tipo desktop, pois essas plataformas normalmente têm mecanismos para converter automaticamente uma data / hora + fuso horário em um horário local e depois formatar a saída, tudo com base nas configurações regionais do usuário.

Para a Web, que é uma arquitetura inerentemente desconectada, mesmo com os dados de back-end configurados corretamente, é mais complexo porque você precisa de informações sobre o cliente para poder fazer a conversão e / ou formatação. Isso geralmente é feito por meio das configurações de preferência do usuário (o aplicativo converte / formata as coisas antes da saída) ou simplesmente mostra as coisas com o mesmo formato fixo e deslocamento de fuso horário para todos (que é o que a plataforma Stack Exchange atualmente faz).

Você pode ver como se os dados de back-end não estiverem configurados corretamente, muito rapidamente eles ficarão complicados e invasivos. Eu não recomendaria seguir nenhum desses caminhos porque você acabará tendo mais problemas no final da linha.

Nota 1:

O deslocamento UTC de um fuso horário não é fixo: considere o horário de verão em que o deslocamento UTC de uma zona varia em mais ou menos uma hora. Também as datas de horário de verão das zonas variam regularmente. Portanto, o uso datetimeoffset(ou um composto de local timee UTC offset at that time) resulta na recuperação máxima de informações.

Nota 2:

É sobre o controle de entradas de dados. Embora não exista uma maneira infalível de validar valores recebidos, é melhor impor um padrão simples que não envolva cálculos. Se uma API pública espera um tipo de dados que inclua um deslocamento, esse requisito ficará claro para o chamador.

Se não for esse o caso, o chamador deve confiar na documentação (se a ler), ou o cálculo é feito incorretamente etc. Há menos modos de falha / erro ao exigir um deslocamento, especialmente para um sistema distribuído ( ou apenas web / banco de dados em servidores separados, como o caso aqui).

Armazenar o deslocamento de qualquer maneira mata dois coelhos com uma cajadada; e mesmo se isso não for necessário agora , disponibilizará a possibilidade posteriormente, se necessário. É verdade que é preciso mais armazenamento, mas acho que vale a pena a troca, porque os dados são perdidos se nunca forem gravados.

Jon Seigel
fonte
3
Pelo que entendi um deslocamento não é o mesmo que fuso horário ... vezes armazenados em datetimeoffset informações perder como a luz do dia consciência horário de ... Mais informações: stackoverflow.com/tags/timezone/info
Peter McEvoy
1
@PeterMcEvoy DST não tem relevância aqui. datetimeoffsetsignifica "esta é a hora local do usuário / computador quando a coisa aconteceu" - não precisamos saber se o horário de verão estava em vigor ou não, porque o deslocamento UTC fornecido está sempre lá (incorporado ao datetimeoffsetvalor).
Dai
12

Eu desenvolvi uma solução abrangente para conversão de fuso horário no SQL Server. Consulte Suporte ao fuso horário do SQL Server no GitHub .

Ele lida adequadamente com conversões entre fusos horários e para e do UTC, incluindo o horário de verão.

É semelhante em conceito à solução "T-SQL Toolbox" descrita na resposta do adss, mas usa os fusos horários mais comuns da IANA / Olson / TZDB em vez dos da Microsoft e inclui utilitários para manter os dados mantidos como novas versões do TZDB sair.

Exemplo de uso (para responder ao OP):

SELECT Tzdb.UtcToLocal(sysutcdatetime(), 'Asia/Riyadh') as CurrentTimeInSaudiArabia

Veja o leia-me no GitHub para APIs adicionais e explicações detalhadas.

Além disso, observe que, como se trata de uma solução baseada em UDF, ela não foi projetada com o desempenho como objetivo principal. Ainda seria melhor se essa funcionalidade fosse incorporada ao SQL Server, da mesma forma que a CONVERT_TZfunção existe no Oracle e no MySQL.

Matt Johnson-Pint
fonte
6

O SQL Server fez modificações em 2005 em diante, onde o fuso horário interno é salvo no UTC. Isso se deve, em grande parte, à replicação geográfica e aos projetores de HA que envolvem o envio de logs, e os tempos de envio de logs salvos em diferentes fusos horários impossibilitavam o método antigo de restaurá-los.

Assim, salvar tudo internamente no horário UTC permitiu que o SQL Server funcionasse bem globalmente. Esse é um dos motivos pelos quais o horário de verão é difícil de lidar no Windows, porque outros produtos da Microsoft, como o Outlook, também salvam a data / hora internamente como UTC e criam um deslocamento que precisa ser corrigido.

Eu trabalho em uma empresa em que temos milhares de servidores (embora não sejam servidores MS SQL, mas todos os tipos de servidores) espalhados por todo o mundo, e se não forçássemos tudo especificamente a UTC, todos ficaríamos loucos muito rapidamente.

Ali Razeghi
fonte
5

Eu desenvolvi e publiquei o projeto “T-SQL Toolbox” no codeplex para ajudar qualquer pessoa que lute com a manipulação de data e hora e fuso horário no SQL Server. É de código aberto e totalmente gratuito.

Oferece UDFs fáceis de conversão de data e hora usando T-SQL simples com tabelas de configuração adicionais prontas para uso.

No seu exemplo, você pode usar o seguinte exemplo:

SELECT [DateTimeUtil].[UDF_ConvertLocalToLocalByTimezoneIdentifier] (
    'GMT Standard Time', -- the original timezone in which your datetime is stored
    'Middle East Standard Time', -- the target timezone for your user
    '2014-03-30 01:55:00' -- the original datetime you want to convert
)

Isso retornará o valor de data e hora convertido para seu usuário.

Uma lista de todos os fusos horários suportados pode ser encontrada na tabela "DateTimeUtil.Timezone" também fornecida no banco de dados T-SQL Toolbox.

adss
fonte
Esse kit de ferramentas parece ser uma grande ajuda para um desenvolvedor. No entanto, uma função escalar é uma caixa preta para o otimizador de consulta. Isso significa que, quando eu aplicar sua função a uma coluna, as tabelas de configuração mencionadas serão atingidas quantas vezes houver linhas no conjunto de resultados (supondo que eu use a função apenas na cláusula SELECT). Essa não parece uma boa perspectiva em termos de desempenho. Eu realmente não testei o seu kit de ferramentas, portanto, não posso dizer com certeza, isso é apenas uma preocupação geral com base no que sei.
Andriy M
Andriy, com certeza, você está certo da perspectiva de desempenho. As tabelas de consulta UDFs não foram projetadas para operações de dados em massa, mas para facilitar o uso. No entanto, eles executam quase 100.000 registros na minha máquina.
adss 23/05
2
Se for necessário processar mais registros em seu caso de uso e o desempenho for importante, você deve pensar melhor em dados pré-calculados persistentes. Mas, mesmo assim, esses UDFs podem ajudar a manter os valores de data e hora nos fusos horários necessários.
adss 23/05
Na minha perspectiva, esse problema não é sobre desempenho e mais sobre como obter as funcionalidades básicas. Ser capaz de retornar informações precisas de uma consulta é a primeira coisa. A elaboração de alguma tabela temporária para armazenar em cache as coisas e, em seguida, consulte a consulta ou, no entanto, você a manipula para melhorar o desempenho.
Ação Dan
1

Talvez esteja faltando algo óbvio, mas se tudo o que você quer fazer é exibir a data usando o fuso horário do usuário e o formato do idioma, a maneira mais simples é sempre armazenar as datas no UTC no banco de dados e permitir que o aplicativo cliente converta do UTC para o local fuso horário e use as configurações regionais do usuário para formatar a data adequadamente.

20c
fonte