(Oracle)Java JVM如何知道正在发生闰秒?忽略闰秒计算已用时间时无闰秒关于 java.time

2022-09-01 03:16:06

闰秒将于 2015 年 6 月 30 日发生。不同的操作系统似乎以不同的方式处理这种情况。在我的特殊案例中,我们正在运行一个带有自定义Java(JDK 1.7)软件的Red Hat 6.4系统,该软件在很大程度上依赖于时间。根据我最近发现的一些红帽发布的信息,我们系统的NTP守护程序将通过重复23:59:59两次来确保操作系统自动处理闰秒。

我的问题是:如果我有一个长时间运行的JDK 1.7进程,它怎么知道正在发生闰秒?我的意思是,Java最终如何知道IERS的人已经决定插入闰秒?日期文档似乎表明知道闰秒,但似乎无济于事。我是否可以假设 JDK,当构造或调用适当的对象时,它是传递到底层操作系统的日期时间处理,以获得适当的“实时”时间值?(在我的情况下,这听起来像是重复第二个23:59:59,因为这是操作系统将如何处理它的方式)。DateCalendar.getInstance()


答案 1

述的答案是正确的。此答案添加了一些由对该答案的评论触发的想法。该评论提到在安排闰秒时计算午夜经过的时间。

这个答案也确实解决了最初的问题,指出对于实际使用,闰秒的问题是没有意义的,被日期时间框架所忽略。

忽略闰秒

据我所知,所有常见的Java日期时间框架都忽略了闰秒。这些包括:

  • java.time
  • 城大时间
  • java.util.Date/.Calendar(现在被java.time和Joda-Time淘汰)

医生

以下是每个框架文档的摘录,表明它们有效地忽略了闰秒。粗体字的强调是我的。

Instant 类的 java.time 文档:

...鉴于上述精确计时的复杂性,此 Java API 定义了自己的时间尺度,即 Java 时间尺度。

Java 时间刻度将每个日历日划分为 86400 个细分,称为秒。这些秒数可能与 SI 秒数不同。它与事实上的国际民事时间尺度非常吻合,其定义不时变化。

Java时间尺度对时间线的不同部分的定义略有不同,每个定义都基于用作民事时间基础的共识国际时间尺度。每当修改或替换国际商定的时间尺度时,必须为其定义Java时间尺度的新部分。每个航段必须满足以下要求:

  • Java时间尺度应与基本的国际民事时间尺度紧密匹配;
  • Java时间尺度应与每天中午的国际民事时间尺度完全一致;
  • Java时间尺度应与国际民事时间尺度有精确界定的关系。

截至2013年,Java时间尺度目前有两个部分。

对于1972-11-03的段(下面讨论的确切边界),直到另行通知,共识国际时间尺度是UTC(带有闰秒)。在此段中,Java 时间尺度与 UTC-SLS 相同。这与没有闰秒的日子里的UTC相同。在有闰秒的日子里,闰秒在一天的最后1000秒内平均分布,保持每天正好86400秒的外观

对于1972-11-03之前的段,任意向后延伸,共识国际时间尺度被定义为UT1,按比例应用,相当于本初子午线上的(平均)太阳时(格林威治)。在这一部分中,Java时间尺度与共识国际时间尺度相同。两段之间的确切边界是 UT1 = UTC 在 1972-11-03T00:00 和 1972-11-04T12:00 之间的时刻。

使用 JSR-310 API 实现 Java 时间刻度不需要提供任何亚秒级精度或单调或平稳进行时钟。因此,实现不需要实际执行 UTC-SLS 压摆或以其他方式了解闰秒。但是,JSR-310 确实要求实现必须记录它们在定义表示当前时刻的时钟时使用的方法。有关可用时钟的详细信息,请参阅时钟。

Java 时间刻度用于所有日期时间类。这包括 Instant、LocalDate、LocalTime、OffsetDateTime、ZonedDateTime 和 Duration。

商大时间常见问题

Joda-Time不支持闰秒。可以通过编写新的专用年表或对现有的 ZonedChronology 类进行一些增强来支持闰秒。在任何一种情况下,Joda-Time的未来版本默认情况下都不会启用闰秒。大多数应用程序不需要它,并且可能会有额外的性能成本。

Java.util.Date 类文档

秒由从0到61的整数表示;值 60 和 61 仅出现在闰秒中,即使如此,也仅在实际正确跟踪闰秒的 Java 实现中出现

据我所知,OpenJDK和Oracle提供的实现跟踪闰秒。如果找到,请发布此类文档。

计算已用时间时无闰秒

因此,这些框架在计算经过的时间时不会报告额外的闰秒。

下面是一些示例代码,用于计算从午夜前一分钟到午夜过后的一分钟(2015 年 6 月 30 日至 7 月 1 日计划闰秒)的经过时间。此代码在 中测试 Joda-Time 2.8.1 和 java-time。我忽略了java.util.Date/。日历,因为我尽可能避免这些课程;如果需要,可以随意在此处为这种情况添加代码。java version "1.8.0_45"

第一次 Joda-Time

// Joda-Time 2.8.1
DateTime startJoda = new DateTime( 2015, 06, 30, 23, 59, 00, DateTimeZone.UTC );
DateTime stopJoda = new DateTime( 2015, 07, 01, 00, 01, 00, DateTimeZone.UTC );
long elapsedMillisJoda = ( stopJoda.getMillis( ) - startJoda.getMillis( ) );

System.out.println( "startJoda: " + startJoda + " stopJoda: " + stopJoda + " = elapsedMillisJoda: " + elapsedMillisJoda );

...和java.time ...

// java.time
ZonedDateTime startZdt = ZonedDateTime.of( 2015, 06, 30, 23, 59, 00, 00, ZoneOffset.UTC );
ZonedDateTime stopZdt = ZonedDateTime.of( 2015, 07, 01, 00, 01, 00, 00, ZoneOffset.UTC );
long elapsedMillisZdt = startZdt.until( stopZdt, ChronoUnit.MILLIS );

System.out.println( "startZdt: " + startZdt + " stopZdt: " + stopZdt + " = elapsedMillisZdt: " + elapsedMillisZdt );

运行时,我们看到偶数的结果,正好是两分,120秒或120,000毫秒。

startJoda: 2015-06-30T23:59:00.000Z stopJoda: 2015-07-01T00:01:00.000Z = elapsedMillisJoda: 120000
startZdt: 2015-06-30T23:59Z stopZdt: 2015-07-01T00:01Z = elapsedMillisZdt: 120000

关于 java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧日期时间类,如java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在处于维护模式,建议迁移到 java.time 类。

要了解更多信息,请参阅 Oracle 教程。搜索 Stack Overflow 以获取许多示例和解释。规格是JSR 310

使用与 JDBC 4.2 或更高版本兼容的 JDBC 驱动程序,您可以直接与数据库交换 java.time 对象。不需要字符串,也不需要java.sql.*类。

从哪里获取 java.time 类?

ThreeTen-Extra 项目通过其他类扩展了 java.time。这个项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuarter


答案 2

这取决于您的jdk版本。例如,如果您正在运行更新 80,则可以查看发行说明

JDK 7u80 包含 IANA 时区数据版本 2015a。有关更多信息,请参阅 JRE 软件中的时区数据版本。

然后按照时区数据版本的链接找到2015a。然后按照 TZ 更新程序版本 1.4.11 的链接操作

新的闰秒 2015-06-30 23:59:60 UTC,根据 IERS 公告 C 49。(感谢Tim Parenti。

它以前似乎没有被包括在内,所以如果你运行的是旧版本的JDK 7,你可能不会得到调整。有关其内部工作原理的更多信息,请参阅此处

老实说,我从未测试过它在实践中是如何工作的。