簡體   English   中英

獲取當前時間並處理特定時區的夏令時

[英]Get current time and deal with daylight saving time for a particular timezone

我在 SQL Server 2012 中使用日期時間時遇到問題,我的 SQL 數據庫托管在使用與我想用於我的應用程序的時區不同的時區的服務器上,而且我想使用的時區處於夏令時,所以每次(夏令時)發生時我都必須更改代碼

所以如果我使用:

SELECT Getdate()

我將獲取服務器的當前日期和時間(無論您的數據庫托管在哪里),結果將類似於:

2015-12-23 09:09:40.303

無論如何或者我可以使用SQL中的內置方法來檢索特定時區的當前日期和時間,該時區也遵守夏令時,或者特別是在這種情況下它是中央標准時間(對於美國),我想要它自動處理夏令時,而不必在每次夏令時發生時手動修改代碼。

好的,再次重申我的評論,我會說您可以先獲取 UTC 時間,然后獲取區域的實際時間信息。

所以這樣的事情可以達到目的-

TimeZoneInfo centralUSAZone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");

DateTime centralUSATime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, centralUSAZone);

ConvertTimeFromUtc函數本質上需要兩個參數。

日期時間類型:System.DateTime

協調世界時 (UTC)。

destinationTimeZone 類型:System.TimeZoneInfo

要將 dateTime 轉換為的時區。

詳細查看 msdn 文檔。 https://msdn.microsoft.com/en-us/library/system.timezoneinfo.converttimefromutc.aspx

您可以使用SWITCHOFFSET從一個時區偏移到另一個時區。 但是第二個參數要求您針對夏令時進行調整。

來自 MSDN:

time_zone 是格式為 [+|-]TZH:TZM 的字符串或表示時區偏移量的有符號整數(分鍾),並假定為夏令時感知和調整。

有一個關於Stack DBA的討論可能會對您有所幫助。 TL/DR:使用 CLR 是最流行的答案。

非夏令時示例:

SELECT
    SYSDATETIMEOFFSET()                                AS Server_Date_Time_WithTZone,
    SWITCHOFFSET(SYSDATETIMEOFFSET(), '+04:00')        AS NewTZone
;

不幸的是,由於夏令時,時區在美國變得非常困難。 您可能應該使用庫或 CLR 函數,但如果不能,這里是一個簡單的 SQL-only 實現。 這很幼稚,因為:

  1. 它假定僅適用於美國的規則(DST 在某些預定義的星期日是凌晨 2 點,等等)。
  2. 它假設您沒有 1970 年之前的日期
  3. 它假設您知道本地時區偏移量(即:EST=-05:00、EDT=-04:00 等)

這是 SQL:

-- make a table (#dst) of years 1970-2101. Note that DST could change in the future and
-- everything was all custom and jacked before 1970 in the US.
declare @first_year varchar(4) = '1970'
declare @last_year varchar(4) = '2101'

-- make a table of all the years desired
if object_id('tempdb..#years') is not null drop table #years
;with cte as (
    select cast(@first_year as int) as int_year
          ,@first_year as str_year
          ,cast(@first_year + '-01-01' as datetime) as start_of_year
    union all
    select int_year + 1
          ,cast(int_year + 1 as varchar(4))
          ,dateadd(year, 1, start_of_year)
    from cte
    where int_year + 1 <= @last_year
)
select *
into #years
from cte
option (maxrecursion 500);

-- make a staging table of all the important DST dates each year
if object_id('tempdb..#dst_stage') is not null drop table #dst_stage
select dst_date
      ,time_period
      ,int_year
      ,row_number() over (order by dst_date) as ordinal
into #dst_stage
from (
    -- start of year
    select y.start_of_year as dst_date
          ,'start of year' as time_period
          ,int_year
    from #years y

    union all
    select dateadd(year, 1, y.start_of_year)
          ,'start of year' as time_period
          ,int_year
    from #years y
    where y.str_year = @last_year

    -- start of dst
    union all
    select
        case
            when y.int_year >= 2007 then
                -- second sunday in march
                dateadd(day, ((7 - datepart(weekday, y.str_year + '-03-08')) + 1) % 7, y.str_year + '-03-08')
            when y.int_year between 1987 and 2006 then
                -- first sunday in april
                dateadd(day, ((7 - datepart(weekday, y.str_year + '-04-01')) + 1) % 7, y.str_year + '-04-01')
            when y.int_year = 1974 then
                -- special case
                cast('1974-01-06' as datetime)
            when y.int_year = 1975 then
                -- special case
                cast('1975-02-23' as datetime)
            else
                -- last sunday in april
                dateadd(day, ((7 - datepart(weekday, y.str_year + '-04-24')) + 1) % 7, y.str_year + '-04-24')
        end
        ,'start of dst' as time_period
        ,int_year
    from #years y

    -- end of dst
    union all
    select
        case
            when y.int_year >= 2007 then
                -- first sunday in november
                dateadd(day, ((7 - datepart(weekday, y.str_year + '-11-01')) + 1) % 7, y.str_year + '-11-01')
            else
                -- last sunday in october
                dateadd(day, ((7 - datepart(weekday, y.str_year + '-10-25')) + 1) % 7, y.str_year + '-10-25')
        end
        ,'end of dst' as time_period
        ,int_year
    from #years y
) y
order by 1

-- assemble a final table
if object_id('tempdb..#dst') is not null drop table #dst
select a.dst_date +
          case
             when a.time_period = 'start of dst' then ' 03:00'
             when a.time_period = 'end of dst' then ' 02:00'
             else ' 00:00'
          end as start_date
      ,b.dst_date +
          case
             when b.time_period = 'start of dst' then ' 02:00'
             when b.time_period = 'end of dst' then ' 01:00'
             else ' 00:00'
          end as end_date
      ,cast(case when a.time_period = 'start of dst' then 1 else 0 end as bit) as is_dst
      ,cast(0 as bit) as is_ambiguous
      ,cast(0 as bit) as is_invalid
into #dst
from #dst_stage a
join #dst_stage b on a.ordinal + 1 = b.ordinal
union all
select a.dst_date + ' 02:00' as start_date
      ,a.dst_date + ' 03:00' as end_date
      ,cast(1 as bit) as is_dst
      ,cast(0 as bit) as is_ambiguous
      ,cast(1 as bit) as is_invalid
from #dst_stage a
where a.time_period = 'start of dst'
union all
select a.dst_date + ' 01:00' as start_date
      ,a.dst_date + ' 02:00' as end_date
      ,cast(0 as bit) as is_dst
      ,cast(1 as bit) as is_ambiguous
      ,cast(0 as bit) as is_invalid
from #dst_stage a
where a.time_period = 'end of dst'
order by 1

-------------------------------------------------------------------------------

-- Test Eastern
select
    the_date as eastern_local
    ,todatetimeoffset(the_date, case when b.is_dst = 1 then '-04:00' else '-05:00' end) as eastern_local_tz
    ,switchoffset(todatetimeoffset(the_date, case when b.is_dst = 1 then '-04:00' else '-05:00' end), '+00:00') as utc_tz
    --,b.*
from (
    select cast('2015-03-08' as datetime) as the_date
    union all select cast('2015-03-08 02:30' as datetime) as the_date
    union all select cast('2015-03-08 13:00' as datetime) as the_date
    union all select cast('2015-11-01 01:30' as datetime) as the_date
    union all select cast('2015-11-01 03:00' as datetime) as the_date
) a left join
#dst b on b.start_date <= a.the_date and a.the_date < b.end_date

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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