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
当我如下声明t
和t1
时:
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};
)。
我正在尝试使用 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
当我如下声明t
和t1
时:
time_point<system_clock, milliseconds> t{duration_cast<milliseconds>(duration<double>{d})}, t1;
代码按预期工作,即输出两行相同的行
它可以解析浮点数,但你真的不想。建议使用 milliseconds
进行修复。
解释:
当您格式化基于双精度的秒时,它使用默认为 6 位小数的 fixed
格式。这就是为什么您会在 .828
.
在解析时,预期精度由输入类型的精度驱动,即使 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};
)。