As pointed out in comments, right now (March 2022) this calculation looks likely to change next year: US may not switch off of DST in the fall.
Don’t forget that daylight saving time schedules change depending on country, and also are subject to change over the years: the current (as of 2013 through 2022) US system took effect in 2007, for example.
Assuming you want the current system for the US, here’s one form of an answer for any given year.
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
or as functions, but you have to know that DateFirst is set to 7, otherwise the math will be off.
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