C# 存储用户时区首选项

C# 存储用户时区首选项,c#,.net,timezone,C#,.net,Timezone,我在这里的要求相当标准;我需要允许用户选择他们的当前时区,并根据他们的帐户保存它。然后,我将使用此值将存储的DateTime值转换为本地化时间 我目前的想法是,我将允许用户从列表中选择.NETTimeZoneInfo.Id(保存在数据库中以允许更友好的描述,但使用TimeZoneInfo.GetSystemTimeZones()构建)-然后,我将使用此值使用TimeZoneInfo.FindSystemTimeZoneById()返回相关的TimeZoneInfo实例并执行转换。我在这里看到的主

我在这里的要求相当标准;我需要允许用户选择他们的当前时区,并根据他们的帐户保存它。然后,我将使用此值将存储的
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 Update保持更新。一些信息来自注册表,但本地化的资源字符串来自
tzres.dll
。当然,所有这些都被
TimeZoneInfo
和相关类隐藏起来

但是,如果要将这些时区
Id
值传递给其他系统,请小心。Windows的早期版本有一些变化。例如,我知道显示名称过去都是“GMT”,现在更准确地说是“UTC”。
Id
值是相同的,但谁知道还有什么不一致呢。特别是如果目标计算机没有收到与您相同的Windows更新集。顺便说一下,更新已经公布了

您还应该了解有关Windows时区数据库的一些信息:

  • 它不能表示每个日历年超过两个DST转换。例如-。微软发布了,但这只是一个折衷的修正,而不是一个历史上准确的修正。还有其他一些区域发生了类似这样的历史性变化,无法正确地表示出来

  • 在Windows XP/2003和Vista/2008之间,有些事情发生了变化。关于这方面有一些非常好的信息,以及关于如何使用注册表的很多细节

  • 微软是唯一拥有自己时区数据库的主要参与者。世界其他地区都在使用这一技术。这会导致互操作性问题。我在这本书里写了一篇不错的文章

您还应该知道
TimeZoneInfo
DateTime
DateTimeOffset
类有着千丝万缕的联系。他们有自己的一套怪癖
DateTimeOffset
在某种程度上是可用的,但是
DateTime
充满了细微差别。阅读:

  • 乔恩·斯凯特
  • 马特·约翰逊(我)
对于.Net中的日期和时间,更好的解决方案是使用。该库实现两个时区数据库,包括它们之间的CLDR映射。它还提供了一个更安全的API,不会让您陷入麻烦。这可能需要一些重新学习,但在您了解它之前,您将使用诸如
LocalDateTime
ZonedDateTime
OffsetDateTime
Instant
之类的类

你仍然有一个问题,那就是时区数据库经常更新,因为我们把计时规则留给了政客们。但在保持数据库历史准确性方面做得相当好,并且在区域名称更改时提供别名/链接。您可以看到tz名称的列表


此外,如果使用NodaTime,您可以选择使用编译到发行版中的TZDB副本,也可以随应用程序提供自己的副本。您可以(理论上)编写自己的代码,从IANA中下载最新版本并保持应用程序的更新—所有这些都不依赖主机操作系统。

您看过吗?或者?相关:@Oded DateTimeOffset的问题在于它不是日光节约aware@Blam-是的,我很清楚这一点。这就是为什么我还建议使用
NodaTime
。如果您想要本地时区,为什么不直接使用TimeZoneInfo.local.Id?如果您想从列表中进行选择,那么可以使用TimeZoneInfo.GetSystemTimeZones()。整个TimeZoneInfo.ID都没有意义。ID应该是遵循某种ISO标准方式的标识符。但是,我刚刚发现,
FindSystemTimeZoneById
在不同版本的Windows中无法正常工作。在一个版本中,返回的字符串都是小写,另一个是驼峰大小写,更糟糕的是,FindSystemTimeZoneById不区分大小写。算了吧@拉吉夫-时区没有ISO标准。IANA/Olson标识符(如上所述)是与