我的本地开发服务器在中东,但我的生产服务器在英国。
我需要向用户显示其所在时区的日期。例如,如果用户在沙特阿拉伯,那么我需要根据沙特阿拉伯格式显示时间。
是否应该创建一个名为TimeZone的新数据库表并将时间保存在UTC中?
我的本地开发服务器在中东,但我的生产服务器在英国。
我需要向用户显示其所在时区的日期。例如,如果用户在沙特阿拉伯,那么我需要根据沙特阿拉伯格式显示时间。
是否应该创建一个名为TimeZone的新数据库表并将时间保存在UTC中?
Answers:
不幸的是,没有快速解决方案。应用程序的国际化应作为最初设计讨论的一部分,因为它实际上进入了许多不同领域的核心,包括日期/时间比较和输出格式。
无论如何,要走正确的道路,必须将时区信息与时间存储在一起。换句话说,意识到日期/时间20130407 14:50
是没有意义的,而没有(a)包括时区当时的UTC偏移量(注释1),或者(b)确保所有插入这些值的逻辑先转换为某个固定偏移量(最有可能是0)。没有这些东西,两个给定的时间值是不可比的,并且数据已损坏。(顺便说一句,后一种方法正在玩火(注2);请勿这样做。)
在SQL Server 2008+中,您可以使用datetimeoffset
数据类型直接将偏移量与时间一起存储。(为完整起见,在2005年及之前,我将添加第二列以存储当时的当前UTC偏移值(以分钟为单位)。)
这使桌面型应用程序变得容易,因为这些平台通常具有将日期/时间+时区自动转换为本地时间,然后根据用户的区域设置进行格式化以输出的机制。
对于本质上是断开连接的架构的Web,即使正确设置了后端数据,它也更加复杂,因为您需要有关客户端的信息才能进行转换和/或格式化。这通常是通过用户首选项设置(应用程序在输出之前转换/格式化事物)来完成的,或者只是对所有人显示具有相同固定格式和时区偏移的事物(这是Stack Exchange平台当前所做的事情)。
您会看到如果后端数据设置不正确,如何很快使它变得复杂和棘手。我不建议您选择任何一条路径,因为您最终会遇到更多问题。
注1:
时区的UTC偏移量不是固定的:考虑夏令时,其中时区的UTC偏移量会相差一个小时或一个小时。另外,区域的夏时制日期也会定期变化。因此,使用datetimeoffset
(或复合的local time
和UTC offset at that time
)的结果在最大信息恢复。
笔记2:
这是关于控制数据输入的。虽然没有万无一失的方法来验证传入的值,但是最好执行一个不涉及计算的简单标准。如果公共API期望包含偏移量的数据类型,则调用者将清楚该要求。
如果不是这种情况,则调用者必须依靠文档(如果他们阅读了文档),或者计算不正确,等等。需要偏移量时,失败/错误模式更少,尤其是对于分布式系统(甚至是单独服务器上的网络/数据库(如此处所示)。
无论如何,存储偏移量可以用一块石头杀死两只鸟。即使现在不需要,也可以在以后根据需要使用。的确,它占用了更多的存储空间,但是我认为这是值得进行权衡的,因为如果一开始就从不记录数据,那么数据将会丢失。
datetimeoffset
意思是“这是发生事件时用户/计算机的本地时间”-我们不需要知道DST是否有效,因为给定的UTC偏移始终存在(嵌入在datetimeoffset
值中)。
我为SQL Server中的时区转换开发了一个全面的解决方案。请参阅GitHub上的SQL Server时区支持。
它可以正确处理时区之间以及往返于UTC的转换,包括夏令时。
它在概念上与adss的答案中所述的“ T-SQL工具箱”解决方案相似,但是它使用的是更常见的IANA / Olson / TZDB时区,而不是Microsoft的时区,并且它包括一些实用程序,可将数据保持为最新版本。 TZDB问世。
用法示例(回答OP):
SELECT Tzdb.UtcToLocal(sysutcdatetime(), 'Asia/Riyadh') as CurrentTimeInSaudiArabia
有关其他API和详细说明,请参见GitHub上的自述文件。
另外,请注意,由于这是基于UDF的解决方案,因此其设计并非以性能为主要目标。如果将此功能内置到SQL Server中,效果会更好,类似于CONVERT_TZ
Oracle和MySQL中该功能的存在方式。
SQL Server从2005年开始进行了修改,将内部时区保存在UTC中。这主要是由于地理复制和HA投影机涉及到原木运输,并且原木运输时间保存在不同时区中,使得旧方法无法恢复原木。
因此,在UTC时间内部保存所有内容使SQL Server可以在全局范围内正常工作。这就是为什么夏令时在Windows中很难解决的原因之一,因为其他MS产品(例如Outlook)也在内部将日期/时间保存为UTC,并创建了需要修补的偏移量。
我在一家公司工作,那里有成千上万的服务器(虽然不是MS SQL Server,而是各种各样的服务器)分布在世界各地,并且如果我们不专门强迫所有事情通过UTC进行处理,我们都会发疯很快
我已经在Codeplex上开发并发布了“ T-SQL工具箱”项目,以帮助在SQL Server中处理日期时间和时区问题的任何人。它是开源的,完全免费使用。
它提供了使用普通T-SQL的简便的日期时间转换UDF,并带有开箱即用的其他配置表。
在您的示例中,您可以使用以下示例:
SELECT [DateTimeUtil].[UDF_ConvertLocalToLocalByTimezoneIdentifier] (
'GMT Standard Time', -- the original timezone in which your datetime is stored
'Middle East Standard Time', -- the target timezone for your user
'2014-03-30 01:55:00' -- the original datetime you want to convert
)
这将为您的用户返回转换后的日期时间值。
在T-SQL工具箱数据库中也提供的表“ DateTimeUtil.Timezone”中可以找到所有受支持的时区的列表。