Ruby中的负Unix时间戳

前端开发 2026-07-12

我有如下的Unix时间戳:

time = Time.at(-8_640_000_000_000) # => -271821-04-19 19:00:00 -0500
time.strftime('%B %-d, %Y at %-I:%M %p') # => "April 19, -271821 at 7:00 PM"

但在Discord通过时间戳格式显示同一时间时:

<t:-8640000000000> # => November 19, 271817 at 7:03 PM

显示的时间戳差异极大。我猜测这里可能发生了某处的精度损失。是否有办法判断一个负的Unix时间戳何时会过于陈旧而无法可靠表示?

解决方案

是否有办法知道负的Unix时间戳何时会过于陈旧而无法可靠表示?

没有可靠的方法。取决于具体平台选择以何种方式表示时间戳,以及日期/时间库有多么天真。传统上,大多数Unix平台将Unix时间戳表示为32位二进制补码有符号整数,这使得可以表示的时间戳范围为 -2_147_483_648+2_147_483_647,对应1901-12-13T20:45:52Z–2038-01-19T03:14:07Z。这被称为 Y2k38问题,类似于 Y2k问题

将Unix时间戳存储为 无符号 32位整数的系统可以表示的时间范围是1970-01-01T00:00:00Z–2106-02-07T06:28:15Z。

使用带符号64位整数来表示Unix时间戳的系统可以表示的时间范围是 -292277022657-01-27T08:29:52Z–292277026596-12-04T15:30:07Z。

维基百科列出了上述以及更多此类 时间格式化和存储错误 的条目。

你的时间戳需要大约43位来表示,因此显然超出了这两种32位范围中的任意一个。

不过,Discord输出的时间戳也超出了32位范围,因此不能简单地用32位溢出来解释。TypeScript/ECMAScript numbers能够准确表示高达53位的整数,因此也不是ECMAScript number 精度问题。 (Discord的部分实现使用TypeScript。)

鉴于符号翻转,这似乎是某种上溢/下溢/回绕/滚动的问题,而不是精度问题。

真正的问题在于ECMAScript Date 对象只能表示Unix纪元前后约 8_640_000_000_000_000 毫秒范围内的日期。(这个限制也存在于更新的 Temporal API中。)现在,你的时间戳恰好是 -8_640_000_000_000_000 毫秒,因此它应该正好在边界处,因此是正确的,对吧?好吧,它理论上确实是UTC,但似乎被解释为本地时间,且该时区在UTC之下,这会把时间戳放在可表示的最早瞬间之前。

不过,这里还存在其他问题:我不认为实际上存在一个“正确”的解释。按照POSIX,Unix时间戳是绑定到UTC的。但UTC只在1972年被定义,之前并不存在。

此外,几乎所有使用“朴素”日期/时间库的软件都假设格里高利历,而格里高利历也是现代的发明:最早采用它的国家在1582年 10月 15日,最后一个直到2016年才采用!你的日期明显处在石器时代,甚至勉强进入Homo sapiens出现的阶段。日历的概念不存在,小时、分钟、秒的概念也不存在——在那个时间段要求一个具体日期,在很多方面都毫无意义。

回到你的问题:

是否有办法知道负的Unix时间戳何时会过于陈旧而无法可靠表示?

根据地点/辖区、存储格式以及所使用的库,可能晚至2016、1972、1970、1901、1582、公元前271821年,或处于之间或更远的时间。如果你想浪费一个下午,我鼓励你阅读这份关于日期与时间的程序员误解的元清单中列出的文章。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章