web-dev-qa-db-fra.com

Comment caster datetime en datetimeoffset?

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?

35
Ian Boyd

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:

  • un integer, représentant un nombre de minutes
  • un string, représentant une heure et des minutes (en {+|-}TZH:THM format)

Nous avons besoin du décalage UTC actuel du serveur

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

Le mettre tout à fait

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;

Exemple d'utilisation

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.

60
Ian Boyd

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;
3
Joachim Isaksson

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.

0
Nathan Teague