获取当前时间并处理特定时区的夏令时

时间:2015-12-23 16:21:31

标签: sql sql-server sql-server-2012

我在SQL Server 2012中使用datetime时遇到了麻烦,我的SQL数据库托管在一个服务器上,该服务器使用的时区不同于我想用于我的应用程序的时区,我想要使用的时区也是在白天节省时间,所以每次这个(夏令时)发生时我都要更改代码

所以,如果我使用:

SELECT Getdate()

我将获得服务器的当前日期和时间(无论您的数据库是从哪里托管的),结果将类似于:

  

2015-12-23 09:09:40.303

无论如何还是SQL中的内置方法我可以用来检索特定时区的当前日期和时间,这也考虑了夏令时,或者特别是在这种情况下它是中央标准时间(对于美国),我希望它能够自动处理夏令时,而不必在每次夏令时发生时手动修改代码。

3 个答案:

答案 0 :(得分:1)

好的再次重申我的评论我会说你可以先获得UTC时间然后获取区域的实际时间信息。

所以这样的事情可以达到目的 -

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

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

ConvertTimeFromUtc函数本质上有两个参数。

  

日期时间      键入:System.DateTime

     

协调世界时(UTC)。

     

destinationTimeZone       键入:System.TimeZoneInfo

     

将dateTime转换为。

的时区

详细了解msdn doc。https://msdn.microsoft.com/en-us/library/system.timezoneinfo.converttimefromutc.aspx

答案 1 :(得分:0)

您可以使用SWITCHOFFSET从一个时区偏移到另一个时区。但第二个参数要求您调整夏令时。

来自MSDN:

  

time_zone是格式为[+ | - ] TZH:TZM或签名的字符串   表示时区偏移的整数(分钟),是   假设是节省日光并调整。

有关Stack DBA的讨论可能会对您有所帮助。 TL / DR:使用CLR是最受欢迎的答案。

非夏令时示例:

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

答案 2 :(得分:0)

不幸的是,由于夏令时,时区在美国会变得非常困难。您应该使用库或CLR函数,但如果不能,这是一个简单的SQL实现。这很天真,因为:

  1. 它假定仅限美国规则(DST在某些预定义的星期日等于2AM,等等)。
  2. 假设您没有1970年以前的日期
  3. 假设您知道当地时区偏移(即:EST = -05:00,EDT = -04:00等)
  4. 这是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