2010-08-04 6 views
16

ich möchte über einen Zeitraum in tsql Schleife, und drucken Sie die UTC-Datum und unsere lokale Variante. Wir leben in UTC +1, also könnte ich leicht 1 Stunde hinzufügen, aber im Sommer leben wir in UTC +2.Wie berechnet man die lokale Datetime von einem utc datetime in tsql (sql 2005)?

In C# kann ich eine Datetime erstellen und eine Methode verwenden, um nach der UTC-Variante und umgekehrt zu fragen.

Bis jetzt habe ich dieses:

declare @counter int 
declare @localdate datetime 
declare @utcdate datetime 
set @counter = 0 
while @counter < 100 
begin 
    set @counter = @counter + 1 
    print 'The counter is ' + cast(@counter as char) 
    set @utcdate = DATEADD(day,@counter,GETUTCDATE()) 
    --set @localdate = ???? 

    print @localdate 
    print @utcdate 
end 

Antwort

5

Angenommen, Sie SQL 2005 verwenden nach oben, können Sie eine SQL-CLR-Funktion entwickeln ein UTC-Datum nehmen und zum lokalen Datum konvertieren.

This link ist ein MSDN How-To, das erläutert, wie Sie eine skalare UDF in C# erstellen können.

Erstellen Ihnen eine SQL-Funktion entlang der Linien von

[SqlFunction()] 
public static SqlDateTime ConvertUtcToLocal(SqlDateTime utcDate) 
{ 
    // over to you to convert SqlDateTime to DateTime, specify Kind 
    // as UTC, convert to local time, and convert back to SqlDateTime 
} 

über Ihre Probe dann

set @localdate = dbo.ConvertUtcToLocal(@utcdate) 

SQL CLR hat seine Gemeinkosten in Bezug auf der Bereitstellung werden würde, aber ich fühle mich Fällen wie diesen, wo es passt am besten.

+0

Danke, werde versuchen, dass ich Sie wissen lassen – Michel

+0

herausgefunden, wie zu konvertieren UTC andere Zeitzonen, UND versucht, eine SqlCLR-Funktion zu implementieren. Die Kombination dieser Funktionen funktionierte jedoch nicht, da ich das TimeZoneInfo-Objekt zur Berechnung der Datumsunterschiede verwende und ich nicht auf die Assembly verweisen kann, in der sich die Klasse von meinem SqlProject befindet (es scheint, dass Sie nur auf eine Teilmenge der .net verweisen können) Framework) – Michel

+0

OK - neugierig, warum Sie die TimeZoneInfo-Klasse benötigen, wenn Sie UTC in Local konvertieren müssen. Wenn Ihr SQL-Server so konfiguriert ist, dass er sich in _ihrer_ lokalen Zeitzone befindet (vereinbart - dies ist eine Einschränkung), wird Ihre C# -Funktion so etwas wie "return new SqlDateTime (utcDate.Value.toLocalTime());" . Sie müssen keine Zeitzone angeben. Habe ich falsch verstanden? –

1

GETUTCDATE() nur gibt Ihnen die aktuelle Uhrzeit in UTC, jede DATEADD() Sie auf diesen Wert tun wird keine Sommerzeitverschiebungen sind.

Ihre beste Wette ist Ihre eigene UTC Umrechnungstabelle bauen oder einfach nur so etwas wie folgt verwenden:

http://www.codeproject.com/KB/database/ConvertUTCToLocal.aspx

+1

wow. Ich hoffe, jemand kommt und sagt mir, dass dies nicht wahr ist, dass SQL Server das nicht tun kann? – Michel

+1

SQL Server kann es nicht out of the box tun, müssen Sie Ihre eigene Funktion erstellen oder füllen Sie Ihre eigene Look-up-Tabelle –

3

Diese Lösung scheint zu offensichtlich.

Wenn Sie UTC Datum mit GETUTCDATE() und Sie können Ihr lokales Datum mit GETDATE get() Sie haben einen Offset, dass Sie für jede Datumzeit anwenden können

SELECT DATEADD(hh, DATEPART(hh, GETDATE() - GETUTCDATE()) - 24, GETUTCDATE()) 

dies die lokale Zeit zurückkehren sollten Sie die Abfrage ausgeführt,

SELECT DATEADD(hh, DATEPART(hh, GETDATE() - GETUTCDATE()) - 24, N'1/14/2011 7:00:00' ) 

dies wird wieder 2011-01-14 02: 00: 00.000, weil ich in UTC +5

bin Solange ich etwas fehlt?

+21

Ich denke nicht, dass Sommer-Offsets verarbeitet werden – Tomas

+6

Nicht kostenlos zu stapeln, aber nicht nur Sommerzeit (oder Sommerzeit) nicht behandelt, aber es verarbeitet keine historischen Änderungen in Zeitzonen oder Kalender entweder. –

-1

Ich musste vor kurzem dasselbe tun. Der Trick ist, den Offset von UTC herauszufinden, aber es ist kein harter Trick. Sie verwenden einfach DateDiff, um die Differenz in Stunden zwischen lokaler und UTC zu erhalten. Ich habe eine Funktion geschrieben, die das erledigt.

Create Function ConvertUtcDateTimeToLocal(@utcDateTime DateTime) 
Returns DateTime 
Begin 
    Declare @utcNow DateTime 
    Declare @localNow DateTime 
    Declare @timeOffSet Int 

    -- Figure out the time difference between UTC and Local time 
    Set @utcNow = GetUtcDate() 
    Set @localNow = GetDate() 
    Set @timeOffSet = DateDiff(hh, @utcNow, @localNow) 

    DECLARE @localTime datetime 

    Set @localTime = DateAdd(hh, @timeOffset, @utcDateTime) 

    -- Check Results 
    return @localTime 

End 
GO 

Dies hat auf entscheidende kurze Kommen: Wenn eine Zeitzone einen Bruch versetzt, wie Nepal verwendet, die GMT + 5: 45, wird dies, weil diese nur befasst sich mit ganzen Stunden ausfallen. Es sollte jedoch genau Ihren Bedürfnissen entsprechen.

+13

Leider geht es nicht um die Sommerzeit. Der Unterschied zwischen GetDate() und GetUtcDate() ist nicht das ganze Jahr über konstant. – Nik

23

Ich habe seit 5 Jahren auf eine elegantere Lösung gewartet, aber da einer noch nicht aufgetaucht ist, werde ich das posten, was ich bisher benutzt habe ...

CREATE FUNCTION [dbo].[UDTToLocalTime](@UDT AS DATETIME) 
RETURNS DATETIME 
AS 
BEGIN 
--==================================================== 
--Set the Timezone Offset (NOT During DST [Daylight Saving Time]) 
--==================================================== 
DECLARE @Offset AS SMALLINT 
SET @Offset = -5 

--==================================================== 
--Figure out the Offset Datetime 
--==================================================== 
DECLARE @LocalDate AS DATETIME 
SET @LocalDate = DATEADD(hh, @Offset, @UDT) 

--==================================================== 
--Figure out the DST Offset for the UDT Datetime 
--==================================================== 
DECLARE @DaylightSavingOffset AS SMALLINT 
DECLARE @Year as SMALLINT 
DECLARE @DSTStartDate AS DATETIME 
DECLARE @DSTEndDate AS DATETIME 
--Get Year 
SET @Year = YEAR(@LocalDate) 

--Get First Possible DST StartDay 
IF (@Year > 2006) SET @DSTStartDate = CAST(@Year AS CHAR(4)) + '-03-08 02:00:00' 
ELSE    SET @DSTStartDate = CAST(@Year AS CHAR(4)) + '-04-01 02:00:00' 
--Get DST StartDate 
WHILE (DATENAME(dw, @DSTStartDate) <> 'sunday') SET @DSTStartDate = DATEADD(day, 1,@DSTStartDate) 


--Get First Possible DST EndDate 
IF (@Year > 2006) SET @DSTEndDate = CAST(@Year AS CHAR(4)) + '-11-01 02:00:00' 
ELSE    SET @DSTEndDate = CAST(@Year AS CHAR(4)) + '-10-25 02:00:00' 
--Get DST EndDate 
WHILE (DATENAME(dw, @DSTEndDate) <> 'sunday') SET @DSTEndDate = DATEADD(day,1,@DSTEndDate) 

--Get DaylightSavingOffset 
SET @DaylightSavingOffset = CASE WHEN @LocalDate BETWEEN @DSTStartDate AND @DSTEndDate THEN 1 ELSE 0 END 

--==================================================== 
--Finally add the DST Offset 
--==================================================== 
RETURN DATEADD(hh, @DaylightSavingOffset, @LocalDate) 
END 



GO 

Hinweise:

Dies ist für die nordamerikanischen Servern, die Beobachter Sommerzeit. Bitte ändern Sie die Variable @Offest auf die Zeitzone des Servers versetzt die SQL-Funktion ausgeführt wird (zwar nicht die Sommerzeit Beobachten) ...

--==================================================== 
--Set the Timezone Offset (NOT During DST [Daylight Saving Time]) 
--==================================================== 
DECLARE @Offset AS SMALLINT 
SET @Offset = -5 

Da die DST-Regeln aktualisieren, um sie ändern ...

--Get First Possible DST StartDay 
IF (@Year > 2006) SET @DSTStartDate = CAST(@Year AS CHAR(4)) + '-03-08 02:00:00' 
ELSE    SET @DSTStartDate = CAST(@Year AS CHAR(4)) + '-04-01 02:00:00' 
--Get DST StartDate 
WHILE (DATENAME(dw, @DSTStartDate) <> 'sunday') SET @DSTStartDate = DATEADD(day, 1,@DSTStartDate) 


--Get First Possible DST EndDate 
IF (@Year > 2006) SET @DSTEndDate = CAST(@Year AS CHAR(4)) + '-11-01 02:00:00' 
ELSE    SET @DSTEndDate = CAST(@Year AS CHAR(4)) + '-10-25 02:00:00' 
--Get DST EndDate 
WHILE (DATENAME(dw, @DSTEndDate) <> 'sunday') SET @DSTEndDate = DATEADD(day,1,@DSTEndDate) 

Cheers,

+2

Vielen Dank für das Teilen! – Rachel

+0

Das hat ein paar Bugs. Siehe meine Antwort mit Erläuterungen und aktualisiertem Code: http://stackoverflow.com/a/36361558/341942 –

0

Bobman Antwort liegt in der Nähe, aber hat ein paar Fehler: 1) Sie müssen lokale Sommerzeit vergleichen (statt der lokalen Standardzeit) zum Sommerende Datetime. 2) SQL BETWEEN ist Inclusive, also sollten Sie vergleichen mit "> = und <" anstelle von BETWEEN. Hier

ist eine Arbeits modifizierte Version zusammen mit einigen Testfälle: (Auch dies funktioniert nur für USA)

-- Test cases: 
-- select dbo.fn_utc_to_est_date('2016-03-13 06:59:00.000') -- -> 2016-03-13 01:59:00.000 (Eastern Standard Time) 
-- select dbo.fn_utc_to_est_date('2016-03-13 07:00:00.000') -- -> 2016-03-13 03:00:00.000 (Eastern Daylight Time) 
-- select dbo.fn_utc_to_est_date('2016-11-06 05:59:00.000') -- -> 2016-11-06 01:59:00.000 (Eastern Daylight Time) 
-- select dbo.fn_utc_to_est_date('2016-11-06 06:00:00.000') -- -> 2016-11-06 01:00:00.000 (Eastern Standard Time) 
CREATE FUNCTION [dbo].[fn_utc_to_est_date] 
(
    @utc datetime 
) 
RETURNS datetime 
as 
begin 
    -- set offset in standard time (WITHOUT daylight saving time) 
    declare @offset smallint 
    set @offset = -5 --EST 

    declare @localStandardTime datetime 
    SET @localStandardTime = dateadd(hh, @offset, @utc) 

    -- DST in USA starts on the second sunday of march and ends on the first sunday of november. 
    -- DST was extended beginning in 2007: 
    -- https://en.wikipedia.org/wiki/Daylight_saving_time_in_the_United_States#Second_extension_.282005.29 
    -- If laws/rules change, obviously the below code needs to be updated. 

    declare @dstStartDate datetime, 
      @dstEndDate datetime, 
      @year int 
    set @year = datepart(year, @localStandardTime) 

    -- get the first possible DST start day 
    if (@year > 2006) set @dstStartDate = cast(@year as char(4)) + '-03-08 02:00:00' 
    else    set @dstStartDate = cast(@year as char(4)) + '-04-01 02:00:00' 
    while ((datepart(weekday,@dstStartDate) != 1)) begin --while not sunday 
     set @dstStartDate = dateadd(day, 1, @dstStartDate) 
    end 

    -- get the first possible DST end day 
    if (@year > 2006) set @dstEndDate = cast(@year as char(4)) + '-11-01 02:00:00' 
    else    set @dstEndDate = cast(@year as char(4)) + '-10-25 02:00:00' 
    while ((datepart(weekday,@dstEndDate) != 1)) begin --while not sunday 
     set @dstEndDate = dateadd(day, 1, @dstEndDate) 
    end 

    declare @localTimeFinal datetime, 
      @localTimeCompare datetime 
    -- if local date is same day as @dstEndDate day, 
    -- we must compare the local DAYLIGHT time to the @dstEndDate (otherwise we compare using local STANDARD time). 
    -- See: http://www.timeanddate.com/time/change/usa?year=2016 
    if (datepart(month,@localStandardTime) = datepart(month,@dstEndDate) 
      and datepart(day,@localStandardTime) = datepart(day,@dstEndDate)) begin 
     set @localTimeCompare = dateadd(hour, 1, @localStandardTime) 
    end 
    else begin 
     set @localTimeCompare = @localStandardTime 
    end 

    set @localTimeFinal = @localStandardTime 

    -- check for DST 
    if (@localTimeCompare >= @dstStartDate and @localTimeCompare < @dstEndDate) begin 
     set @localTimeFinal = dateadd(hour, 1, @localTimeFinal) 
    end 

    return @localTimeFinal 
end 
1

Hier ist eine Funktion (wieder nur USA), aber es ist ein bisschen flexibler . Es konvertiert ein UTC-Datum in die lokale Serverzeit. Es beginnt mit der Anpassung des Termindatums basierend auf dem aktuellen Offset und passt dann basierend auf der Differenz des aktuellen Offsets und des Offsets des Termindatums an.

CREATE FUNCTION [dbo].[fnGetServerTimeFromUTC] 
(
    @AppointmentDate AS DATETIME, 
    @DateTimeOffset DATETIMEOFFSET 
) 
RETURNS DATETIME 
AS 
BEGIN 
    --DECLARE @AppointmentDate DATETIME; 
    --SET @AppointmentDate = '2016-12-01 12:00:00'; SELECT @AppointmentDate; 

    --Get DateTimeOffset from Server 
    --DECLARE @DateTimeOffset; SET @DateTimeOffset = SYSDATETIMEOFFSET(); 
    DECLARE @DateTimeOffsetStr NVARCHAR(34) = @DateTimeOffset; 

    --Set a standard DatePart value for Sunday (server configuration agnostic) 
    DECLARE @dp_Sunday INT = 7 - @@DATEFIRST + 1; 

    --2006 DST Start First Sunday in April (earliest is 04-01) Ends Last Sunday in October (earliest is 10-25) 
    --2007 DST Start Second Sunday March (earliest is 03-08) Ends First Sunday Nov (earliest is 11-01) 
    DECLARE @Start2006 NVARCHAR(6) = '04-01-'; 
    DECLARE @End2006 NVARCHAR(6) = '10-25-'; 
    DECLARE @Start2007 NVARCHAR(6) = '03-08-'; 
    DECLARE @End2007 NVARCHAR(6) = '11-01-'; 

    DECLARE @ServerDST SMALLINT = 0; 
    DECLARE @ApptDST SMALLINT = 0; 
    DECLARE @Start DATETIME; 
    DECLARE @End DATETIME; 

    DECLARE @CurrentMinuteOffset INT; 

    DECLARE @str_Year NVARCHAR(4) = LEFT(@DateTimeOffsetStr,4); 
    DECLARE @Year INT = CONVERT(INT, @str_Year); 

    SET @CurrentMinuteOffset = CONVERT(INT, SUBSTRING(@DateTimeOffsetStr,29,3)) * 60 + CONVERT(INT, SUBSTRING(@DateTimeOffsetStr,33,2)); --Hours + Minutes 

    --Determine DST Range for Server Offset 
    SET @Start = CASE 
     WHEN @Year <= 2006 THEN CONVERT(DATETIME, @Start2006 + @str_Year + ' 02:00:00') 
     ELSE CONVERT(DATETIME, @Start2007 + @str_Year + ' 02:00:00') 
     END; 
    WHILE @dp_Sunday <> DATEPART(WEEKDAY, @Start) BEGIN 
     SET @Start = DATEADD(DAY, 1, @Start) 
    END; 

    SET @End = CASE 
     WHEN @Year <= 2006 THEN CONVERT(DATETIME, @End2006 + @str_Year + ' 02:00:00') 
     ELSE CONVERT(DATETIME, @End2007 + @str_Year + ' 02:00:00') 
     END; 
    WHILE @dp_Sunday <> DATEPART(WEEKDAY, @End) BEGIN 
     SET @End = DATEADD(DAY, 1, @End) 
    END; 

    --Determine Current Offset based on Year 
    IF @DateTimeOffset >= @Start AND @DateTimeOffset < @End SET @ServerDST = 1; 

    --Determine DST status of Appointment Date 
    SET @Year = YEAR(@AppointmentDate); 

    SET @Start = CASE 
     WHEN @Year <= 2006 THEN CONVERT(DATETIME, @Start2006 + @str_Year + ' 02:00:00') 
     ELSE CONVERT(DATETIME, @Start2007 + @str_Year + ' 02:00:00') 
     END; 
    WHILE @dp_Sunday <> DATEPART(WEEKDAY, @Start) BEGIN 
     SET @Start = DATEADD(DAY, 1, @Start) 
    END; 

    SET @End = CASE 
     WHEN @Year <= 2006 THEN CONVERT(DATETIME, @End2006 + @str_Year + ' 02:00:00') 
     ELSE CONVERT(DATETIME, @End2007 + @str_Year + ' 02:00:00') 
     END; 
    WHILE @dp_Sunday <> DATEPART(WEEKDAY, @End) BEGIN 
     SET @End = DATEADD(DAY, 1, @End) 
    END; 

    --Determine Appointment Offset based on Year 
    IF @AppointmentDate >= @Start AND @AppointmentDate < @End SET @ApptDST = 1; 

    SET @AppointmentDate = DATEADD(MINUTE, @CurrentMinuteOffset + 60 * (@ApptDST - @ServerDST), @AppointmentDate) 

    RETURN @AppointmentDate 
END 
GO 
Verwandte Themen