Howard Hinnant 的 date::parse() 函数是否适用于浮点持续时间?

Does Howard Hinnant's date::parse() function work with floating-point durations?

我正在尝试使用 Howard Hinnant 的 Date 库将 2020-03-25T08:27:12.828Z 解析为 std::chrono::time_point<system_clock, duration<double>>

预计下面的代码会输出两个相同的字符串:

#include "date.h"
#include <chrono>
#include <string>
#include <iostream>

using namespace std;
using namespace std::chrono;
using namespace date;

int main() {
  double d = 1585124832.828;

  time_point<system_clock, duration<double>> t{duration<double>{d}}, t1;

  string s {format("%FT%TZ", t) };
  cout << s << "\n";

  stringstream ss {s};
  ss >> parse("%FT%TZ", t1);
  cout << format("%FT%TZ", t1) << "\n";

}

但我得到:

2020-03-25T08:27:12.828000Z
1970-01-01T00:00:00.000000Z

当我如下声明tt1时:

time_point<system_clock, milliseconds> t{duration_cast<milliseconds>(duration<double>{d})}, t1;

代码按预期工作,即输出两行相同的行

可以解析浮点数,但你真的不想。建议使用 milliseconds 进行修复。

解释:

当您格式化基于双精度的秒时,它使用默认为 6 位小数的 fixed 格式。这就是为什么您会在 .828.

之后看到 3 个尾随零

在解析时,预期精度由输入类型的精度驱动,即使 rep 是浮点数。因此 duration<double> 它只解析秒的整数部分。然后它开始寻找尾随 Z 并找到 .。这会导致解析失败。如果您在解析字符串中没有 Z,它不会失败,但它也不会解析秒的小数部分。

如果您将 time_point 更改为基于双精度的 microseconds,那么它将再次起作用:

time_point<system_clock, duration<double, micro>> t{duration<double>{d}}, t1;

但我认为这种方式过于神秘和微妙,而且它还有另一个您尚未解决的问题:在 C++17 中,round 由供应商提供为 std::chrono::round ,这是在 parse 的幕后使用的。并且 round 的 C++17 版本不允许目标 duration 具有浮点数 rep。所以你的代码甚至不会在 C++17 或更高版本中编译。

使用基于整数的 milliseconds 避免了解析浮点值的所有这些复杂情况。如果需要,您仍然可以将结果转换回 duration<double>

一个微妙的建议:

time_point<system_clock, milliseconds> t{round<milliseconds>(duration<double>{d})}, t1;

浮点型转换为基于整数的rep时,我喜欢使用round而不是duration_cast。当基础 double 没有 完全 表示所需的值并且 duration_cast 在错误的方向(朝向零)截断时,这避免了差一错误。 round 将四舍五入到最接近的可表示值。

以上内容可以使用 date::sys_time 模板化类型别名进一步简化:

sys_time<milliseconds> t{round<milliseconds>(duration<double>{d})}, t1;

以上完全等价。 sys_time<duration>time_point<system_clock, duration> 的类型别名。在 C++20 中,它通过新的 CTAD 规则进一步简化:

sys_time t{round<milliseconds>(duration<double>{d})};

<milliseconds> 是从参数的类型推导出来的 (C++20)。虽然你必须单独声明 t1,或者给 t1 一个 milliseconds 初始值(例如 , t1{0ms};)。