我最近阅读了很多关于 UNIX Time 的文章,其中大部分内容不连贯,大部分内容相互矛盾。我正在尝试协调 UNIX 时间(以下简称 UXT)、TAI 和 UTC 之间的转换,为此,我需要正确理解 UXT。问题是,我似乎找不到其他人这样做了。
以下是我通过繁琐研究从无数来源重建的最佳解释。也有哪里不对。 我正在寻找对以下内容的整体分析和逐点验证/反驳。本质上,修复以下内容以使其正常工作。
TAI 是一个单调递增的时间标准。它滴答 SI 秒,并忽略 DST 和闰秒。
UTC 与 TAI 相同,但通过整数个闰 SI 秒进行校正(转换为时间字符串将其反映为第 60 秒),以便在天文时间标准 UT1 的 0.9 SI 秒内。
UXT 是自 1970-01-01 00:00:00 UTC 以来的UNIX 秒计数。每天总是正好有 86400 秒。然而,UXT 与 UTC 相关。
这怎么可能?嗯,UNIX 秒需要与 SI 秒不同,并且因为闰秒不是完全规则的,UNIX 秒不能是一个明确定义的时间长度。
UNIX 规范第4.15 节中从 UTC 到 UXT 的转换将不同的 UTC 时间别名为相同的 UXT 时间戳,从而有效地使 UNIX 秒与 SI 秒相同(UNIX 闰秒除外,它们是两个 SI 秒)。
在实践中,实际发生的情况各不相同。大多数计算机基于远程服务器进行同步,因此它们在同步期间隐式地处理闰秒更新。
所有这一切意味着,虽然每个单独的 UXT 时间戳都可以轻松转换为 UTCgmtime或从 UTC 转换(分别使用或 §4.15),但您无法真正通过算术来找出使用它们的任何内容。特别是,difftime返回 UNIX seconds,所以你不能用它做任何事情,包括将它添加到不同的时间戳,除非你知道所有相关的闰秒在哪里。
我想到目前为止我都明白了。
difftime并且只是希望它足够好(或者不知道存在问题),但是计时库也是错误的。
libtai提供了一个转换(tai_now.c:7从UXT如)到TAI: TAI := 4,611,686,018,427,387,914 + UXT。由于 TAI 以 SI 秒为单位,而 UXT 以 UNIX 秒为单位,因此您不能这样做。然而,由于libtai明确处理闰秒,因此这是一个粗心的错误似乎并不合理。
libtai. 你到处都能看到这种事情。所以:第 1-6 点与第 7 点不一致。也就是说,大量现有代码与其所代表的时间标准相矛盾。 什么地方出了错?
小智 5
问题是大多数文档不使用可以区分时间尺度而没有歧义句子的词汇。我建议http://www.ucolick.org/~sla/leapsecs/picktwo.html作为对历史使用中存在两种不相关的秒的问题的介绍 - 一种是居民日历日的细分地球,并且是在特定参考系中测量的恒定持续时间。任何跨越 1970 年之前和之后日期的时间库都试图利用这两种秒,最终提供的答案类似于声称提供 arcsin(-2) 的函数——也就是说,有所涉及的复杂性需要仔细解释和定义到底什么是重要的。
出色地 。。。未来的你在这里,写作是为了一种结束的感觉。
\n在你写这个问题几年后,但还是在今天之前几年,你写了一个巨大的教程来解决这个问题。它构成了现代时间标准的完整引导,从 TAI 到 UTC 到偏移量和 UXT。
\n至于你的问题,看来你确实得到了基本正确的点 1\xe2\x80\x936 。你的第 7 点可以概括为“软件在这方面不可能这么糟糕,对吧?”
\n剧透警告:可以。关于 libtai 的特别抱怨不是一个很好的例子\xe2\x80\x94,该库包含闰秒,但只能通过组合适当的函数,而不仅仅是您查看的单个函数。对我们来说,这感觉像是一个巨大的(并且明显令人困惑的)缺陷,即使它最终得到了正确的答案(谁知道它是否正确),但喋喋不休是没有用的,因为这种事情非常常见。不仅大多数软件实际上会出错,而且软件会以不同的、不兼容的方式出错!
\n哎呀,基本 UXT 原语的文档time(...)甚至都不清楚。标准 C 文档(参考),(参考),遵循 POSIX 标准(参考)(的一部分),表示它是自纪元以来的秒数。但在 POSIX 标准(参考文献)的其他地方,它不是秒数,而是从纪元开始的秒数,不包括闰秒,这是正确的表征。这个想法是为了让计算变得更简单,但这显然会让计算变得更困难,因为当你在数轴上引入不连续性时,减法不起作用。difftime(...)例如,任何使用 的程序都必然是不正确的。
这里的关键思维模型是 SI 秒和 UNIX 秒是不同的。这一点在 POSIX 规范的另一个地方最为清楚。(参考)。本节通过日期时间转换将 UXT 与 UTC 挂钩。UTC 包括闰秒,但本节还指定天的长度为 86\xe2\x80\xaf400 秒。我们怎样才能调和这一点呢?关键的认识是,有些 UNIX 秒的长度是 2 SI 秒,有些是 0(显然,绝大多数是 1)。还困惑吗?去阅读教程吧!现实世界的代码如此混乱也就不足为奇了。
\n您很遗憾无法引导读者编写代码。您编写了自己的计时库,但它既复杂又强大,而且还没有真正适合生产。
\n我想,首先,调用time(...)以获取 UNIX 秒的 POSIX 历元相对计数,减去 220\xe2\x80\xaf924\xe2\x80\xaf790,并添加闰秒(从表中)以获得 TAI- SI 秒的纪元相对计数。反向执行即可返回。(注:TAI 纪元有些含糊不清;我选择了 1977-01-01 00:00:00 TAI(即 1976-12-31 23:59:45 UTC),因为这是一个纪元标准化 TAI、JD TAI、TCB、TCG、TT 和 TDB (参考)。)
在 C++20 中,我们将使用std::chrono::tai_clock,纪元为 1958-01-01 00:00:00 TAI(即 1957-12-31 23:59:50 UTC)。希望这是正确的,我们可以把这整个肮脏的事情抛在脑后。