Comment puis-je convertir une valeur SQL Server datetime
en une valeur datetimeoffset
?
Par exemple, une table existante contient des valeurs de datetime
qui sont toutes dans l'heure "locale" du serveur.
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
Mon serveur ( se trouve ) avec (en ce moment, aujourd'hui) quatre heures de retard sur UTC (en ce moment, dans le fuseau horaire de l'Est américain, avec l'heure d'été active) :
SELECT SYSDATETIMEOFFSET()
2013-07-25 14:42:41.6450840 -04:00
je veux convertir les valeurs datetime
stockées en valeurs datetimeoffset
; en utilisant les informations de décalage de fuseau horaire actuelles du serveur.
Les valeurs i désir sont:
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
Vous pouvez voir les caractéristiques souhaitables:
2013-07-19 04:00:03.7800000 -04:00
--------------------------- ------
| |
a "local" datetime the offset from UTC
Mais à la place, les valeurs réelles sont:
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
Avec les caractéristiques invalides:
2013-07-19 04:00:03.7800000 +00:00
--------------------------- ------
^
|
No offset from UTC present
Donc j'essaye d'autres choses au hasard:
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
Avec des résultats:
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
Aucun ne renvoie les valeurs souhaitées.
Quelqu'un peut-il suggérer quelque chose qui renvoie ce que je veux intuitivement?
je l'ai compris. L'astuce est qu'il existe une fonction SQL Server intégrée ToDateTimeOffset
, qui attache des informations de décalage arbitraires à tout datetime
fourni.
Par exemple, les requêtes identiques:
SELECT ToDateTimeOffset('2013-07-25 15:35:27', -240) -- -240 minutes
SELECT ToDateTimeOffset('2013-07-25 15:35:27', '-04:00') -- -4 hours
les deux retournent:
2013-07-25 15:35:27.0000000 -04:00
Remarque : Le paramètre d'offset à ToDateTimeOffset
peut être soit:
integer
, représentant un nombre de minutesstring
, représentant une heure et des minutes (en {+|-}TZH:THM
format)Ensuite, nous avons besoin du décalage actuel du serveur par rapport à UTC. Je peux demander à SQL Server de renvoyer le integer
nombre de minutes depuis UTC:
DATEPART(TZOFFSET, SYSDATETIMEOFFSET())
DATEDIFF(minute, GETUTCDATE(), GETDATE())
les deux reviennent
-240
Brancher ceci dans la fonction TODATETIMEOFFSET
:
SELECT ToDateTimeOffset(
'2013-07-25 15:35:27',
DATEPART(TZOFFSET, SYSDATETIMEOFFSET()) --e.g. -240
)
renvoie la valeur datetimeoffset
que je veux:
2013-07-25 15:35:27.0000000 -04:00
Maintenant, nous pouvons avoir une meilleure fonction pour convertir un datetime en un datetimeoffset:
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;
SELECT TOP 5
ChangeDate,
dbo.ToDateTimeOffset(ChangeDate) AS ChangeDateOffset
FROM AuditLog
renvoie le résultat souhaité:
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
Cela aurait été idéal si la fonction intégrée venait de faire ceci:
TODATETIMEOFFSET(value)
plutôt que d'avoir à créer un "surcharge":
dbo.ToDateTimeOffset(value)
Remarque : Tout code est publié dans le domaine public. Aucune attribution requise.
La conversion d'une heure locale en un datetimeoffset avec le décalage horaire actuel semble prendre un peu de ruse. Il existe probablement un moyen plus simple, mais cela semble le faire;
SELECT ChangeDate,
CONVERT(DATETIMEOFFSET, CONVERT(VARCHAR, ChangeDate, 120) +
RIGHT(CONVERT(VARCHAR, SYSDATETIMEOFFSET(), 120), 6), 120)
FROM AuditLog;
Cela vaut probablement la peine de créer une fonction;
CREATE FUNCTION LOCALIFY(@dt DATETIME)
RETURNS DATETIMEOFFSET AS
BEGIN
RETURN CONVERT(DATETIMEOFFSET,
CONVERT(VARCHAR, @dt, 120) +
RIGHT(CONVERT(VARCHAR, SYSDATETIMEOFFSET(), 120), 6), 120)
END;
... et puis juste ...
SELECT ChangeDate, dbo.LOCALIFY(ChangeDate) FROM AuditLog;
C'est un peu plus tard dans le temps de l'OP, mais ce fil est utile pour noter les méthodes de conversion de datetime
en datetimeoffset
.
J'avais utilisé certaines fonctionnalités, mais je suggérerais également d'utiliser un champ avec la valeur par défaut définie sur sysdatetimeoffset()
, de sorte que lorsque les éléments seraient insérés (l'horodatage actuel) serait relatif à la date de leur insertion. Ensuite si des modifications sont nécessaires, la mise à jour pourrait utiliser le TZ de la source dans la procédure.
Cela est devenu particulièrement évident dans les transactions OData v4, qui nécessitent datetimeoffset
.