我在这里的要求相当标准;我需要允许用户选择他们当前的时区并将其保存在他们的帐户中。然后,我将使用此值将存储的DateTime
值转换为本地化时间。
我目前的想法是,我将允许用户从列表中选择一个 .NET TimeZoneInfo.Id
(保存在数据库中以允许更友好的描述,但使用 TimeZoneInfo.GetSystemTimeZones()
构造( - 然后我将使用此值使用 TimeZoneInfo.FindSystemTimeZoneById()
返回相关的TimeZoneInfo
实例并执行转换。我在这里看到的主要问题是TimeZoneInfo.Id
是注册表中保存的值,而不是"标准"ID(例如与Olson相比(。因此,服务器更新/迁移可能会使存储的 ID 完全无效并中断转换。
简而言之 - 这种方法有效/安全吗?如果没有,是否有更好的方法来存储用户时区首选项,同时处理夏令时等,而无需大量额外的逻辑?
我刚刚发现了这个古老的未回答的问题,所以我想我应该试一试。
简而言之 - 这种方法有效/安全吗?
这完全取决于您打算如何处理它们。
如果您只是在服务器端代码中使用它,那么是的。 您只需存储时区的Id
,并向用户显示相应的DisplayName
。 FindSystemTimeZoneById
和GetSystemTimeZones
对此完全有效。
请记住,虽然Id
值始终相同,但DisplayName
属性将根据运行代码的 Windows 操作系统的语言而有所不同。 实现无法识别区域性,因此仅在 .Net 中设置不同的目标区域性不会更改DisplayName
字符串。
Microsoft Windows 时区数据库中的记录相当稳定,并通过 Windows 更新保持更新。 某些信息来自注册表,但本地化的资源字符串来自tzres.dll
。 当然,所有这些都是通过TimeZoneInfo
和相关类隐藏的。
但是,如果要将这些时区Id
值传递给其他系统,请注意。 以前版本的 Windows 有一些变化。 例如,我知道过去显示名称中都说"GMT",现在更准确地说"UTC"。 Id
值是相同的,但谁知道还有什么不一致。 特别是如果目标计算机未收到与你相同的一组 Windows 更新。 顺便说一下 - 更新在这里宣布。
您还应该了解有关 Windows 时区数据库的一些事项:
-
它无法表示每个日历年两次以上的 DST 转换。 例如 - 2010 年,埃及开罗有四个过渡。 Microsoft发布了修补程序,但这只进行了折衷更正,而不是历史上准确的更正。 还有其他区域具有这样的历史变化,无法正确表示。
-
在Windows XP/2003和Vista/2008之间发生了一些变化。 这里有一些非常好的信息,以及有关如何使用注册表的大量详细信息。
-
Microsoft是唯一拥有自己的时区数据库的主要参与者。 世界其他地区使用 IANA/Olson 数据库。 这会导致互操作性问题。 我在时区标签维基中有一篇不错的文章。
您还应该知道,TimeZoneInfo
与DateTime
和DateTimeOffset
类密不可分。 这些都有自己的一套怪癖。 DateTimeOffset
有些可用,但DateTime
充满了细微差别。 读:
- DateTime到底有什么问题?作者:Jon Skeet
- The Case Against DateTime.Now by Matt Johnson (me(
在.Net中,日期和时间更好的解决方案是使用NodaTime。 此库实现两个时区数据库,包括它们之间的 CLDR 映射。 它还提供了一个更安全的 API,不会让您遇到麻烦。 这可能需要一些重新学习,但在您知道之前,您将使用LocalDateTime
,ZonedDateTime
,OffsetDateTime
和Instant
等类。
您仍然会遇到时区数据库经常更新的问题,因为我们将计时规则留给政治家。 但是 TZDB 人员在保持数据库历史准确性方面做得很好,并在区域名称更改时提供别名/链接。 您可以在此处查看 tz 名称的列表。
此外,如果您使用 NodaTime,您可以选择坚持使用编译到发行版中的 TZDB 副本,或者您可以将自己的副本与您的应用程序一起发布。 您可以(理论上(编写自己的代码来从 IANA 中提取最新版本并保持应用程序更新 - 所有这些都无需依赖主机操作系统。