【问题标题】:Is there a way to determine if a date/time does not exist?有没有办法确定日期/时间是否不存在?
【发布时间】:2016-07-29 08:34:43
【问题描述】:

有趣的事实是,我相信我们中的大多数玩时间领域的人都知道 - 有些日期/时间可能看起来有效但实际上并不存在,例如夏令时切换时间凌晨 2:30。

在 C++(标准或 Windows)中是否有办法确定给定日期/时间在给定时区规范中是否有效?

【问题讨论】:

  • 一个有趣的问题,当然。您是否还需要支持闰第二天,例如 2015 年 6 月 30 日、23.59.60? (如果这样排除了 Google 的时间 API)我认为提升日期时间库将是最好的起点。我使用这些库,但不是为了这个特定的东西。
  • 我已经标记了“boost-date-time”,也许是调皮地标记了“boost”,因为我认为解决方案可能是开箱即用的,或者不是特别难以实施那个框架。
  • 标准 C++ 库无法处理时间。如果您以 Windows 为目标,则可以使用 SystemTimeToFileTime() 或 SystemTimeToTzSpecificLocalTime()。传递错误的日期会使函数失败并显示 ERROR_INVALID_PARAMETER。如果它必须是跨平台的,那么你必须去购买图书馆。
  • C++ 标准库故意不合适。有太多的边缘情况意味着通用的通用日期时间库无法充分标准化。我遇到的最接近的(增强)依赖于配置文件。看看 Java 用那些简单的 java.util.Datejava.util.Calendar 东西把自己弄得一团糟。
  • 也许 Howard Hinnant 的 library 是另一种选择? (cppcon talk)

标签: c++ windows boost time boost-date-time


【解决方案1】:

使用特定于 Windows 的函数,您可以使 a call to TzSpecificLocalTimeToSystemTime() 后跟 a call to SystemTimeToTzSpecificLocalTime() 与相关时区。

完成后,如果两者不同,则您的时间无效,因为 TzSpecificLocalTimeToSystemTime 会将无效时间转换为“实时”时间。

bool IsValidTime(TIME_ZONE_INFORMATION tz, SYSTEMTIME st)
{
    SYSTEMTIME utcSystemTime, st2;
    TzSpecificLocalTimeToSystemTime(&tz, &st, &utcSystemTime);
    SystemTimeToTzSpecificLocalTime(&tz, &utcSystemTime, &st2);

    return (st != st2);
}

【讨论】:

    【解决方案2】:

    使用这个free, open-source library

    #include "tz.h"
    #include <iostream>
    
    int
    main()
    {
        using namespace date;
        using namespace std::chrono_literals;
        try
        {
            auto zone = locate_zone("America/New_York");
            zone->to_sys(local_days{mar/13/2016} + 2h + 30min);
        }
        catch (const std::exception& e)
        {
            std::cout << e.what() << '\n';
        }
    }
    

    这个程序的输出是:

    2016-03-13 02:30 is in a gap between
    2016-03-13 02:00:00 EST and
    2016-03-13 03:00:00 EDT which are both equivalent to
    2016-03-13 07:00:00 UTC
    

    简而言之,该程序尝试使用时区“America/New_York”将 2016-03-13 02:30:00 的区域设置日期/时间转换为 UTC。由于本地时间不存在,翻译会引发异常。如果本地时间不明确,例如将本地时钟从夏令时设置回来时,也会引发异常(带有不同的错误消息)。

    如果需要,该库还提供了避免这些异常的语法。

    这适用于 VS-2013、VS-2015、clang 和 gcc。它需要 C++11、C++14 或 C++1z。

    上面的链接指向文档。这是github存储库:

    https://github.com/HowardHinnant/date

    【讨论】:

      【解决方案3】:

      time_t 格式是一个整数值(自 1970 年 1 月 1 日 00:00 UTC 以来的秒数),因此 time_t 的每个可能值都存在(当然在使用的整数类型的限制范围内)。

      另一方面,并​​非所有可能的“struct tm”值(您有代表日、月、年、小时、分钟和秒的成员)都存在(这可能就是您的意思)。要检查“struct tm”是否存在,您必须执行以下操作:

      • 将成员 tm_isdst 设置为 -1。这表明你不知道它是否是夏令时。
        • 使用 mktime() 将“struct tm”转换为 time_t。
        • 使用 localtime() 将 time_t 转换回“struct tm”。
        • 将生成的“struct tm”与初始的“struct tm”进行比较,忽略 tm_isdst 字段(或使用它来查看您是否处于 DST 中)。

      【讨论】:

      • 返回正常时间时,如果您在 02:30 提供值,mktime 会做什么?这里的问题是日历时间将重复 02:xx 小时两次(一次使用 dst,一次没有)。如果您不知道您的意思是计算机也可能无法告诉您...
      • 该标准不要求 time_t 是整数,但除此之外,该标准似乎暗示您的解决方案可以工作。如果它真的能在实现上起作用是另一个问题......
      • @Skyking:你是对的(部分)。当时钟拨回一小时(DST --> non-DST),并且您没有指定您是否处于 DST 时,CRT 会猜测,但至少我希望它会返回 2: 30 DST,或 2:30 非 DST。无论如何,测试它的最佳方法是尝试它。结果可能确实取决于 CRT 的实现。
      猜你喜欢
      • 2012-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-13
      • 1970-01-01
      相关资源
      最近更新 更多