我需要向用户显示其所在时区的日期。例如,如果用户在沙特阿拉伯,那么我需要根据沙特阿拉伯格式显示时间。
我应该创建一个名为TimeZone的新数据库表并将时间保存为UTC吗?
#1 楼
不幸的是,没有快速解决方案。应用程序的国际化应作为最初设计讨论的一部分,因为它实际上涉及许多不同领域的核心,包括日期/时间比较和输出格式。无论如何,继续在正确行事的轨道上,必须将时区信息与时间一起存储。换句话说,意识到日期/时间
20130407 14:50
是没有意义的,而没有(a)包括时区当时的UTC偏移量(注1),或者(b)确保所有插入这些值的逻辑先转换为某个固定偏移量(最有可能为0)。没有这些东西,两个给定的时间值是不可比的,并且数据已损坏。 (顺便说一句,后一种方法正在玩火(注2);请不要这样做。)在SQL Server 2008+中,您可以使用
datetimeoffset
数据类型。 (为完整起见,在2005年及之前,我将添加第二列以存储当时的UTC偏移值(以分钟为单位)。)这使得桌面型应用程序变得容易,因为这些平台通常具有自动将日期/时间+时区转换为本地时间,然后根据用户的区域设置进行格式化以进行输出的机制。
对于网络而言,这是固有的断开连接架构,即使正确设置了后端数据,它也更加复杂,因为您需要有关客户端的信息才能进行转换和/或格式化。这通常是通过用户首选项设置(应用程序在输出之前转换/格式化事物)来完成的,或者只是对所有人显示具有相同固定格式和时区偏移的事物(这是Stack Exchange平台当前所做的事情)。
您会看到如果后端数据设置不正确,如何很快使它变得复杂和棘手。我不建议您选择任何一条路径,因为这样一来您最终会遇到更多问题。
注1:
时区的UTC偏移量是固定的:考虑夏令时,其中某个区域的UTC偏移量会相差一个小时或一个小时以下。另外,区域的夏令时日期也会定期变化。因此,使用
datetimeoffset
(或local time
和UTC offset at that time
的组合)可以最大程度地恢复信息。注2:
这与控制数据输入有关。虽然没有万无一失的方法来验证输入值,但最好执行一个不涉及计算的简单标准。如果公共API期望包含偏移量的数据类型,则调用方将清楚此要求。
如果不是这种情况,则调用方必须依赖文档(如果他们读取了该文档) ),或者计算不正确等。在需要偏移量时,故障/错误模式较少,特别是对于分布式系统(或者甚至是单独服务器上的Web /数据库)。
反正存储偏移量会用一颗石头杀死两只鸟;即使现在不需要,也可以在以后根据需要使用。的确,它占用了更多的存储空间,但是我认为这是值得进行权衡的,因为如果一开始就从未记录过数据,则会丢失数据。
评论
据我了解,偏移量与时区不同...存储在datetimeoffset中的时间会丢失诸如夏令时意识等信息...更多信息:stackoverflow.com/tags/timezone/info
– Peter McEvoy
2015年11月10日在16:25
@PeterMcEvoy DST与此处无关。 datetimeoffset的意思是“这是发生事件时用户/计算机的本地时间”-我们不需要知道DST是否有效,因为给定的UTC偏移始终存在(嵌入datetimeoffset值内)。
–代
16年6月15日在5:27
不要将“公共API”与有关内部存储方式的决定混为一谈。公共API可以指定期望的偏移量。在内部,日期时间可以(仍然)始终存储在UTC + 0中。两全其美,恕我直言。在每个日期时间存储偏移量可能会使偏移量与日期时间分开。而且很难按时间排序。同样,您最终将转换为本地时间偏移,或从本地时间偏移转换为本地事件时间偏移。例如。在其他地方的活动。不要通过存储偏移量来使其复杂化;偏移量是动态的,取决于POV。太太你的方法也是有效的
–ToolmakerSteve
20年4月1日在1:03
#2 楼
我为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中,还是更好,类似于Oracle和MySQL中
CONVERT_TZ
函数的存在方式。#3 楼
SQL Server从2005年开始进行了修改,将内部时区保存在UTC中。这主要是由于地理复制和HA投影仪涉及到原木运输,并且原木运输时间保存在不同的时区中,使得旧方法无法恢复原木。因此,在UTC时间内部保存所有内容使SQL Server可以在全局范围内正常工作。这就是为什么夏令时在Windows中很难解决的原因之一,因为其他MS产品(例如Outlook)也在内部将日期/时间保存为UTC,并创建了需要修补的偏移量。
我在一家公司工作,那里有成千上万的服务器(虽然不是MS SQL Server,而是各种各样的服务器)分布在世界各地,如果我们不专门强制所有工作,通过UTC,我们所有人都会很快疯掉。
#4 楼
我已经在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”中找到的信息。
评论
这样的工具包似乎对开发人员有很大帮助。但是,标量函数是查询优化程序的黑匣子。这意味着当我将函数应用于列时,您提到的配置表将被击中的次数与结果集中有行的次数相同(假设我仅在SELECT子句中使用该函数)。就性能而言,这似乎不是一个很好的观点。不过,我还没有真正测试过您的工具包,因此我不能肯定地说,这只是基于我所知的普遍问题。
– Andriy M
2014年5月23日13:13
从性能角度来看,安德里(Andriy)当然是正确的。 UDF查询表并非为海量数据操作而设计,而是易于使用。但是,它们在我的机器上最多可以执行约100.000条记录。
–adss
2014年5月23日13:36
如果一个人必须在用例中处理更多记录,并且性能很重要,那么您最好考虑保留预先计算的数据。但是即使这样,这些UDF仍可能有助于将所需的时区中的数据时间值保持不变。
–adss
2014年5月23日13:44
从我的角度来看,这个问题与性能无关,而更多的是获得基本功能。能够从查询中返回准确的信息是第一件事。制定一些临时表以缓存内容,然后在查询中引用该表,但是您可以通过处理临时表来提高性能,这是次要的。
–动作Dan
17年11月8日在23:32
#5 楼
我可能会遗漏一些明显的东西,但是如果您要做的只是使用用户时区和语言格式显示日期,最简单的方法是始终将日期存储在数据库中的UTC中,并让客户端应用程序从UTC转换为本地时区并使用用户区域设置相应地格式化日期。评论
在大多数情况下,这是正确的,但请考虑夏令时。如果您在夏季记录时间,则在冬季将显示为其他时间。这是Exchange / Outlook中多年来存在的错误,困扰着客户,确切时间可能意味着赢得或丢失专利的客户。因此,如果这不是您想要的,那么您将需要知道记录此时间时的时区。理想情况下,您甚至会在记录时间的时候知道此信息,因为您可能需要有关时区的历史信息,该信息有时会改变。
– Arwin
20年5月4日,9:24
评论
在SQL 2005中有效地在UTC与本地时间(即PST)之间转换日期http://stackoverflow.com/questions/24797/effective-converting-dates-between-utc-and-local-ie-pst-time-in- sql-2005这是Web应用程序还是桌面应用程序,还是...?客户端交付机制的实现方式差异很大,因为您所需要的取决于客户端。
该网站以及本机手机。是的,这会影响不同的客户。
似乎您的问题不仅涉及数据库本身,还涉及应用程序开发。然后,这个堆栈溢出问题是您必须阅读的:夏时制和时区最佳实践