How*_*ant 4 c++ c++-chrono c++20
受到这个 SO Answer 的启发,其中有一个问题:
我只是希望 chrono 能让我做这样的事情:
std::chrono::time_point<std::chrono::system_clock> xmas = std::chrono::datetime("2023-12-25");
Run Code Online (Sandbox Code Playgroud)
例如,让我根据 ISO 8601 指定的日期获取固定时间点。如果日期以某种方式错误,则引发异常或以其他方式将时间点设置为纪元,并使用任何认为合适的错误处理。不支持非 ISO 8601 日期,但值得注意的是也可以实施其他标准。
或者,您可以使用 XXXX 表示当年,例如 XXXX-01-01 变为今年 1 月 1 日,XXXX-12-25 变为 12 月 1 日。25 号,但现在我真的在愿望清单上了:)
将 C++20 视为std::chrono一组日期和时间代码的构建块。确实没有什么是你不能用这些基本构建块轻松构建的,同时避免与时间、时区和日历相关的大部分技巧。
例如,这里有一个只有几十行长的函数,它允许您完全执行您想要的操作,包括获取您希望列表中的所有内容。代码后面逐行解释:
#include <chrono>
#include <sstream>
#include <stdexcept>
namespace my
{
std::chrono::system_clock::time_point
datetime(std::string const& s)
{
std::istringstream in{s};
std::chrono::year y;
std::chrono::month_day md;
if (in.peek() == 'X')
{
in >> std::chrono::parse("XXXX-%m-%d", md);
if (in.fail())
throw std::runtime_error(
"Unable to parse a date of the form XXXX-mm-dd out of \"" + s + '"');
y = std::chrono::year_month_day{
std::chrono::floor<std::chrono::days>(
std::chrono::system_clock::now())}.year();
}
else
{
in >> std::chrono::parse("%Y-", y) >> std::chrono::parse("%m-%d", md);
if (in.fail())
throw std::runtime_error(
"Unable to parse a date of the form yyyy-mm-dd out of \"" + s + '"');
}
auto date = y/md;
if (!date.ok())
throw std::runtime_error("Parsed invalid date out of \"" + s + '"');
return std::chrono::sys_days{date};
}
} // namespace my
Run Code Online (Sandbox Code Playgroud)
首先要做的是查明字符串是否具有XXXX-mm-ddor的形式yyyy-mm-dd。通过查看字符串的第一个字符可以轻松完成此操作。如果是,X那么它一定是XXXX-mm-dd,否则它一定是yyyy-mm-dd,否则这是一个错误,我们通过抛出带有详细错误消息的异常来标记。
如果字符串看起来具有以下形式XXXX-mm-dd,则chrono::month_day使用格式字符串解析 a "XXXX-%m-%d"。如果存在任何解析错误,或者解析的内容month_day不可能有效,则解析将失败。
如果解析失败,则抛出异常并提供有用的错误消息。
如果解析成功,则计算当前年份 (UTC) 并将其分配给y。如果需要本地年份,或者需要任何 IANA 时区的年份,只需要多写几行代码即可。
否则,字符串必须采用以下形式yyyy-mm-dd。分别解析为achrono::year和a chrono::month_day。
如果任何解析失败,请抛出异常并提供有用的错误消息。
最后将 theyear和 the组合month_day成 a year_month_day(date在此演示代码中调用)。
检查 是否year有效,以及month_day是否有效,但两者的组合无效。这将捕获非闰年的 2 月 29 日等日期。如果找到,则抛出异常并提供有用的错误消息。
通过首先转换为datea ,然后让隐式转换将精度细化为 ,将解析的结果转换为 a 。system_clock::time_pointsys_dayssystem_clock::time_point
可以这样练习:
#include <iostream>
int
main()
{
auto xmas = my::datetime("2023-12-25");
std::cout << xmas << '\n';
xmas = {};
xmas = my::datetime("XXXX-12-25");
std::cout << xmas << '\n';
try
{
xmas = my::datetime("XXXX-25-12");
}
catch (std::exception const& e)
{
std::cout << e.what() << '\n';
}
}
Run Code Online (Sandbox Code Playgroud)
哪个输出:
2023-12-25 00:00:00.000000
2023-12-25 00:00:00.000000
Unable to parse a date of the form XXXX-mm-dd out of "XXXX-25-12"
Run Code Online (Sandbox Code Playgroud)
请注意,我们的代码捕获了正确语法的错误,但捕获了 25 月 12 日的无效日期。
另请注意,如果需要,可以通过更多检查和分支来支持其他日期格式。