接近夏令时的日期时间转换?

5
我正在开发一个软件,用于为全年无休运行的GPS设备生成报告。报告输出的一部分需要将我们存储的数据库时间(保存在中央标准时间)转换为用户所需的时区。每年两次夏令时切换时,当人们运行在时刻变更之前开始并在之后结束的报告时,会遇到问题。它会在一行代码上失败:
return TimeZoneInfo.ConvertTime(dateToConvert, DatabaseTime, UserTime);
dateToConvert是待转换的DateTime对象。 DatabaseTimeUserTime都是TimeZoneInfo对象。在处理靠近夏令时转换时间的DateTimes时,不要尝试任何巧妙或复杂的操作,否则会出现异常,例如3/10/2013 2:02:11 AM, 尽管它从中央时间转换到中央时间。

如何最好地处理靠近夏令时变更的DateTimes?


3
也许这需要进行一次大修,但最好将所有的日期/时间存储为格林尼治标准时间(因为中部标准时间经历了夏令时的变化,导致其度量存在间断性,而在格林尼治标准时间中则不存在这种情况),仅在显示时转换(以及可能转换输入参数)。 - twalberg
如果我从头开始,我肯定会这样做。我喜欢数据尽可能中立的想法,而我认为这是实现的方法。 - Corey Ogburn
请确认一下:如果时间是在跳过的那个小时内,您的数据库是否可以存储“2013年3月10日2:02:11 AM”? - Joel Rondeau
@JoelRondeau 这是一个问题,我一定要问问我的同事,他曾经在系统的那个部分工作过。那个 DateTime 实际上在我们的系统中,我只需要弄清楚为什么它会出现。 - Corey Ogburn
4个回答

4

您的数据库中存在垃圾数据,2013年3月10日上午2:02:11从未存在过。那天早上1:59后的一分钟是3:00 AM,时钟被调整了一个小时。.NET不会容忍这种错误日期。

您需要找出如何将这些垃圾时间戳放入数据库。显然,从一个时区到另一个时区的转换不考虑夏令时规则,如在一个时区中活动但在另一个时区中不活动,是该垃圾数据的高度可能来源。如果您无法修复数据库以使用UTC,则至少可以在代码中执行此操作。首先在一个时区中转换为UTC,然后在另一个时区中转换回本地时间。使用TimeZoneInfo类,ConvertTimeFrom/ToUtc方法。


将 GPS 数据输入数据库的后端代码全部由 VB6 编写(我讨厌它,迫不及待地希望我们能够推出 C# 更改)。我在想,也许我们的 VB6 在计算过程中没有足够关注 DST 的问题。 - Corey Ogburn
是的,VB6后端能够正确地完成这项任务的可能性非常之小。 - Hans Passant

3

我遇到了这个问题。我通过添加一个新的GMT时间列来解决它。这使应用程序能够使用原始数据以及任何对GMT的修复操作。然后,我更改了应用程序,以便在夏令时出现问题时,访问此新列的任何代码都会访问此新列。随着时间的推移,我将用于计算的任何代码重新指向此新列,而留下显示与旧列配合使用。这不是优雅的解决方案,但它有效且易于实现。


2

转换应该正常工作,因为时间并不是真正的垃圾,正如汉斯所说,它只是未经调整的(这是我刚刚发明的一个术语)。3/10/2013 2:02:11 AM CDT == 3/10/2013 8:02:11 AM UTC == 3/10/2013 3:02:11 AM CDT...它们都是语义上等价的。如果你不相信我,请在timeanddate.com上进行转换,并查看它们是否相等(尽管对于他们的计算器来说,要四舍五入到最近的5分钟)。至于.NET代码是否允许这种语义等价性,我没有尝试过,因为我目前不在我的开发机前。

更新#1:

在设置为CST时区的计算机上运行以下代码:

using System;

namespace TimeZoneSample
{
    public static class Program
    {
        public static void Main()
        {
            DateTime t = DateTime.Parse("3/10/2013 2:02:11 AM");
            Console.WriteLine(t);
            Console.WriteLine(t.ToUniversalTime());
            Console.WriteLine(t.ToUniversalTime().ToLocalTime());
        }
    }
}

这将产生以下控制台输出:
3/10/2013 2:02:11 AM
3/10/2013 8:02:11 AM
3/10/2013 3:02:11 AM

证明我的原始解释是正确的。quod erat demonstrandum

我完全同意你的观点,但是 .Net 的 DateTime 似乎不能这样工作。 - Corey Ogburn
根据您的编辑,当我暂停调试并将其插入监视列表时,它可以工作,但是当代码实际尝试执行 return TimeZoneInfo.ConvertTime(dateToConvert.ToUniversalTime().ToLocalTime(), DatabaseTime, UserTime); 时,我会收到一个错误,指出 DatabaseTime 需要是 TimeZoneInfo.Local。我不知道在我们的系统中是否可以做出这种可接受的假设。 - Corey Ogburn
我和我的团队交流了一下,结果发现这是一个足够安全的假设,因为我们托管自己的服务器。 - Corey Ogburn

1
如果可能的话,我会遵循其他答案 - 您希望正确修复此问题。如果在秋季转换期间您的时间不正确,则不会生成异常,它只会随机偏差一个小时。
有一个解决当前困境的方法。由于只有在春天缺少一小时才会导致异常,因此您可以捕获异常并在重复转换之前将一个小时添加到时间中。

我已经花了半个小时来尝试快速修复这个问题。 - Corey Ogburn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接