用 std::localtime + std::mktime 归一化计算当月天数最可靠(C++17及之前),或用 C++20 的 year_month_day_last;硬编码或速查表易因闰年、时区、夏令时出错。

用 std::chrono 和 std::time_t 获取当前月份天数不靠谱
直接用 std::chrono 的日历类型(如 C++20 的 year_month_day)最干净,但如果你还在用 C++17 或更早版本,std::localtime + std::mktime 是唯一可移植的方案。别信网上那些只算 30/31 的“速查表”——闰年二月、时区切换、夏令时都会让结果错。
std::localtime 返回的 tm_mon 是从 0 开始的
这是最容易踩的坑:tm.tm_mon 表示 0–11(对应 1–12 月),但 tm.tm_mday 是 1–31,且它**不是当前月天数,而是当前日期**。要得当月天数,得把日期设为下个月第 0 天,再让 std::mktime 归一化:
std::time_t now = std::time(nullptr); std::tm tm = *std::localtime(&now); // 设为下个月第 0 天(即本月最后一天) tm.tm_mon += 1; tm.tm_mday = 0; tm.tm_hour = tm.tm_min = tm.tm_sec = 0; // 清空,避免跨天影响 std::mktime(&tm); // 自动回滚到上月最后一天 int days_in_month = tm.tm_mday;
- 必须调用
std::mktime,否则tm_mday = 0不会被解释为“上月最后一天” -
std::localtime返回的是静态缓冲区指针,多线程下要加锁或改用std::localtime_r(POSIX) - Windows 下用
_localtime64_s更安全,避免 32 位 time_t 溢出
C++20 起用 year_month_day_last 最稳
如果你能用 C++20,<chrono> 提供了真正语义清晰的方案,完全绕过 C 风格时间结构的歧义:
using namespace std::chrono;
year_month_day_last ymdl = floor<days>(system_clock::now()).time_since_epoch() / days{1};
int days = static_cast<int>(ymdl.day().last().ok());
-
year_month_day_last直接表示“某年某月的最后一天”,day().last().ok()返回该月天数(int) - 不依赖本地时区设置,
system_clock::now()是 UTC 时间,但月份天数在所有时区都一样 - 编译器支持需开启
-std=c++20,MSVC 19.30+、GCC 11+、Clang 14+ 均可用
别硬编码 28/29/30/31 判断逻辑
手写闰年判断(如 (y%4==0 && y%100!=0) || y%400==0)再套月份数组,看似简单,其实漏掉两个关键点:
立即学习“C++免费学习笔记(深入)”;
- 公历改革(1582 年 10 月跳过 10 天)在某些
std::tm实现中仍被考虑,硬编码会和系统行为不一致 - 2100 年不是闰年,但很多手写判断会错;而
std::mktime和year_month_day_last都严格遵循 Gregorian 日历规则 - 嵌入式或精简 libc(如 newlib、musl)可能对
tm字段处理有差异,走标准归一化路径更可靠
真正要注意的其实是时区变更窗口期:如果程序在 3 月第二个周日凌晨运行,刚好撞上夏令时切换,std::localtime 可能返回模糊时间,此时应优先用 C++20 方案或强制指定时区(如 zoned_time)。


















