2009-07-30 23 views
58

Estamos tratando con una aplicación que necesita manejar datos de tiempo globales de diferentes zonas horarias y configuraciones de horario de verano. La idea es almacenar todo en formato UTC internamente y solo convertir hacia adelante y hacia atrás para las interfaces de usuario localizadas. ¿El SQL Server ofrece algún mecanismo para tratar las traducciones dadas una hora, un país y una zona horaria?TSQL: ¿Cómo convertir la hora local a UTC? (SQL Server 2008)

Esto debe ser un problema común, así que estoy sorprendido de que Google no muestre nada utilizable.

¿Alguna sugerencia?

+0

Tengo mi servidor mssql vinculado a un servidor mysql. Me pregunto si es posible ejecutar mysql CONVERT_TZ (time, srczone, dstzone) en las consultas :-) Extraña que esta función falta; está integrado en Linux. –

+0

@BuschnicK Vea mi respuesta a continuación. En realidad, creo que puedes aceptarlo, incluso así es más fácil de encontrar para otros. –

Respuesta

21

7 años pasados ​​y ...
Actualmente existe esta nueva característica de SQL Server 2016 que hace exactamente lo que necesita.
Se llama AT TIME ZONE y convierte la fecha a un huso horario especificado teniendo en cuenta los cambios de horario de verano (horario de verano).
Más información aquí: https://msdn.microsoft.com/en-us/library/mt612795.aspx

+8

Ya no estoy trabajando en esto, no en ese proyecto, no en SQL Server, no en la misma compañía y ni siquiera en el mismo país ;-) Así que no me va a ayudar, pero yo Lo votaremos para que las personas encuentren esta pregunta ahora. – BuschnicK

+0

@BuschnicK Sí, supongo, pero llegué aquí en busca de una solución para el mismo problema que tenía, así que decidí publicar una respuesta ahora que hay una solución real: D –

+1

La pregunta es para SQL Server 2008. Actualice el pregunta si aceptas esta respuesta. Thx – Robert

3

Sí, hasta cierto punto como se detalla here.
El enfoque que he usado (antes de 2008) es hacer la conversión en la lógica de negocio .NET antes de insertar en la base de datos.

2

puede utilizar la función GETUTCDATE() para obtener UTC fecha y hora Probablemente puede seleccionar diferencia entre GETUTCDATE() y GETDATE() y usar esta diferencia al ajust la fecha para UTC

pero estoy de acuerdo con el mensaje anterior, que es mucho más fácil controlar el correcto datetime en la capa de negocios (en .NET, por ejemplo).

+12

¡No! La diferencia depende de la fecha exacta. Depende del ahorro de luz natural. – usr

+3

No cuenta para el horario de verano. Estuve usando soluciones como esta por un tiempo y esto causó problemas importantes. Debe determinar si la fecha en la que compara está en horario de verano. –

13

SQL Server 2008 tiene un tipo llamado datetimeoffset. Es realmente útil para este tipo de cosas.

http://msdn.microsoft.com/en-us/library/bb630289.aspx

continuación, puede utilizar la función de SWITCHOFFSET para moverlo de una zona horaria a otra, pero manteniendo el mismo valor GMT.

http://msdn.microsoft.com/en-us/library/bb677244.aspx

Rob

+3

SWITCHOFFSET no cuenta para el horario de verano, por lo que solo es útil en algunas situaciones. – robocat

+1

No. Pero la pregunta era acerca de poder manejar el cambio a la zona horaria solicitada. –

+0

De la pregunta "diferentes zonas horarias y configuraciones de horario de verano". Nosotros también estamos buscando una solución para los tiempos locales. Su sugerencia no resuelve el problema del horario de verano, ¿verdad? – robocat

51

Esto funciona para las fechas que actualmente tienen desplazamiento como anfitrión de SQL Server al mismo UTC; no tiene en cuenta los cambios en el horario de verano. Reemplace YOUR_DATE con la fecha local para convertir.

SELECT DATEADD(second, DATEDIFF(second, GETDATE(), GETUTCDATE()), YOUR_DATE);

+2

Gracias, esta es una buena idea, pero solo funciona para una sola zona horaria: la de la máquina local. Necesitamos que funcione para zonas horarias arbitrarias aunque ... – BuschnicK

+0

inteligente, pero sí, ten cuidado con las zonas horarias. – Krip

+39

Esto no tiene en cuenta el horario de verano –

21

Si bien algunas de estas respuestas le conseguirá en el estadio de béisbol, no se puede hacer lo que estamos tratando de hacer con las fechas arbitrarias para SqlServer 2005 y antes debido a los cambios de horario. Usar la diferencia entre el UTC actual local y el actual me dará el desplazamiento tal como existe hoy. No he encontrado una manera de determinar cuál habría sido el desplazamiento para la fecha en cuestión.

Dicho esto, sé que SqlServer 2008 proporciona algunas nuevas funciones de fecha que pueden abordar ese problema, pero las personas que usan una versión anterior deben conocer las limitaciones.

Nuestro enfoque consiste en mantener UTC y realizar la conversión en el lado del cliente donde tenemos más control sobre la precisión de la conversión.

+2

es triste que esta es la respuesta! –

4

Tiendo a usar DateTimeOffset para todo el almacenamiento de fecha y hora que no está relacionado con un evento local (es decir: reunión/fiesta, etc., de 12 p.m. a 3 p.m. en el museo).

para obtener la corriente DTO como UTC:

DECLARE @utcNow DATETIMEOFFSET = CONVERT(DATETIMEOFFSET, SYSUTCDATETIME()) 
DECLARE @utcToday DATE = CONVERT(DATE, @utcNow); 
DECLARE @utcTomorrow DATE = DATEADD(D, 1, @utcNow); 
SELECT @utcToday [today] 
     ,@utcTomorrow [tomorrow] 
     ,@utcNow [utcNow]

NOTA: Siempre voy a usar UTC cuando se envía a través del cable ... JS del lado del cliente puede conseguir fácilmente a/desde UTC local.Ver: new Date().toJSON() ...

El siguiente JS se encargará de analizar una fecha UTC/GMT en formato ISO8601 a un horario local.

if (typeof Date.fromISOString != 'function') { 
    //method to handle conversion from an ISO-8601 style string to a Date object 
    // Date.fromISOString("2009-07-03T16:09:45Z") 
    // Fri Jul 03 2009 09:09:45 GMT-0700 
    Date.fromISOString = function(input) { 
    var date = new Date(input); //EcmaScript5 includes ISO-8601 style parsing 
    if (!isNaN(date)) return date; 

    //early shorting of invalid input 
    if (typeof input !== "string" || input.length < 10 || input.length > 40) return null; 

    var iso8601Format = /^(\d{4})-(\d{2})-(\d{2})((([T ](\d{2}):(\d{2})(:(\d{2})(\.(\d{1,12}))?)?)?)?)?([Zz]|([-+])(\d{2})\:?(\d{2}))?$/; 

    //normalize input 
    var input = input.toString().replace(/^\s+/,'').replace(/\s+$/,''); 

    if (!iso8601Format.test(input)) 
     return null; //invalid format 

    var d = input.match(iso8601Format); 
    var offset = 0; 

    date = new Date(+d[1], +d[2]-1, +d[3], +d[7] || 0, +d[8] || 0, +d[10] || 0, Math.round(+("0." + (d[12] || 0)) * 1000)); 

    //use specified offset 
    if (d[13] == 'Z') offset = 0-date.getTimezoneOffset(); 
    else if (d[13]) offset = ((parseInt(d[15],10) * 60) + (parseInt(d[16],10)) * ((d[14] == '-') ? 1 : -1)) - date.getTimezoneOffset(); 

    date.setTime(date.getTime() + (offset * 60000)); 

    if (date.getTime() <= new Date(-62135571600000).getTime()) // CLR DateTime.MinValue 
     return null; 

    return date; 
    }; 
}
+0

+1 También he cambiado a DateTimeOffset. Evita una serie de problemas con las conversiones UTC + locales. Sin embargo, por razones similares, recomiendo enviar valores con un offset over-the-wire (a través de JSON) también. – user2864740

+0

Como nota, hago lo que haces * exactamente * al revés. Para DateTimes que están vinculados a un evento local, almaceno un DateTimeOffset. Para un DateTime que no está vinculado a un evento local, almaceno un DateTime en UTC. El primero tiene dos puntos de datos relevantes (cuando es en hora local, y qué hora local es esa), el último solo uno (cuando lo es) – Martijn

+0

@Martijn Pero la zona horaria no le da la ubicación, y usted tiene que almacenar por separado de todos modos. – Tracker1

-1

Ejemplo de uso:

SELECT 
    Getdate=GETDATE() 
    ,SysDateTimeOffset=SYSDATETIMEOFFSET() 
    ,SWITCHOFFSET=SWITCHOFFSET(SYSDATETIMEOFFSET(),0) 
    ,GetutcDate=GETUTCDATE() 
GO 

devoluciones:

Getdate SysDateTimeOffset SWITCHOFFSET GetutcDate 
2013-12-06 15:54:55.373 2013-12-06 15:54:55.3765498 -08:00 2013-12-06 23:54:55.3765498 +00:00 2013-12-06 23:54:55.373 
10

Puede utilizar mi proyecto SQL Server Time Zone Support para convertir entre IANA zonas horarias estándar, as listed here.

UTC a local es la siguiente:

SELECT Tzdb.UtcToLocal('2015-07-01 00:00:00', 'America/Los_Angeles') 

local a UTC es así:

SELECT Tzdb.LocalToUtc('2015-07-01 00:00:00', 'America/Los_Angeles', 1, 1) 

Las opciones numéricas son bandera para controlar el comportamiento cuando los valores de la hora local se ven afectados por la luz del día ahorrando tiempo. Estos se describen en detalle en la documentación del proyecto.

+0

How man ... este proyecto de Matt es genial y no requiere el CLR. Matt merece mucho crédito por este +100 – buckley

2

Aquí el código para convertir una zona a otra zona DateTimeDateTime

DECLARE @UTCDateTime DATETIME = GETUTCDATE(); 
    DECLARE @ConvertedZoneDateTime DATETIME; 

    -- 'UTC' to 'India Standard Time' DATETIME 
    SET @ConvertedZoneDateTime = @UTCDateTime AT TIME ZONE 'UTC' AT TIME ZONE 'India Standard Time' 
    SELECT @UTCDateTime AS UTCDATE,@ConvertedZoneDateTime AS IndiaStandardTime 

    -- 'India Standard Time' to 'UTC' DATETIME 
    SET @UTCDateTime = @ConvertedZoneDateTime AT TIME ZONE 'India Standard Time' AT TIME ZONE 'UTC' 
    SELECT @ConvertedZoneDateTime AS IndiaStandardTime,@UTCDateTime AS UTCDATE 

Nota: Este (AT TIME ZONE) trabajar sólo en SQL Server 2016+ y esto la ventaja es considerando automáticamente la luz del día mientras se convierte a la zona horaria particular

Cuestiones relacionadas