FILETIME是自1601年1月1日UTC起的100纳秒计数,不能直接除10000000得Unix时间戳,因起点不同(差11644473600秒),须先转秒再减偏移,且需用ULARGE_INTEGER安全读取、int64_t避免溢出。

FILETIME 是什么,为什么不能直接除 10000000
Windows FILETIME 是一个 64 位整数,表示自 1601-01-01 00:00:00 UTC 起的 100 纳秒(即 0.1 微秒)数。Unix 时间戳是自 1970-01-01 00:00:00 UTC 起的秒数。两者起点不同、单位不同,直接用 filetime / 10000000 只能得到从 1601 年起的秒数,不是 Unix 时间戳。
关键差值是:1970-01-01 UTC 比 1601-01-01 UTC 晚 11644473600 秒(这个常量必须记牢或硬编码)。所以正确转换要先转成秒,再减去这个偏移。
最简安全转换:用 ULARGE_INTEGER 拆高位低位
FILETIME 在内存中是两个 32 位字段(dwLowDateTime 和 dwHighDateTime),直接按 uint64_t 解释可能因对齐或符号扩展出错。推荐用 ULARGE_INTEGER 联合体安全读取:
FILETIME ft; // ... 获取 ft ULARGE_INTEGER uli; uli.LowPart = ft.dwLowDateTime; uli.HighPart = ft.dwHighDateTime; int64_t unix_ts = static_cast<int64_t>(uli.QuadPart / 10000000) - 11644473600LL;
- 必须用
int64_t接收,避免 32 位截断(2038 年后时间会溢出int32_t) -
11644473600LL后缀确保是 long long,防止整型提升错误 - 除法在整数域做,向下取整——对绝大多数场景没问题;若需纳秒级精度或处理闰秒,得另走浮点路径
反向转换:Unix 时间戳 → FILETIME
反过来时,顺序不能错:先加偏移,再乘 10000000,最后拆到 FILETIME 字段:
立即学习“C++免费学习笔记(深入)”;
int64_t unix_ts = 1717023456LL; ULARGE_INTEGER uli; uli.QuadPart = (unix_ts + 11644473600LL) * 10000000LL; ft.dwLowDateTime = uli.LowPart; ft.dwHighDateTime = uli.HighPart;
- 加法必须在乘法前完成,否则可能溢出(
1717023456 * 10000000已超 32 位) - 注意
FILETIME不支持负时间戳(早于 1601 年),传入负的unix_ts会导致高位全 1,结果未定义 - 如果输入来自
time_t,且平台time_t是 32 位,2038 年后值无效,务必确认类型宽度
用 SYSTEMTIME 中转?不推荐,除非真要年月日
有人想用 FileTimeToSystemTime() → 手动算秒 → 再用 mktime(),这条路问题很多:
-
SYSTEMTIME年份字段是WORD(0–65535),但mktime()通常只接受 1900–2038 年范围 -
mktime()默认按本地时区解析,而FILETIME是 UTC,漏掉_mkgmtime()或时区校正就会差几个小时 - 性能差:两次系统调用 + 大量分支判断,比纯算术慢 10 倍以上
纯时间戳转换,坚持用 64 位整数算术。只有当你确实需要“2024 年 5 月 29 日星期三”这种格式时,才值得进 SYSTEMTIME。


















