所以我做一些像:
// Convert user's datetime to UTC var userEnteredDateTime = DateTime.Parse("11/11/2014 9:00:00"); // TimeZoneInfo id will be stored along with the UTC datetime var tz = TimeZoneInfo.FindSystemTimeZoneById("FLE Standard Time"); // following produces: 9/11/2014 7:00:00 AM (winter time - 1h back) var utcDateTime = TimeZoneInfo.ConvertTimeToUtc(userEnteredDateTime,tz);
并保存记录.让我们假设用户在8月1日这样做,而他的时区偏离UTC仍然是03:00,但是未来上市的保存日期是正确的02:00值,因为转换考虑了“冬天”时间那个时期.
问题是,如果我将尝试在11/11/2014转换该产品的“从”到“产品的当地时区”,如果例如由于一些新的规则向冬季转变,我将获得什么日期时间值被遗弃了,所以时区还是03:00而不是02:00?
// Convert back var userLocalTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime,tz);
我会得到10AM或9AM,因为OS / .NET补丁会处理这个?
谢谢!
P.S .: TimeZoneInfo有ToSerializedString()方法,如果我相当存储这个值而不是时区id,这将保证通过UTC datetime serialized timezoneinfo我将永远能够转换到用户的原始datetime输入?
解决方法
这说明了建议“永远存储UTC”的缺陷之一.当你处理未来的事件时,它并不总是奏效.问题在于政府经常改变时空的想法.有时他们给予合理的通知(例如美国,2007年),但有时候他们不会(例如埃及,2014年).
当您将本地时间的原始转换作为UTC时,您有意决定相信时区规则不会改变.换句话说,您决定将事件根据当时知道的时区规则将通用时间轴分配给通用时间表.
避免这种情况的方法很简单:未来事件应该在当地时间安排.现在,我不是说“本地到你的电脑”,而是“本地的用户”,所以你需要知道用户的时区,你也应该把时区的ID存储在某个地方.
如果事件属于daylight saving time的春季或后期转换,您还需要决定要做什么.这对于重现模式尤为重要.
最终,您需要确定何时运行该事件.或者在您的情况下,您需要确定事件是否已通过.有几种不同的方法可以实现这一点:
选项1
>您可以计算每个本地时间的相应UTC值,并将其保存在单独的字段中.
>在某些周期(每天,每周等),您可以从当地值重新计算即将到来的UTC值,以及当前对时区规则的理解.或者,如果您手动应用时区更新,则可以选择重新计算当时的所有内容.
选项2
>您可以将值存储为DateTimeOffset类型而不是DateTime.它将包含原始的当地时间,以及根据您在进入时知道的时区规则计算的偏移量.
> DateTimeOffset值很容易被强制回到UTC,所以它们往往很好地为此工作.您可以在DateTime vs DateTimeOffset阅读更多.
>就像在选项1中一样,您可以定期或时区数据更新后重新访问值,并调整偏移量以与新时区数据对齐.
>这是我通常推荐的,特别是如果您使用的数据库支持DateTimeOffset类型,如sql Server或RavenDB.
选项3
>您可以将值存储为本地DateTime.
>查询时,您将计算目标时区中的当前时间,并与该值进行比较.
DateTime now = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow,targetTZ); bool passed = now >= eventTime;
>此选项的缺点在于,如果您在许多不同的时区有事件,则可能需要进行许多查询.
>您也可能会遇到接近落后的DST转换的值的问题,所以如果您使用这种方法,请小心.
我建议不要把时区序列化.如果时区发生变化,那么它已经改变了.假装它不是一个很好的解决方法.