Konwertuj kolumnę Datetime z czasu UTC na czas lokalny w instrukcji select


208

Robię kilka zapytań SQL do wyboru i chciałbym przekonwertować moją kolumnę czasu UTC na czas lokalny, aby był wyświetlany jako czas lokalny w moich wynikach zapytania. Uwaga: NIE zamierzam przeprowadzać tej konwersji za pomocą kodu, ale raczej gdy wykonuję ręczne i losowe zapytania SQL względem moich baz danych.


Czy to pytanie wydaje się podobne do twoich okoliczności? stackoverflow.com/questions/3404646/…
Taryn East

Odpowiedzi:


322

Możesz to zrobić w następujący sposób na SQL Server 2008 lub nowszym:

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

Możesz także zrobić mniej szczegółowe:

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

Cokolwiek robisz, nie używaj -do odejmowania dat, ponieważ operacja nie jest atomowa, a czasami otrzymasz nieokreślone wyniki z powodu warunków wyścigu między datetime systemowym a lokalnym datetime sprawdzanym w różnych momentach (tj. Nie-atomowo) .

Pamiętaj, że ta odpowiedź nie uwzględnia czasu letniego. Jeśli chcesz dołączyć korektę czasu letniego, zobacz także następujące pytanie SO:

Jak utworzyć funkcję Start and End czasu letniego w programie SQL Server


38
Czy istnieje sposób, aby to konto pozwoliło zaoszczędzić na świetle dziennym?
Steve

15
Nie widzę tutaj nazwy strefy czasowej, więc jest to niepoprawne. To naprawdę zły pomysł, aby założyć, że można przeliczyć na czas lokalny, wykonując arytmetykę
JonnyRaa

7
@MichaelGoldshteyn, jeśli masz przesunięcie strefy czasowej, nadal nie wiesz, jaka jest logiczna strefa czasowa. Strefy czasowe to rzeczy społeczne i mogą być zmieniane przez rządy w różnych punktach. Przesunięcie dla strefy czasowej może być różne w różnych punktach czasu / historii (często jest to czas letni). Czas jest kompensowany przez bieżące przesunięcie względem UTC ... Kolejną kwestią jest to, że da to strefę czasową serwera, a nie klienta, co może być kolejnym problemem, jeśli hostujesz w innym kraju.
JonnyRaa

4
@Niloofar Prosta odpowiedź brzmi: nie masz i nie powinieneś. Czasy danych powinny zawsze być przechowywane w UTC (najlepiej na podstawie zegara serwera bazy danych, a nie serwera WWW, serwera aplikacji, a na pewno nie zegara klienta). Wyświetlanie tej daty i godziny jest funkcją warstwy interfejsu użytkownika i odpowiada za konwersję daty i godziny na żądany format (w tym strefę czasową, jeśli to konieczne).
Robert McKee,

11
Dla każdego używającego sql azure to podejście nie zadziała, ponieważ wszystkie funkcje daty i godziny zwracają UTC, więc porównywanie GETDATE () z GETUTCDATE () nie daje nic do pracy, a wynik jest taki sam, jak na początku.
Brian Surowiec

59

Nie znalazłem żadnego z tych przykładów pomocnych w uzyskaniu daty i godziny zapisanej jako UTC do daty i godziny w określonej strefie czasowej (NIE strefy czasowej serwera, ponieważ bazy danych Azure SQL działają jako UTC). Tak sobie z tym poradziłem. Nie jest elegancki, ale jest prosty i daje właściwą odpowiedź bez konieczności utrzymywania innych tabel:

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

4
Działa tylko w 2016 roku i korzysta z rejestru systemowego. Ale świetne rozwiązanie dla platformy Azure.
Dan Cundy,

2
Ten działa na lazurowe czasy przechowywane w UTC, dzięki chłopaki
Null

3
Oto lista ciągów znaków, których możesz użyć w strefach czasowych: stackoverflow.com/a/7908482/631277
Matt Kemp

1
W przypadku korzystania z SQL 2016 i AT TIME ZONEskładni, należy rozważyć stackoverflow.com/a/44941536/112764 - można łańcuchowe wielu konwersji razem po prostu łącząc kilka at time zone <blah>s.
NateJ

3
To również trochę dziwne, ale wygląda na to, że niektóre bardzo podstawowe testy też mogą działać - dateTimeField AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time'- po prostu łączenie łańcuchów AT TIME ZONEinstrukcji. (@NateJ wspomniał o tym powyżej, widzę teraz)
David Mohundro,

22

Jeśli podana jest lokalna data Eastern Standard Timei chcesz przekonwertować ją z UTC, to w Azure SQL i SQL Server 2016 i nowszych wersjach możesz:

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

Pełną listę nazw stref czasowych można znaleźć w:

SELECT * FROM sys.time_zone_info 

I tak, strefy czasowe są źle nazwane - nawet jeśli tak jest Eastern Standard Time, uwzględniane są oszczędności światła dziennego.


2
Strefy czasowe i przesunięcia można również znaleźć w SQL Server tutaj: WYBIERZ * Z sys.time_zone_info
rayzinnz

21

Jeśli potrzebujesz konwersji innej niż lokalizacja twojego serwera, oto funkcja, która pozwala przekazać standardowe przesunięcie i rozlicza się z US Daylight Savings Times:

-- =============================================
-- 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 Wiem, że jest to stary post, ale byłem ciekawy, co oznaczają zakodowane na stałe „0314” i „1107” w uzyskaniu zakresu DST. Wydaje się, że są to dni zakodowane na stałe, które zmieniają się z powodu DTS. Po co kodować to na sztywno, kiedy powinna to być obliczona data, ponieważ dni zmieniają się w zależności od tego, gdzie w kalendarzu przypada druga niedziela marca i pierwsza niedziela listopada. Ciężko zakodowane dni sprawiłyby, że kod ten byłby zasadniczo wadliwy.
Mike

2
Dobre pytanie :) To są maksymalne daty, w których może nastąpić druga niedziela marca i pierwsza niedziela listopada. Kolejne wiersze ustawiają zmienne na rzeczywistą datę.
Ron Smith,

8

Korzystanie z nowych możliwości programu 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

Ale procedura CLR działa 5 razy szybciej: „- (

Zwróć uwagę, że przesunięcie dla jednej strefy czasowej może zmienić się na czas zimowy lub letni. Na przykład

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'

wyniki:

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

Nie można po prostu dodać stałego przesunięcia.


5

Jeśli włączanie CLR w bazie danych jest opcją, a także używanie strefy czasowej serwera SQL, można ją dość łatwo zapisać w .Net.

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

Wchodzi wartość daty i godziny UTC i wychodzi lokalna wartość godziny i czasu względem serwera. Wartości null zwracają wartość null.


5

Nie ma prostego sposobu, aby to zrobić we właściwy i ogólny sposób.

Przede wszystkim należy zrozumieć, że przesunięcie zależy od danej daty, strefy czasowej i czasu letniego. GetDate()-GetUTCDatedaje tylko offset dzisiaj w TZ serwera, co nie ma znaczenia.

Widziałem tylko dwa działające rozwiązania i dużo szukałem.

1) Niestandardowa funkcja SQL z kilkoma tabelami danych podstawowych, takimi jak strefy czasowe i reguły DST na TZ. Działa, ale niezbyt elegancko. Nie mogę tego opublikować, ponieważ nie mam kodu.

EDYCJA: Oto przykład tej metody https://gist.github.com/drumsta/16b79cee6bc195cd89c8

2) Dodaj zestaw .net do db, .Net może to zrobić bardzo łatwo. Działa to bardzo dobrze, ale wadą jest to, że musisz skonfigurować kilka parametrów na poziomie serwera, a konfiguracja jest łatwa do złamania, np. Podczas przywracania bazy danych. Używam tej metody, ale nie mogę jej opublikować, ponieważ nie jestem właścicielem kodu.


4

Żadne z nich nie działało dla mnie, ale poniżej działało 100%. Mam nadzieję, że pomoże to innym próbować przekonwertować to tak, jak ja.

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
To powinna być zaakceptowana odpowiedź. Jedyne, co chciałbym zmienić, to nazwa, ponieważ sugeruje, że jest to czas EST, podczas gdy tak naprawdę jest to czas lokalny, a StandardOffset jest przekazywany jako parametr.
Greg Gum

Zgadzam się, najlepsza odpowiedź ... ale zamiast przekazywać offset jako parametr, dodałem to w treści funkcji:declare @StandardOffset int = datediff (hh, GETUTCDATE(), GETDATE())
Tom Warfield

Kontynuacja mojego poprzedniego słownika - jeśli obliczasz @StandardOffset, nie musisz korygować DST.
Tom Warfield,

3

Oto wersja uwzględniająca czas letni, przesunięcie UTC i nie jest zablokowana na określony rok.

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

Odkryłem, że sposób jednorazowy jest zbyt wolny, gdy jest dużo danych. Zrobiłem to, łącząc się z funkcją tabeli, która pozwoliłaby na obliczenie różnicy godzin. Zasadniczo są to segmenty daty i godziny z przesunięciem godziny. Rok będzie 4 rzędy. Więc funkcja stołu

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

zwróci tę tabelę:

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

Uwzględnia oszczędności w świetle dziennym. Próbka jego wykorzystania znajduje się poniżej, a pełny post na blogu znajduje się tutaj .

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

Wydaje się, że nie uwzględnia DST we właściwy sposób. Nie jestem pewien, czy zakładasz, że istnieją tylko USA i że zasady DST nigdy się nie zmieniają.
vikjon0

@ vikjon0 tak, projekt, dla którego zbudowałem ten projekt, obejmował wyłącznie amerykańskie strefy czasowe.
JBrooks

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

1

Odpowiedź Rona zawiera błąd. Wykorzystuje czas lokalny 2:00 AM, gdy wymagany jest ekwiwalent UTC. Nie mam wystarczającej liczby punktów reputacji, aby skomentować odpowiedź Rona, więc poprawiona wersja pojawia się poniżej:

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

To funkcja, a nie błąd :) Większość Stanów Zjednoczonych zaczyna czas letni o 2:00 en.wikipedia.org/wiki/Daylight_saving_time
Ron Smith

@RonSmith Tak, o 2:00 czasu lokalnego, który musimy przekonwertować na UTC, aby wykryć, czy podany czas UTC jest w zakresie czasu letniego.
jlspublic

1

Znacznik czasu UNIX to tylko liczba sekund między określoną datą a Epoką Uniksową,

SELECT DATEDIFF (SECOND, {d '1970-01-01'}, GETDATE ()) // To zwróci znacznik czasu UNIX na serwerze SQL

można utworzyć funkcję konwersji lokalnej daty i godziny na Unix UTC za pomocą funkcji przesunięcia kraju na uniksowy znacznik czasu na serwerze SQL


1

To proste. Wypróbuj to dla Azure SQL Server:

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

W przypadku lokalnego serwera SQL:

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

1
Co dzieje się z tym podczas czasu letniego (ponieważ strefa czasowa mówi „wschodni czas standardowy”)?
Mark

1

Dla użytkowników Azure SQL i @@Version> = SQL Server 2016, poniżej jest prosta funkcja przy użyciu 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

Aby @LocalTimeZonezapoznać się z typami wartości, które można przyjąć, przejdź do tego linku lub Przejdź doKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones


0

Jako ostrzeżenie - jeśli zamierzasz użyć następujących (zwróć uwagę na milisekundy zamiast minut):

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

Należy pamiętać, że część DATEDIFF nie zawsze zwraca ten sam numer. Więc nie używaj go do porównywania DateTimes do milisekund.


0

Przekonałem się, że ta funkcja jest szybsza niż inne rozwiązania wykorzystujące osobną tabelę lub pętle. To tylko podstawowe stwierdzenie przypadku. Biorąc pod uwagę, że wszystkie miesiące między kwietniem a październikiem mają przesunięcie o 4 godziny (czas wschodni), musimy tylko dodać kilka kolejnych linii przypadków na marginesie dni. W przeciwnym razie przesunięcie wynosi -5 godzin.

Jest to specyficzne dla konwersji z czasu UTC na czas wschodni, ale w razie potrzeby można dodać dodatkowe funkcje strefy czasowej.

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

To powinno być w stanie uzyskać czas serwera z DST

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

sysdatetimeoffset bierze pod uwagę czas letni

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

0

Pierwsza funkcja: skonfigurowana dla włoskiej strefy czasowej (+1, +2), przełącza daty: ostatnia niedziela marca i października, zwraca różnicę między bieżącą strefą czasową i datetime jako parametr.

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

Kod to:

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

Następnie funkcja konwertująca datę

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

- Uzyskaj czas indyjski standardowy z utc

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

    RETURN dateadd(minute,330,@UTCDate)

END
GO

0

Można to zrobić bez funkcji. Poniższy kod przekształci czas UTC w czas górski, uwzględniając oszczędności w ciągu dnia. Dostosuj odpowiednio wszystkie liczby -6 i -7 do swojej strefy czasowej (tj. Dla EST ustawisz odpowiednio na -4 i -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

Musisz ponownie sformatować ciąg, a także przekonwertować na właściwy czas. W tym przypadku potrzebowałem czasu 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

Najlepszy sposób dla Oracle:

Z zakodowaną datetime:

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

Z nazwami kolumn i tabel:

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

Mam kod do wykonania czasu UTC na czas lokalny i czas lokalny na czas UTC, który umożliwia konwersję przy użyciu takiego kodu

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

i

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

Funkcje mogą obsługiwać wszystkie lub podzbiory stref czasowych w IANA / TZDB dostarczone przez NodaTime - patrz pełna lista na https://nodatime.org/TimeZones

Pamiętaj, że mój przypadek użycia oznacza, że ​​potrzebuję tylko „bieżącego” okna, umożliwiającego konwersję czasów w przedziale około +/- 5 lat od teraz. Oznacza to, że metoda, której użyłem, prawdopodobnie nie jest dla Ciebie odpowiednia, jeśli potrzebujesz bardzo długiego okresu czasu, ze względu na sposób, w jaki generuje kod dla każdego interwału strefy czasowej w danym zakresie dat.

Projekt jest na GitHub: https://github.com/elliveny/SQLServerTimeConversion

Generuje to kod funkcji SQL zgodnie z tym przykładem


0

Cóż, jeśli przechowujesz dane jako datę UTC w bazie danych, możesz zrobić coś tak prostego jak

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

było to zawsze lokalne od punktu serwera i nie masz problemów z AT TIME ZONE 'your time zone name', jeśli twoja baza danych zostanie przeniesiona do innej strefy czasowej, takiej jak instalacja klienta, strefa czasowa zakodowana na stałe może cię ugryźć.


0

W Postgresie działa to bardzo ładnie. Powiedz serwerowi czas, w którym czas jest zapisany, „utc”, a następnie poproś go o konwersję do określonej strefy czasowej, w tym przypadku „Brazylia / Wschód”

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

Uzyskaj pełną listę stref czasowych z poniższym wyborem;

select * from pg_timezone_names;

Zobacz szczegóły tutaj.

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


-1

Oto prostszy, który uwzględnia dst

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

6
To nie bierze pod uwagę czasu letniego. Wystarczy spróbować: SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, GETUTCDATE(), '20150101'), GETDATE()). Jestem obecnie w CEST (UTC + 2), ale DST nie będzie obowiązywał w Nowy Rok, więc poprawna odpowiedź będzie dla mnie 1 stycznia 2015 01:00. Twoja odpowiedź, podobnie jak odpowiedź zaakceptowana, zwraca 1 stycznia 2015 02:00.
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.