当前位置:  开发笔记 > 数据库 > 正文

在SQL 2005中有效地转换UTC和本地(即PST)时间之间的日期

如何解决《在SQL2005中有效地转换UTC和本地(即PST)时间之间的日期》经验,为你挑选了6个好方法。

将UTC日期时间转换为本地日期时间的最佳方法是什么.它不像getutcdate()和getdate()那样简单,因为差异会根据日期而变化.

CLR集成对我来说也不是一个选择.

几个月前我提出解决这个问题的解决方案就是有一个夏令时表,存储了接下来100年左右的开始和结束夏令时,这个解决方案似乎不太优雅,但转换很快(简单)表查找)



1> Eric Z Beard..:

创建两个表,然后加入它们以将存储的GMT日期转换为本地时间:

TimeZones     e.g.
---------     ----
TimeZoneId    19
Name          Eastern (GMT -5)
Offset        -5

创建夏令时表并尽可能多地填充信息(当地法律一直在变化,因此无法预测未来几年的数据)

DaylightSavings
---------------
TimeZoneId    19
BeginDst      3/9/2008 2:00 AM
EndDst        11/2/2008 2:00 AM

像这样加入他们:

inner join  TimeZones       tz on x.TimeZoneId=tz.TimeZoneId
left join   DaylightSavings ds on tz.TimeZoneId=ds.LocalTimeZone 
    and x.TheDateToConvert between ds.BeginDst and ds.EndDst

转换这样的日期:

dateadd(hh, tz.Offset + 
    case when ds.LocalTimeZone is not null 
    then 1 else 0 end, TheDateToConvert)


因为dateadd只接受整数,所以我在几分钟内存储了偏移量来处理9.5等偏移量.

2> Bob Albright..:

如果您在美国并且只对从UTC/GMT到固定时区(例如EDT)感兴趣,则此代码应该足够了.我今天掀起了它,并认为这是正确的,但使用风险自负.

假设您的日期位于"日期"列中,则将计算列添加到表'myTable'.希望别人觉得这很有用.

ALTER TABLE myTable ADD date_edt AS 
  dateadd(hh, 
        -- The schedule through 2006 in the United States was that DST began on the first Sunday in April 
        -- (April 2, 2006), and changed back to standard time on the last Sunday in October (October 29, 2006). 
        -- The time is adjusted at 02:00 local time.
              CASE WHEN YEAR(date) <= 2006 THEN  
                    CASE WHEN 
                              date >=  '4/' + CAST(abs(8-DATEPART(dw,'4/1/' + CAST(YEAR(date) as varchar)))%7 + 1 as varchar) +  '/' + CAST(YEAR(date) as varchar) + ' 2:00' 
                          AND 
                              date < '10/' + CAST(32-DATEPART(dw,'10/31/' + CAST(YEAR(date) as varchar)) as varchar) +  '/' + CAST(YEAR(date) as varchar) + ' 2:00' 
                    THEN -4 ELSE -5 END
              ELSE
        -- By the Energy Policy Act of 2005, daylight saving time (DST) was extended in the United States in 2007. 
        -- DST starts on the second Sunday of March, which is three weeks earlier than in the past, and it ends on 
        -- the first Sunday of November, one week later than in years past. This change resulted in a new DST period 
        -- that is four weeks (five in years when March has five Sundays) longer than in previous years.[35] In 2008 
        -- daylight saving time ended at 02:00 on Sunday, November 2, and in 2009 it began at 02:00 on Sunday, March 8.[36]
                    CASE WHEN 
                              date >= '3/' + CAST(abs(8-DATEPART(dw,'3/1/' + CAST(YEAR(date) as varchar)))%7 + 8 as varchar) +  '/' + CAST(YEAR(date) as varchar) + ' 2:00' 
                          AND 
                              date < 
                                '11/' + CAST(abs(8-DATEPART(dw,'11/1/' + CAST(YEAR(date) as varchar)))%7 + 1 as varchar) +  '/' + CAST(YEAR(date) as varchar) + ' 2:00' 
                    THEN -4 ELSE -5 END
              END
  ,date)


许多年后突然道歉,但是......它看起来好像你在0200Z发生过渡,但它们发生在0100EST/0200EDT,即0600Z.否则看起来对我很好,我用它来解决我在数据库中混合本地和UTC邮票的缺乏远见.至少我的服务器和用户都在一个TZ中.

3> 小智..:

FOR READ-ONLY使用此(受Bob Albright错误解决方案的启发):

SELECT
  date1, 
  dateadd(hh,
    -- The schedule through 2006 in the United States was that DST began on the first Sunday in April 
    -- (April 2, 2006), and changed back to standard time on the last Sunday in October (October 29, 2006). 
    -- The time is adjusted at 02:00 local time (which, for edt, is 07:00 UTC at the start, and 06:00 GMT at the end).
    CASE WHEN YEAR(date1) <= 2006 THEN
         CASE WHEN 
                  date1 >=  '4/' + CAST((8-DATEPART(dw,'4/1/' + CAST(YEAR(date1) as varchar)))%7 + 1 as varchar) +  '/' + CAST(YEAR(date1) as varchar) + ' 7:00' 
                AND 
                  date1 < '10/' + CAST(32-DATEPART(dw,'10/31/' + CAST(YEAR(date1) as varchar)) as varchar) +  '/' + CAST(YEAR(date1) as varchar) + ' 6:00' 
              THEN -4 ELSE -5 END
    ELSE
        -- By the Energy Policy Act of 2005, daylight saving time (DST) was extended in the United States in 2007. 
        -- DST starts on the second Sunday of March, which is three weeks earlier than in the past, and it ends on 
        -- the first Sunday of November, one week later than in years past. This change resulted in a new DST period 
        -- that is four weeks (five in years when March has five Sundays) longer than in previous years. In 2008 
        -- daylight saving time ended at 02:00 edt (06:00 UTC) on Sunday, November 2, and in 2009 it began at 02:00 edt (07:00 UTC) on Sunday, March 8
        CASE WHEN 
                 date1 >= '3/' + CAST((8-DATEPART(dw,'3/1/' + CAST(YEAR(date1) as varchar)))%7 + 8 as varchar) +  '/' + CAST(YEAR(date1) as varchar) + ' 7:00' 
               AND 
                 date1 < '11/' + CAST((8-DATEPART(dw,'11/1/' + CAST(YEAR(date1) as varchar)))%7 + 1 as varchar) +  '/' + CAST(YEAR(date1) as varchar) + ' 6:00' 
             THEN -4 ELSE -5 END
    END
   , date1) as date1Edt
  from MyTbl

在我尝试编辑Bob Albright的错误答案后,我发布了这个答案.我纠正了时间并删除了多余的abs(),但我的编辑被多次拒绝了.我尝试解释,但被解雇为一个菜鸟.他是解决这个问题的绝佳方法!它让我开始朝着正确的方向前进.当他只需要一个小小的调整时,我讨厌创建这个单独的答案,但我试过了¯\ _(ツ)_ /¯



4> 小智..:

考虑夏令时的更简单通用的解决方案.给定"YourDateHere"中的UTC日期:

--Use Minutes ("MI") here instead of hours because sometimes
--  the UTC offset may be half an hour (e.g. 9.5 hours).
SELECT DATEADD(MI,
               DATEDIFF(MI, SYSUTCDATETIME(),SYSDATETIME()),
               YourUtcDateHere)[LocalDateTime]


不确定此代码是否正确.它得到的时间不同(包括夏令时)并将其应用于YourDate,此时可能有也可能没有相同的DST.

5> Brad Wilson..:

如果这些问题中的任何一个对您有影响,则不应将本地时间存储在数据库中:

    DST表示在回落期间存在"不确定时数",当地时间无法明确转换.如果需要确切的日期和时间,则以UTC格式存储.

    如果您希望向用户显示他们自己时区的日期和时间,而不是发生操作的时区,请以UTC格式存储.



6> 小智..:

在Eric Z Beard的回答中,以下是SQL

inner join  TimeZones       tz on x.TimeZoneId=tz.TimeZoneId 
left join   DaylightSavings ds on tz.TimeZoneId=ds.LocalTimeZone  
    and x.TheDateToConvert between ds.BeginDst and ds.EndDst 

可能更准确地说是:

inner join  TimeZones       tz on x.TimeZoneId=tz.TimeZoneId 
left join   DaylightSavings ds on tz.TimeZoneId=ds.LocalTimeZone  
    and x.TheDateToConvert >= ds.BeginDst and x.TheDateToConvert < ds.EndDst 

(以上代码未经测试)

原因是sql"between"语句是包含的.在DST的后端,这将导致2AM时间未转换为1AM.当然,时间2AM的可能性恰好很小,但它可能发生,并且会导致无效的转换.

推荐阅读
sx-March23
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有