从 UTC 转换为本地时,夏令时在 TimeZoneInfo 中不起作用
Posted
技术标签:
【中文标题】从 UTC 转换为本地时,夏令时在 TimeZoneInfo 中不起作用【英文标题】:Daylight Saving Time not working in TimeZoneInfo, when converting from UTC to Local 【发布时间】:2015-10-15 01:40:56 【问题描述】:我正在开发一个支持时区的 ASP.NET/C# 应用程序。
首先让我解释一下应用程序的流程,我正在为采购订单存储一个对象。所以它里面有datetime
字段。
我将 datetime
作为 UTC 存储在数据库中,并将其绑定到网格中(根据客户的时区)。
在第一页的 Page_Init
方法中,我使用了一个 javascript 代码,它将检测客户端的时区,并返回适当的偏移量。
在Page_Load
方法中,我获取了该javascript 返回值(偏移量)并将其与TimeZoneInfo.GetSystemTimeZones()
中每个区域的偏移量进行比较。
在比较偏移量时,我得到一个 TimeZoneInfo
对象(例如)“(UTC-08:00) Baja California”、“(UTC +05:30) Chennai,Kolkata”
使用特定的 TimeZoneInfo
对象,我将 UTC 日期时间(存储在数据库中)转换为客户端的时区。
按照上述流程,应用程序在一些时区运行良好。
问题是,如果我将客户端计算机的时区更改为 (UTC -8:00),客户端计算机将时区名称显示为“(UTC-08:00) 太平洋时间(美国和加拿大)”,但在应用程序的时区与显示为“(UTC-08:00) Baja California”的客户端系统不同。
重要的是,当我将 UTC 转换为本地时,DST 更改并没有反映出来。 TimeZoneInfo.ConvertTimeFromUtc(DateTime, clientTimezone);
注意:我没有将客户端的时区信息存储在数据库或任何地方。所以每次用户进入应用程序时,应用程序都会识别时区并根据时区做出反应。
我的问题是:
当我们从UTC转换为Local时,TimeZoneInfo类是否可以根据调整规则自动工作?
我们是否必须使用 TimeZoneInfoObject.IsDaylightSavingTime(DateTime)
方法检测特定日期时间的 DST 并进行转换?
.Net 中是否还有其他可以与 Windows 时区同步的类?
【问题讨论】:
基本上,从 UTC 获取当前偏移量不足以确定时区。请参阅***.com/questions/6939685/… 和类似问题。您的标题声称 TimeZoneInfo 不起作用,我在问题中没有看到任何迹象 - 在我看来,您只是没有正确的时区。 (顺便说一句,我已将您的问题重新格式化为 很多 更易于阅读。到处都加粗体确实不利于可读性。) 旁注:您真的需要 .NET 代码中的客户端本地时间吗?如果您始终向 Javascript 提供 UTC 值,则可以转换为本地时间,因此您实际上不需要检测时区。 JS 可以将其转换为本地,我使用选择器输入日期时间,所以我想将选择器设置为客户端当前时间,并在客户端提交点击后。我想将它作为 UTC 传递给它。那我该怎么做呢? @JonSkeet 请仔细阅读我之前的评论 - 它解释了您应该从时区检测系统(不仅仅是偏移量)获得什么以及如何处理它。 【参考方案1】:你应该明白的几件事:
时区与时区偏移不同。不能只取数字 -8 并假设时区应该是太平洋时间。
偏移量可以在单个时区内更改。例如,太平洋时间通常使用 -8,但在夏令时生效时切换为 -7。
TimeZoneInfo
的DisplayName
属性中的偏移量只是标准 偏移量。它们与 BaseOffset
属性匹配。它们不会更改以反映 当前 偏移量。
JavaScript 中的时区检测不完善。只有三种方法:
使用Date
类的getTimezoneOffset
函数,它应该返回调用它的日期的偏移量。例如new Date().getTimezoneOffset()
为您提供当前 偏移量。使用这种方法,您还应该注意,a bug in the ES5 spec 可能会导致在较早的日期调用时有时会返回错误的偏移量。
使用诸如 jsTimezoneDetect 之类的库,它会多次调用 getTimezoneOffset
以尝试猜测 IANA 时区标识符。当向用户呈现时区列表时,该猜测适用于设置默认时区。这只是一个猜测,可能是错误的。如果您想在.NET 的后端使用它,您需要Noda Time,因为TimeZoneInfo
目前不支持 IANA 时区。 (如果需要,您可以选择 convert 到 Windows 时区)。
一些较新的浏览器支持ECMAScript Internationalization API,它有一个可选实现的函数来返回时区。它可能在某些浏览器中工作,但不能保证在任何地方都返回有效结果。
Intl.DateTimeFormat().resolvedOptions().timeZone
同样,您需要在后端使用 Noda Time。
你说:
问题是,如果我将客户端计算机的时区更改为 (UTC -8:00),客户端计算机将时区名称显示为“(UTC-08:00) 太平洋时间(美国和加拿大)”,但在应用程序的时区与显示为“(UTC-08:00) Baja California”的客户端系统不同。
这可能与您在应用程序代码中选择时区的方式有关。在我看来,您正在扫描服务器时区列表并选择第一个符合某些条件的时区。由于这两个时区具有相同的基本偏移量,因此您可能只是选择了错误的时区,无论如何您都不应该这样做。但是由于您没有显示您的代码的那部分,所以我无法帮助您。
回答您的具体问题:
当我们从UTC转换为Local时,TimeZoneInfo类是否可以根据调整规则自动工作?
是的,可以。 TimeZoneInfo
没有任何问题,关键在于你如何使用它。您可能选择了错误的时区。
我们是否必须使用
TimeZoneInfoObject.IsDaylightSavingTime(DateTime)
方法检测特定日期时间的 DST 并进行转换?
不,您不应该仅仅为了从 UTC 转换到特定时区而这样做。 ConvertTimeFromUtc
函数将为您处理。
.Net 中是否还有其他可以与 Windows 时区同步的类?
TimeZoneInfo
是 .NET Framework 中唯一内置的。 Noda Time 是一个很好的替代方案,可以使用 Windows 时区或 IANA 时区。
最后,我将重申 Jon 在 cmets 中所说的话。如果您所做的只是向最终用户显示特定的时间点,那么请完全忘记时区检测或使用服务器上的本地时间。只需将 UTC 时间发送到客户端,然后使用 JavaScript Date
对象上的 UTC 函数,或使用像 moment.js 这样的库。两者都可以在 UTC 和本地工作,并且可以在它们之间转换。例如(使用 moment.js):
var valueFromServer = "2015-07-26T12:00:00Z"; // the Z means UTC
var localTime = moment(valueFromServer).format(); // "2015-07-26T05:00:00-07:00" (Pacific)
【讨论】:
以上是关于从 UTC 转换为本地时,夏令时在 TimeZoneInfo 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章
将 UTC 转换为 EST 不适用于 Android 上的夏令时