2010-08-04 34 views
16

tôi muốn lặp qua một khoảng thời gian trong tsql và in utcimes và biến thể cục bộ của chúng tôi. Chúng tôi sống ở UTC +1, vì vậy tôi có thể dễ dàng thêm 1 giờ, nhưng vào mùa hè, chúng tôi sống ở UTC +2.Cách tính giờ datetime địa phương từ ngày giờ utc trong tsql (sql 2005)?

Trong C# i có thể tạo một datetime và sử dụng một phương pháp để yêu cầu biến thể UTC và ngược lại.

Đến bây giờ tôi có điều này:

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 

Trả lời

5

Giả sử bạn đang sử dụng SQL 2005 trở lên, bạn có thể phát triển một hàm SQL CLR để có một ngày tính theo giờ UTC và chuyển đổi sang ngày địa phương.

This link là cách làm MSDN giải thích cách bạn có thể tạo UDF vô hướng trong C#.

Tạo một hàm SQL dọc theo dòng của

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

mẫu của bạn trên sau đó sẽ trở thành

set @localdate = dbo.ConvertUtcToLocal(@utcdate) 

SQL CLR có các chi phí của mình về triển khai, nhưng tôi cảm thấy trường hợp như thế này đang ở đâu nó phù hợp nhất.

+0

cảm ơn, hãy thử điều đó, tôi sẽ cho bạn biết – Michel

+0

tìm ra cách chuyển đổi utc thành các múi giờ khác, AND đã cố gắng triển khai hàm SqlCLR. Tuy nhiên, việc kết hợp chúng không hoạt động, bởi vì tôi đang sử dụng đối tượng TimeZoneInfo để tính toán các khoảng thời gian khác nhau, và tôi không thể tham chiếu đến assembly mà nó nằm trong SqlProject của tôi (vì có vẻ như bạn chỉ có thể tham khảo một tập hợp con của .net khuôn khổ) – Michel

+0

OK - tò mò là tại sao bạn cần lớp TimeZoneInfo cho yêu cầu của bạn chuyển đổi UTC thành Local. Nếu máy chủ SQL của bạn được cấu hình là trong _your_ múi giờ địa phương (đã đồng ý - đây là một ràng buộc), thì hàm C# của bạn trở thành một cái gì đó như 'trả về SqlDateTime mới (utcDate.Value.toLocalTime());' . Bạn không cần chỉ định múi giờ. Tôi có hiểu sai không? –

1

GETUTCDATE() chỉ cung cấp cho bạn thời gian hiện tại UTC, bất kỳ DATEADD(), bạn làm gì để giá trị này sẽ không bao gồm bất kỳ ánh sáng ban ngày thay đổi thời gian tiết kiệm.

Tốt nhất là xây dựng bảng UTC chuyển đổi của riêng bạn hoặc chỉ cần sử dụng một cái gì đó như thế này:

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

+1

wow. Tôi hy vọng một người nào đó đến và nói với tôi rằng điều này là không đúng sự thật rằng máy chủ sql không thể làm điều này? – Michel

+1

Máy chủ SQL không thể làm điều đó ra khỏi hộp, bạn sẽ cần phải xây dựng chức năng của riêng bạn hoặc điền bảng tìm kiếm của riêng bạn –

3

Giải pháp này có vẻ quá rõ ràng.

Nếu bạn có thể nhận được tính theo giờ UTC Date with GETUTCDATE() và bạn có thể nhận được cập nhật tại địa phương với GETDATE() bạn có một bù đắp mà bạn có thể áp dụng cho bất kỳ datetime

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

này nên trở về thời gian địa phương bạn thực hiện truy vấn,

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

này sẽ trở lại 2011/01/14 02: 00: 00.000, vì tôi đang ở UTC 5

Trừ khi tôi là thiếu cái gì?

+21

Tôi không nghĩ rằng sẽ xử lý thời gian bù giờ mùa hè – Tomas

+6

Không được xếp chồng lên nhau một cách vô cớ, nhưng điều này không chỉ xử lý thời gian mùa hè (hoặc tiết kiệm ánh sáng ban ngày), mà còn không xử lý các thay đổi lịch sử trong múi giờ hoặc lịch hoặc. –

-1

Gần đây tôi đã phải làm điều tương tự. Bí quyết là tìm ra sự bù đắp từ UTC, nhưng nó không phải là một thủ thuật khó. Bạn chỉ cần sử dụng DateDiff để có được sự khác biệt về giờ giữa địa phương và UTC. Tôi đã viết một chức năng để chăm sóc điều này.

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 

này có trên rất quan trọng sắp tới ngắn: Nếu một múi giờ sử dụng một phân đoạn bù đắp, chẳng hạn như Nepal đó là GMT + 5: 45, điều này sẽ thất bại bởi vì đây chỉ giao dịch với cả tiếng đồng hồ. Tuy nhiên, nó phải phù hợp với nhu cầu của bạn tốt.

+13

Thật không may điều này không giải quyết được với tiết kiệm ánh sáng ban ngày. Sự khác biệt giữa GetDate() và GetUtcDate() không phải là hằng số trong suốt cả năm. – Nik

23

Tôi đã chờ đợi 5 năm cho một giải pháp thanh lịch hơn nhưng kể từ khi một đã không nổi lên, tôi sẽ đăng những gì tôi đã sử dụng cho đến nay ...

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 

Ghi chú:

này là dành cho máy chủ Bắc Mỹ mà quan sát Daylight Saving Time. Vui lòng thay đổi @Offest biến để các múi giờ bù đắp của máy chủ chạy các chức năng SQL (Trong khi KHÔNG Quan sát thời gian Daylight Savings) ...

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

Như các quy tắc DST thay đổi cập nhật chúng ở đây ...

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

Chúc mừng,

+2

Cảm ơn bạn rất nhiều vì đã chia sẻ điều này! – Rachel

+0

Điều này có một vài lỗi. Xem câu trả lời của tôi có chứa lời giải thích và mã được cập nhật: http://stackoverflow.com/a/36361558/341942 –

2

Bạn có thể sử dụng dự án SQL Server Time Zone Support của tôi để chuyển đổi giữa các múi giờ chuẩn IANA, as listed here.

Ví dụ: Câu trả lời

SELECT Tzdb.UtcToLocal('2015-07-01 00:00:00', 'America/Los_Angeles') 
0

Bobman là gần, nhưng có một vài lỗi: 1) Bạn phải so sánh điều chỉnh giờ địa phương (thay vì thời gian TIÊU CHUẨN địa phương) đến Daylight Saving End DateTime. 2) SQL GIỮA được bao gồm, do đó bạn nên so sánh bằng cách sử dụng "> = và <" thay vì GIỮA.

Đây là một phiên bản làm việc sửa đổi cùng với một số trường hợp thử nghiệm: (Một lần nữa, điều này chỉ làm việc cho Hoa Kỳ)

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

Đây là một chức năng (một lần nữa Mỹ CHỈ) nhưng nó là một chút linh hoạt hơn . Nó sẽ chuyển đổi ngày UTC thành giờ địa phương của máy chủ. Bắt đầu bằng cách điều chỉnh ngày hẹn dựa trên mức bù hiện tại và sau đó điều chỉnh dựa trên chênh lệch giá trị bù hiện tại và chênh lệch ngày của cuộc hẹn.

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 
Các vấn đề liên quan