【发布时间】:2014-02-24 19:50:51
【问题描述】:
我们正在将应用程序迁移到 Azure,但目前我们所有的日期都存储为东部标准时间。由于 SQL Azure 使用 UTC 并且我们的许多日期都是通过 getdate() 调用生成的,如果我们保持原样,我们将会遇到问题。似乎最不让人头疼的选项是将我们所有存储的日期转换为 UTC,并在前端将它们转换为本地时间。
不幸的是,似乎没有一种内置方法可以将 SQL 日期时间从一个时区转换为另一个时区,而且我发现的许多解决方案都没有考虑夏令时。我发现考虑 DST 的解决方案涉及导入日历和时区表,并且非常复杂。
我只是在寻找适合单次转换的东西,而且它不必很漂亮。有什么想法吗?
更新:我编写了以下函数,我相信它会完成我所需要的。有人看到这个有什么漏洞吗?我所有的日期目前都是东部标准时间,但我的日期是从 2002 年左右开始的,所以我不得不在 2007 年处理法律变更。
我想我会像这样使用这个函数:
UPDATE myTable SET myDate = dbo.fnESTtoUTC(myDate)
函数如下:
CREATE FUNCTION [dbo].[fnESTtoUTC]
(@pESTDate DATETIME)
RETURNS DATETIME
AS
BEGIN
DECLARE @TimeZoneOffset INT
DECLARE @Year INT
DECLARE @Day INT
DECLARE @DSTStart DATETIME
DECLARE @DSTEnd DATETIME
SELECT @Year = DATEPART(year, @pESTDate)
IF @Year >= 2007
BEGIN
SELECT @Day = 8
WHILE @DSTStart IS NULL
BEGIN
-- Second Sunday in March
IF DATEPART(weekday, DATEFROMPARTS(@Year, 3, @Day)) = 1
SELECT @DSTStart = DATETIMEFROMPARTS(@Year, 3, @Day, 2, 0, 0, 0)
SELECT @Day = @Day + 1
END
SELECT @Day = 1
WHILE @DSTEnd IS NULL
BEGIN
-- First Sunday in November
IF DATEPART(weekday, DATEFROMPARTS(@Year, 11, @Day)) = 1
SELECT @DSTEnd = DATETIMEFROMPARTS(@Year, 11, @Day, 1, 0, 0, 0)
SELECT @Day = @Day + 1
END
END
ELSE
BEGIN
SELECT @Day = 1
WHILE @DSTStart IS NULL
BEGIN
-- First Sunday in April
IF DATEPART(weekday, DATEFROMPARTS(@Year, 4, @Day)) = 1
SELECT @DSTStart = DATETIMEFROMPARTS(@Year, 4, @Day, 2, 0, 0, 0)
SELECT @Day = @Day + 1
END
SELECT @Day = 31
WHILE @DSTEnd IS NULL
BEGIN
-- Last Sunday in October
IF DATEPART(weekday, DATEFROMPARTS(@Year, 10, @Day)) = 1
SELECT @DSTEnd = DATETIMEFROMPARTS(@Year, 10, @Day, 1, 0, 0, 0)
SELECT @Day = @Day - 1
END
END
IF @pESTDate >= @DSTStart AND @pESTDate < @DSTEnd
BEGIN
-- Date is in DST
SELECT @TimeZoneOffset = 4
END
ELSE
BEGIN
-- Not DST
SELECT @TimeZoneOffset = 5
END
RETURN ( dateadd(hh, @TimeZoneOffset, @pESTDate) )
END
【问题讨论】:
-
涉及表格的原因是因为时区本身的定义会随着时间而变化,因此不可能可靠地更改时区之间的日期(地理也很重要,例如,就在几年前,印第安纳州没有'不支持 DST,但现在支持)。您将如何编写准确转换 1996 年 11 月 20 日和 2013 年 11 月 20 日的“单次转换”?我不知道,但我认为其中一个在 DST 中,另一个不在。不要害怕额外的桌子。我已经做到了这一点,并且在有限的 # 个时区中运行良好。
-
这不是对您问题的回答,而是一个建议。将您的设计拆分为 n 层/MVC。所以数据、业务、用户界面。现在,在您的数据层中,以 UTC 格式存储日期/时间以及源日期/时间的时区指示符。这意味着您的所有日期/时间都已标准化。现在,在业务层中,对所需的标准化“本地”日期/时间(再次基于所需的 TZ)进行必要的对话,以在您的 UI 层中显示。恕我直言,不要使用 SQL 作为您的业务引擎 - 在您的业务层中完成大部分工作。我希望这会有所帮助。
-
@robnick 但仍然需要注意时区当时是否处于 DST 中。表示层可以判断 today 是否在 DST 中,但不能判断某个任意日期(不要只考虑过去的日期,也要考虑未来)。我完全同意日期时间应存储为 UTC(实际上将我所有的服务器都设置为 UTC),但即使您将 TZ + DST 信息与日期一起存储,对于未来的日期,您也需要能够在某些时间进行调整总统决定再次更改 DST 的范围(或完全取消它)。
-
没有很多软件能正确处理这个问题是有原因的——这是一个很难解决的问题。在 Outlook 中安排从现在起 16 个月后的会议,并将其发送给遵守和不遵守 DST 的时区的人,看看他们是否都希望同时出现(不要忘记包括来自纽芬兰位于其中一个有趣的时区:30 个时区)。甚至 SQL Server 也添加了
DATETIMEOFFSET,但你猜怎么着?它不支持 DST。 -
@AaronBertrand 感谢您的回复和建议。幸运的是,我所有的日期都来自一个时区,所以对于一次性转换,我认为我不必考虑所有地区的所有可能性。我用我拼凑的快速功能更新了我的问题。您认为它不起作用的任何原因吗?
标签: sql-server timezone azure-sql-database utc dst