os.Stat 是读取文件修改时间的唯一可靠方法,返回 FileInfo 的 ModTime() 值;需检查错误、注意时区统一、避免循环调用以提升性能。

用 os.Stat 读取文件修改时间最直接
Go 标准库不提供单独“只读修改时间”的函数,os.Stat 是唯一可靠入口。它返回 os.FileInfo,其中 ModTime() 方法给出的就是最后修改时间(time.Time 类型)。
常见错误是误用 os.Lstat(只对符号链接本身有效)或忽略错误处理——哪怕文件存在,权限不足或路径跨挂载点时也会失败。
- 必须检查
err != nil,不能假设os.Stat总成功 - 如果目标是符号链接,
os.Stat返回的是目标文件的时间;要链接本身的元信息,才用os.Lstat -
ModTime()在 Windows 和 Unix 系统下语义一致,但底层精度不同(Windows 通常为 100ns,ext4 默认为秒级,XFS 可到纳秒)
fi, err := os.Stat("config.json")
if err != nil {
log.Fatal(err)
}
fmt.Println(fi.ModTime()) // 2024-05-22 14:30:11.123456789 +0800 CST
注意 time.Time 的时区和比较陷阱
ModTime() 返回的 time.Time 值默认带本地时区信息(由系统决定),但文件系统存储的其实是 UTC 时间戳,Go 在读取时做了自动转换。这意味着:直接用 Before/After 比较两个不同来源的 time.Time 是安全的;但若手动构造时间做比较,必须统一时区。
- 不要写
time.Now().After(fi.ModTime())来判断“是否过期”——time.Now()是本地时区,fi.ModTime()也是本地时区,表面看没问题,但跨机器部署时可能因系统时区配置不一致出错 - 更稳妥的做法是全部转成 UTC:
time.Now().UTC().After(fi.ModTime().UTC()) - 如果要序列化时间(比如存进 JSON 或日志),记得调用
.UTC().Format(...),避免时区歧义
批量读取多个文件时别反复调用 os.Stat
在循环里对每个文件都跑一次 os.Stat 是常见性能坑——每次调用都触发一次系统调用,开销远高于内存操作。尤其在目录下有几百个文件时,延迟会明显上升。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 优先用
filepath.WalkDir(Go 1.16+)或filepath.Walk一次性遍历,它们在回调中直接提供DirEntry或FileInfo,避免重复 stat - 如果只是需要修改时间,
DirEntry.Info()内部仍会调用Stat,但比手写循环调用更可控;若只需文件名和类型,用DirEntry.Type()和DirEntry.Name()完全免 syscall - Linux 下可考虑
getdents64系统调用(需 cgo),但绝大多数场景没必要——标准库已足够
Windows 上的硬链接和重解析点会影响 ModTime 吗?
不影响。硬链接共享同一 inode(Windows 是 MFT 记录),所有链接的 ModTime 始终一致;重解析点(如符号链接、目录交接点)本身没有独立的修改时间,os.Stat 总是穿透到目标,返回目标的 ModTime。
真正容易被忽略的是:Windows 默认禁用对符号链接的支持(需管理员权限创建,且普通用户调用 os.Stat 时可能静默失败或返回错误)。如果你的程序在 CI 或容器里跑,很可能根本没启用符号链接支持。
- 测试前确认:
fsutil behavior query SymlinkEvaluation(Windows CMD) - 代码里别假设
os.Stat对符号链接一定成功——加errors.Is(err, fs.ErrPermission)判断并降级处理 - 重解析点(junction)和符号链接行为不同,但 Go 层面对使用者透明,无需区分

















