将UTC日期时间转换为本地日期时间的最佳方法是什么.它不像getutcdate()和getdate()那样简单,因为差异会根据日期而变化.
CLR集成对我来说也不是一个选择.
几个月前我提出解决这个问题的解决方案就是有一个夏令时表,存储了接下来100年左右的开始和结束夏令时,这个解决方案似乎不太优雅,但转换很快(简单)表查找)
创建两个表,然后加入它们以将存储的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)
如果您在美国并且只对从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)
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(),但我的编辑被多次拒绝了.我尝试解释,但被解雇为一个菜鸟.他是解决这个问题的绝佳方法!它让我开始朝着正确的方向前进.当他只需要一个小小的调整时,我讨厌创建这个单独的答案,但我试过了¯\ _(ツ)_ /¯
考虑夏令时的更简单通用的解决方案.给定"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]
如果这些问题中的任何一个对您有影响,则不应将本地时间存储在数据库中:
DST表示在回落期间存在"不确定时数",当地时间无法明确转换.如果需要确切的日期和时间,则以UTC格式存储.
如果您希望向用户显示他们自己时区的日期和时间,而不是发生操作的时区,请以UTC格式存储.
在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的可能性恰好很小,但它可能发生,并且会导致无效的转换.