[英]How to create Daylight Savings time Start and End function in SQL Server
我需要在SQL服務器中創建一個函數,該函數返回夏令時開始日期時間和夏令時結束日期時間。
我在網上看到了一些例子,但他們都使用了3月的第1個日期和11月的第1個日期,這在技術上並不正確。
夏令時從3月的第2個星期日凌晨2點開始,到11月的第一個星期日凌晨2點結束。
我從下面的代碼開始,但我確定它錯了。 任何幫助表示贊賞! :)
DECLARE @DSTSTART DATETIME
SELECT @DSTSTART = CASE WHEN
DATEPART(MONTH, SYSDATETIME()) = 3
AND DATEPART(weekday, SYSDATETIME()) = 1
AND DATEDIFF(week,dateadd(week, datediff(week, 0, dateadd(month, datediff(month, 0, SYSDATETIME()), 0)), 0), SYSDATETIME() - 1) = 2
AND DATEPART(HOUR, SYSDATETIME()) = 2
THEN SYSDATETIME()
END
RETURN (@DSTSTART)
END
GO
不要忘記,夏令時時間表會根據國家/地區而變化,並且多年來也會發生變化:例如,當前的美國系統於2007年生效。
假設你想要美國當前的系統,這是任何一年的答案的一種形式。
SET DATEFIRST 7
DECLARE @year INT = 2013
DECLARE
@StartOfMarch DATETIME ,
@StartOfNovember DATETIME ,
@DstStart DATETIME ,
@DstEnd DATETIME
SET @StartOfMarch = DATEADD(MONTH, 2, DATEADD(YEAR, @year - 1900, 0))
SET @StartOfNovember = DATEADD(MONTH, 10, DATEADD(YEAR, @year - 1900, 0));
SET @DstStart = DATEADD(HOUR, 2,
DATEADD(day,
( ( 15 - DATEPART(dw, @StartOfMarch) ) % 7 )
+ 7, @StartOfMarch))
SET @DstEnd = DATEADD(HOUR, 2,
DATEADD(day,
( ( 8 - DATEPART(dw, @StartOfNovember) ) % 7 ),
@StartOfNovember))
SELECT
@DstStart AS DstStartInUS ,
@DstEnd AS DstEndInUS
或作為函數,但您必須知道DateFirst設置為7,否則數學將關閉。
CREATE FUNCTION GetDstStart ( @Year AS INT )
RETURNS DATETIME
AS
BEGIN
DECLARE
@StartOfMarch DATETIME ,
@DstStart DATETIME
SET @StartOfMarch = DATEADD(MONTH, 2,
DATEADD(YEAR, @year - 1900, 0))
SET @DstStart = DATEADD(HOUR, 2,
DATEADD(day,
( ( 15 - DATEPART(dw,
@StartOfMarch) )
% 7 ) + 7, @StartOfMarch))
RETURN @DstStart
END
GO;
CREATE FUNCTION GetDstEnd ( @Year AS INT )
RETURNS DATETIME
AS
BEGIN
DECLARE
@StartOfNovember DATETIME ,
@DstEnd DATETIME
SET @StartOfNovember = DATEADD(MONTH, 10,
DATEADD(YEAR, @year - 1900, 0))
SET @DstEnd = DATEADD(HOUR, 2,
DATEADD(day,
( ( 8 - DATEPART(dw,
@StartOfNovember) )
% 7 ), @StartOfNovember))
RETURN @DstEnd
END
就我個人而言,我認為11月的第一個星期日比3月份的第二個星期天更容易找到。 幸運的是,如果你找到一個,你可以找到另一個,因為它們之間總是有238天。 所以這里有一個方便的功能來找到Dst的結束:
create function GetDstEnd (
@Year int
)
returns datetime
as
begin
declare @DstEnd datetime;
;with FirstWeekOfNovember
as (
select top(7)
cast(@Year as char(4))
+ '-11-0'
+ cast(row_number() over(order by object_id) as char(1))
+ ' 02:00:00'
'DST_Stops'
from sys.columns
)
select @DstEnd = DST_Stops
from FirstWeekOfNovember
where datepart(weekday,DST_Stops) = 1
return @DstEnd;
end;
現在,Dst的開始是相同的功能,僅在238天之前。
create function GetDstStart (
@Year int
)
returns datetime
as
begin;
declare @DstStart datetime;
;with FirstWeekOfNovember
as (
select top(7)
cast(@Year as char(4))
+ '-11-0'
+ cast(row_number() over(order by object_id) as char(1))
+ ' 02:00:00'
'DST_Stops'
from sys.columns
)
select @DstStart = dateadd(day,-238,DST_Stops)
from FirstWeekOfNovember
where datepart(weekday,DST_Stops) = 1
return @DstStart;
end;
go
SQL Server 2016版將一勞永逸地解決這個問題。 對於早期版本,CLR解決方案可能最簡單。 或者對於特定的DST規則(僅限於美國),T-SQL功能可以相對簡單。
但是,我認為通用的T-SQL解決方案可能是可行的。 只要xp_regread
有效,試試這個:
CREATE TABLE #tztable (Value varchar(50), Data binary(56));
DECLARE @tzname varchar(150) = 'SYSTEM\CurrentControlSet\Control\TimeZoneInformation'
EXEC master.dbo.xp_regread 'HKEY_LOCAL_MACHINE', @tzname, 'TimeZoneKeyName', @tzname OUT;
SELECT @tzname = 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\' + @tzname
INSERT INTO #tztable
EXEC master.dbo.xp_regread 'HKEY_LOCAL_MACHINE', @tzname, 'TZI';
SELECT -- See http://msdn.microsoft.com/ms725481
CAST(CAST(REVERSE(SUBSTRING(Data, 1, 4)) AS binary(4)) AS int) AS BiasMinutes, -- UTC = local + bias: > 0 in US, < 0 in Europe!
CAST(CAST(REVERSE(SUBSTRING(Data, 5, 4)) AS binary(4)) AS int) AS ExtraBias_Std, -- 0 for most timezones
CAST(CAST(REVERSE(SUBSTRING(Data, 9, 4)) AS binary(4)) AS int) AS ExtraBias_DST, -- -60 for most timezones: DST makes UTC 1 hour earlier
-- When DST ends:
CAST(CAST(REVERSE(SUBSTRING(Data, 13, 2)) AS binary(2)) AS smallint) AS StdYear, -- 0 = yearly (else once)
CAST(CAST(REVERSE(SUBSTRING(Data, 15, 2)) AS binary(2)) AS smallint) AS StdMonth, -- 0 = no DST
CAST(CAST(REVERSE(SUBSTRING(Data, 17, 2)) AS binary(2)) AS smallint) AS StdDayOfWeek, -- 0 = Sunday to 6 = Saturday
CAST(CAST(REVERSE(SUBSTRING(Data, 19, 2)) AS binary(2)) AS smallint) AS StdWeek, -- 1 to 4, or 5 = last <DayOfWeek> of <Month>
CAST(CAST(REVERSE(SUBSTRING(Data, 21, 2)) AS binary(2)) AS smallint) AS StdHour, -- Local time
CAST(CAST(REVERSE(SUBSTRING(Data, 23, 2)) AS binary(2)) AS smallint) AS StdMinute,
CAST(CAST(REVERSE(SUBSTRING(Data, 25, 2)) AS binary(2)) AS smallint) AS StdSecond,
CAST(CAST(REVERSE(SUBSTRING(Data, 27, 2)) AS binary(2)) AS smallint) AS StdMillisec,
-- When DST starts:
CAST(CAST(REVERSE(SUBSTRING(Data, 29, 2)) AS binary(2)) AS smallint) AS DSTYear, -- See above
CAST(CAST(REVERSE(SUBSTRING(Data, 31, 2)) AS binary(2)) AS smallint) AS DSTMonth,
CAST(CAST(REVERSE(SUBSTRING(Data, 33, 2)) AS binary(2)) AS smallint) AS DSTDayOfWeek,
CAST(CAST(REVERSE(SUBSTRING(Data, 35, 2)) AS binary(2)) AS smallint) AS DSTWeek,
CAST(CAST(REVERSE(SUBSTRING(Data, 37, 2)) AS binary(2)) AS smallint) AS DSTHour,
CAST(CAST(REVERSE(SUBSTRING(Data, 39, 2)) AS binary(2)) AS smallint) AS DSTMinute,
CAST(CAST(REVERSE(SUBSTRING(Data, 41, 2)) AS binary(2)) AS smallint) AS DSTSecond,
CAST(CAST(REVERSE(SUBSTRING(Data, 43, 2)) AS binary(2)) AS smallint) AS DSTMillisec
FROM #tztable;
DROP TABLE #tztable
(復雜)T-SQL函數可以使用此數據來確定當前DST規則期間所有日期的精確偏移量。
我對在網上找到的將UTC轉換為當地時間的任何解決方案都不滿意,所以我想出了這個功能。 在這里看看我的答案
有一些邏輯根據DST使用的標准日期范圍計算夏令時是否有效(3月的第二個星期日凌晨2點,時鍾向前移動; 11月的第一個星期日恢復到標准時間)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.