获取本地时区零点毫秒时间戳需用std::localtime提取年月日并重建time_t,再转回system_clock::time_point;直接floor会得到utc零点,非本地零点;windows建议用getlocaltime+systemtimetofiletime规避mktime夏令时问题。

用 std::chrono 和 std::localtime 组合获取本地时区零点毫秒时间戳
直接用 std::chrono::system_clock::now() 只能得到当前时刻,没法自动归零到当天 00:00:00.000。必须先提取年月日,再重建时间点。关键在于:不能只截断时间部分(会丢失时区偏移),也不能硬写 0, 0, 0(忽略夏令时和本地规则)。
实操建议:
- 用
std::chrono::system_clock::to_time_t转成time_t,再用std::localtime得到本地日历时间(含时区信息) - 把
tm_year、tm_mon、tm_mday提出来,构造新struct tm,其他字段清零(注意tm_isdst = -1让系统自动判断夏令时) - 用
std::mktime转回time_t,再转成std::chrono::system_clock::time_point - 最后用
time_point.time_since_epoch().count()提取毫秒数(需除以1'000'000,因为system_clock精度通常是微秒)
auto now = std::chrono::system_clock::now(); std::time_t t = std::chrono::system_clock::to_time_t(now); std::tm tm_local = *std::localtime(&t); tm_local.tm_hour = tm_local.tm_min = tm_local.tm_sec = 0; tm_local.tm_isdst = -1; // 让 mktime 自动推断 std::time_t zero_t = std::mktime(&tm_local); auto zero_tp = std::chrono::system_clock::from_time_t(zero_t); auto ms = std::chrono::duration_cast<:chrono::milliseconds>(zero_tp.time_since_epoch()).count(); </:chrono::milliseconds>
用 std::chrono::floor 截断到天但要注意时区陷阱
std::chrono::floor<:chrono::days>(now)</:chrono::days> 确实能快速归零到当天起始,但它操作的是 UTC 时间点 —— 即使你用的是 system_clock,它底层仍是基于 Unix 时间戳(UTC)。如果你在东八区,floor 出来的“零点”其实是 UTC 零点,对应北京时间是上午 8 点,完全不是你要的本地零点。
所以除非你明确要 UTC 零点,否则别直接用 floor。如果坚持用,得手动加时区偏移:
- 先用
std::localtime获取tm_gmtoff(POSIX 扩展,Linux/macOS 支持;Windows 需用_get_timezone替代) - 把
floor后的 UTC 零点 time_point 加上该偏移量(单位秒),再转成毫秒 - 注意:
tm_gmtoff在 Windows 的 MSVC 中不可用,跨平台项目慎用
Windows 下避免依赖 localtime 的替代方案
MSVC 的 localtime_s 是线程安全版,但参数顺序和返回值与 POSIX 不同;而且 mktime 在 Windows 对 tm_isdst = -1 的处理偶尔有偏差。更稳的方式是用 Windows API GetLocalTime + SystemTimeToFileTime:
-
GetLocalTime填充SYSTEMTIME,把wHour/wMinute/wSecond/wMilliseconds全设为 0 - 用
SystemTimeToFileTime转成 64 位 FILETIME(100 纳秒精度) - 减去 Unix epoch 偏移(116444736000000000LL),再除以 10000 得到毫秒
- 这个路径不经过 C 运行时,规避了
mktime的夏令时误判问题
毫秒精度下容易被忽略的夏令时边界问题
比如在 3 月第二个周日凌晨 2 点,时钟会跳到 3 点 —— 这一小时根本不存在。此时若用 mktime 构造一个落在该区间内的 tm,返回值可能为 -1 或自动调整到 3 点,导致零点时间错误。同样,11 月第一个周日凌晨 2 点会回拨,出现两个 1:xx 小时段,mktime 默认按夏令时结束前解释,可能选错。
应对方式只有两种:
- 业务允许误差:接受
mktime的自动修正(多数场景够用) - 业务要求精确:改用 IANA 时区数据库(如
date.h库),显式指定时区名("Asia/Shanghai"),再调用locate_zone+make_zoned构造零点
纯标准库方案无法绕过夏令时歧义,这点在金融或日志对齐类场景里必须提前确认清楚。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











