c++20 推荐用 std::chrono::year_month_day 和 weekday 计算本周周一:先取 today,再 today - (wd - monday);旧标准需用 tm_wday 判断周日(0)减6天、其余减(tm_wday-1)天,并调整 tm_mday 后调用 mktime 归一化。

如何用 std::chrono 和 std::time_t 算出本周周一
标准库没有直接返回“本周周一”的函数,得自己算。核心思路是:获取当前系统时间 → 转成日历日期 → 查出今天是星期几(注意:std::chrono::weekday 中周一为 monday,但 std::tm::tm_wday 中周日是 0,周一为 1,容易混)→ 减去对应天数。
推荐用 C++20 的 std::chrono 日历 API,它语义清晰、无时区陷阱(默认本地日历):
auto now = std::chrono::system_clock::now();
auto today = std::chrono::floor<:chrono::days>(now);
auto wd = std::chrono::year_month_day{today}.weekday();
auto monday = today - (wd - std::chrono::Monday); // wd 是 weekday 类型,支持减法
</:chrono::days>
这里 wd - std::chrono::Monday 返回一个 std::chrono::days,值为 0(周一)、1(周二)…6(周日),所以减它就回退到周一。
用 std::tm 和 localtime 的兼容写法(C++11/14/17)
老项目没上 C++20?那就得手动处理 tm_wday 的偏移。关键点:tm_wday == 0 是周日,要回退 6 天才是周一;tm_wday == 1 是周一,不需回退。
常见错误是写成 days_to_subtract = tm_wday - 1,结果周日变成 -1 天 —— 必须做模运算或条件判断:
- 若
tm_wday == 0(周日),则减 6 天 - 否则减
tm_wday - 1天
示例代码:
std::time_t t = std::time(nullptr); std::tm* lt = std::localtime(&t); int days_to_subtract = (lt->tm_wday == 0) ? 6 : lt->tm_wday - 1; std::time_t monday_t = t - days_to_subtract * 24 * 3600;
注意:这个方法有精度风险 —— 直接用秒数相减会忽略夏令时切换、闰秒等,仅适用于对精度要求不高的场景(比如日志归档、周报生成)。真正需要精确日期计算的,别绕开 C++20 日历。
为什么不能直接用 mktime 强制设 tm_wday
tm_wday 是只读字段,mktime 会根据年月日自动重算它,你改了也没用。有人试过:
lt->tm_wday = 1; // 无效!mktime 会覆盖它 std::mktime(lt);
结果还是当天的星期几。正确做法是只调 mktime 前修改 tm_mday(日),其他字段保持一致,再让 mktime 归一化。
更安全的做法是:先算出目标日期的年、月、日,再构造 tm 并调 mktime:
lt->tm_mday -= days_to_subtract; // 修改日 std::mktime(lt); // 自动修正越界年月、更新 tm_wday 等
跨午夜或夏令时切换时的坑
用秒数加减(如 t - 3*24*3600)在夏令时开始/结束当日可能错一天。例如:某地 3 月10日凌晨2点跳到3点,你减 24 小时可能落到“不存在的时间”或“重复的时间”,localtime 解析会出错或偏移。
C++20 的 year_month_day + weekday 路径完全避开时间戳和时区转换,只操作日历日期,天然抗这类问题。
如果必须用旧 API,建议统一转成 UTC 时间做运算,或使用 timegm(POSIX)代替 mktime 避免本地时区干扰 —— 但要注意 timegm 不是标准 C++ 函数,Windows 上需用 _mkgmtime。
真正麻烦的不是“怎么写”,而是“要不要考虑 DST”。业务逻辑里如果只是按自然周切分数据,多数情况用 C++20 方案最省心;如果还在维护 C++11 项目,至少把秒数运算换成 tm_mday 调整,别碰 tm_wday。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











