簡體   English   中英

如何在SQL Server中創建夏令時開始和結束功能

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM