如何安全度过clock_cast天?

How to safely clock_cast days?

我正在使用 HowardHinnant/date 代替 Clang/GCC 中尚不可用的新 C++20 calendar/timezone 功能。我的问题同样适用于两种实现方式:How do I safely clock_cast time_points having days duration?

当我尝试时:

using namespace date; // or using std::chrono in C++20
tai_time<days> tai{days{42}};
sys_days sys = clock_cast<std::chrono::system_clock>(tai);

最后一条语句出现“无可行转换”错误。事实证明,结果使用的持续时间是 common_type<days, seconds>,它来自转换中使用的 utc_clock::to_sys()。常见的时长类型是std::chrono::seconds,所以不能直接转换成days时长是正常的。

如果我使用显式 duration_cast:

我可以让它编译
using namespace date; // or using std::chrono in C++20
tai_time<days> tai{days{42}};
auto casted = clock_cast<std::chrono::system_clock>(tai);
sys_days sys{std::chrono::duration_cast<days>(casted.time_since_epoch())};

...但我担心结果可能会因截断而偏离一天(尤其是对于纪元之前的日期)。这是做我想做的事情的正确方法吗?我应该使用 floor 而不是 duration_cast 吗?

为什么 utc_clock::to_sys() 里还有一个 std::common_type_t<Duration, std::chrono::seconds>?它不应该只是 return 相同的持续时间类型吗?

之所以clock_cast坚持至少seconds的精度是因为system_clocktai_clock的纪元之间的偏移精度为seconds:

auto diff = sys_days{} - clock_cast<system_clock>(tai_time<days>{});
cout << diff << " == " << duration<double, days::period>{diff} << '\n';

输出:

378691210s == 4383.000116d

所以 days 精确转换会造成损失。这是另一种看待它的方式:

cout << clock_cast<tai_clock>(sys_days{2021_y/June/1}) << '\n';

输出:

2021-06-01 00:00:37

TAI is currently 37 seconds ahead of system_clock 按日历计算。

如果你只想要日期,我推荐round<days>(result):

cout << round<days>(clock_cast<tai_clock>(sys_days{2021_y/June/1})) << '\n';

输出:

2021-06-01 00:00:00

可以想象在一个方向上使用 floor 而在另一个方向上使用 ceil,但这很容易出错。 round 会很好,因为与整数天数的偏移量目前只有 37 秒并且增长非常缓慢。