获取文件修改时间的纳秒精度

7
我需要在Python 2程序中遍历文件系统树,并获取每个文件的完整纳秒精度修改时间戳。我希望在Python中完成这个操作,因为为每个文件生成新的子进程会很慢。
在Linux的C库中,您可以通过查看stat结果的st_mtime_nsec字段来获取纳秒级精度的时间戳。例如:
#include <sys/stat.h>
#include <stdio.h>
int main() {
    struct stat stat_result;
    if(!lstat("/", &stat_result)) {
        printf("mtime = %lu.%lu\n", stat_result.st_mtim.tv_sec, stat_result.st_mtim.tv_nsec);
    } else {
        printf("error\n");
        return 1;
    }
}

打印 mtime = 1380667414.213703287/ 在支持纳秒时间戳的 ext4 文件系统上,时钟为 UTC)。

同样地,date --rfc-3339=ns --reference=/ 打印 2013-10-01 22:43:34.213703287+00:00

Python(2.7.3)的 os.path.getmtime(filename)os.lstat(filename).st_mtime 将 mtime 给出为 float。但是,结果是错误的:

In [1]: import os
In [2]: os.path.getmtime('/') % 1
Out[2]: 0.21370339393615723
In [3]: os.lstat('/').st_mtime % 1
Out[3]: 0.21370339393615723

——只有前6位数字是正确的,可能是由于浮点误差导致的。

3个回答

10
os.stat('/').st_mtime是一个浮点型对象,但是浮点数的精度对于纳秒级别的时间戳来说太低了。
Python中浮点数的底层类型是IEEE 754双精度浮点数,仅适用于大约16位十进制数字。在小数点前有十个数字的情况下,只剩下六个数字用于亚秒分辨率,这比保留POSIX纳秒分辨率时间戳所需的范围短三个数字。 via: This Week in Python Stupidity: os.stat, os.utime and Sub-Second Timestamps 如果您可以使用Python 3,那么有一个新属性称为 st_mtime_ns ,它是以纳秒为单位的 st_mtime 。试一试。
>>> os.stat('.').st_mtime
1381571932.044594
>>> os.stat('.').st_mtime_ns
1381571932044593972

参考资料:

PEP 410 -- 使用 decimal.Decimal 类型表示时间戳

os.stat():增加新字段以获取纳秒精度的时间戳 Decimal 对象

向 os.stat() 添加 st_*time_ns 字段,向 os.utime() 添加 ns 关键字,os.utimens() 期望传入纳秒数


3

或者你可以使用cffi库,它可以在Python 2中运行,以下是相应的代码(在Linux上进行了测试):

from __future__ import print_function

from cffi import FFI

ffi = FFI()
ffi.cdef("""
typedef long long time_t;

typedef struct timespec {
    time_t   tv_sec;
    long     tv_nsec;
    ...;
};

typedef struct stat {
    struct timespec st_mtim;
    ...;
};

int lstat(const char *path, struct stat *buf);
""")

C = ffi.verify()

result = ffi.new("struct stat *")
p = C.lstat("foo.txt", result)
print("mtime = {0:d}.{1:09d}".format(result.st_mtim.tv_sec, result.st_mtim.tv_nsec))

这与你在问题中提到的C程序具有相同的行为。 这将产生以下输出:
$ ./test.py
mtime = 1381711568.315075616

与您的C程序具有相同的精度:

$ gcc test.c -o test
$ ./test
mtime = 1381711568.315075616

2
{1:d} 应更改为 {1:09d} - falsetru
我不确定那会有什么太大的影响 :) 但我还是会进行修改 :) - James Mills
尝试 import os; open('/tmp/a.tmp','w').close(); os.utime('/tmp/a.tmp', (1.0001, 1.001)); stat = ffi.new('struct stat *'); print(os.path.getmtime('/tmp/a.tmp')); c.stat('/tmp/a.tmp', stat); print('{}.{}'.format(stat.st_mtim.tv_sec, stat.st_mtim.tv_nsec)) - falsetru

1
我本来也想说和 glasslion 一样的话 - Python 会将 st_mtime 转换为浮点数,这会导致丢失重要数字。
另一种选择是使用 "ctypes" 模块或 cython 直接访问 C 库,这应该返回一个漂亮的无符号长整型(unsigned long)纳秒字段(因为你没有提供关于操作系统的任何信息,所以无法给出示例)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接