c++ - 使用 C++ 的日期库读取时间

标签 c++ date

我正在尝试使用 Howard Hinnant 的日期库 ( https://github.com/HowardHinnant/date ) 将用户输入读入日期时间对象。我想使用这个库,因为它是现代的,并且将被包含在 C++20 中。
该程序应该能够接受 ISO 8601 格式的日期时间( YYYY-MM-DDTHH:MM:SS+HHMM 等,例如 2020-07-07T18:30+0100 )以及形式为 DD-MM HH:MM 的简单日期时间或 HH:MM .在第二种情况下,应该假设缺失的信息要用当前日期/年份来填充(时区稍后处理)。
这是我这样做的尝试。

using namespace date;


int main(int argc, char ** argv)
{
    std::istringstream ss { argv[1] };
    std::chrono::system_clock::time_point dt 
        { std::chrono::system_clock::now() };
    
    if ( date::from_stream(ss, "%F T %T %z", dt) ) {
        std::cout << "Cond 1 entered\n";
    }
    else if ( ss.clear(), ss.seekg(0); date::from_stream(ss, "%d-%m %R", dt)) {
        std::cout << "Cond 2 entered\n";
    }
    std::cout << dt << "\n";
}
对于第一种格式,这按预期工作:
./a.out 2020-06-07T18:30:00+0200
Cond 1 entered
2020-06-07 16:30:00.000000000
然而,第二种方法返回一些奇怪的东西,这取决于所使用的编译器。当使用 GCC 和 -std=c++17/-std=c++2a 编译时:
./a.out "07-08 15:00"
Cond 2 entered
1754-04-06 13:43:41.128654848
编辑 2:当使用 LLVM 和 -std=c++2a 编译时:
./a.out "07-08 15:00"
Cond 2 entered
0000-08-07 15:00:00.000000
这更接近我的预期。
我宁愿不依赖于所使用的编译器的行为!
我真的对这里发生的事情感到困惑,而且我似乎无法理解文档。
我怎样才能得到date::from_stream简单地覆盖时间和日期并保留其他所有内容?

编辑 1:
为清楚起见,我(错误地)期望在输入第二个条件时保留当前年份,因为 time_point对象是用当前年份初始化的。例如,我希望第二次调用 from_stream会离开time_point对象为 2020-08-07 15:00:33.803726000在我的第二个例子中。
请参阅评论以获取更多信息。
编辑 2:
添加了尝试不同编译器的结果。

最佳答案

好问题!!!
你做得不对,您在 date.h 中发现了一个错误! :-)
一、I've fixed the bug you hit here .问题是我对 not_a_year 的值有误。在 from_stream ,而且那个 bug 已经藏在里面好多年了!非常感谢你帮我找到它!要更新只需拉动主分支的尖端。
当您的程序以固定 date.h 和参数 "07-08 15:00" 运行时,它不进入任何条件并打印出当前时间。
解释:from_stream(stream, fmt, x)的语义是如果 stream不包含足够的信息来完全指定 x使用 fmt ,然后 stream.failbit设置和 x未修改。和 "07-08 15:00"没有完全指定 system_clock::time_point .
date.h 中的错误是 date.h 未能认识到没有足够的信息来完全指定 system_clock::time_point ,并且正在向它写入确定性垃圾。由于 system_clock::time_point 的精度不同,这些垃圾碰巧在 LLVM/libc++ 和 gcc 上产生了两个不同的值。 ( microsecondsnanoseconds )。
通过错误修复,解析完全失败,因此不会写入垃圾。
我相信你的下一个问题将是:

How do I make the second parse work?

int main(int argc, char ** argv)
{
    std::istringstream ss { argv[1] };
    auto dt = std::chrono::system_clock::now();
    ss >> date::parse("%FT%T%z", dt);
    if (!ss.fail())
    {
        std::cout << "Cond 1 entered\n";
    }
    else
    {
        ss.clear();
        ss.seekg(0);
        date::month_day md;
        std::chrono::minutes hm;
        ss >> date::parse("%d-%m", md) >> date::parse(" %R", hm);
        if (!ss.fail())
        {
            std::cout << "Cond 2 entered\n";
            using namespace date;
            auto y = year_month_day{floor<days>(dt)}.year();
            dt = sys_days{y/md} + hm;
        }
    }
    std::cout << dt << "\n";
}
第一次解析和你一样,只是我切换了 parse 的使用为 from_stream这是一个更高级别的 API。这对于第一次解析并不重要,但会使第二次解析更整洁。
对于第二次解析,您需要解析两个项目:
  • 一个 month_day
  • 一天中的某个时间

  • 然后将这两个元素与当前 year 结合起来产生所需的 time_point .
    现在每个解析都完全指定了它从流中解析的变量。
    您最初犯的错误是想象在system_clock::time_point 的引擎盖下有一个“年份字段”。 .实际上,这个数据结构只不过是自 1970-01-01 00:00:00 UTC 以来的微秒或纳秒(或其他)计数。所以第二个解析必须:
  • 解析字段,然后
  • 解构time_point进入字段以获取当前年份,然后
  • 将字段重新组合成 time_point .
  • 关于c++ - 使用 C++ 的日期库读取时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62795859/

    相关文章:

    c++ - `memory_resource` 的用途和用途是什么?

    c++ - 当我的对象在 C++ 中的右侧时,如何重载运算符 *?

    linux - 得到没有。为某个日期范围打印的行数

    javascript - javascript 中的新数据返回不同的日期

    python - 如何在 Django 中定义跨年、月的日期范围?

    c++ - 如何使用虚函数对基类进行抽象?

    c++ - TEMPLATE1 之前预期的非限定错误 '{'

    c++ - 如何确保 const int 在 C++ 中具有唯一值

    android - 将系统日期和时间与数据库日期和时间分开比较,以开始另一项 Activity

    java - 将 joda.time.DateTime 转换为 java.sql.Date 并保留时区