Converter coluna Datetime de UTC para hora local na instrução select

208

Estou fazendo algumas consultas de seleção SQL e gostaria de converter minha coluna de data e hora UTC em horário local para ser exibida como horário local nos resultados da minha consulta. Observe que NÃO pretendo fazer essa conversão via código, mas quando estou fazendo consultas SQL manuais e aleatórias nos meus bancos de dados.

Nugs
fonte
Essa pergunta parece semelhante às suas circunstâncias? stackoverflow.com/questions/3404646/…
Taryn East
possível duplicata do TSQL: Como converter a hora local para UTC? (SQL Server 2008)
Restabelecer Monica Por favor,

Respostas:

322

Você pode fazer isso da seguinte maneira no SQL Server 2008 ou superior:

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

Você também pode fazer o menos detalhado:

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

Faça o que fizer, não use -para subtrair datas, porque a operação não é atômica, e você vai de vez em quando obter resultados indeterminados devido a condições de corrida entre a data e hora do sistema ea data e hora local, sendo verificado em momentos diferentes (isto é, não-atomicamente) .

Observe que esta resposta não leva em consideração o horário de verão. Se você deseja incluir um ajuste de horário de verão, consulte também a seguinte pergunta do SO:

Como criar a função de Início e Fim do Horário de Verão no SQL Server

Michael Goldshteyn
fonte
38
Existe uma maneira de fazer essa conta para o horário de verão?
30512 Steve Steve
15
Não vejo o nome de um fuso horário aqui, portanto isso está incorreto. É uma péssima idéia supor que você pode converter para a hora local, fazendo aritmética
JonnyRaa
7
@MichaelGoldshteyn se você tiver um deslocamento de fuso horário, ainda não saberá em que fuso horário lógico está o tempo. Os fusos horários são coisas sociais e podem ser alterados pelos governos em diferentes pontos. O deslocamento para um fuso horário pode ser diferente em diferentes pontos no tempo / histórico (o horário de verão é comum). Você está compensando um horário pelo deslocamento atual do UTC ... outro ponto é que isso indicará o fuso horário do servidor e não o cliente, o que poderia ser um problema adicional se você estiver hospedando em um país diferente.
precisa saber é o seguinte
4
@ Niloofar A resposta simples é que você não deve e não deve. As datas sempre devem ser armazenadas no UTC (de preferência com base no relógio do servidor de banco de dados, não no servidor da web, no servidor de aplicativos e, definitivamente, não no relógio do cliente). A exibição desse horário é uma função da camada da interface do usuário e é responsável por converter o horário no formato desejado (incluindo um fuso horário, se necessário).
Robert McKee
11
Para qualquer pessoa que use o sql azure, essa abordagem não funcionará porque todas as funções de data / hora retornam UTC, portanto, comparar GETDATE () a GETUTCDATE () não fornece nada para você trabalhar e seu resultado é o mesmo de onde você começou.
Brian Surowiec
59

Não achei nenhum exemplo útil para obter um horário datado como UTC para um horário especificado em um fuso horário especificado (NÃO o fuso horário do servidor porque os bancos de dados SQL do Azure são executados como UTC). É assim que eu lidei com isso. Não é elegante, mas é simples e fornece a resposta certa sem manter outras tabelas:

select CONVERT(datetime, SWITCHOFFSET(dateTimeField, DATEPART(TZOFFSET, 
dateTimeField AT TIME ZONE 'Eastern Standard Time')))
Aiden Kaskela
fonte
4
Funciona apenas para 2016 e usa o registro do sistema. Mas ótima solução para o Azure.
Dan Cundy
2
Este funciona para horários azuis armazenados no UTC, obrigado pessoal
Null
3
Aqui está uma lista das cordas que você pode usar para os fusos horários: stackoverflow.com/a/7908482/631277
Matt Kemp
1
Se estiver usando o SQL 2016 e a AT TIME ZONEsintaxe, considere stackoverflow.com/a/44941536/112764 - você pode encadear várias conversões juntas, simplesmente concatenando vários at time zone <blah>s.
NateJ
3
Isso também é um pouco estranho, mas alguns testes básicos também podem funcionar dateTimeField AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time'- apenas encadeando as AT TIME ZONEinstruções. (@NateJ mencionou isso acima, agora)
David Mohundro 25/04/19
22

Se a hora local da data for informada Eastern Standard Timee você desejar converter do UTC para isso, no Azure SQL e SQL Server 2016 e superior, você poderá:

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

A lista completa de nomes de fuso horário pode ser encontrada em:

SELECT * FROM sys.time_zone_info 

E sim, os fusos horários são mal nomeados - mesmo sendo Eastern Standard Time, o horário de verão é levado em consideração.

Matt Frear
fonte
2
Fusos
horários
21

Se você precisar de uma conversão diferente da localização do seu servidor, aqui está uma função que permite passar um deslocamento padrão e contabilizar o horário de verão americano:

-- =============================================
-- 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
Ron Smith
fonte
2
Ron Smith Eu sei que este é um post antigo, mas fiquei curioso sobre o que '0314' e '1107' codificados representam na obtenção da faixa de horário de verão. Parece ser dias codificados que mudam devido ao DTS. Por que você codificaria isso quando deveria ser uma data calculada, porque os dias mudam com base em onde no calendário o segundo domingo de março e o primeiro domingo de novembro caem. Os dias codificados dificultariam esse código.
Mike
2
Boa pergunta :) Essas são as datas máximas em que o segundo domingo de março e o primeiro domingo de novembro podem ocorrer. As linhas a seguir definem as variáveis ​​para a data real.
Ron Smith
8

Usando novas oportunidades do 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

Mas o procedimento clr funciona 5 vezes mais rápido: '- (

Observe que o deslocamento de um fuso horário pode mudar para o inverno ou o verão. Por exemplo

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'

resultados:

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

Você não pode simplesmente adicionar deslocamento constante.

Pavel Samoylenko
fonte
5

Se ativar o CLR no seu banco de dados for uma opção e também usar o fuso horário do servidor sql, ele poderá ser gravado no .Net com bastante facilidade.

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());
    }
}

Um valor de data e hora UTC entra e o valor de data e hora local relativo ao servidor sai. Valores nulos retornam nulos.

JGates
fonte
5

Não existe uma maneira simples de fazer isso de maneira correta e genérica.

Antes de tudo, deve-se entender que a compensação depende da data em questão, o fuso horário e o horário de verão. GetDate()-GetUTCDatehoje fornece apenas o deslocamento hoje na TZ do servidor, o que não é relevante.

Eu vi apenas duas soluções funcionais e tenho muita pesquisa.

1) Uma função SQL personalizada com um par de tabelas de dados base, como fusos horários e regras de horário de verão por TZ. Trabalhando, mas não muito elegante. Não posso publicá-lo, pois não possuo o código.

EDIT: Aqui está um exemplo deste método https://gist.github.com/drumsta/16b79cee6bc195cd89c8

2) Adicione um conjunto .net ao banco de dados, o .Net pode fazer isso com muita facilidade. Isso está funcionando muito bem, mas a desvantagem é que você precisa configurar vários parâmetros no nível do servidor e a configuração é facilmente interrompida, por exemplo, se você restaurar o banco de dados. Eu uso esse método, mas não posso publicá-lo, pois não possuo o código.

vikjon0
fonte
4

Nada disso funcionou para mim, mas isso abaixo funcionou 100%. Espero que isso ajude outras pessoas a tentar convertê-lo como eu.

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
Mike
fonte
1
Essa deve ser a resposta aceita. A única coisa que eu mudaria é o nome, pois implica que é a hora EST, quando realmente é a hora local e o StandardOffset é passado como parâmetro.
Greg Gum
Concordo, melhor resposta ... mas, em vez de ter que passar no deslocamento como parâmetro, adicionei isso no corpo da função:declare @StandardOffset int = datediff (hh, GETUTCDATE(), GETDATE())
Tom Warfield
Seguindo minha sugestão anterior - Se você calcular o @StandardOffset, não precisará fazer a correção do horário de verão.
Tom Warfield
3

Aqui está uma versão que responde pelo horário de verão, compensada pelo UTC, e não está bloqueada em um ano específico.

---------------------------------------------------------------------------------------------------
--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
Patrick Slesicki
fonte
3

Descobri que a função única é muito lenta quando há muitos dados. Então, eu fiz isso ingressando em uma função de tabela que permitiria o cálculo da diferença de horas. São basicamente segmentos de data e hora com o deslocamento da hora. Um ano seria de 4 linhas. Então a função de tabela

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

retornaria esta tabela:

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

Isso explica o horário de verão. Uma amostra de como é usada está abaixo e a postagem completa do blog está aqui .

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
JBrooks
fonte
Parece não considerar o horário de verão de maneira correta. Não tenho certeza se você presume que apenas os EUA existem e que as regras de horário de verão nunca mudam.
vikjon0
@ vikjon0 sim, o projeto para o qual construí tinha apenas fusos horários nos EUA.
JBrooks
2
 declare @mydate2 datetime
 set @mydate2=Getdate()
 select @mydate2 as mydate,
 dateadd(minute, datediff(minute,getdate(),@mydate2),getutcdate())
Looking_for_answers
fonte
1

A resposta de Ron contém um erro. Ele usa as 2:00 da manhã no horário local em que o equivalente ao UTC é necessário. Eu não tenho pontos de reputação suficientes para comentar a resposta de Ron, então uma versão corrigida aparece abaixo:

-- =============================================
-- 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
jlspublic
fonte
É um recurso, não um bug :) A maioria dos Estados Unidos começa horário de verão às 2:00 am en.wikipedia.org/wiki/Daylight_saving_time
Ron Smith
@RonSmith Sim, às 02:00, horário local, que precisamos converter para UTC para detectar se a hora UTC especificada está no intervalo de horário de verão.
Jspublic
1

O registro de data e hora do UNIX é apenas o número de segundos entre uma data específica e a época do Unix,

SELECT DATEDIFF (SEGUNDO, {d '1970-01-01'}, GETDATE ()) // Isso retornará o registro de data e hora do UNIX no SQL server

você pode criar uma função para a hora local da data em conversão UTC do Unix usando a Função de deslocamento do país para carimbo de data e hora do Unix no SQL server

Vasanthlal VA
fonte
1

É simples. Tente isto para o Azure SQL Server:

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

Para o SQL Server local:

SELECT CONVERT(datetime2, SWITCHOFFSET(CONVERT(datetimeoffset, gETDATE()), DATENAME(TzOffset, gETDATE() AT TIME ZONE 'Eastern Standard Time'))) FROM YourTable
lijuthomas
fonte
1
O que acontece com isso durante o horário de verão (já que o fuso horário diz especificamente "Horário padrão do leste")?
Mark
1

Para @@Versionusuários do Azure SQL e > = SQL Server 2016, Abaixo está uma função simples usando 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

Para tipos de valores que @LocalTimeZonepodem assumir, acesse este link ou Vá paraKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones

guarda
fonte
0

Como aviso - se você usar o seguinte (observe os milissegundos em vez de minutos):

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

Lembre-se de que a parte DATEDIFF nem sempre retornará o mesmo número. Portanto, não use-o para comparar DateTimes em milissegundos.

Sasquatch
fonte
0

Descobri que essa função é mais rápida do que outras soluções usando uma tabela ou loops separados. É apenas uma declaração básica de caso. Como todos os meses entre abril e outubro têm um deslocamento de -4 horas (horário do leste), precisamos adicionar mais algumas linhas de casos para os dias seguintes. Caso contrário, o deslocamento é de -5 horas.

Isso é específico para uma conversão do UTC para o horário do Leste, mas funções adicionais de fuso horário podem ser adicionadas conforme necessário.

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
Jason Green
fonte
0

Isso deve conseguir tempo do servidor com o horário de verão

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

sysdatetimeoffset leva em consideração o horário de verão

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

Primeira função: configurada para o fuso horário italiano (+1, +2), alternar datas: último domingo de março e outubro, retornar a diferença entre o fuso horário atual e a data e hora como parâmetro.

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

O código é:

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

Então a função que converte a data

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
GigiS
fonte
0

- obtenha o horário padrão indiano do utc

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

    RETURN dateadd(minute,330,@UTCDate)

END
GO
Meghraj Swami
fonte
0

Isso pode ser feito sem uma função. O código abaixo converterá um horário UTC para Horário da montanha, contabilizando o horário de verão. Ajuste todos os números -6 e -7 ao seu fuso horário de acordo (ou seja, para EST, você ajustaria para -4 e -5, respectivamente)

--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
Alex Fenech
fonte
0

Você precisa reformatar a sequência e converter para a hora correta. Nesse caso, eu precisava de tempo zulu.

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;
Tommyz
fonte
0

Melhor maneira para Oracle:

Com data e hora codificada:

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

Com nomes de colunas e tabelas:

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
PRUMO
fonte
0

Eu tenho um código para executar os horários UTC para Local e Local para UTC, o que permite a conversão usando um código como este

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

e

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

As funções podem suportar todo ou um subconjunto de fusos horários no IANA / TZDB, conforme fornecido pelo NodaTime - veja a lista completa em https://nodatime.org/TimeZones

Esteja ciente de que meu caso de uso significa que eu preciso apenas de uma janela 'atual', permitindo a conversão de tempos dentro do intervalo de aproximadamente +/- 5 anos a partir de agora. Isso significa que o método que eu usei provavelmente não é adequado para você, se você precisar de um período muito amplo, devido à maneira como gera código para cada intervalo de fuso horário em um determinado período.

O projeto está no GitHub: https://github.com/elliveny/SQLServerTimeConversion

Isso gera código de função SQL conforme este exemplo

Elliveny
fonte
0

Bem, se você armazenar os dados como data UTC no banco de dados, poderá fazer algo tão simples quanto

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

era sempre local do ponto do servidor e você não se atrapalhava com o AT TIME ZONE 'your time zone name', se seu banco de dados fosse movido para outro fuso horário como uma instalação de cliente, um fuso horário codificado pode morder você.

Walter Vehoeven
fonte
0

No postgres, isso funciona muito bem. Diga ao servidor o horário em que o horário é salvo, 'utc', e solicite a conversão para um fuso horário específico, neste caso 'Brasil / Leste'

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

Obtenha uma lista completa de fusos horários com a seguinte seleção;

select * from pg_timezone_names;

Veja detalhes aqui.

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

HHarada
fonte
-1

Aqui está um mais simples que leva o dst a levar em conta

CREATE FUNCTION [dbo].[UtcToLocal] 
(
    @p_utcDatetime DATETIME 
)
RETURNS DATETIME
AS
BEGIN
    RETURN DATEADD(MINUTE, DATEDIFF(MINUTE, GETUTCDATE(), @p_utcDatetime), GETDATE())
END
Morten Sølvberg
fonte
6
Na verdade, isso não leva em consideração o horário de verão. Basta experimentá-lo: SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, GETUTCDATE(), '20150101'), GETDATE()). Atualmente, estou em CEST (UTC + 2), mas o horário de verão não entrará em vigor no dia de Ano Novo, portanto, a resposta correta para mim seria 1 de janeiro de 2015 às 01:00. Sua resposta, como a resposta aceita, retorna 1 de janeiro de 2015 às 02:00.