Convertir la colonne Datetime d'UTC en heure locale dans l'instruction select


208

Je fais quelques requêtes SQL et je voudrais convertir ma colonne UTC datetime en heure locale pour l'afficher en tant qu'heure locale dans mes résultats de requête. Remarque, je ne cherche PAS à effectuer cette conversion via du code mais plutôt lorsque je fais des requêtes SQL manuelles et aléatoires sur mes bases de données.


Cette question vous semble-t-elle similaire à votre situation? stackoverflow.com/questions/3404646/…
Taryn East

Réponses:


322

Vous pouvez le faire comme suit sur SQL Server 2008 ou supérieur:

SELECT CONVERT(datetime, 
               SWITCHOFFSET(CONVERT(datetimeoffset, 
                                    MyTable.UtcColumn), 
                            DATENAME(TzOffset, SYSDATETIMEOFFSET()))) 
       AS ColumnInLocalTime
FROM MyTable

Vous pouvez également faire le moins verbeux:

SELECT DATEADD(mi, DATEDIFF(mi, GETUTCDATE(), GETDATE()), MyTable.UtcColumn) 
       AS ColumnInLocalTime
FROM MyTable

Quoi que vous fassiez, ne l' utilisez pas- pour soustraire des dates, car l'opération n'est pas atomique, et vous obtiendrez parfois des résultats indéterminés en raison de conditions de concurrence entre le système datetime et le local datetime vérifiés à des moments différents (c'est-à-dire non atomiquement) .

Veuillez noter que cette réponse ne prend pas en compte l'heure d'été. Si vous souhaitez inclure un ajustement d'heure d'été, veuillez également consulter la question SO suivante:

Comment créer la fonction de début et de fin de l'heure d'été dans SQL Server


38
Existe-t-il un moyen de créer ce compte pour des économies de lumière du jour?
Steve

15
Je ne vois pas le nom d'un fuseau horaire ici, donc c'est incorrect. C'est une très mauvaise idée de supposer que vous pouvez convertir en heure locale en faisant de l'arithmétique
JonnyRaa

7
@MichaelGoldshteyn si vous avez un décalage de fuseau horaire, vous ne savez toujours pas dans quel fuseau logique une heure se trouve. Les fuseaux horaires sont des choses sociales et peuvent être modifiés par les gouvernements à différents moments. Le décalage pour un fuseau horaire peut être différent à différents moments dans le temps / l'histoire (l'heure d'été étant courante). Vous décalez une heure du décalage actuel par rapport à l'UTC ... un autre point est que cela donnera le fuseau horaire du serveur et non du client, ce qui pourrait être un problème supplémentaire si vous hébergez dans un autre pays.
JonnyRaa

4
@Niloofar La réponse simple est que vous ne le faites pas et ne devriez pas. Les horaires doivent toujours être stockés en UTC (de préférence en fonction de l'horloge du serveur de base de données, et non de celle du serveur Web, du serveur d'applications et certainement pas de celle du client). L'affichage de cette date / heure est une fonction de la couche d'interface utilisateur et elle est responsable de la conversion de la date / heure au format souhaité (y compris un fuseau horaire si nécessaire).
Robert McKee

11
Pour toute personne utilisant sql azure, cette approche ne fonctionnera pas car les fonctions date / heure renvoient toutes UTC, donc comparer GETDATE () à GETUTCDATE () ne vous donne rien à travailler et votre résultat est le même que celui avec lequel vous avez commencé.
Brian Surowiec

59

Je n'ai trouvé aucun de ces exemples utiles pour obtenir un datetime stocké en UTC à un datetime dans un fuseau horaire spécifié (PAS le fuseau horaire du serveur car les bases de données Azure SQL s'exécutent en UTC). C'est ainsi que je l'ai géré. Ce n'est pas élégant mais c'est simple et vous donne la bonne réponse sans conserver d'autres tableaux:

select CONVERT(datetime, SWITCHOFFSET(dateTimeField, DATEPART(TZOFFSET, 
dateTimeField AT TIME ZONE 'Eastern Standard Time')))

4
Fonctionne pour 2016 uniquement et utilise le registre système. Mais une excellente solution pour Azure.
Dan Cundy

2
Celui-ci fonctionne pour les heures d'azur stockées en UTC, merci les gars
Null

3
Voici une liste des chaînes que vous pouvez utiliser pour les fuseaux horaires: stackoverflow.com/a/7908482/631277
Matt Kemp

1
Si vous utilisez SQL 2016 et la AT TIME ZONEsyntaxe, pensez à stackoverflow.com/a/44941536/112764 - vous pouvez enchaîner plusieurs conversions en concaténant simplement plusieurs at time zone <blah>s.
NateJ

3
C'est un peu étrange aussi, mais certains tests très basiques semblent également fonctionner - dateTimeField AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time'- enchaînant simplement les AT TIME ZONEinstructions. (@NateJ l'a mentionné ci-dessus, je vois maintenant)
David Mohundro

22

Si votre heure locale est indiquée Eastern Standard Timeet que vous souhaitez convertir de l'UTC en cela, alors dans Azure SQL et SQL Server 2016 et versions ultérieures , vous pouvez faire:

SELECT YourUtcColumn AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time' AS
       LocalTime
FROM   YourTable

La liste complète des noms de fuseau horaire peut être trouvée avec:

SELECT * FROM sys.time_zone_info 

Et oui, les fuseaux horaires sont mal nommés - même si c'est le cas Eastern Standard Time, l'heure d'été est prise en compte.


2
Les fuseaux horaires et les décalages peuvent également être trouvés dans SQL Server ici: SELECT * FROM sys.time_zone_info
rayzinnz

21

Si vous avez besoin d'une conversion autre que l'emplacement de votre serveur, voici une fonction qui vous permet de passer un décalage standard et qui prend en compte l'heure d'été américaine:

-- =============================================
-- Author:      Ron Smith
-- Create date: 2013-10-23
-- Description: Converts UTC to DST
--              based on passed Standard offset
-- =============================================
CREATE FUNCTION [dbo].[fn_UTC_to_DST]
(
    @UTC datetime,
    @StandardOffset int
)
RETURNS datetime
AS
BEGIN

    declare 
        @DST datetime,
        @SSM datetime, -- Second Sunday in March
        @FSN datetime  -- First Sunday in November

    -- get DST Range
    set @SSM = datename(year,@UTC) + '0314' 
    set @SSM = dateadd(hour,2,dateadd(day,datepart(dw,@SSM)*-1+1,@SSM))
    set @FSN = datename(year,@UTC) + '1107'
    set @FSN = dateadd(second,-1,dateadd(hour,2,dateadd(day,datepart(dw,@FSN)*-1+1,@FSN)))

    -- add an hour to @StandardOffset if @UTC is in DST range
    if @UTC between @SSM and @FSN
        set @StandardOffset = @StandardOffset + 1

    -- convert to DST
    set @DST = dateadd(hour,@StandardOffset,@UTC)

    -- return converted datetime
    return @DST

END

GO

2
Ron Smith Je sais que c'est un vieux post mais j'étais curieux de savoir ce que les codes codés en dur '0314' et '1107' représentent dans l'obtention de la gamme DST. Il semble que ce soient des jours codés en dur qui changent en raison du DTS. Pourquoi coderiez-vous en dur alors que ce devrait être une date calculée parce que les jours changent en fonction de l'endroit du calendrier où le deuxième dimanche de mars et le premier dimanche de novembre tombent. Les jours codés en dur rendraient ce code fondamentalement imparfait.
Mike

2
Bonne question :) Ce sont les dates maximales où le deuxième dimanche de mars et le premier dimanche de novembre peuvent avoir lieu. Les lignes suivantes définissent les variables sur la date réelle.
Ron Smith

8

Utilisation de nouvelles opportunités SQL Server 2016:

CREATE FUNCTION ToLocalTime(@dtUtc datetime, @timezoneId nvarchar(256))
RETURNS datetime
AS BEGIN

return @dtUtc AT TIME ZONE 'UTC' AT TIME ZONE @timezoneId

/* -- second way, faster

return SWITCHOFFSET(@dtUtc , DATENAME(tz, @dtUtc AT TIME ZONE @timezoneId))

*/

/* -- third way

declare @dtLocal datetimeoffset
set @dtLocal = @dtUtc AT TIME ZONE @timezoneId
return dateadd(minute, DATEPART (TZoffset, @dtLocal), @dtUtc)

*/

END
GO

Mais la procédure clr fonctionne 5 fois plus vite: '- (

Faites attention à ce que le décalage pour une zone horaire puisse changer en heure d'hiver ou d'été. Par exemple

select cast('2017-02-08 09:00:00.000' as datetime) AT TIME ZONE 'Eastern Standard Time'
select cast('2017-08-08 09:00:00.000' as datetime) AT TIME ZONE 'Eastern Standard Time'

résultats:

2017-02-08 09:00:00.000 -05:00
2017-08-08 09:00:00.000 -04:00

Vous ne pouvez pas simplement ajouter un décalage constant.


5

Si l'activation de CLR sur votre base de données est une option ainsi que l'utilisation du fuseau horaire du serveur SQL, il peut être écrit en .Net assez facilement.

public partial class UserDefinedFunctions
{
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlDateTime fn_GetLocalFromUTC(SqlDateTime UTC)
    {
        if (UTC.IsNull)
            return UTC;

        return new SqlDateTime(UTC.Value.ToLocalTime());
    }
}

Une valeur datetime UTC entre et la valeur datetime locale relative au serveur sort. Les valeurs nulles renvoient null.


5

Il n'y a pas de moyen simple de le faire de manière correcte ET générique.

Tout d'abord, il faut comprendre que le décalage dépend de la date en question, du fuseau horaire ET de l'heure d'été. GetDate()-GetUTCDatevous donne seulement le décalage aujourd'hui au TZ du serveur, ce qui n'est pas pertinent.

Je n'ai vu que deux solutions de travail et j'ai beaucoup cherché.

1) Une fonction SQL personnalisée avec quelques tables de données de base telles que les fuseaux horaires et les règles DST par TZ. Fonctionne mais pas très élégant. Je ne peux pas le poster car je ne possède pas le code.

EDIT: Voici un exemple de cette méthode https://gist.github.com/drumsta/16b79cee6bc195cd89c8

2) Ajoutez un assemblage .net à la base de données, .Net peut le faire très facilement. Cela fonctionne très bien mais l'inconvénient est que vous devez configurer plusieurs paramètres au niveau du serveur et la configuration est facilement interrompue, par exemple si vous restaurez la base de données. J'utilise cette méthode mais je ne peux pas la poster car je ne possède pas le code.


4

Rien de tout cela n'a fonctionné pour moi, mais cela ci-dessous a fonctionné à 100%. J'espère que cela peut aider les autres à essayer de le convertir comme je l'ai été.

CREATE FUNCTION [dbo].[fn_UTC_to_EST]
(
    @UTC datetime,
    @StandardOffset int
)
RETURNS datetime
AS
BEGIN

declare 
    @DST datetime,
    @SSM datetime, -- Second Sunday in March
    @FSN datetime  -- First Sunday in November
-- get DST Range
set @SSM = DATEADD(dd,7 + (6-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(GETDATE())-1900) * 12 + 2,0))%7)),DATEADD(mm,(YEAR(GETDATE())-1900) * 12 + 2,0))+'02:00:00' 
set @FSN = DATEADD(dd, (6-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(GETDATE())-1900) * 12 + 10,0))%7)),DATEADD(mm,(YEAR(GETDATE())-1900) * 12 + 10,0)) +'02:00:00'

-- add an hour to @StandardOffset if @UTC is in DST range
if @UTC between @SSM and @FSN
    set @StandardOffset = @StandardOffset + 1

-- convert to DST
set @DST = dateadd(hour,@StandardOffset,@UTC)

-- return converted datetime
return @DST

END

1
Cela devrait être la réponse acceptée. La seule chose que je changerais est le nom car cela implique qu'il s'agit de l'heure EST, alors qu'il s'agit vraiment de l'heure locale et que le StandardOffset est passé en paramètre.
Greg Gum

D'accord, meilleure réponse ... mais au lieu d'avoir à passer l'offset comme paramètre, j'ai ajouté ceci dans le corps de la fonction:declare @StandardOffset int = datediff (hh, GETUTCDATE(), GETDATE())
Tom Warfield

Suite à ma suggestion précédente - Si vous calculez @StandardOffset, vous n'avez pas besoin de faire la correction DST.
Tom Warfield

3

Voici une version qui tient compte de l'heure d'été, du décalage UTC et n'est pas verrouillée sur une année particulière.

---------------------------------------------------------------------------------------------------
--Name:     udfToLocalTime.sql
--Purpose:  To convert UTC to local US time accounting for DST
--Author:   Patrick Slesicki
--Date:     3/25/2014
--Notes:    Works on SQL Server 2008R2 and later, maybe SQL Server 2008 as well.
--          Good only for US States observing the Energy Policy Act of 2005.
--          Function doesn't apply for years prior to 2007.
--          Function assumes that the 1st day of the week is Sunday.
--Tests:        
--          SELECT dbo.udfToLocalTime('2014-03-09 9:00', DEFAULT)
--          SELECT dbo.udfToLocalTime('2014-03-09 10:00', DEFAULT)
--          SELECT dbo.udfToLocalTime('2014-11-02 8:00', DEFAULT)
--          SELECT dbo.udfToLocalTime('2014-11-02 9:00', DEFAULT)
---------------------------------------------------------------------------------------------------
ALTER FUNCTION udfToLocalTime
    (
    @UtcDateTime    AS DATETIME
    ,@UtcOffset     AS INT = -8 --PST
    )
RETURNS DATETIME
AS 
BEGIN
    DECLARE 
        @PstDateTime    AS DATETIME
        ,@Year          AS CHAR(4)
        ,@DstStart      AS DATETIME
        ,@DstEnd        AS DATETIME
        ,@Mar1          AS DATETIME
        ,@Nov1          AS DATETIME
        ,@MarTime       AS TIME
        ,@NovTime       AS TIME
        ,@Mar1Day       AS INT
        ,@Nov1Day       AS INT
        ,@MarDiff       AS INT
        ,@NovDiff       AS INT

    SELECT
        @Year       = YEAR(@UtcDateTime)
        ,@MarTime   = CONVERT(TIME, DATEADD(HOUR, -@UtcOffset, '1900-01-01 02:00'))
        ,@NovTime   = CONVERT(TIME, DATEADD(HOUR, -@UtcOffset - 1, '1900-01-01 02:00'))
        ,@Mar1      = CONVERT(CHAR(16), @Year + '-03-01 ' + CONVERT(CHAR(5), @MarTime), 126)
        ,@Nov1      = CONVERT(CHAR(16), @Year + '-11-01 ' + CONVERT(CHAR(5), @NovTime), 126)
        ,@Mar1Day   = DATEPART(WEEKDAY, @Mar1)
        ,@Nov1Day   = DATEPART(WEEKDAY, @Nov1)

    --Get number of days between Mar 1 and DST start date
    IF @Mar1Day = 1 SET @MarDiff = 7
    ELSE SET @MarDiff = 15 - @Mar1Day

    --Get number of days between Nov 1 and DST end date
    IF @Nov1Day = 1 SET @NovDiff = 0
    ELSE SET @NovDiff = 8 - @Nov1Day

    --Get DST start and end dates
    SELECT 
        @DstStart   = DATEADD(DAY, @MarDiff, @Mar1)
        ,@DstEnd    = DATEADD(DAY, @NovDiff, @Nov1)

    --Change UTC offset if @UtcDateTime is in DST Range
    IF @UtcDateTime >= @DstStart AND @UtcDateTime < @DstEnd SET @UtcOffset = @UtcOffset + 1

    --Get Conversion
    SET @PstDateTime = DATEADD(HOUR, @UtcOffset, @UtcDateTime)
    RETURN @PstDateTime
END
GO

3

J'ai trouvé que la fonction unique était trop lente lorsqu'il y avait beaucoup de données. Je l'ai donc fait en joignant une fonction de table qui permettrait de calculer le différentiel horaire. Il s'agit essentiellement de segments datetime avec le décalage horaire. Une année serait de 4 rangées. Donc, la fonction de table

dbo.fn_getTimeZoneOffsets('3/1/2007 7:00am', '11/5/2007 9:00am', 'EPT')

retournerait ce tableau:

startTime          endTime   offset  isHr2
3/1/07 7:00     3/11/07 6:59    -5    0
3/11/07 7:00    11/4/07 6:59    -4    0
11/4/07 7:00    11/4/07 7:59    -5    1
11/4/07 8:00    11/5/07 9:00    -5    0

Il tient compte de l'heure d'été. Un exemple de la façon dont il est utilisé est ci-dessous et l'article de blog complet est ici .

select mt.startTime as startUTC, 
    dateadd(hh, tzStart.offset, mt.startTime) as startLocal, 
    tzStart.isHr2
from MyTable mt 
inner join dbo.fn_getTimeZoneOffsets(@startViewUTC, @endViewUTC, @timeZone)  tzStart
on mt.startTime between tzStart.startTime and tzStart.endTime

Il ne semble pas considérer l'heure d'été correctement. Je ne sais pas si vous supposez que seuls les États-Unis existent et que les règles d'heure d'été ne changent jamais.
vikjon0

@ vikjon0 oui, le projet pour lequel j'ai construit cela n'avait que des fuseaux horaires américains.
JBrooks

2
 declare @mydate2 datetime
 set @mydate2=Getdate()
 select @mydate2 as mydate,
 dateadd(minute, datediff(minute,getdate(),@mydate2),getutcdate())

1

La réponse de Ron contient une erreur. Il utilise 2:00 AM heure locale où l'équivalent UTC est requis. Je n'ai pas assez de points de réputation pour commenter la réponse de Ron, donc une version corrigée apparaît ci-dessous:

-- =============================================
-- Author:      Ron Smith
-- Create date: 2013-10-23
-- Description: Converts UTC to DST
--              based on passed Standard offset
-- =============================================
CREATE FUNCTION [dbo].[fn_UTC_to_DST]
(
    @UTC datetime,
    @StandardOffset int
)
RETURNS datetime
AS
BEGIN

declare 
    @DST datetime,
    @SSM datetime, -- Second Sunday in March
    @FSN datetime  -- First Sunday in November
-- get DST Range
set @SSM = datename(year,@UTC) + '0314' 
set @SSM = dateadd(hour,2 - @StandardOffset,dateadd(day,datepart(dw,@SSM)*-1+1,@SSM))
set @FSN = datename(year,@UTC) + '1107'
set @FSN = dateadd(second,-1,dateadd(hour,2 - (@StandardOffset + 1),dateadd(day,datepart(dw,@FSN)*-1+1,@FSN)))

-- add an hour to @StandardOffset if @UTC is in DST range
if @UTC between @SSM and @FSN
    set @StandardOffset = @StandardOffset + 1

-- convert to DST
set @DST = dateadd(hour,@StandardOffset,@UTC)

-- return converted datetime
return @DST

END

C'est une fonctionnalité, pas un bug :) La plupart des États-Unis commencent l'heure d'été à 2h00 du matin en.wikipedia.org/wiki/Daylight_saving_time
Ron Smith

@RonSmith Oui, à 02h00 heure locale, que nous devons convertir en UTC pour détecter si l'heure UTC donnée est dans la plage d'heure d'été.
jlspublic

1

L'horodatage UNIX est simplement le nombre de secondes entre une date particulière et l'époque Unix,

SELECT DATEDIFF (SECOND, {d '1970-01-01'}, GETDATE ()) // Cela retournera l'horodatage UNIX dans le serveur SQL

vous pouvez créer une fonction pour la conversion de l'heure locale en date UTC Unix UTC à l'aide de la fonction de décalage du pays vers l'horodatage Unix dans le serveur SQL


1

C'est simple. Essayez ceci pour Azure SQL Server:

SELECT YourDateTimeColumn AT TIME ZONE 'Eastern Standard Time' FROM YourTable

Pour Local SQL Server:

SELECT CONVERT(datetime2, SWITCHOFFSET(CONVERT(datetimeoffset, gETDATE()), DATENAME(TzOffset, gETDATE() AT TIME ZONE 'Eastern Standard Time'))) FROM YourTable

1
Que se passe-t-il avec cela pendant l'heure d'été (puisque le fuseau horaire indique spécifiquement "heure normale de l'Est")?
Mark

1

Pour les utilisateurs Azure SQL et @@Version> = SQL Server 2016, voici une fonction simple à utiliser AT TIME ZONE.

CREATE FUNCTION [dbo].[Global_Convert_UTCTimeTo_LocalTime]
(
   @LocalTimeZone        VARCHAR(50),
   @UTCDateTime          DATETIME
)
RETURNS DATETIME
AS
BEGIN
   DECLARE @ConvertedDateTime DATETIME;

   SELECT @ConvertedDateTime = @UTCDateTime AT TIME ZONE 'UTC' AT TIME ZONE @LocalTimeZone
   RETURN @ConvertedDateTime

END
GO

Pour les types de valeurs qui @LocalTimeZonepeuvent prendre, veuillez cliquer sur ce lien ou surKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones


0

Comme avertissement - si vous allez utiliser ce qui suit (notez les millisecondes au lieu des minutes):

    SELECT DATEADD(ms, DATEDIFF(ms, GETUTCDATE(), GETDATE()), MyTable.UtcColumn) 
    AS ColumnInLocalTime
    FROM MyTable

Gardez à l'esprit que la partie DATEDIFF ne renverra pas toujours le même numéro. Donc, ne l'utilisez pas pour comparer DateTimes en millisecondes.


0

J'ai trouvé que cette fonction est plus rapide que d'autres solutions utilisant une table ou des boucles distinctes. C'est juste une déclaration de cas de base. Étant donné que tous les mois entre avril et octobre ont un décalage de -4 heures (heure de l'Est), nous devons simplement ajouter quelques lignes de cas supplémentaires pour les jours marginaux. Sinon, le décalage est de -5 heures.

Ceci est spécifique à une conversion de l'heure UTC en heure de l'Est, mais des fonctions de fuseau horaire supplémentaires peuvent être ajoutées au besoin.

USE [YourDatabaseName]
GO

/****** Object:  UserDefinedFunction [dbo].[ConvertUTCtoEastern]    Script Date: 11/2/2016 5:21:52 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO


CREATE FUNCTION [dbo].[ConvertUTCtoEastern]
(
@dtStartDate DATETIME
)
RETURNS DATETIME
AS
BEGIN
DECLARE @Working DATETIME
DECLARE @Returned DATETIME

SET @Working = @dtStartDate
SET @Working = 
case when month(@Working) between 4 and 10 then dateadd(HH,-4,@Working) 
     when @Working between '2017-03-12' and '2017-11-05' then dateadd(HH,-4,@Working) 
     when @Working between '2016-03-13' and '2016-11-06' then dateadd(HH,-4,@Working) 
     when @Working between '2015-03-08' and '2015-11-01' then dateadd(HH,-4,@Working) 
     when @Working between '2014-03-09' and '2014-11-02' then dateadd(HH,-4,@Working) 
     when @Working between '2013-03-10' and '2013-11-03' then dateadd(HH,-4,@Working) 
     when @Working between '2012-03-11' and '2012-11-04' then dateadd(HH,-4,@Working) 
else dateadd(HH,-5,@Working) end

SET @Returned = @Working

RETURN @Returned

END


GO

0

Cela devrait être en mesure d'obtenir l'heure du serveur avec l'heure d'été

declare @dt datetime
set @dt = getutcdate() -- GMT equivalent

sysdatetimeoffset prend en compte l'heure d'été

select [InputTime] = @dt
       , [LocalTime2] = dateadd(mi, datediff(mi, sysdatetimeoffset(),getdate()), @dt) 

0

Première fonction: configurée pour le fuseau horaire italien (+1, +2), changer les dates: dernier dimanche de mars et octobre, renvoyer la différence entre le fuseau horaire actuel et le datetime comme paramètre.

Returns:
current timezone < parameter timezone ==> +1
current timezone > parameter timezone ==> -1
else 0

Le code est:

CREATE FUNCTION [dbo].[UF_ADJUST_OFFSET]
(
    @dt_utc datetime2(7)
)
RETURNS INT
AS
BEGIN


declare @month int,
        @year int,
        @current_offset int,
        @offset_since int,
        @offset int,
        @yearmonth varchar(8),
        @changeoffsetdate datetime2(7)

declare @lastweek table(giorno datetime2(7))

select @current_offset = DATEDIFF(hh, GETUTCDATE(), GETDATE())

select @month = datepart(month, @dt_utc)

if @month < 3 or @month > 10 Begin Set @offset_since = 1 Goto JMP End

if @month > 3 and @month < 10 Begin Set @offset_since = 2 Goto JMP End

--If i'm here is march or october
select @year = datepart(yyyy, @dt_utc)

if @month = 3
Begin

Set @yearmonth = cast(@year as varchar) + '-03-'

Insert Into @lastweek Values(@yearmonth + '31 03:00:00.000000'),(@yearmonth + '30 03:00:00.000000'),(@yearmonth + '29 03:00:00.000000'),(@yearmonth + '28 03:00:00.000000'),
                         (@yearmonth + '27 03:00:00.000000'),(@yearmonth + '26 03:00:00.000000'),(@yearmonth + '25 03:00:00.000000')

--Last week of march
Select @changeoffsetdate = giorno From @lastweek Where  datepart(weekday, giorno) = 1

    if @dt_utc < @changeoffsetdate 
    Begin 
        Set @offset_since = 1 
    End Else Begin
        Set @offset_since = 2
    End
End

if @month = 10
Begin

Set @yearmonth = cast(@year as varchar) + '-10-'

Insert Into @lastweek Values(@yearmonth + '31 03:00:00.000000'),(@yearmonth + '30 03:00:00.000000'),(@yearmonth + '29 03:00:00.000000'),(@yearmonth + '28 03:00:00.000000'),
                         (@yearmonth + '27 03:00:00.000000'),(@yearmonth + '26 03:00:00.000000'),(@yearmonth + '25 03:00:00.000000')

--Last week of october
Select @changeoffsetdate = giorno From @lastweek Where  datepart(weekday, giorno) = 1

    if @dt_utc > @changeoffsetdate 
    Begin 
        Set @offset_since = 1 
    End Else Begin
        Set @offset_since = 2
    End
End

JMP:

if @current_offset < @offset_since Begin
    Set @offset = 1
End Else if @current_offset > @offset_since Set @offset = -1 Else Set @offset = 0

Return @offset

END

Ensuite, la fonction qui convertit la date

CREATE FUNCTION [dbo].[UF_CONVERT]
(
    @dt_utc datetime2(7)
)
RETURNS datetime
AS
BEGIN

    declare @offset int


    Select @offset = dbo.UF_ADJUST_OFFSET(@dt_utc)

    if @dt_utc >= '9999-12-31 22:59:59.9999999'
        set @dt_utc = '9999-12-31 23:59:59.9999999'
    Else
        set @dt_utc = (SELECT DATEADD(mi, DATEDIFF(mi, GETUTCDATE(), GETDATE()), @dt_utc) )

    if @offset <> 0
        Set @dt_utc = dateadd(hh, @offset, @dt_utc)

    RETURN @dt_utc

END

0

- obtenir l'heure standard indienne d'utc

CREATE FUNCTION dbo.getISTTime
(
@UTCDate datetime
)
RETURNS datetime
AS
BEGIN

    RETURN dateadd(minute,330,@UTCDate)

END
GO

0

Cela peut se faire sans fonction. Le code ci-dessous convertira une heure UTC en heure de la montagne en tenant compte de l'heure d'été. Ajustez tous les nombres -6 et -7 à votre fuseau horaire en conséquence (c'est-à-dire que pour EST, vous ajusteriez respectivement -4 et -5)

--Adjust a UTC value, in the example the UTC field is identified as UTC.Field, to account for daylight savings time when converting out of UTC to Mountain time.
CASE
    --When it's between March and November, it is summer time which is -6 from UTC
    WHEN MONTH ( UTC.Field ) > 3 AND MONTH ( UTC.Field ) < 11 
        THEN DATEADD ( HOUR , -6 , UTC.Field )
    --When its March and the day is greater than the 14, you know it's summer (-6)
    WHEN MONTH ( UTC.Field ) = 3
        AND DATEPART ( DAY , UTC.Field ) >= 14 
        THEN
            --However, if UTC is before 9am on that Sunday, then it's before 2am Mountain which means it's still Winter daylight time.
            CASE 
                WHEN DATEPART ( WEEKDAY , UTC.Field ) = 1 
                    AND UTC.Field < '9:00'
                    --Before 2am mountain time so it's winter, -7 hours for Winter daylight time
                    THEN DATEADD ( HOUR , -7 , UTC.Field )
                --Otherwise -6 because it'll be after 2am making it Summer daylight time
                ELSE DATEADD ( HOUR , -6 , UTC.Field )
            END
    WHEN MONTH ( UTC.Field ) = 3
        AND ( DATEPART ( WEEKDAY , UTC.Field ) + 7 ) <= DATEPART ( day , UTC.Field ) 
        THEN 
            --According to the date, it's moved onto Summer daylight, but we need to account for the hours leading up to 2am if it's Sunday
            CASE 
                WHEN DATEPART ( WEEKDAY , UTC.Field ) = 1 
                    AND UTC.Field < '9:00'
                    --Before 9am UTC is before 2am Mountain so it's winter Daylight, -7 hours
                    THEN DATEADD ( HOUR , -7 , UTC.Field )
                --Otherwise, it's summer daylight, -6 hours
                ELSE DATEADD ( HOUR , -6 , UTC.Field )
            END
    --When it's November and the weekday is greater than the calendar date, it's still Summer so -6 from the time
    WHEN MONTH ( UTC.Field ) = 11
        AND DATEPART ( WEEKDAY , UTC.Field ) > DATEPART ( DAY , UTC.Field ) 
        THEN DATEADD ( HOUR , -6 , UTC.Field )
    WHEN MONTH ( UTC.Field ) = 11
        AND DATEPART ( WEEKDAY , UTC.Field ) <= DATEPART ( DAY , UTC.Field ) 
            --If the weekday is less than or equal to the calendar day it's Winter daylight but we need to account for the hours leading up to 2am.
            CASE 
                WHEN DATEPART ( WEEKDAY , UTC.Field ) = 1 
                    AND UTC.Field < '8:00'
                    --If it's before 8am UTC and it's Sunday in the logic outlined, then it's still Summer daylight, -6 hours
                    THEN DATEADD ( HOUR , -6 , UTC.Field )
                --Otherwise, adjust for Winter daylight at -7
                ELSE DATEADD ( HOUR , -7 , UTC.Field )
            END
    --If the date doesn't fall into any of the above logic, it's Winter daylight, -7
    ELSE
        DATEADD ( HOUR , -7 , UTC.Field )
END

0

Vous devez reformater la chaîne ainsi que la conversion à l'heure correcte. Dans ce cas, j'avais besoin de temps zoulou.

Declare @Date datetime;
Declare @DateString varchar(50);
set @Date = GETDATE(); 
declare @ZuluTime datetime;

Declare @DateFrom varchar (50);
Declare @DateTo varchar (50);
set @ZuluTime = DATEADD(second, DATEDIFF(second, GETDATE(), GETUTCDATE()), @Date);
set @DateString =  FORMAT(@ZuluTime, 'yyyy-MM-ddThh:mm:ssZ', 'en-US' )  
select @DateString;

0

Le meilleur moyen pour Oracle:

Avec datetime codé en dur:

SELECT TO_CHAR(CAST((FROM_TZ(CAST(TO_DATE('2018-10-27 21:00', 'YYYY-MM-DD HH24:MI') AS TIMESTAMP), 'UTC') AT  TIME ZONE 'EET') AS DATE), 'YYYY-MM-DD HH24:MI') UTC_TO_EET FROM DUAL

Result: 2018-10-28 00:00

Avec les noms de colonne et de table:

SELECT TO_CHAR(CAST((FROM_TZ(CAST(COLUMN_NAME AS TIMESTAMP), 'UTC') AT  TIME ZONE 'EET') AS DATE), 'YYYY-MM-DD HH24:MI') UTC_TO_EET FROM TABLE_NAME

0

J'ai du code pour effectuer des heures UTC vers Local et Local vers UTC qui permet la conversion en utilisant un code comme celui-ci

DECLARE @usersTimezone VARCHAR(32)='Europe/London'
DECLARE @utcDT DATETIME=GetUTCDate()
DECLARE @userDT DATETIME=[dbo].[funcUTCtoLocal](@utcDT, @usersTimezone)

et

DECLARE @usersTimezone VARCHAR(32)='Europe/London'
DECLARE @userDT DATETIME=GetDate()
DECLARE @utcDT DATETIME=[dbo].[funcLocaltoUTC](@userDT, @usersTimezone)

Les fonctions peuvent prendre en charge tout ou partie des fuseaux horaires dans l'IANA / TZDB comme fourni par NodaTime - voir la liste complète sur https://nodatime.org/TimeZones

Sachez que mon cas d'utilisation signifie que je n'ai besoin que d'une fenêtre «actuelle», permettant la conversion des temps dans une fourchette d'environ +/- 5 ans à partir de maintenant. Cela signifie que la méthode que j'ai utilisée ne vous convient probablement pas si vous avez besoin d'une très longue période, en raison de la façon dont elle génère du code pour chaque intervalle de fuseau horaire dans une plage de dates donnée.

Le projet est sur GitHub: https://github.com/elliveny/SQLServerTimeConversion

Cela génère du code de fonction SQL selon cet exemple


0

Eh bien, si vous stockez les données en tant que date UTC dans la base de données, vous pouvez faire quelque chose d'aussi simple que

select 
 [MyUtcDate] + getdate() - getutcdate()
from [dbo].[mytable]

c'était toujours local du point de vue du serveur et vous ne tentez pas avec AT TIME ZONE 'your time zone name', si votre base de données est déplacée vers un autre fuseau horaire comme une installation client, un fuseau horaire codé en dur peut vous mordre.


0

En postgres, cela fonctionne très bien .. Indiquez au serveur l'heure à laquelle l'heure est enregistrée, «utc», puis demandez-lui de convertir en un fuseau horaire spécifique, dans ce cas «Brésil / Est»

quiz_step_progresses.created_at  at time zone 'utc' at time zone 'Brazil/East'

Obtenez une liste complète des fuseaux horaires avec la sélection suivante;

select * from pg_timezone_names;

Voir les détails ici.

https://popsql.com/learn-sql/postgresql/how-to-convert-utc-to-local-time-zone-in-postgresql


-1

En voici une plus simple qui tient compte de dst

CREATE FUNCTION [dbo].[UtcToLocal] 
(
    @p_utcDatetime DATETIME 
)
RETURNS DATETIME
AS
BEGIN
    RETURN DATEADD(MINUTE, DATEDIFF(MINUTE, GETUTCDATE(), @p_utcDatetime), GETDATE())
END

6
Cela ne prend pas réellement en compte l'heure d'été. Il suffit de l' essayer: SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, GETUTCDATE(), '20150101'), GETDATE()). Je suis actuellement en CEST (UTC + 2), mais l'heure d'été ne sera pas en vigueur le jour du Nouvel An, donc la bonne réponse pour moi serait le 1er janvier 2015 à 01h00. Votre réponse, comme la réponse acceptée, renvoie le 1er janvier 2015 02:00.
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.