应将日期归一到ISO周周一再比较,而非直接比对年份和周数;C++20可用std::chrono::Monday与floor实现,旧标准需手动推算并注意时区与跨月边界。

用 std::chrono 计算周一基准日再比对周数
直接比较年份和“第几周”容易出错——比如 2024-12-30 是 2025 年第 1 周,但日期还在 2024 年。稳妥做法是把两个日期都归一到当周的周一(ISO 周定义),再看是否相等。
关键不是“年+周数”,而是“本周一的日期”。C++20 的 std::chrono 提供了 floor<weeks></weeks> 和 Monday 常量,能干净地做这件事:
auto to_monday(const std::chrono::sys_days& dp) {
return dp - (dp.time_since_epoch() % std::chrono::weeks{1})
+ std::chrono::Monday;
}注意:sys_days 是 std::chrono::time_point 类型,需从 year_month_day 构造;Monday 是 C++20 引入的枚举值,不是整数 1。
手动计算 ISO 周一(兼容 C++11/14/17)
如果项目不能用 C++20,就得自己算 ISO 周一:先转为 std::tm,再用 strftime("%G%V", ...) 获取 ISO 年+周,但这样仍无法反推周一日期。更可靠的做法是基于 std::mktime 往前推算:
立即学习“C++免费学习笔记(深入)”;
- 用
std::mktime得到tm_wday(周日=0,周一=1…周六=6) - 若
tm_wday == 0(周日),往前推 6 天;否则往前推tm_wday - 1天 - 但要注意:
tm_wday是本地时区,且mktime会修改tm_yday、tm_mday等字段,必须深拷贝原始tm
这个逻辑容易漏掉时区偏移或跨月边界(比如 1 月 1 日是周五,周一就是上一年 12 月 29 日),建议封装成函数并加单元测试覆盖 12 月 31 日、1 月 1 日等临界点。
别用 tm.tm_yday 或 %U/%W 直接除 7
tm_yday 是年内天数(0~365),简单除以 7 得到的“周数”既不遵循 ISO 标准(周一为周始),也不处理跨年问题。同理,strftime("%U")(周日为首)和 "%W"(周一为首)返回的是“年内第几周”,但 2024-12-30 属于 2025 年第 1 周,用 %W 却会返回 “2024 年第 52 周”——两者根本不在同一逻辑维度上。
常见错误写法:
// ❌ 错误:忽略 ISO 周定义,且未处理跨年 int week1 = (tm1.tm_yday / 7); int week2 = (tm2.tm_yday / 7); return tm1.tm_year == tm2.tm_year && week1 == week2;
这种逻辑在每年 12 月下旬和 1 月上旬必然失败。
第三方库如 date.h 能省事但要权衡
Howard Hinnant 的 date.h(已被吸收到 C++20)提供 iso_week_start() 和 year_week_weekday,一行就能拿到 ISO 周信息:
using namespace date;
auto yww1 = year_week_weekday{sys_days{y1/m1/d1}};
auto yww2 = year_week_weekday{sys_days{y2/m2/d2}};
return yww1.year() == yww2.year() && yww1.week() == yww2.week();但引入外部头文件意味着构建依赖增加,且需确认 license(MIT)是否符合项目要求。如果只是轻量判断,手写 C++20 版本更可控;如果已有 date.h,直接用它最稳。
真正麻烦的不是算周数,而是搞清你到底要“自然周”(日历上横跨的七天)、“ISO 周”(周一始、含当年多数周四的周),还是“公司自定义周”(比如周五到周四)。一旦需求模糊,代码越写越像补丁。


















