std::chrono::year_month_day::ok()是c++20唯一标准化的日期合法性检查方式,需显式调用以验证年(-32767~32767)、月(1~12)、日(符合当月实际天数)是否有效,不调用则无校验。

用 std::chrono 原生类型无法防止无效日期
直接用 std::chrono::year_month_day 构造时,C++20 标准不校验日期合法性。比如 year_month_day{year{2023}, month{2}, day{30}} 不报错,但 2023 年 2 月根本没有 30 日——它会静默转换成一个“溢出”值(内部存储为 day{30}),后续调用 .ok() 才返回 false,但很多用户根本没查。
year_month_day::ok() 是唯一标准检查入口
这是 C++20 提供的、唯一标准化的合法性判断方式。它检查年份范围(-32767 ~ 32767)、月份是否为 1~12、日是否在该年月实际天数范围内(自动考虑闰年)。
- 必须显式调用,不调用就等于没检查
- 仅对已构造的
year_month_day对象有效,不能用于“预判”构造是否安全 - 注意:
ok()返回false时不抛异常,需手动处理(比如抛std::invalid_argument)
auto ymd = year_month_day{year{2023}, month{2}, day{30}};
if (!ymd.ok()) {
throw std::invalid_argument("invalid date: " + std::to_string(ymd.year()) + "-" +
std::to_string(ymd.month()) + "-" +
std::to_string(ymd.day()));
}
从字符串解析时别依赖 std::get_time
std::get_time 解析 "2023-02-30" 会成功(只校验格式,不校验逻辑),返回 tm 结构体,而 tm.tm_mday == 30 是完全合法的字段值——它甚至不保证是归一化后的日期。后续转 year_month_day 仍需调用 .ok()。
- 更稳妥的做法:用
std::chrono::parse(C++20)配合year_month_day直接解析,并立即检查.ok() - 若用第三方库(如 Howard Hinnant 的
date.h),它的parse()在失败时会设流状态位,但依然建议解析后补一次.ok() - 避免手写正则提取年月日后直接构造——闰年、大小月、世纪年规则容易漏判
自定义构造函数要封装 .ok() 检查
如果你提供类似 Date(int y, int m, int d) 的接口,不能只做范围裁剪(比如把 m=13 改成 m=1),那不是“防止无效日期”,只是掩盖问题。真正安全的做法是构造后立刻验证。
- 构造
year_month_day后必须调用.ok(),失败则抛异常或返回std::optional<date></date> - 不要尝试自己实现“某年某月有多少天”的逻辑——
year_month_day内部已包含完整规则,重复实现易出错 - 注意时区无关性:所有校验都基于本地日历(ISO calendar),不涉及 UTC 偏移或夏令时
最容易被忽略的是:.ok() 不是可选步骤,它是唯一防线。哪怕你用最严格的输入过滤,只要路径中存在任何手动构造 year_month_day 的地方,就一定得跟一句 if (!x.ok()) ——没有例外,也没有隐式保障。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











