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

Using C++'s date library to read times

本文关键字:读取 取时间 日期 C++ 使用      更新时间:2023-10-16

我正试图使用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:MMHH: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 enteredn";
}
else if ( ss.clear(), ss.seekg(0); date::from_stream(ss, "%d-%m %R", dt)) {
std::cout << "Cond 2 enteredn";
}
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!:-(中发现了一个错误

首先,我已经修复了你在这里遇到的错误。问题是我在from_stream中的not_a_year值错误,这个错误已经隐藏了好几年了!非常感谢你帮我找到它!要更新,只需拉动主分支的顶端。

当您的程序使用参数为"07-08 15:00"的固定日期.h运行时,它既不输入任何条件,也不打印出当前时间。

说明:

from_stream(stream, fmt, x)的语义是,如果stream不包含足够的信息来使用fmt完全指定x,则设置stream.failbit,而不修改x。而CCD_ 21并没有完全指定一个CCD_。

date.h中的错误是date.h未能识别出没有足够的信息来完全指定system_clock::time_point,并正在向其写入确定性垃圾。由于system_clock::time_point的精度不同(microsecondsnanoseconds(,该垃圾碰巧在LLVM/libc++和gcc上产生了两个不同的值。

通过修复错误,解析会彻底失败,因此不会写入垃圾。

我相信你的下一个问题是:

如何进行第二次解析?

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 enteredn";
}
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 enteredn";
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。这对于第一次解析来说并不重要,但会使第二次解析更加整洁。

对于第二个解析,您需要解析两个项目:

  1. month_day
  2. 一天中的某个时间

然后将这两个元素与当前的year组合以产生所需的time_point

现在,每个解析都完全指定了它从流中解析的变量。

你最初犯的错误是想象有一个";年份字段";在CCD_ 32的掩护下。实际上,自1970-01-01 00:00:00 UTC以来,这个数据结构只不过是微秒或纳秒(或其他(的计数。所以第二个解析必须:

  1. 分析字段,然后
  2. time_point分解为字段以获得当前年份,然后
  3. 将字段重新组合到time_point