TSQL: Wie konvertiere ich die Ortszeit in UTC? (SQL Server 2008)

82

Wir haben es mit einer Anwendung zu tun, die globale Zeitdaten aus verschiedenen Zeitzonen und Sommerzeiteinstellungen verarbeiten muss. Die Idee ist, alles intern im UTC-Format zu speichern und nur für die lokalisierten Benutzeroberflächen hin und her zu konvertieren. Bietet der SQL Server Mechanismen für den Umgang mit den Übersetzungen in einer bestimmten Zeit, einem bestimmten Land und einer bestimmten Zeitzone?

Dies muss ein häufiges Problem sein, daher bin ich überrascht, dass Google nichts Nutzbares auftauchen lässt.

Irgendwelche Hinweise?

BuschnicK
quelle
Ich habe meinen MSSQL-Server mit einem MySQL-Server verbunden. Ich frage mich, ob es möglich ist, mysql CONVERT_TZ (time, srczone, dstzone) für die Abfragen auszuführen :-) Seltsamerweise fehlt diese Funktion; Es ist in Linux eingebaut.
Leif Neland
1
@BuschnicK Siehe meine Antwort unten. Eigentlich denke ich, dass Sie es akzeptieren können, obwohl es für andere leichter zu finden ist.
Piotr Owsiak
Die kurze Antwort: Vor SQL Server 2016 gibt es keine integrierte Möglichkeit, dies zu tun. Daher ist in früheren Versionen benutzerdefinierter Code erforderlich.
Lehiester
Die mittlere Antwort: Alle Zeitversatzfunktionen vor SQL Server 2016 funktionierten nur mit absoluten Versätzen, ohne Unterstützung für die variablen Versätze, die in den meisten Zeitzonen aufgrund der Sommerzeit auftreten. Abfragen haben keine Möglichkeit, auf Zeitversätze zuzugreifen, außer auf den aktuellen Versatz der Ortszeit des Servers, der für den Versuch, die Konvertierung zu automatisieren, unbrauchbar ist.
Lehiester

Antworten:

49

7 Jahre vergingen und ...
tatsächlich gibt es diese neue SQL Server 2016-Funktion, die genau das tut, was Sie brauchen.
Es heißt AT TIME ZONE und konvertiert das Datum unter Berücksichtigung der Änderungen der Sommerzeit in eine bestimmte Zeitzone.
Weitere Informationen finden Sie hier: https://msdn.microsoft.com/en-us/library/mt612795.aspx

Piotr Owsiak
quelle
19
Ich arbeite nicht mehr daran - nicht bei diesem Projekt, nicht bei SQL Server, nicht bei derselben Firma und nicht einmal im selben Land ;-) Es hilft mir also nicht, aber ich werde es für Leute, die diese Frage finden, positiv bewerten jetzt.
BuschnicK
2
@BuschnicK Ja, ich denke, aber ich bin hierher gekommen, um nach einer Lösung für das gleiche Problem zu suchen, das Sie hatten. Deshalb habe ich beschlossen, eine Antwort zu veröffentlichen, da es eine echte Lösung gibt: D
Piotr Owsiak
5
Die Frage bezieht sich auf SQL Server 2008. Bitte aktualisieren Sie die Frage, wenn Sie diese Antwort akzeptieren. Thx
Robert
1
Um in UTC zu konvertieren, können Sie 'AT TIME ZONE' UTC 'ausführen.
Krzyserious
1
@Piotr Owsiak: Ja, aber es wird davon ausgegangen, dass die Eingabezeit UTC ist, was sinnlos ist, wenn Sie von Ortszeit zu UTC konvertieren möchten ... Es sind also 7 Jahre vergangen, und sie glauben immer noch nicht, dass es sich lohnt, dies richtig zu handhaben. ..
Stefan Steiger
65

Dies funktioniert für Daten, die derzeit denselben UTC-Offset wie der Host von SQL Server haben. Änderungen der Sommerzeit werden nicht berücksichtigt. Durch YOUR_DATEdas zu konvertierende lokale Datum ersetzen .

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

shaig
quelle
2
Danke, das ist eine gute Idee, aber es funktioniert nur für genau eine Zeitzone - die der lokalen Maschine. Wir brauchen es, um für beliebige Zeitzonen zu arbeiten ...
BuschnicK
51
Dies berücksichtigt nicht die Sommerzeit
Gabriel McAdams
10
Nein! Die Differenz ist abhängig vom genauen Datum. Es kommt auf die Sommerzeit an.
usr
3
In meinem Fall brauchte ich nur die 1 Zeitzone, und das hat super funktioniert! Vielen Dank.
M Thelen
10
Dies funktioniert gut, wenn Sie wissen, dass Sie heute nicht historisch laufen, danke
David Adlington
22

Während einige dieser Antworten Sie in den Ballpark bringen, können Sie aufgrund der Sommerzeit nicht das tun, was Sie mit beliebigen Daten für SqlServer 2005 und früher versuchen. Wenn ich die Differenz zwischen der aktuellen lokalen und der aktuellen UTC verwende, erhalte ich den Offset, wie er heute existiert. Ich habe keine Möglichkeit gefunden, den Offset für das betreffende Datum zu bestimmen.

Ich weiß jedoch, dass SqlServer 2008 einige neue Datumsfunktionen bietet, mit denen dieses Problem möglicherweise behoben werden kann. Benutzer einer früheren Version müssen sich jedoch der Einschränkungen bewusst sein.

Unser Ansatz ist es, UTC beizubehalten und die Konvertierung auf der Clientseite durchzuführen, wo wir mehr Kontrolle über die Genauigkeit der Konvertierung haben.

user890155
quelle
16

Verwenden Sie für SQL Server 2016 und höher sowie für Azure SQL Database die integrierte AT TIME ZONEAnweisung .

Für ältere Versionen von SQL Server können Sie meine verwenden SQL Server Zeitzonen - Unterstützung Projekt zu konvertieren zwischen IANA Standardzeitzonen, wie hier aufgelistet .

UTC zu Local ist wie folgt:

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

Lokal zu UTC ist wie folgt:

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

Die numerischen Optionen sind ein Flag zur Steuerung des Verhaltens, wenn die lokalen Zeitwerte von der Sommerzeit beeinflusst werden. Diese sind in der Projektdokumentation ausführlich beschrieben.

Matt Johnson-Pint
quelle
1
Wie man ... dieses Projekt von Matt ist großartig und erfordert keine CLR. Matt verdient viel Anerkennung für diese +100
Buckley
14

SQL Server 2008 hat einen Typ namens datetimeoffset. Es ist wirklich nützlich für diese Art von Sachen.

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

Dann können Sie die Funktion verwenden SWITCHOFFSET, um sie von einer Zeitzone in eine andere zu verschieben, wobei Sie jedoch den gleichen UTC-Wert beibehalten.

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

rauben

Rob Farley
quelle
6
SWITCHOFFSET berücksichtigt keine Sommerzeit und ist daher nur in bestimmten Situationen nützlich.
Robocat
2
Nein. Aber die Frage war, ob es möglich ist, in die gewünschte Zeitzone zu wechseln.
Rob Farley
Aus der Frage "Unterschiedliche Zeitzonen und Sommerzeiteinstellungen". Auch wir suchen nach einer Lösung für die Ortszeit. Ihr Vorschlag löst das Problem der Sommerzeit nicht, oder?
Robocat
Immer wenn Sie die Zeitzone auf einem Client erkennen und dann die Datenbankrückgabezeiten in der angegebenen Zeitzone haben müssen, ist die SWITCHOFFSET-Funktion sehr nützlich.
Rob Farley
3
@RobFarley Das Erkennen der Zeitzone auf dem Client und die Verwendung von SWITCHOFFSET können immer noch Fehler verursachen. Sie müssen wissen, ob für das Datum und die Uhrzeit, die Sie konvertieren, die Sommerzeit angewendet wird oder nicht. Das einfache Erkennen der Zeitzone und das Anwenden des aktuellen Offsets auf UTC kann eine Stunde dauern - und das in einem einfachen Fall, in dem sich alle Ihre Conversions im selben Land befinden. Nicht jedes Land wechselt an denselben Daten zur Sommerzeit. SWITCHOFFSET funktioniert gut, wenn Sie die Ortszeit speichern und den Unterschied zwischen der ursprünglichen und der Zielzone innerhalb desselben Landes kennen.
JamieSee
12

Hier ist der Code zum Konvertieren einer Zone DateTimein eine andere ZoneDateTime

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

Hinweis : Funktioniert AT TIME ZONE nur unter SQL Server 2016+ und hat den Vorteil, dass bei der Konvertierung in eine bestimmte Zeitzone automatisch Tageslicht berücksichtigt wird

KarthikeyanMlp
quelle
2
Ich liebe das, wenn auch aus keinem anderen Grund als der Tatsache, dass es zeigt, dass Sie mehrere AT TIME ZONEAnrufe (Phrasen?) Aneinander ketten können ! Einfach elegant. Ich habe vorhin gesagt, dass stackoverflow.com/a/44579178/112764 meine Anforderungen erfüllt, aber das ist noch besser. Ein großes Lob.
NateJ
DECLARE @UTCDateTime DATETIME = GETUTCDATE(); DECLARE @ConvertedZoneDateTime DATETIME; -- 'UTC' to 'India Standard Time' to 'Eastern Standard Time' DATETIME SET @ConvertedZoneDateTime = @UTCDateTime AT TIME ZONE 'UTC' AT TIME ZONE 'India Standard Time' AT TIME ZONE 'Eastern Standard Time' SELECT @UTCDateTime AS UTCDATE,@ConvertedZoneDateTime AS EasternStandardTime Ja, Sie können mehrere AT TIME ZONEAnrufe verketten, aber Von und Bis reicht für jede Konvertierung und die meisten, die wir brauchten
KarthikeyanMlp
4

Ich neige dazu, DateTimeOffset für alle Datums- und Zeitspeicher zu verwenden, die nicht mit einem lokalen Ereignis zusammenhängen (z. B. Besprechung / Party usw., 12-15 Uhr im Museum).

So erhalten Sie das aktuelle DTO als 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]

HINWEIS: Ich verwende immer UTC, wenn ich über das Kabel sende ... clientseitige JS können problemlos zu / von lokalen UTC gelangen. Siehe: new Date().toJSON()...

Der folgende JS behandelt das Parsen eines UTC / GMT-Datums im ISO8601-Format auf eine lokale Datums- / Uhrzeitangabe.

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;
  };
}
Tracker1
quelle
+1 Ich habe auch zu DateTimeOffset gewechselt. Es vermeidet eine Reihe von Problemen mit lokalen UTC + -Konvertierungen. Aus ähnlichen Gründen empfehle ich jedoch, Werte mit einem Offset über das Kabel (über JSON) zu senden.
user2864740
Als Hinweis mache ich das, was Sie tun, genau umgekehrt. Für DateTimes, die an ein lokales Ereignis gebunden sind, speichere ich ein DateTimeOffset. Für eine DateTime, die nicht an ein lokales Ereignis gebunden ist, speichere ich eine DateTime in UTC. Ersteres hat zwei relevante Datenpunkte (wann ist es in der Ortszeit und welche Ortszeit ist das), letzteres nur einen (wann ist es)
Martijn
@Martijn Aber die Zeitzone gibt dir nicht den Ort und du musst sowieso separat speichern.
Tracker1
@ tracker1 Das funktioniert nur, wenn der Standort eine Möglichkeit hat, seine Zeitzone zu kennen, und selbst dann ist das Konvertieren ein kompletter Schmerz.
Martijn
3

Ja, bis zu einem gewissen Grad, wie hier beschrieben .
Der Ansatz, den ich (vor 2008) verwendet habe, besteht darin, die Konvertierung in der .NET-Geschäftslogik durchzuführen, bevor sie in die Datenbank eingefügt wird.

AdaTheDev
quelle
1

Sie können die Funktion GETUTCDATE () verwenden, um die UTC-Datumszeit abzurufen. Wahrscheinlich können Sie den Unterschied zwischen GETUTCDATE () und GETDATE () auswählen und diesen Unterschied verwenden, um Ihre Daten an UTC anzupassen

Ich stimme jedoch der vorherigen Nachricht zu, dass es viel einfacher ist, die richtige Datums- und Uhrzeitangabe in der Geschäftsschicht zu steuern (z. B. in .NET).

Bogdan_Ch
quelle
12
Nein! Die Differenz ist abhängig vom genauen Datum. Es kommt auf die Sommerzeit an.
usr
3
Berücksichtigt nicht die Sommerzeit. Ich habe einige Zeit solche Lösungen verwendet und es hat große Probleme verursacht. Sie müssen feststellen, ob das Datum, mit dem Sie vergleichen, in der Sommerzeit liegt.
Jeff Davis
-1

Beispielnutzung:

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

Kehrt zurück:

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
Robert Cantor
quelle