it-swarm.com.de

Wie wandle ich datetime in datetimeoffset um?

Wie kann ich einen SQL Server datetime Wert in einen datetimeoffset Wert konvertieren?


Beispielsweise enthält eine vorhandene Tabelle datetime Werte, die sich alle in "lokaler" Serverzeit befinden.

SELECT TOP 5 ChangeDate FROM AuditLog

ChangeDate
=========================
2013-07-25 04:00:03.060
2013-07-24 04:00:03.073
2013-07-23 04:00:03.273
2013-07-20 04:00:02.870
2013-07-19 04:00:03.780

Mein Server ( ist zufällig ) (jetzt, heute) vier Stunden hinter UTC (jetzt, in der US Eastern-Zeitzone, mit aktivierter Sommerzeit). :

SELECT SYSDATETIMEOFFSET()

2013-07-25 14:42:41.6450840 -04:00

ich möchte die gespeicherten datetime Werte in datetimeoffset Werte konvertieren. Verwenden der aktuellen Zeitzonen-Versatzinformationen des Servers.

Die Werte i Wunsch sind:

ChangeDate               ChangeDateOffset
=======================  ==================================
2013-07-25 04:00:03.060  2013-07-25 04:00:03.0600000 -04:00
2013-07-24 04:00:03.073  2013-07-24 04:00:03.0730000 -04:00
2013-07-23 04:00:03.273  2013-07-23 04:00:03.2730000 -04:00
2013-07-20 04:00:02.870  2013-07-20 04:00:02.8700000 -04:00
2013-07-19 04:00:03.780  2013-07-19 04:00:03.7800000 -04:00

Sie können die gewünschten Eigenschaften sehen:

2013-07-19 04:00:03.7800000 -04:00
--------------------------- ------
           |                    |
   a "local" datetime        the offset from UTC

Stattdessen sind die tatsächlichen Werte:

SELECT TOP 5
   ChangeDate,
   CAST(ChangeDate AS datetimeoffset) AS ChangeDateOffset
FROM AuditLog

ChangeDate               ChangeDateOffset
=======================  ==================================
2013-07-25 04:00:03.060  2013-07-25 04:00:03.0600000 +00:00
2013-07-24 04:00:03.073  2013-07-24 04:00:03.0730000 +00:00
2013-07-23 04:00:03.273  2013-07-23 04:00:03.2730000 +00:00
2013-07-20 04:00:02.870  2013-07-20 04:00:02.8700000 +00:00
2013-07-19 04:00:03.780  2013-07-19 04:00:03.7800000 +00:00

Mit den ungültigen Merkmalen:

2013-07-19 04:00:03.7800000 +00:00
--------------------------- ------
                              ^
                              |
                             No offset from UTC present

Also versuche ich andere Dinge nach dem Zufallsprinzip:

SELECT TOP 5
    ChangeDate, 
    CAST(ChangeDate AS datetimeoffset) AS ChangeDateOffset,
    DATEADD(minute, DATEDIFF(minute, GETDATE(), GETUTCDATE()), ChangeDate) AS ChangeDateUTC,
    CAST(DATEADD(minute, DATEDIFF(minute, GETDATE(), GETUTCDATE()), ChangeDate) AS datetimeoffset) AS ChangeDateUTCOffset,
    SWITCHOFFSET(CAST(ChangeDate AS datetimeoffset), DATEDIFF(minute, GETUTCDATE(), GETDATE())) AS ChangeDateSwitchedOffset
FROM AuditLog
ORDER BY ChangeDate DESC

Mit Ergebnissen:

ChangeDate               ChangeDateOffset                    ChangeDateUTC            ChangeDateUTCOffset                 ChangeDateSwitchedOffset
=======================  ==================================  =======================  ==================================  ==================================
2013-07-25 04:00:03.060  2013-07-25 04:00:03.0600000 +00:00  2013-07-25 08:00:03.060  2013-07-25 08:00:03.0600000 +00:00  2013-07-25 00:00:03.0600000 -04:00
2013-07-24 04:00:03.073  2013-07-24 04:00:03.0730000 +00:00  2013-07-24 08:00:03.073  2013-07-24 08:00:03.0730000 +00:00  2013-07-24 00:00:03.0730000 -04:00
2013-07-23 04:00:03.273  2013-07-23 04:00:03.2730000 +00:00  2013-07-23 08:00:03.273  2013-07-23 08:00:03.2730000 +00:00  2013-07-23 00:00:03.2730000 -04:00
2013-07-20 04:00:02.870  2013-07-20 04:00:02.8700000 +00:00  2013-07-20 08:00:02.870  2013-07-20 08:00:02.8700000 +00:00  2013-07-20 00:00:02.8700000 -04:00
2013-07-19 04:00:03.780  2013-07-19 04:00:03.7800000 +00:00  2013-07-19 08:00:03.780  2013-07-19 08:00:03.7800000 +00:00  2013-07-19 00:00:03.7800000 -04:00
                         ----------------------------------                           ----------------------------------  ----------------------------------
                                              No UTC offset                           Time in UTC          No UTC offset  Time all wrong

Keiner von ihnen gibt die gewünschten Werte zurück.

Kann jemand etwas vorschlagen, das zurückgibt, was ich intuitiv will?

35
Ian Boyd

ich habe es herausgefunden. Der Trick ist, dass es eine integrierte SQL Server-Funktion ToDateTimeOffset gibt, die beliebige Versatzinformationen anfügt zu einem gelieferten datetime.

Zum Beispiel die identischen Abfragen:

SELECT ToDateTimeOffset('2013-07-25 15:35:27', -240)     --  -240 minutes
SELECT ToDateTimeOffset('2013-07-25 15:35:27', '-04:00') --  -4 hours

beide geben zurück:

2013-07-25 15:35:27.0000000 -04:00

Hinweis : Der Offset-Parameter für ToDateTimeOffset kann entweder sein:

  • ein integer, das eine Anzahl von Minuten darstellt
  • ein string, das Stunden und Minuten darstellt (in {+|-}TZH:THM Format)

Wir benötigen den aktuellen UTC-Offset des Servers

Als nächstes benötigen wir den aktuellen Offset des Servers von UTC. Ich habe zwei Möglichkeiten, wie SQL Server die integer Anzahl der Minuten von UTC zurückgeben kann:

DATEPART(TZOFFSET, SYSDATETIMEOFFSET()) 
DATEDIFF(minute, GETUTCDATE(), GETDATE())

beide kehren zurück

-240

Einstecken in die Funktion TODATETIMEOFFSET:

SELECT ToDateTimeOffset(
      '2013-07-25 15:35:27',
      DATEPART(TZOFFSET, SYSDATETIMEOFFSET()) --e.g. -240
)

gibt den gewünschten datetimeoffset -Wert zurück:

2013-07-25 15:35:27.0000000 -04:00

Alles in allem

Jetzt können wir eine bessere Funktion haben, um eine Datumszeit in eine Datumszeitverschiebung umzuwandeln:

CREATE FUNCTION dbo.ToDateTimeOffset(@value datetime2)
    RETURNS datetimeoffset AS
BEGIN
/*
    Converts a date/time without any timezone offset into a datetimeoffset value, 
    using the server's current offset from UTC. 

    For this we use the builtin ToDateTimeOffset function; 
    which attaches timezone offset information with a datetimeoffset value.

    The trick is to use DATEDIFF(minutes) between local server time and UTC 
    to get the offset parameter.

    For example:
        DATEPART(TZOFFSET, SYSDATETIMEOFFSET())
    returns the integer
        -240

    for people in EDT (Eastern Daylight Time), which is 4 hours (240 minutes) behind UTC.
    Pass that value to the SQL Server function:
        TODATETIMEOFFSET(@value, -240)
*/

    RETURN TODATETIMEOFFSET(@value, DATEPART(TZOFFSET, SYSDATETIMEOFFSET()))
END;

Beispielnutzung

SELECT TOP 5
    ChangeDate, 
    dbo.ToDateTimeOffset(ChangeDate) AS ChangeDateOffset
FROM AuditLog

gibt den gewünschten zurück:

ChangeDate               ChangeDateOffset
=======================  ==================================
2013-07-25 04:00:03.060  2013-07-25 04:00:03.0600000 -04:00
2013-07-24 04:00:03.073  2013-07-24 04:00:03.0730000 -04:00
2013-07-23 04:00:03.273  2013-07-23 04:00:03.2730000 -04:00
2013-07-20 04:00:02.870  2013-07-20 04:00:02.8700000 -04:00
2013-07-19 04:00:03.780  2013-07-19 04:00:03.7800000 -04:00

Es wäre ideal gewesen, wenn die eingebaute Funktion dies gerade getan hätte:

TODATETIMEOFFSET(value)

anstatt eine "Überladung" zu erzeugen:

dbo.ToDateTimeOffset(value)

Hinweis : Jeder Code wird als gemeinfrei veröffentlicht. Keine Zuordnung erforderlich.

60
Ian Boyd

Das Konvertieren von einer Ortszeit in einen Datums-/Zeitversatz mit dem aktuellen Zeitversatz scheint etwas schwierig zu sein. Es gibt wahrscheinlich einen einfacheren Weg, aber dies scheint es zu tun;

SELECT ChangeDate, 
  CONVERT(DATETIMEOFFSET, CONVERT(VARCHAR, ChangeDate, 120) + 
          RIGHT(CONVERT(VARCHAR, SYSDATETIMEOFFSET(), 120), 6), 120)
FROM AuditLog;

Es lohnt sich wahrscheinlich, eine Funktion zu erstellen.

CREATE FUNCTION LOCALIFY(@dt DATETIME) 
  RETURNS DATETIMEOFFSET AS
BEGIN
 RETURN CONVERT(DATETIMEOFFSET, 
          CONVERT(VARCHAR, @dt, 120) + 
          RIGHT(CONVERT(VARCHAR, SYSDATETIMEOFFSET(), 120), 6), 120)
END;

... und dann einfach ...

SELECT ChangeDate, dbo.LOCALIFY(ChangeDate) FROM AuditLog;
3

Es ist etwas später vom OP, aber dieser Thread ist hilfreich, um Methoden zum Konvertieren von datetime in datetimeoffset zu notieren.

Ich hatte einige der Funktionen verwendet, aber ich würde auch vorschlagen, ein Feld mit der Standardeinstellung sysdatetimeoffset() zu verwenden, damit beim Einfügen von Elementen (der aktuelle Zeitstempel) der Zeitpunkt des Einfügens berücksichtigt wird. Dann Wenn Änderungen erforderlich sind, kann das Update die TZ aus der Quelle in der Prozedur verwenden.

Dies wurde besonders deutlich in OData v4 Transaktionen, die datetimeoffset erfordern.

0
Nathan Teague