¿Cómo manejar TimeZone correctamente en SQL Server?

34

Mi servidor de desarrollo local está en el Medio Oriente, pero mi servidor de producción está en el Reino Unido.

Necesito mostrar la fecha al usuario en su zona horaria. Por ejemplo, si un usuario está en Arabia Saudita, entonces necesito mostrar la hora de acuerdo con el formato de Arabia Saudita.

¿Debo crear una nueva tabla de base de datos llamada TimeZone y guardar la hora en UTC?

usuario960567
fuente
Conversión efectiva de fechas entre UTC y hora local (es decir, PST) en SQL 2005 http://stackoverflow.com/questions/24797/effectively-converting-dates-between-utc-and-local-ie-pst-time-in- sql-2005
mehdi
¿Es esta una aplicación web, una aplicación de escritorio o ...? La forma en que esto se implementa varía en gran medida según el mecanismo de entrega del cliente, porque lo que necesita depende del lado del cliente.
Jon Seigel
Esta web, así como móviles nativos. Sí, esto afecta a diferentes clientes de manera diferente.
user960567
1
Parece que su pregunta no se trata solo de la base de datos en sí, sino que también implica el desarrollo de aplicaciones. Esta pregunta de desbordamiento de pila es una lectura obligada para usted: horario de verano y mejores prácticas de zona horaria
Gan

Respuestas:

48

No hay una solución rápida para esto, desafortunadamente. La internacionalización de una aplicación debe ser parte de las primeras discusiones de diseño, ya que realmente va al núcleo de muchas áreas diferentes, incluidas las comparaciones de fecha / hora y el formato de salida.

De todos modos, para seguir el camino de Hacer lo correcto, es esencial almacenar la información de la zona horaria con la hora . En otras palabras, darse cuenta de que la fecha / hora no 20130407 14:50tiene sentido sin (a) incluir el desplazamiento UTC actual de la zona horaria (nota 1) , o (b) garantizar que toda la lógica que inserte estos valores primero se convierta en un cierto desplazamiento fijo ( muy probablemente 0). Sin ninguna de esas cosas, dos valores de tiempo dados son incomparables y los datos están corruptos . (El último método es jugar con fuego (nota 2) , por cierto; no hagas eso).

En SQL Server 2008+, puede almacenar el desplazamiento con el tiempo directamente utilizando el datetimeoffsettipo de datos. (Para completar, en 2005 y antes, agregaría una segunda columna para almacenar el valor de compensación UTC actual (en minutos)).

Esto facilita la aplicación de tipo escritorio, ya que estas plataformas normalmente tienen mecanismos para convertir automáticamente una fecha / hora + zona horaria a una hora local y luego formatear para la salida, todo en función de la configuración regional del usuario.

Para la web, que es una arquitectura intrínsecamente desconectada, incluso con los datos de fondo configurados correctamente, es más complejo porque necesita información sobre el cliente para poder realizar la conversión y / o formateo. Esto generalmente se hace a través de la configuración de preferencias del usuario (la aplicación convierte / formatea cosas antes de la salida), o simplemente muestra cosas con el mismo formato fijo y desplazamiento de zona horaria para todos (que es lo que hace actualmente la plataforma Stack Exchange).

Puede ver cómo si los datos de fondo no están configurados correctamente, muy rápidamente se volverá complicado y complicado. No recomendaría ir por ninguno de esos caminos porque terminarás con más problemas en el futuro.

Nota 1:

El desplazamiento UTC de una zona horaria no es fijo: considere el horario de verano donde el desplazamiento UTC de una zona varía en más o menos una hora. Además, las fechas de horario de verano de las zonas varían regularmente. Por lo tanto, usar datetimeoffset(o una combinación de local timey UTC offset at that time) da como resultado la máxima recuperación de información.

Nota 2:

Se trata de controlar las entradas de datos. Si bien no hay una forma infalible de validar los valores entrantes, es mejor aplicar un estándar simple que no implique cálculos. Si una API pública espera un tipo de datos que incluya un desplazamiento, ese requisito será claro para la persona que llama.

Si ese no fuera el caso, la persona que llama tiene que confiar en la documentación (si la leyó), o el cálculo se realiza incorrectamente, etc. Hay menos modos de falla / error cuando se requiere un desplazamiento, en particular para un sistema distribuido ( o incluso solo web / base de datos en servidores separados como el caso aquí).

Almacenar el desplazamiento de todos modos mata a dos pájaros de un tiro; e incluso si eso no se requiere ahora , hace que la posibilidad esté disponible más adelante si es necesario. Es cierto que ocupa más espacio de almacenamiento, pero creo que vale la pena el intercambio porque los datos se pierden si nunca se registran en primer lugar.

Jon Seigel
fuente
3
Según tengo entendido, un desplazamiento no es lo mismo que la zona horaria ... las horas almacenadas en el desplazamiento de la fecha y hora pierden información como la conciencia del horario de verano ... Más información: stackoverflow.com/tags/timezone/info
Peter McEvoy
1
@PeterMcEvoy DST no tiene relevancia aquí. datetimeoffsetsignifica "esta es la hora local del usuario / computadora cuando sucedió la cosa" - no necesitamos saber si el horario de verano estaba vigente o no, porque el desplazamiento UTC dado siempre está ahí (incrustado dentro del datetimeoffsetvalor).
Dai
12

He desarrollado una solución integral para la conversión de zona horaria en SQL Server. Consulte Soporte de zona horaria de SQL Server en GitHub .

Maneja adecuadamente las conversiones entre zonas horarias y hacia y desde UTC, incluido el horario de verano.

Es similar en concepto a la solución "T-SQL Toolbox" descrita en la respuesta de adss, pero utiliza las zonas horarias más comunes de IANA / Olson / TZDB en lugar de las de Microsoft, e incluye utilidades para mantener los datos mantenidos como nuevas versiones del TZDB salir.

Ejemplo de uso (para responder al OP):

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

Consulte el archivo Léame en GitHub para obtener API adicionales y explicaciones detalladas.

Además, tenga en cuenta que, dado que se trata de una solución basada en UDF, no está diseñada con el rendimiento como objetivo principal. Aún sería mejor si esta funcionalidad se integrara en SQL Server, de manera similar a cómo CONVERT_TZexiste la función en Oracle y MySQL.

Matt Johnson-Pint
fuente
6

SQL Server realizó modificaciones desde 2005 en adelante, donde la zona horaria interna se guarda en UTC. Esto se debió en gran medida a la geo-replicación y a los proyectores de alta disponibilidad que implican el envío de registros, y el hecho de que los tiempos de envío de registros se hayan guardado en diferentes zonas horarias hizo que fuera imposible para el antiguo método restaurarlos.

Por lo tanto, guardar todo internamente en tiempo UTC permitió que SQL Server funcionara bien globalmente. Esta es una de las razones por las cuales el ahorro de luz diurna es un poco difícil de manejar en Windows, porque otros productos de MS como Outlook también guardan la fecha / hora internamente como UTC y crean un desplazamiento que necesita ser reparado.

Trabajo en una compañía donde tenemos miles de servidores (no servidores MS SQL, sino todo tipo de servidores) repartidos por todo el mundo, y si no forzáramos específicamente que todo fuera por UTC, todos nos volveríamos locos muy rápidamente.

Ali Razeghi
fuente
5

Desarrollé y publiqué el proyecto "T-SQL Toolbox" en codeplex para ayudar a cualquiera que tenga dificultades con el manejo de fecha y hora y zona horaria en SQL Server. Es de código abierto y completamente gratuito.

Ofrece UDF de conversión de fecha y hora fáciles utilizando T-SQL simple con tablas de configuración adicionales listas para usar.

En su ejemplo, puede usar la siguiente muestra:

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
)

Esto devolverá el valor de fecha y hora convertido para su usuario.

Se puede encontrar una lista de todas las zonas horarias admitidas en la tabla "DateTimeUtil.Timezone" también incluida en la base de datos de T-SQL Toolbox.

adss
fuente
Tal kit de herramientas parece ser de gran ayuda para un desarrollador. Sin embargo, una función escalar es un cuadro negro para el optimizador de consultas. Significa que cuando aplique su función a una columna, las tablas de configuración que está mencionando se verán tantas veces como haya filas en el conjunto de resultados (suponiendo que use la función solo en la cláusula SELECT). Esa no parece una buena perspectiva en términos de rendimiento. Sin embargo, realmente no he probado su kit de herramientas, así que no puedo decir con certeza, eso es solo una preocupación general basada en lo que sé.
Andriy M
Andriy, claro, tienes razón desde una perspectiva de rendimiento. Las tablas de consulta de UDF no están diseñadas para operaciones de datos masivos, sino para un uso fácil. Sin embargo, funcionan bastante bien hasta aproximadamente 100.000 registros en mi máquina.
adss
2
Si uno tiene que procesar más registros en su caso de uso, y el rendimiento es importante, debería pensar mejor en los datos precalculados persistentes. Pero incluso entonces, estos UDF podrían ayudar a mantener los valores de tiempo de datos en las zonas de tiempo requeridas.
adss
Desde mi punto de vista, este problema no se trata de rendimiento, y más solo de obtener la funcionalidad básica allí. Ser capaz de devolver información precisa de una consulta es lo primero. Elaborar alguna tabla temporal para almacenar en caché las cosas y luego hacer referencia a eso en la consulta o como lo maneje para mejorar el rendimiento se vuelve secundario.
Acción Dan
1

Es posible que me falte algo obvio, pero si todo lo que desea hacer es mostrar la fecha utilizando la zona horaria del usuario y el formato de idioma, la forma más simple es almacenar siempre las fechas en UTC en la base de datos y dejar que la aplicación cliente se convierta de UTC a local zona horaria y utilice la configuración regional del usuario para formatear la fecha en consecuencia.

20c
fuente