在写文件管理、缓存清理或者增量同步这类功能时,几乎都会碰到一个问题:怎么用C++拿到一个文件的时间信息。C++17把std::filesystem纳入标准之后,filesystem::last_write_time成了最顺手的工具,但翻开文档你会发现,标准库只给了最后修改时间,压根没有"创建时间"这个接口。这篇文章就把这件事讲透:last_write_time怎么用、返回的时间怎么转换成可读格式、以及在Windows和Linux下分别怎么真正拿到文件的创建时间。

一、last_write_time的基本用法与返回值处理
last_write_time定义在<filesystem>头文件中,它有两个重载:一个只读,返回文件的最后修改时间;另一个可以传入时间点来修改文件时间。只读版本的签名很简单,传入一个path或者directory_entry,返回一个file_time_type类型的值。
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
int main() {
fs::path p = "test.txt";
if (!fs::exists(p)) {
std::cerr << "文件不存在" << std::endl;
return 1;
}
// 获取最后修改时间
fs::file_time_type ftime = fs::last_write_time(p);
// 转换为系统时钟时间点
auto systime = std::chrono::clock_cast<std::chrono::system_clock>(ftime);
std::time_t tt = std::chrono::system_clock::to_time_t(systime);
std::cout << "最后修改时间: " << std::ctime(&tt);
return 0;
}这里有个关键点:file_time_type的时钟是file_clock,它和system_clock不是同一个时钟,epoch(起始时刻)不同,不能直接拿来当时间戳用。C++20提供了clock_cast来做转换,非常方便。如果你还在用C++17,就得自己算偏移量,常用的写法是利用file_time_type::clock::to_sys,不过这个接口是libc++和部分标准库的扩展,MSVC支持得比较好:
// C++17下的转换写法(MSVC和较新的libstdc++可用)
auto ftime = fs::last_write_time(p);
auto sctp = std::chrono::system_clock::now();
auto fnow = fs::file_time_type::clock::now();
// 用两个时钟的当前时刻做差,抵消epoch差异
auto diff = std::chrono::duration_cast<std::chrono::seconds>(fnow.time_since_epoch())
- std::chrono::duration_cast<std::chrono::seconds>(sctp.time_since_epoch());
std::time_t tt = std::chrono::system_clock::to_time_t(sctp)
+ diff.count();除了读,修改时间也是一行代码的事:fs::last_write_time(p, fs::file_time_type::clock::now());就能把文件时间刷成当前时刻,写测试用例的时候特别有用。
二、为什么标准库没有"创建时间"接口
很多人第一反应是这是标准库的缺失,其实这是有意为之的设计。文件创建时间这个概念在不同操作系统上的语义完全不一样:Windows的NTFS会记录真实的创建时间(creation time),而Linux传统的ext系列文件系统只有inode中的ctime(inode状态变更时间)、mtime(内容修改时间)和atime(访问时间),很多发行版甚至默认挂载了noatime选项,连atime都不更新。Linux内核从4.11开始支持create birth time(statx系统调用的STATX_BTIME),但取决于文件系统是否真正记录这个值。
正因为语义不统一,C++标准委员会没有把它放进std::filesystem,否则同一个函数在Linux上返回的可能只是inode变更时间,会产生严重的误导。所以正确的做法是:需要创建时间时,直接调用平台原生API,Windows用GetFileAttributesEx,Linux用statx或stat结构体。
三、Windows下获取文件创建时间
Windows的方案最直接,GetFileAttributesEx一次调用就能拿到创建时间、最后访问时间和最后修改时间三组值,返回的是FILETIME结构,需要转换成Unix时间戳:
#include <windows.h>
#include <ctime>
#include <iostream>
// FILETIME转Unix时间戳
time_t FileTimeToUnix(const FILETIME& ft) {
ULARGE_INTEGER ull;
ull.LowPart = ft.dwLowDateTime;
ull.HighPart = ft.dwHighDateTime;
// FILETIME以1601-01-01起算,单位100纳秒,与1970相差11644473600秒
return static_cast<time_t>((ull.QuadPart - 116444736000000000ULL) / 10000000ULL);
}
int main() {
WIN32_FILE_ATTRIBUTE_DATA info;
if (GetFileAttributesExA("test.txt", GetFileExInfoStandard, &info)) {
time_t ct = FileTimeToUnix(info.ftCreationTime);
std::cout << "创建时间: " << std::ctime(&ct);
}
return 0;
}注意FILETIME的起点是1601年1月1日,和Unix时间戳的1970年差了11644473600秒,这个魔数一定要减,否则时间会差三个多世纪。另外如果文件在FAT32分区上,创建时间的精度只有2秒,做精确比较时要留意。
四、Linux下的stat与statx方案
传统做法是调用stat,拿到的st_ctime是inode变更时间而不是创建时间,这一点务必区分清楚。真正的创建时间要用较新的statx系统调用:
#include <sys/stat.h>
#include <iostream>
int main() {
struct stat st;
if (stat("test.txt", &st) == 0) {
std::cout << "内容修改时间: " << ctime(&st.st_mtime) << std::endl;
std::cout << "inode变更时间: " << ctime(&st.st_ctime) << std::endl;
}
return 0;
}如果内核版本在4.11以上且文件系统支持(ext4、btrfs、xfs都支持birth time),可以封装一个基于statx的实现,请求STATX_BTIME掩码,从返回的stx_btime字段拿到真实的创建时间。glibc从2.28开始提供了statx的包装函数,直接包含<sys/stat.h>就能用。
五、一套跨平台封装思路
把上面的平台代码用条件编译包起来,对外暴露统一的FileTimeInfo结构体,是工程里最常见的做法。可以配合std::filesystem做路径校验和遍历,时间获取部分按平台分发:
#include <filesystem>
#include <ctime>
#include <optional>
struct FileTimeInfo {
time_t create_time; // 创建时间
time_t modify_time; // 修改时间
};
std::optional<FileTimeInfo> GetFileTimes(const std::filesystem::path& p) {
if (!std::filesystem::exists(p)) return std::nullopt;
FileTimeInfo info{};
#ifdef _WIN32
WIN32_FILE_ATTRIBUTE_DATA attr;
if (!GetFileAttributesExA(p.string().c_str(), GetFileExInfoStandard, &attr))
return std::nullopt;
ULARGE_INTEGER u;
u.LowPart = attr.ftCreationTime.dwLowDateTime;
u.HighPart = attr.ftCreationTime.dwHighDateTime;
info.create_time = (u.QuadPart - 116444736000000000ULL) / 10000000ULL;
u.LowPart = attr.ftLastWriteTime.dwLowDateTime;
u.HighPart = attr.ftLastWriteTime.dwHighDateTime;
info.modify_time = (u.QuadPart - 116444736000000000ULL) / 10000000ULL;
#else
struct stat st;
if (stat(p.string().c_str(), &st) != 0) return std::nullopt;
info.create_time = st.st_ctime; // Linux回退为inode变更时间
info.modify_time = st.st_mtime;
#endif
return info;
}这样上层业务代码完全不用关心平台差异。还有一个小建议:如果只是判断文件是否被更新过(比如缓存场景),优先用last_write_time就够了,它的语义在各平台上一致,比创建时间可靠得多。只有用户明确需要"这个文件是什么时候生成的"这种信息时,才去走创建时间的平台API,这样代码的可移植性和正确性都能兼顾。
C++获取文件创建时间filesystem last_write_timeC++17文件时间修改时间:2026-09-13 22:43:00