应使用 std::localtime + mktime 归零日期字段:设 tm_mday=1、清零时分秒、tm_isdst=-1 后调用 mktime 规范化,再转为 time_t;跨平台 utc 时间戳需用 timegm(posix)或 _mkgmtime(windows)。

用 std::chrono 和 std::gmtime 组合计算当前月份第一秒
标准库没有直接提供“某月第一天 00:00:00”的时间戳接口,必须手动归零日期字段。关键在于:先获取当前本地时间(注意时区),再构造当月1日的 std::tm,最后转为 time_t。别直接对当前 time_t 减去秒数——闰秒、夏令时切换会让这种算术失效。
常见错误是忽略 tm_mon 从 0 开始(1 月是 0)、tm_year 是距 1900 年的偏移量,以及忘记调用 mktime() 重新规范化时间(否则 tm_mday=1 但其他字段残留旧值会导致结果错乱):
-
tm.tm_mday = 1必须设,且要清零tm.tm_hour、tm.tm_min、tm.tm_sec - 调用
mktime(&tm)前,tm.tm_isdst = -1让系统自动判断夏令时 - 如果需要 UTC 时间戳(即不考虑本地时区),用
std::gmtime+timegm()(POSIX)或手动转为 UTC(Windows 需_mkgmtime)
跨平台写法:用 std::chrono::system_clock 提取年月再重建
避免 tm 结构体的手动操作,更安全的方式是用 std::chrono 拆解当前时间点,再构造当月首日零点的时间点。C++20 的 <chrono></chrono> 提供了 year_month_day 和 sys_days,但若用 C++17 或更早,需自行处理:
示例(C++17 兼容):
auto now = std::chrono::system_clock::now(); auto t = std::chrono::system_clock::to_time_t(now); auto tm_local = *std::localtime(&t); // 归零到当月第一天 00:00:00 tm_local.tm_mday = 1; tm_local.tm_hour = 0; tm_local.tm_min = 0; tm_local.tm_sec = 0; tm_local.tm_isdst = -1; auto first_second_of_month = std::chrono::system_clock::from_time_t(std::mktime(&tm_local)); auto timestamp = std::chrono::system_clock::to_time_t(first_second_of_month);
注意:std::mktime 输入的是本地时间结构,输出的是本地时间对应的 time_t(即 Unix 时间戳),它隐含了本地时区偏移。如果你在东八区执行,得到的就是北京时间当月 1 日 00:00:00 对应的 UTC 时间戳(即比 UTC 快 8 小时的那个整数秒)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
Windows 下 timegm 不可用?用 _mkgmtime
Windows 默认不提供 POSIX 的 timegm,直接链接会报 LNK2019。替代方案是使用 Microsoft 扩展函数 _mkgmtime,它接受 UTC 时间的 tm 结构并返回对应 UTC 时间戳:
- 先用
std::gmtime获取 UTC 时间的tm - 设
tm.tm_mday = 1,清空时分秒,保持tm.tm_isdst = 0(UTC 无夏令时) - 调用
_mkgmtime(&tm)得到 UTC 时间戳
代码片段:
auto t = std::time(nullptr); auto tm_utc = *std::gmtime(&t); tm_utc.tm_mday = 1; tm_utc.tm_hour = 0; tm_utc.tm_min = 0; tm_utc.tm_sec = 0; tm_utc.tm_isdst = 0; auto utc_timestamp = _mkgmtime(&tm_utc); // Windows only
Linux/macOS 应改用 timegm(&tm),跨平台项目建议宏判断。
精度与边界问题:2 月 30 日不会溢出?
有人担心设 tm_mday = 1 后调用 mktime 是否安全——完全不必。mktime 会自动归一化:比如你传入 2024 年 2 月 30 日,它会转成 2024 年 3 月 1 日;同理,设 tm_mday = 1 时,哪怕原始 tm_mon 是 11(12 月),也不会变成下一年 1 月 1 日——因为 mktime 内部已知年月,只重置日、时、分、秒。
真正容易被忽略的是:不同系统对 tm.tm_yday 和 tm.tm_wday 的处理可能不同,但只要你不依赖这两个字段,就无需关心。重点永远是设对 tm_mday、清零时分秒、设 tm_isdst = -1,然后交由 mktime 规范化。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










