我的要求是相当标准的;我需要允许用户选择他们当前的TimeZone并将其保存在他们的帐户中。然后,我将使用此值将存储的DateTime
值转换为本地化时间。
我目前的想法是,我将允许用户从列表中选择一个.NET TimeZoneInfo.Id
(在数据库中保存以允许更友好的描述,但使用TimeZoneInfo.GetSystemTimeZones()
构建) - 我将使用此使用TimeZoneInfo
返回相关TimeZoneInfo.FindSystemTimeZoneById()
实例的值并执行转换。我在这里看到的主要问题是TimeZoneInfo.Id
是注册表中保存的值,而不是“标准”Id(例如与Olson相比)。因此,服务器更新/迁移可能会使存储的ID完全无效并破坏转换..
简而言之 - 这种方法有效/安全吗?如果没有,是否有更好的方法来存储用户时区偏好,同时还可以处理夏令时等而无需额外的逻辑?
答案 0 :(得分:8)
我刚刚发现了这个老问题尚未解决的问题,所以我认为我应该抓住这个问题。
简而言之 - 这种方法有效/安全吗?
这一切都取决于你打算用它们做什么。
如果您只是在服务器端代码中使用它,那么是的。您只需存储时区的Id
,并向用户显示相应的DisplayName
。 FindSystemTimeZoneById
和GetSystemTimeZones
对此完全有效。
请注意,虽然Id
值始终相同,但DisplayName
属性将根据您运行代码的Windows操作系统的语言而有所不同。实现不文化意识,因此只在.Net中设置不同的目标文化不会更改DisplayName
字符串。
Microsoft Windows时区数据库中的记录相当稳定,并通过Windows Update保持更新。 某些信息来自注册表,但本地化资源字符串来自tzres.dll
。当然,所有这些都是TimeZoneInfo
和相关类隐藏的。
但是,如果您将这些时区Id
值传递给其他系统,请注意。 Windows的早期版本有一些变化。例如,我知道所有显示名称都使用“GMT”,现在更准确地说“UTC”。 Id
值是相同的,但是谁知道其他什么不一致。特别是如果目标计算机没有收到您拥有的同一组Windows更新。顺便说一句 - 更新公布here。
您还应该了解有关Windows时区数据库的一些信息:
每个日历年无法代表两个以上的DST转换。例如 - in 2010, Cairo, Egypt had four transitions。微软发布了a hotfix,但这只做了妥协修正,而不是历史上准确的修正。还有其他区域具有此类历史更改,无法正确表示。
Windows XP / 2003和Vista / 2008之间发生了一些变化。有关此here的一些非常好的信息,以及有关如何使用注册表的大量详细信息。
Microsoft是唯一的主要播放器,拥有自己的时区数据库。世界其他地方使用IANA/Olson database。这导致了互操作性问题。我在the timezone tag wiki中对此进行了很好的阐述。
您还应该知道TimeZoneInfo
与DateTime
和DateTimeOffset
类密不可分。这些都有各自的怪癖。 DateTimeOffset
有点可用,但DateTime
充满了细微差别。读:
<。>远更好的.Net日期和时间解决方案是使用NodaTime。该库实现了两个时区数据库,包括它们之间的CLDR映射。它还提供了更安全的API,不会让您遇到麻烦。这可能需要一些重新学习,但在您知道之前,您将使用LocalDateTime
,ZonedDateTime
,OffsetDateTime
和Instant
等类。
您仍然遇到时区数据库频繁更新的问题,因为我们将计时规则留给了政治家。但是the TZDB folks可以很好地保持数据库的历史记录准确性,并在区域名称更改时提供别名/链接。您可以看到tz名称here的列表。
此外,如果您使用NodaTime,您可以选择坚持编译到发行版中的TZDB副本,也可以随应用程序一起发送您自己的副本。您可以(理论上)编写自己的代码以从IANA下载最新版本并保持应用程序更新 - 所有这些都不依赖于主机操作系统。