首页 文章

如果时区规则发生变化,UTC将转换为以前保存的日期时间的本地时间转换

提问于
浏览
10

我正在db中存储产品 . 所有日期(sql server datetime)都是UTC,以及我存储该产品的时区ID的日期 . 用户在列表中输入产品“from”和“until”的日期 . 所以我做了类似的事情:

// 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值,因为转换考虑了“冬天”的时间那个时期 .

问题是如果我试图在2014年11月11日将该产品的“从”和“直到”日期转换为产品的当地时区,那么我将得到什么日期时间值,例如,由于一些新规则过渡到冬季时间被遗弃,因此时区仍然是03:00而不是02:00?

// Convert back
var userLocalTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, tz);

我会得到10AM或正确的9AM,因为OS / .NET补丁会处理这个吗?

谢谢!

P.S . :TimeZoneInfo有ToSerializedString()方法,如果我宁愿存储这个值而不是时区id,这会保证通过UTC datetime序列化的timezoneinfo我总是可以转换为用户的原始日期时间输入吗?

1 回答

  • 5

    在您描述的场景中,您将在上午10:00到达 . 时区转换功能不会知道该值最初是在上午9:00输入的,因为您只保存了UTC时间7:00 AM .

    这说明了“始终存储UTC”的建议存在缺陷的情况之一 . 当您处理未来事件时,它并不总是有效 . 问题是政府经常改变他们对时区的看法 . 有时候他们给出了合理的通知(例如美国,2007年),但有时却没有(例如埃及,2014年) .

    当您从本地时间到UTC进行原始转换时,您有意决定相信时区规则不会更改 . 换句话说,您决定仅根据您当时所知的时区规则将事件分配到通用时间轴 .

    避免这种情况的方法很简单: Future events should be scheduled in local time . 现在,我没有't mean 1176986 , but rather 1176987 , so you will need to know the user'的时区,你还应该在某个地方存储时区的ID .

    如果事件属于daylight saving time的前进或后退过渡,您还需要决定要做什么 . 这对于复发模式尤为重要 .

    但最终,您需要确定何时运行该事件 . 或者在您的情况下,您需要确定事件是否已通过 . 有几种不同的方法可以实现这一目标:

    Option 1

    • 您可以为每个本地时间计算相应的UTC值,并将其保存在单独的字段中 .

    • 在某个周期(每天,每周等),您可以从其本地值和您当前对时区规则的理解重新计算即将到来的UTC值 . 或者,如果您手动应用时区更新,则可以选择重新计算当时的所有内容 .

    Option 2

    • 您可以将值存储为 DateTimeOffset 类型而不是 DateTime . 它将包含原始本地时间,以及您在输入时根据时区规则计算的偏移量 .

    • DateTimeOffset 值很容易被强制转换回UTC,因此它们往往能够很好地工作 . 您可以在DateTime vs DateTimeOffset中阅读更多内容 .

    • 就像在选项1中一样,您可以定期或在时区数据更新后重新访问这些值,并调整偏移量以与新的时区数据对齐 .

    • 这是我通常建议的,特别是如果您使用的是支持 DateTimeOffset 类型的数据库,例如SQL Server或RavenDB .

    Option 3

    • 您可以将值存储为本地 DateTime .

    • 查询时,您将计算目标时区的当前时间并与该值进行比较 .

    DateTime now = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, targetTZ);
    bool passed = now >= eventTime;
    
    • 此选项的缺点是,如果您有许多不同时区的事件,则可能需要进行大量查询 .

    • 您可能还会遇到接近DST转换后退的值的问题,因此如果您使用此方法,请务必小心 .

    我建议反对序列化时区本身的想法 . 如果时区已更改,则表示已更改 . 假装它不是一个好的解决方法 .

相关问题