【问题标题】:Date/time conversion problem if moment is exactly at end of wintertime (non-DST)如果时刻恰好在冬季结束(非 DST),则日期/时间转换问题
【发布时间】:2011-01-23 14:58:37
【问题描述】:

在我的应用程序中,我需要使用文件中描述的模式来计算班次。 最近,我的一位客户的应用程序由于以下原因而挂起:

如果您用冬季结束时(非 DST)的确切时刻填写“struct tm”,_mktime 似乎会返回不正确的结果。

代码如下所示:

struct tm tm_start;
tm_start.tm_mday  = startday;
tm_start.tm_mon   = startmonth-1;
tm_start.tm_year  = startyear-1900;
tm_start.tm_hour  = starthour;
tm_start.tm_min   = startmin;
tm_start.tm_sec   = startsec;
tm_start.tm_isdst = -1;             // Don't know if DST is active at this moment

_int64 contTime = _mktime64(&tm_start);

假设在 4 月 5 日 2:00 从冬季时间切换到夏季时间。 在实践中,这意味着我们有以下时刻:

5 April, 1:58
5 April, 1:59
5 April, 3:00

由于我在应用程序中不知道 DST 何时开始或结束(我真的想知道吗?)我使用上面显示的代码将日期“4 月 5 日,2:00”传递给 _mktime64。

我希望 _mktime64 给我对应于 4 月 5 日 3:00 的 time_t 值(与 4 月 5 日 2:00 完全相同)。

但是,事实并非如此。 _mktime64 将 tm_start 更改为 4 月 5 日 1:00 并返回相应的 time_t 值。这意味着我得到了一个完全不同的时刻。 (实际上:2:00 到 3:00 之间的每一刻都会导致 _mktime64 返回 1:00 到 2:00 之间的一个时刻)

我认为这是 Visual Studio 2005 中的一个错误,但显然 Visual Studio 2010 (Release Candidate) 具有相同的行为。

问题出现在 XP 和 Windows7 上(未检查 Vista)。

这是一个已知的错误吗? 或者有其他解决这个问题的技巧吗?

【问题讨论】:

    标签: c++ windows datetime time dst


    【解决方案1】:

    这是使用本地时间而不是 UTC 的必然结果。 DST 的结束也很糟糕,由于本地时间匹配两次,因此在转换期间时间戳是矛盾的。

    这很容易通过使用 UTC 来解决,这也是操作系统所做的。检查您的 CRT 实现,例如 _gmtime64() 或本机 GetFileTime()。

    【讨论】:

    • 你是对的,但仅限于所有内部计算。随意命名:UTC, time_t, ... 这是用于内部计算的类型。但是,如果我的客户想表明 8:00 到 16:00 之间有一个工作时间,他的意思是本地时间,而不是 UTC。此外,如果您的应用程序必须接受日期/时间作为用户输入,则您必须处理当地时间。
    • @Patrick:如果您的所有内部时间处理都采用 UTC,我不明白您的代码会如何挂起。在用户输入时从本地时间转换为 UTC,在输出时转换回来。
    • 这有点难以解释,但它与处理“班次”有关。班次以小时为单位进行描述,不包括日期信息(例如,每天从 2:00 到 3:00 的班次)。这需要我将这个时间转换为 UTC 时刻,并且每天都这样。如果一个班次的结束落在“缺课时间”并且 CRT 返回的时间少一小时,并且班次的结束在班次开始之前下降,那么它将找到的下一个班次是完全相同的班次,并且它挂起。好的,我可以通过添加一些检查来解决问题,但是 mktime 的奇怪让我感到惊讶。
    • 尝试在 DST 生效的晚上在 Outlook 中安排会议。选择新约会,选择 1:00 作为开始。然后选择 3:00 作为结束。在下拉列表中,Outlook 2007 告诉您这是一个 2 小时的会议(而实际上,只有 1 小时)。所以即使是 Microsoft/Office 也无法完美处理。
    【解决方案2】:

    我猜它根本不知道如何正确处理您传入的无效时间。而且它对于您的时区肯定是无效的 - 您认为它是“冬天 1:59 后 1 分钟”暗示您希望它在 DST 重新运行时返回“3:00”,这听起来很合理。但是,它同样可以将其视为“夏季 3:00 之前的一个小时”,这意味着它必须来自冬季,因此返回“1:00”。因此,我不认为这是一个错误——它更有可能是关于如何处理不存在的时间的未定义行为。

    我希望最安全的解决方法是始终使用 UTC,因为这样就不会有歧义了。显然,这可能无法清晰地映射到您的应用程序域,但这就是将时间往后推的阴暗世界!

    【讨论】:

    • +1 表示认为 2:00 比 3:00 早 1 小时的原始观点。实际上,这会产生 1:00。仍然是一个奇怪的结果,但至少这是一个原始的观点。
    【解决方案3】:

    尝试将 5 April, 2:30 转换为 time_t 类似于尝试将 2011 年 2 月 29 日转换为 time_t。在这两种情况下,您都不期望得到有效结果,因为输入不是有效日期。 4 月 5 日,甚至 2:00 都不存在。 1:59.59 之后直接是 3:00:00

    【讨论】:

    • 我已经想通了。问题是如何检查这一点。奇怪的是,mktime 足够聪明,可以将 2 月 29 日转换为 3 月 1 日,甚至 2 月 32 日转换为 3 月 4 日(我认为这甚至可以保证),但不是 2:00 到 3:00。为什么?
    • 我刚刚阅读了 POSIX 描述 (opengroup.org/onlinepubs/9699919799/functions/mktime.html)。它确实允许超出“正常”范围的值,但不指定结果。这是一个相当差的规格,是的。它还指出 tm_yday 被忽略,然后继续描述它的实际计算方式 - 使用 tm_yday 并忽略 tm_montm_mday 等!
    猜你喜欢
    • 1970-01-01
    • 2016-01-31
    • 2021-03-27
    • 1970-01-01
    • 2015-09-03
    • 2010-12-18
    • 2020-02-20
    • 2013-09-29
    • 2016-11-20
    相关资源
    最近更新 更多