我正在尝试使用 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 上产生了两个不同的值。 ( microseconds
与 nanoseconds
)。
通过错误修复,解析完全失败,因此不会写入垃圾。
我相信你的下一个问题将是:
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/