为什么我不能将 GNU/Linux 机器的日期设置为纪元?

wiz*_*own 5 linux date timestamps

在我可以访问的 Raspberry Pi 和 Ubuntu 16.04 x86_64 机器上,运行

# date -s @0

回报

date: cannot set date: Invalid argument
thu 1 jan 1970 01:00:00 CET
Run Code Online (Sandbox Code Playgroud)

我想知道:

  • 不可能背后的原因是什么,
  • 是否有一种编程方式将系统时间重置为 Epoch。

Gil*_*il' 6

我无法在旧系统上重现 \xe2\x80\x9cInvalid argument\xe2\x80\x9d 的纪元,但是:

\n\n
# strace date -s \'@-1\'\n\xe2\x80\xa6\nclock_settime(CLOCK_REALTIME, {4294967295, 0}) = -1 EINVAL (Invalid argument)\nsettimeofday({4294967295, 0}, NULL)     = -1 EINVAL (Invalid argument)\nwrite(2, "date: ", 6date: )                   = 6\nwrite(2, "cannot set date", 15cannot set date)         = 15\nwrite(2, ": Invalid argument", 18: Invalid argument)      = 18\nwrite(2, "\\n", 1\n
Run Code Online (Sandbox Code Playgroud)\n\n

(然后date继续显示它想要设置的日期,即使它实际上尚未设置。)

\n\n

用简单的英语来说,date就是调用内核将日期设置为纪元之前的一秒,内核告诉它建议的日期无效。

\n\n

现在,在上面的跟踪中,您会看到秒数为 4294967295,而不是 -1。这个数字是 2^32-1,我在 32 位机器上运行它。这实际上不是问题的根源:它是 中的显示问题strace,它不知道该值是否应该被签名。事实上,秒数是一个有符号整数类型time_t。Linux 上的 Glibc 2.23 定义time_t__time_tin /usr/include/time.h__time_tas __TIME_T_TYPE/usr/include/bits/types.h __TIME_T_TYPEas和__SYSCALL_SLONG_TYPE中的__SQUAD_TYPE64位有符号类型。在内核方面,从 4.4 开始,调用参数类型的定义要么是秒是 a (在 64 位机器上是有符号 64 位类型),要么是 a是有符号 64 位类型在 32 位机器上键入。所以内核确实将 -1 视为 -1 而不是某个大的正数。/usr/include/bits/typesizes.h__SQUAD_TYPE/usr/include/bits/types.hsettimeofdaystruct timespec__kernel_time_ttime64_t

\n\n

你无法回到纪元之前的原因是 Linux 内核明确拒绝这样做。如果拒绝给定的时间结构,则系统调用返回 EINVAL (\xe2\x80\x9cInvalid argument\xe2\x80\x9d),并且该settimeofday函数拒绝负数秒数。timeval_valid

\n\n

不同版本的内核具有不同组织的代码,但我认为行为没有改变。

\n\n

我看不出有任何理由会拒绝正好为 0 的时间,并且它在我测试的虚拟机中有效。

\n\n

进行此完整性检查的原因大概是有代码将自纪元以来的时间存储为无符号整数,因此无法处理纪元之前的日期。某些代码可能会将 0 解释为 \xe2\x80\x9c,日期未知\xe2\x80\x9d,因此拒绝 0 也是有意义的,但 Linux 实际上并没有这样做,而且它只会持续无论如何。

\n\n

纪元之前的时代当然可以被再现和操纵。许多软件需要跟踪过去事件的时间。几乎每个操作系统使用的时区数据库都包含可追溯到时区编纂时(铁路开始使用的时间)的历史数据。但你无法告诉 Linux 计算机当前时间是 1970 年之前。

\n