(Oracle)Java JVM如何知道闰秒发生了什么?

时间:2015-06-22 15:59:44

标签: java datetime time utc

2015年6月30日的leap second will occur。不同的操作系统似乎以不同的方式处理这种情况。在我的特定情况下,我们运行的Red Hat 6.4系统具有严重依赖于时间的自定义Java(JDK 1.7)软件。根据我发现的一些recent Red Hat released information,我们系统的NTP守护进程将确保操作系统通过重复23:59:59两次来自动处理闰秒。

我的问题是:如果我有一个长时间运行的JDK 1.7进程,它是如何知道闰秒发生的?我的意思是,Java最终如何知道IERS人已决定插入闰秒? Date文档似乎表明它已经知道闰秒,但似乎无益于模糊。我可以假设JDK,当构造适当的Date对象或调用Calendar.getInstance()时,它是传递给底层操作系统的日期时间处理以获得适当的&#34 ;实"时间价值? (在我的情况下,听起来它会重复第二次23:59:59,因为那是操作系统将如何处理它。)

4 个答案:

答案 0 :(得分:24)

The Answer之前的{p> assylias是正确的。本答案增加了一些由该答案评论引发的想法。该评论涉及计算在安排闰秒的午夜时间的经过时间。

本答案确实解决了原始问题,指出在实际应用中,闰秒问题没有实际意义,被日期时间框架所忽略。

忽略闰秒

正如我所理解的,所有常见的Java日期时间框架都忽略了闰秒。其中包括:

  • java.time
  • Joda-Time
  • java.util.Date/.Calendar(现在已经过时了java.time& Joda-Time)

文档

以下是每个框架文档的摘录,显示它们实际上忽略了Leap Seconds。粗体的重点是我的。

Instant类的java.time文档:

  

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

     

Java Time-Scale将每个日历日划分为86400个细分,称为秒。这些秒可能与SI秒不同。它与事实上的国际民事时间表紧密匹配,其定义随时变化。

     

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

     
      
  • Java时标应与基础国际民用时标紧密匹配;
  •   
  • Java时标应与每天中午的国际民用时标完全匹配;
  •   
  • Java时标应与国际民用时标具有精确定义的关系。
  •   
     

目前,截至2013年,Java时间范围内有两个部分。

     

对于1972-11-03的段(下面讨论的确切边界),直到另行通知,共识国际时间刻度为UTC(具有闰秒)。在此段中,Java Time-Scale与UTC-SLS相同。这与没有闰秒的天数相同。在闰秒的日子里,闰秒在当天的最后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 FAQ

  

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

java.util.Date class doc

  

第二个由0到61的整数表示;值60和61仅在闰秒发生,甚至仅在实际正确跟踪闰秒的Java实现中

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

计算经过时间时没有闰秒

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

以下是计算2015年6月30日至7月1日安排Leap Second时从午夜到分钟之间的经过时间的示例代码。此代码在java version "1.8.0_45"中测试Joda-Time 2.8.1和java-time。我忽略了java.util.Date/.Calendar,因为我尽可能避免使用这些类;如果需要,可以随意在这里添加代码。

首先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及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和& SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

使用符合JDBC driver或更高版本的JDBC 4.2,您可以直接与数据库交换 java.time 对象。不需要字符串也不需要java.sql。* classes。

从哪里获取java.time类?

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

答案 1 :(得分:11)

这取决于你的jdk版本。例如,如果您正在运行更新80,则可以检查the release notes

  

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

然后点击timezone data versions的链接,找到2015a。然后按the link to TZ Updater version1.4.11

  

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

之前似乎没有包含它,如果您运行的是旧版本的JDK 7,则可能无法进行调整。有关其内部工作原理的更多信息,请访问here

说实话,我从未测试过如何在实践中发挥作用。

答案 2 :(得分:5)

这些是我的实际操作,在RHEL 6.3上运行实时系统,使用Oracle Java JDK 1.7.0_55-b13(即:过时的JDK)。我添加了启动的调试代码,在2015年6月30日23:59 UTC的闰秒之前每0.5秒记录一次新的DateTime(),然后在闰秒结束后启动。记录如下(对于好奇,打印的双数是自J2000 epoch以来的秒数)

2015-06-30 23:59:59.316 18349217 [main] INFO  - Leaper's time is 488980799.316000 (Tue Jun 30 23:59:59 GMT-00:00 2015)
2015-06-30 23:59:59.817 18349718 [main] INFO  - Leaper's time is 488980799.816000 (Tue Jun 30 23:59:59 GMT-00:00 2015)
2015-07-01 00:00:00.317 18350218 [main] INFO  - Leaper's time is 488980800.317000 (Wed Jul 01 00:00:00 GMT-00:00 2015)
2015-07-01 00:00:00.817 18350718 [main] INFO  - Leaper's time is 488980800.817000 (Wed Jul 01 00:00:00 GMT-00:00 2015)
2015-07-01 00:00:01.318 18351219 [main] INFO  - Leaper's time is 488980801.318000 (Wed Jul 01 00:00:01 GMT-00:00 2015)

不幸的是,这并没有告诉我多少,除了它没有在23:59:60插入规范的闰秒(因为assylias&Basil Bourque的回答表明会发生) )。我的希望是那个新的日期()'将达到底层操作系统(RHEL 6.3,据称可以正确计算闰秒)来询问当前时间。情况似乎并非如此。

我没有资源运行JDK版本和RHEL版本的任何其他组合来测试效果。我目前最好的猜测是,Basil Bourque在当天的最后1000秒内发现闰秒的文档适用于JDK 1.8及更新版本(因为它是Instant文档的一部分,这是Java 8的一个特性)。对于我的特殊情况,由于我的旧JDK 1.7.0_55没有2015年的闰秒调整,我认为assylias的观察适用。也就是说,我们的实时流程现在提前1秒运行,没有注意到闰秒。

在这里学到的经验?如果您想确保它们考虑即将到来的闰秒,请确保您的实时系统已完全修补最新更新。我必须做一些观察和日志分析,但我们的特定系统现在可能比实时提前1秒运行,并且我们需要重新启动我们的服务以便回退。

答案 3 :(得分:-2)

不要忘记闰秒发生在UT0的23:59:59,这是在全球所有时区的同一时刻应用的,所以如果你在区时间加8,它将发生在23 :59:59 - 8 = 15:59:59。

由于夏令时而导致的任何本地时间变化'被忽略了。

以下的第二个是23:59:60然后是00:00:00。

请注意,闰秒规格允许在12月底或6月底插入+ - 1或2秒。