Converti colonna Datetime da UTC all'ora locale nell'istruzione select


208

Sto eseguendo alcune query di selezione SQL e vorrei convertire la mia colonna datetime UTC in ora locale per essere visualizzata come ora locale nei risultati della mia query. Nota, NON sto cercando di eseguire questa conversione tramite codice, ma piuttosto quando eseguo query SQL manuali e casuali sui miei database.


Questa domanda sembra simile alle tue circostanze? stackoverflow.com/questions/3404646/…
Taryn East

Risposte:


322

È possibile farlo come segue su SQL Server 2008 o versioni successive:

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

Puoi anche fare il meno prolisso:

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

Qualunque cosa tu faccia, non utilizzare -per sottrarre le date, perché l'operazione non è atomica e occasionalmente otterrai risultati indeterminati a causa delle condizioni di gara tra il datetime di sistema e il datetime locale che vengono controllati in momenti diversi (cioè, non atomicamente) .

Questa risposta non tiene conto dell'ora legale. Se si desidera includere una regolazione dell'ora legale, consultare anche la seguente domanda SO:

Come creare la funzione di inizio e fine dell'ora legale in SQL Server


38
C'è un modo per rendere questo account per il risparmio di luce del giorno?
Steve

15
Non vedo il nome di un fuso orario qui quindi questo non è corretto. È una pessima idea presumere che tu possa convertirti all'ora locale facendo l'aritmetica
JonnyRaa,

7
@MichaelGoldshteyn se hai un fuso orario non sai ancora in quale fuso orario si trova il tempo. I fusi orari sono cose sociali e possono essere cambiati dai governi in diversi punti. L'offset per un fuso orario può essere diverso in diversi punti nel tempo / nella storia (l'ora legale è comune). Stai compensando un tempo dall'offset corrente da UTC ... un ulteriore punto è che questo darà il fuso orario del server e non del client, il che potrebbe essere un ulteriore problema se stai ospitando in un altro paese.
JonnyRaa,

4
@Niloofar La semplice risposta è che non lo fai e non dovresti. Gli orari dei dati devono sempre essere archiviati in UTC (preferibilmente in base all'orologio del server di database, non al server web, al server delle applicazioni e sicuramente non all'orologio del client). La visualizzazione di quel datetime è una funzione per il livello dell'interfaccia utente ed è responsabile della conversione del datetime nel formato desiderato (incluso un fuso orario, se necessario).
Robert McKee,

11
Per chiunque utilizzi sql azzurro questo approccio non funzionerà perché tutte le funzioni di data / ora restituiscono UTC, quindi il confronto di GETDATE () con GETUTCDATE () non ti dà nulla su cui lavorare e il tuo risultato è lo stesso di quello che hai iniziato.
Brian Surowiec,

59

Non ho trovato nessuno di questi esempi utile per ottenere un datetime archiviato come UTC in un datetime in un fuso orario specificato (NON il fuso orario del server perché i database SQL di Azure vengono eseguiti come UTC). È così che l'ho gestito. Non è elegante ma è semplice e ti dà la risposta giusta senza mantenere altre tabelle:

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

4
Funziona solo per il 2016 e utilizza il registro di sistema. Ma un'ottima soluzione per Azure.
Dan Cundy,

2
Questo funziona per i tempi azzurri memorizzati in UTC, grazie ragazzi
Null

3
Ecco un elenco delle stringhe che è possibile utilizzare per i fusi orari: stackoverflow.com/a/7908482/631277
Matt Kemp

1
Se si utilizza SQL 2016 e la AT TIME ZONEsintassi, considerare stackoverflow.com/a/44941536/112764 : è possibile concatenare più conversioni semplicemente concatenando più messaggi at time zone <blah>.
NateJ,

3
Anche questo è un po 'strano, ma alcuni test di base sembrano funzionare anche questo - dateTimeField AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time'- solo concatenare le AT TIME ZONEdichiarazioni. (@NateJ ne ha parlato sopra, ora lo vedo)
David Mohundro,

22

Se l'ora della tua data locale è detta Eastern Standard Timee vuoi convertire da UTC a quello, quindi in Azure SQL e SQL Server 2016 e versioni successive, puoi fare:

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

L'elenco completo dei nomi di fuso orario è disponibile con:

SELECT * FROM sys.time_zone_info 

E sì, i fusi orari sono mal nominati - anche se lo è Eastern Standard Time, viene presa in considerazione l'ora legale.


2
Fusi
orari

21

Se hai bisogno di una conversione diversa dalla posizione del tuo server, ecco una funzione che ti consente di passare un offset standard e conti per gli orari di ora legale degli Stati Uniti:

-- =============================================
-- 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 So che questo è un vecchio post, ma ero curioso di sapere cosa rappresentassero i codici "0314" e "1107" nell'ottenimento della gamma DST. Sembra essere giorni in codice che cambiano a causa di DTS. Perché dovresti scrivere questo codice quando dovrebbe essere una data calcolata perché i giorni cambiano in base a dove sul calendario cadono la seconda domenica di marzo e la prima domenica di novembre. I giorni duri in codice renderebbero questo codice fondamentalmente imperfetto.
Mike,

2
Bella domanda :) Quelle sono le date massime in cui possono verificarsi la seconda domenica di marzo e la prima domenica di novembre. Le seguenti righe impostano le variabili sulla data effettiva.
Ron Smith,

8

Utilizzo delle nuove opportunità di 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

Ma la procedura clr funziona 5 volte più velocemente: '- (

Fai attenzione che Offset per un fuso orario può cambiare in inverno o in estate. Per esempio

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'

i risultati:

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

Non puoi semplicemente aggiungere un offset costante.


5

Se abilitare CLR sul tuo database è un'opzione e usare il fuso orario del server sql, può essere scritto in .Net abbastanza facilmente.

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

Entra un valore datetime UTC e esce il valore datetime locale relativo al server. I valori null restituiscono null.


5

Non esiste un modo semplice per farlo in modo corretto E generico.

Innanzi tutto si deve comprendere che l'offset dipende dalla data in questione, dal fuso orario e dall'ora legale. GetDate()-GetUTCDateti dà solo l'offset oggi nella TZ del server, che non è rilevante.

Ho visto solo due soluzioni funzionanti e ho cercato molto.

1) Una funzione SQL personalizzata con un paio di tabelle di dati di base come fusi orari e regole DST per TZ. Funzionante ma non molto elegante. Non riesco a pubblicarlo poiché non possiedo il codice.

EDIT: ecco un esempio di questo metodo https://gist.github.com/drumsta/16b79cee6bc195cd89c8

2) Aggiungi un assembly .net al db, .Net può farlo molto facilmente. Funziona molto bene, ma il lato negativo è che è necessario configurare diversi parametri a livello di server e la configurazione si interrompe facilmente, ad esempio se si ripristina il database. Uso questo metodo ma non posso pubblicarlo poiché non possiedo il codice.


4

Nessuno di questi ha funzionato per me, ma questo sotto ha funzionato al 100%. Spero che questo possa aiutare gli altri a cercare di convertirlo come me.

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
Questa dovrebbe essere la risposta accettata. L'unica cosa che vorrei cambiare è il nome in quanto implica l'ora EST, quando in realtà è l'ora locale e StandardOffset viene passato come parametro.
Greg Gum,

D'accordo, migliore risposta ... ma invece di dover passare l'offset come parametro, l'ho aggiunto all'interno del corpo della funzione:declare @StandardOffset int = datediff (hh, GETUTCDATE(), GETDATE())
Tom Warfield

Seguendo il mio suggerimento precedente - Se si calcola @StandardOffset, non è necessario eseguire la correzione dell'ora legale.
Tom Warfield,

3

Ecco una versione che tiene conto dell'ora legale, dell'offset UTC e non è bloccata in un determinato anno.

---------------------------------------------------------------------------------------------------
--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

Ho trovato il modo di funzionamento una tantum troppo lento quando ci sono molti dati. Quindi l'ho fatto unendomi a una funzione di tabella che consentirebbe un calcolo della differenza oraria. Fondamentalmente si tratta di segmenti datetime con offset dell'ora. Un anno sarebbe di 4 file. Quindi la funzione tabella

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

restituirebbe questa tabella:

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

Tiene conto dell'ora legale. Un esempio di come viene utilizzato è di seguito e l'intero post del blog è qui .

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

Non sembra considerare l'ora legale in modo corretto. Non sono sicuro se si presume che esistano solo gli Stati Uniti e che le regole dell'ora legale non cambino mai.
vikjon0,

@ vikjon0 sì, il progetto per cui l'ho realizzato aveva solo fusi orari statunitensi.
JBrooks,

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

1

La risposta di Ron contiene un errore. Utilizza l'ora locale delle 2:00 in cui è richiesto l'equivalente UTC. Non ho abbastanza punti reputazione per commentare la risposta di Ron, quindi una versione corretta appare sotto:

-- =============================================
-- 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

È una caratteristica, non un bug :) La maggior parte degli Stati Uniti inizia l'ora legale alle 2:00 del mattino en.wikipedia.org/wiki/Daylight_saving_time
Ron Smith,

@RonSmith Sì, alle 2:00 ora locale, che dobbiamo convertire in UTC per rilevare se l'ora UTC specificata rientra nell'intervallo DST.
jlspublic,

1

Il timestamp UNIX è semplicemente il numero di secondi tra una data particolare e l'epoca Unix,

SELECT DATEDIFF (SECONDO, {d '1970-01-01'}, GETDATE ()) // Questo restituirà il timestamp UNIX nel server SQL

è possibile creare una funzione per la conversione dell'ora locale in UTC Unix utilizzando la funzione Offset paese in Timestamp Unix nel server SQL


1

È semplice. Prova questo per Azure SQL Server:

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

Per SQL Server locale:

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

1
Cosa succede durante l'ora legale (dal momento che il fuso orario indica specificamente "Ora solare orientale")?
Segna

1

Per gli utenti di Azure SQL e @@Version> = SQL Server 2016, di seguito è disponibile una semplice funzione 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

Per i tipi di valori che @LocalTimeZonepossono assumere, vai a questo link o Vai aKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones


0

Come avvertimento: se si intende utilizzare quanto segue (notare i millisecondi anziché i minuti):

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

Tieni presente che la parte DATEDIFF non restituirà sempre lo stesso numero. Quindi non usarlo per confrontare DateTimes con i millisecondi.


0

Ho scoperto che questa funzione è più veloce di altre soluzioni che utilizzano una tabella o loop separati. È solo un'affermazione di base. Dato che tutti i mesi tra aprile e ottobre hanno un offset di -4 ore (Eastern Time), abbiamo solo bisogno di aggiungere qualche altra riga del caso per i giorni marginali. Altrimenti, l'offset è di -5 ore.

Ciò è specifico per una conversione dall'ora UTC all'ora orientale, ma è possibile aggiungere ulteriori funzioni di fuso orario, se necessario.

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

Questo dovrebbe essere in grado di ottenere l'ora del server con l'ora legale

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

sysdatetimeoffset tiene conto dell'ora legale

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

0

Prima funzione: configurata per il fuso orario italiano (+1, +2), cambia le date: ultima domenica di marzo e ottobre, restituisce la differenza tra il fuso orario corrente e il datetime come parametro.

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

Il codice è:

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

Quindi la funzione che converte la 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

0

- ottieni l'ora standard indiana da utc

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

    RETURN dateadd(minute,330,@UTCDate)

END
GO

0

Questo può essere fatto senza una funzione. Il codice seguente converte l'ora UTC in contabilità dell'ora di montagna per l'ora legale. Regola di conseguenza tutti i numeri -6 e -7 sul tuo fuso orario (cioè per EST dovresti regolare rispettivamente a -4 e -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

Devi riformattare la stringa e convertirla all'ora corretta. In questo caso avevo bisogno di 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;

0

Il modo migliore per Oracle:

Con datetime hardcoded:

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

Con nomi di colonne e tabelle:

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

Ho un codice per eseguire i tempi da UTC a Local e da Local a UTC che consente la conversione usando codice come questo

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)

Le funzioni possono supportare tutti o un sottoinsieme di fusi orari in IANA / TZDB come fornito da NodaTime - vedere l'elenco completo su https://nodatime.org/TimeZones

Tieni presente che il mio caso d'uso significa che ho solo bisogno di una finestra "corrente", che consenta la conversione dei tempi entro un intervallo di circa +/- 5 anni da oggi. Ciò significa che il metodo che ho usato probabilmente non è adatto a te se hai bisogno di un periodo di tempo molto ampio, a causa del modo in cui genera il codice per ogni intervallo di fuso orario in un determinato intervallo di date.

Il progetto è su GitHub: https://github.com/elliveny/SQLServerTimeConversion

Questo genera il codice funzione SQL come da questo esempio


0

Bene, se memorizzi i dati come data UTC nel database, puoi fare qualcosa di semplice

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

questo era sempre locale dal punto del server e non stai armeggiando con AT TIME ZONE 'your time zone name', se il tuo database viene spostato in un altro fuso orario come un'installazione client, un fuso orario codificato potrebbe morderti.



-1

Eccone uno più semplice che tiene conto di dst

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

6
Questo in realtà non tiene conto dell'ora legale. Provare per credere: SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, GETUTCDATE(), '20150101'), GETDATE()). Sono attualmente in CEST (UTC + 2), ma l'ora legale non entrerà in vigore il giorno di Capodanno, quindi la risposta corretta per me sarebbe il 1 gennaio 2015 01:00. La tua risposta, come la risposta accettata, restituisce il 1 ° gennaio 2015 alle 02:00.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.