6

我正在做一个 Java7 项目,我们需要一个国际原子时间的时间戳。我发现了一些与此相关的其他问题,这些问题指向 JSR-310 和 ThreeTen 项目(正在实施 JSR-310):

如何在 Java 中获取 GPS 时间和 TAI 时间?

http://www.coderanch.com/t/549178/java/java/TAI-Atomic-Time-International

但是,我正在努力弄清楚 Java 7 使用什么以及从哪里获取它。ThreeTen 似乎有旧的 SourceForge 和 GitHub 页面,以及一个 OpenJDK 页面。

我找到了 Java 7 backport,但是从 Maven 下载它后,它不包含我真正需要的 TAIInstant 类(TIAInstant 类在 ThreeTen SourceForge JavaDoc 中的 javax.time.TAIInstant 下列出)。

为了完整起见,这是我的 pom.xml 的摘录:

<dependency>
  <groupId>org.threeten</groupId>
  <artifactId>threetenbp</artifactId>
  <version>0.8.1</version>
</dependency>

我应该使用其他东西,我应该从哪里得到它?

注意:抱歉,我无法提供指向我所指的所有页面的链接,StackOverflow 不会让我在没有更高代表的情况下每个帖子有 > 2 个链接。

[编辑] 想要 TAI 的原因是我需要一个单调递增的时间戳,我相信 TAI 满足(即使在正负闰秒期间,因为它不关心闰秒,所有秒数都一样,包括闰秒)。

从各种来源阅读了有关 POSIX / Unix 时间的信息后,我仍然不清楚 Unix 时间在闰秒内到底发生了什么。我知道 Unix 时间在指代 UTC 时间方面是模棱两可的,但我不清楚在闰秒发生的那一刻 Unix 时间会发生什么?例如,Unix 时间会“暂停”还是倒退?也许更重要的是,即使它不应该根据 Unix 时间规范,Unix 实现是否真的遵守关于闰秒的规范......?

最后,我是否正确地说 System.currentTimeMillis() 将获得相当于 POSIX 时间(尽管以毫秒而不是秒为单位)?

注意,我需要一个可跨 JVM 和机器移植的对象(排除 System.nanoTime() 或类似的)。

[结论]

TAI
TAI 是一个测量时间的系统,其中每一秒都被计算并且“所有秒数都相等” - 即。每一秒包含相同的时间段,所有秒(包括闰秒)都计入总秒数。这意味着 TAI 中的秒数(从某个任意起点开始计算,例如 Unix 纪元)是一个单调递增的整数。

POSIX 时间
POSIX 时间是测量时间的标准(不是实现)。它将每一天定义为恰好有 86400 秒。因此,POSIX 时间不计算闰秒(因为偶尔一分钟可能有 61 秒,导致天数 >86400 秒,理论上一分钟可能有 59 秒,导致天数 <86400 秒)。这意味着 POSIX 中的“秒”具有可变长度,并且在闰秒之前/期间/之后不久,POSIX 时钟可能会跳过几秒或重复它们。具体来说,Meno Hochschild 在他的回答中引用的 POSIX 规范指出:“自 Epoch 以来的实际时间与当前秒数之间的关系是未指定的。”

UTC
UTC 是与地球围绕太阳运动的方式有关的时间标准,旨在保持太阳位置与一天中的时间(在阈值内)之间的关系。即在地球的 UTC+0 区域,太阳总是在 UTC 时间的中午处于最高位置。闰秒(正或负)是必要的,因为地球自转的速度不是固定的,并且它不会以可预测的方式变化(这意味着我们无法预测何时需要闰秒 - 或者它们是正闰秒还是负闰秒)

代表时间
在我看来,TAI 和 POSIX 都是“秒数”的表示(即计算机实际存储的容易的东西),而 UTC 是时间的“人类解释”(即年/月/日Hour:Minute:Second.millisecond) 通常不由计算机内部存储。

翻译时间
鉴于上述情况,从 POSIX(不计算闰秒)到 TAI(计算闰秒)存在许多问题:

  1. 它需要维护一个表/闰秒计数来将任何 POSIX 时间转换为 TAI 时间
  2. 即使解决了第 1 点,上述 POSIX 规范也不能保证闰秒期间会发生什么,因此在这种情况下,我们无法准确表示一个明确的时间
  3. 如果多个系统必须通信,在它们之间传递时间戳,我们必须保证闰秒表/计数保持一致

另一方面,很容易从 POSIX 转换为 UTC 的“人类解释”。它不需要闰秒的知识,因为它只是假设每天都有相同的秒数(尽管其中一些“秒”实际上具有不同的时间长度)。在实践中,您只需使用 POSIX 规范中公式的倒数来获得各种 UTC 时间分量(再次,请参阅 Meno Hochschild 引用的 POSIX 规范)。

4

2 回答 2

4

在 JSR-310 过程中删除了classTAIInstant和其他类似的东西。UTCInstant该小组得出的结论是,这些是专业项目,不需要在核心 JDK 中。我仍然相信这是正确的决定。

这样做的结果是 JSR-310 中对时间尺度的支持非常少。但是,有一种正式定义的方法可以将 JSR-310 连接到时间刻度,允许在给定准确源的情况下创建准确的时钟。虽然不是每个人都喜欢这个解决方案,但它对于主流来说是实用的。

总之,为 UTC 和 TAI 单独类的原始设计是合理的(并且对于处理过去和未来的事件是必要的)。但是,它对于 JDK 来说太专业了。

threeten -extra项目现在可以作为 JDK 8 的 jar 使用,其中包含这些类

于 2013-12-25T20:37:04.703 回答
3

JSR-310-backport 仅支持将包含在 Java 8 中的功能。TAI(和真正的 UTC)将不是受支持的功能,因此它不能在 backport 中。唯一的选择是尝试包含类 TAIInstant 的 threeten-extra-project 但整个额外项目可能不是最新的(非常旧的代码)。

我自己在我的库 Time4J 上工作,除了 POSIX 之外,它还支持 TAI、GPS 和 UTC。

[ 2014 年 7 月更新:Time4J 现在可作为稳定版本time4j-v1.0使用。这场辩论已被考虑在内 - 例如参见Moment.toString(TimeScale) 。]


对OP新发布的问题的更正和详细说明:

a) 是的,即使在闰秒期间,TAI 也以 SI 秒为单位单调递增。如果只是你想要的,你可能会选择TAI,但有一个陷阱。如果您想描述民用时间戳,那么 TAI 会给您错误的时间戳(只需比较维基百科图表的第一列和第二列)。原因很简单,公民生活是由 UTC 而不是 TAI 统治的。

b)关于我对维基百科图表错误的评论,我再次仔细查看并改变了主意。POSIX 和 TAI 之间的关系不是固定的(仅在 1972 年偏移 10s),所以请原谅我的错误。到目前为止,我还没有过多地考虑 TAI,而是考虑 POSIX 和 UTC。但是感谢这个问题和这场有启发性的辩论,所以你应该得到我的支持。整个事情很棘手。当我们谈论以不同时间尺度表示的时间戳时,我们需要区分 ymdhms-form 和 epochsecs-form。让我们详细考虑时间 1999-01-01T00:00:00Z(UTC 刻度):

  i) TAI = (29 * 365 + 7) days * 86400 + 22 leap seconds + 10s (offset at 1972) = 915148832
 ii) UTC = TAI - 10 = 915148822 (fixed relation between UTC and TAI on epoch-second-level)
iii) POSIX = UTC - 22 leap seconds = 915148800

=> (ymdhms-form);
  i) TAI (915148800 + 32) = 1999-01-01T00:00:32 (based on TAI-"day" = 86400 SI-secs)
 ii) UTC = 1999-01-01T00:00:00 (stripped off former 22 leap secs in conversion to ymdhms)
iii) POSIX = 1999-01-01T00:00:00 (fixed relation between UTC and POSIX with exception of leapsecs)

那么为什么说 TAI 不计算闰秒呢?它不计算 ymdhms 形式的闰秒,但当然它会在纪元秒级别上计算它们(单调性要求!)。和POSIX?它根本不计算闰秒,既不在 ymdhms 形式也不在 epoch-secs 级别。所以最后我们在 TAI 和 POSIX 之间没有固定的关系。转换需要闰秒表。

c) POSIX 规范对闰秒行为有何说明?见这里。特别注意以下语句:“自 Epoch 以来的实际时间与当前秒数之间的关系是未指定的。” 所以这也涉及闰秒。如果它们在闰秒之前跳转或在闰秒之后跳转或冻结一秒,则取决于时钟实现。

d) 是的,System.currentTimeMillis()将获得相当于 POSIX 时间(尽管以毫秒而不是秒为单位)。

e) 请注意,标签 TAI 不是在 1971 年之前定义的,国际原子时间不是在 1958 年之前定义的,因此三十级 TAIInstant 的预测尺度在某种程度上是胡说八道。所以我不会在 1972 年之前申请 TAI。这里我和时间尺度专家史蒂夫艾伦一起去。

f)如果您需要一个“可跨 JVM 和机器移植”的时间对象,那么 UTC 本身需要在任何地方都分布/存在相同的闰秒表。如果您选择 TAI,您仍然需要此闰秒表,以使应用程序能够将 TAI 时间戳转换为 UTC 或 POSIX 时间戳。所以我怀疑你可以有一个单调增加的时间戳并同时忽略闰秒。TAI 没有为这种困境提供解决方案。

2013 年 12 月 31 日对 OP 问题/摘要的回答:

您对 TAI 和 POSIX 的总结是正确的。

关于 UTC,您首先应该明白 UTC 是一种妥协。一方面,它被设计为跟随太阳,另一方面,UTC 尺度上的秒与 TAI 尺度上的秒完全相同,即 SI 秒(原子定义)。当您说地球自转速度出乎意料地减慢时,您是对的,因此插入了几次闰秒。UT1(平均太阳时)与 UTC 之间的差异应始终小于 0.9 SI 秒。所以这和等SI秒的事实是UTC的核心思想。顺便说一句,JSR-310 中奇异的 UTC-SLS 尺度的适应与 UTC 的这些核心思想不兼容。关于闰秒的可预测性,巴黎的 BIPM 每半年宣布一次,如果 6 个月后是否需要闰秒,

关于UTC部分的一个可能是迂腐的修正,你写道:“太阳总是在UTC时间的中午处于最高点。” 在 java.time.Instant 类的 javadoc 中也给出了关于所谓的 java time-scale 的类似声明。撇开这个事实不谈,你肯定不想说太阳的位置与你当地的位置无关,它甚至在正午的正确经度上都不正确。为什么?从天文/科学的角度来看,您首先不应忘记平均太阳时间与您观看的真正当地太阳时不同(只需给出关键字“时间方程”)。此外,由于 UTC 仍然基于原子时间并使用原子时间进行同步,因此在 UT1 和 UTC 之间存在所谓的 delta-T-relation。这个增量在 0 的范围内。

在我看来,“代表时代”这一部分有点太简单了。好的,我们可以说 TAI 和 POSIX 计算秒数,而 UTC 则以年/月/日/...形式表示。但我们确实可以将这两种表示应用于所有尺度。但我们需要仔细区分这些表示,并彻底思考如何转换。请注意,维基百科甚至在图中为 TAI 选择了 ymdhms-form。好吧,计算机可以最好地存储简单的整数。POSIX 或 TAI 可以很容易地以这种格式存储。但正如我之前所说,这些整数的解释并不总是简单的。在 TAI 的情况下,您甚至需要一个闰秒表来转换为可读的民用 ymdhms 格式(UTC 或 POSIX)。

关于下一节“翻译时间”,我同意第1-3点。

您的最后陈述“另一方面,很容易从 POSIX 转换为 UTC '人类解释'。” 是正确的,除了闰秒。好吧,我在即将到来的图书馆中启用了不同尺度之间的适当转换。它有一个内置但可配置的闰秒表,将来我还计划使用 IANA-TZDB-data 作为此类表的源。

总而言之,要意识到大多数业务开发人员不需要如此精确的事实。大多数人会简单地平衡 POSIX 和 UTC,并且可能会对 Linux 操作系统或 Google NTP 服务器上的任何平滑硬件解决方案感到满意。真正的 UTC 和 TAI(考虑到闰秒)需要更多的努力。所以你必须决定你的软件架构是否需要科学的准确性。

需要注意的是,JSR-310 官方根本没有解决非常广泛的 POSIX,而是他们说,他们的类 Instant 根据定义应该是 UTC-SLS(另见这个有趣的辩论)。

最后,我很高兴参加这次讨论。它还帮助我澄清了我对图书馆中 TAI 的想法。谢谢。

于 2013-12-23T16:21:41.023 回答