std::filesystem::last_write_time在Windows上不准因epoch和精度差异,需转换为system_clock::time_point再比较;递归遍历时应启用skip_permission_denied选项并配合try/catch防中断。

std::filesystem::last_write_time 获取文件修改时间不准?
Windows 上 std::filesystem::last_write_time 返回的是 file_time_type,它和系统时钟(system_clock)不在同一时间轴,直接转成秒数会出错。Linux/macOS 通常没问题,但跨平台代码必须统一处理。
正确做法是用 std::filesystem::file_time_type::clock::to_time_t(C++20 起支持),或手动转换为 system_clock::time_point(需注意 Windows 的 epoch 偏移)。更稳妥的方式是:先获取当前系统时间点,再用 std::chrono::duration_cast 计算差值。
- 别直接比较
file_time_type和system_clock::now()—— 类型不兼容,编译不过 - Windows 下
file_time_type以 100ns 为单位、epoch 是 1601-01-01;system_clock通常是 Unix epoch(1970-01-01),必须转换 - 推荐用
std::filesystem::last_write_time(p).time_since_epoch()+std::chrono::system_clock::from_time_t中间桥接,但最简方案是:用std::chrono::duration_cast<:chrono::hours></:chrono::hours>比较相对时长,避开 epoch 问题
递归遍历目录时如何避免权限错误中断
std::filesystem::recursive_directory_iterator 遇到无权限子目录或损坏符号链接时默认抛异常(std::filesystem::filesystem_error),导致整个删除流程中断。这不是你想要的——日志目录里可能混着只读子目录或残留挂载点。
必须启用“跳过错误”模式:std::filesystem::recursive_directory_iterator 构造时传入 std::filesystem::directory_options::skip_permission_denied。
立即学习“C++免费学习笔记(深入)”;
- 不加这个选项,遇到
Access is denied或Operation not permitted就崩 - 即使加了,仍可能遇到硬链接循环或坏 symlink 导致死循环,建议配合
max_depth限制(如std::filesystem::recursive_directory_iterator(p, std::filesystem::directory_options::skip_permission_denied | std::filesystem::directory_options::pop_on_error)) - 遍历时用
try/catch包一层更保险,尤其在生产环境
删除前判断“超过 N 天”的安全写法
“N 天”指最后修改时间早于当前时间减去 N×24 小时。注意:不是简单用 std::chrono::days,因为 std::filesystem::file_time_type 的精度和时区行为不确定;应统一转为秒级 duration 再比对。
auto now = std::chrono::system_clock::now();
auto cutoff = now - std::chrono::hours(24 * n_days);
for (const auto& entry : std::filesystem::recursive_directory_iterator(log_dir, std::filesystem::directory_options::skip_permission_denied)) {
if (!entry.is_regular_file()) continue;
try {
auto last_mod = std::filesystem::last_write_time(entry.path());
// 转换为 system_clock 时间点(C++20)
auto tp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
last_mod - std::filesystem::file_time_type::clock::now() + std::system_clock::now()
);
if (tp < cutoff) {
std::filesystem::remove(entry.path());
}
} catch (...) { /* 忽略单个文件读取失败 */ }
}- 上面的转换在 C++20 编译器(GCC 10+/Clang 12+)下可靠;老标准可用
std::filesystem::file_time_type::clock::to_time_t再转std::chrono::system_clock::from_time_t - 别用
std::chrono::days(n_days)直接减 —— 它不考虑夏令时跳变,且file_time_type不一定支持 days 精度 - 实际运行前加
std::cout 调试,确认路径和时间逻辑
std::filesystem::remove 删除失败的常见原因
std::filesystem::remove 返回 bool,但失败时不抛异常(除非底层 OS 层报错且未被屏蔽),容易误以为删除成功。典型失败场景包括:文件正被其他进程打开、路径含中文或特殊字符未正确编码、目标是只读文件但没先设权限。
- Windows 下如果文件被记事本、IDE 或日志轮转工具占用,
remove直接返回false,需检查返回值 - 删除前可尝试
std::filesystem::permissions(path, std::filesystem::perms::owner_write)解除只读属性 - 路径含空格或 Unicode 字符时,确保编译器/运行环境 locale 设置正确(尤其 MinGW 或旧 MSVC)
- 不要用
std::filesystem::remove_all清空整个日志目录 —— 它会删掉子目录结构,而你通常只想删旧文件、保留目录骨架
C++20 的 std::filesystem 做这事足够简洁,但时间转换和错误容忍这两块最容易翻车——尤其 Windows 权限模型和 time_point 转换细节,一不留神就删错或删不动。


















