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.