assylias 的The Answer 是正确的。这个答案添加了一些由对该答案的评论引发的想法。该评论提到在计划闰秒时计算午夜经过的时间。
这个答案也确实解决了原始问题,指出对于实际使用,闰秒的问题没有实际意义,被日期时间框架忽略。
忽略闰秒
据我所知,所有常见的 Java 日期时间框架都会忽略闰秒。其中包括:
- java.time
- Joda-Time
- java.util.Date/.Calendar(现在已被 java.time 和 Joda-Time 过时)
文档
这里是每个框架文档的摘录,表明它们实际上忽略了闰秒。粗体字的重点是我的。
Instant 类的 java.time 文档:
...鉴于上述精确计时的复杂性,此 Java API 定义了自己的时间标度,即 Java Time-Scale。
Java Time-Scale 将每个日历日精确地划分为 86400 个细分,称为秒。这些秒数可能与 SI 秒数不同。它与事实上的国际民事时间尺度非常吻合,其定义不时变化。
Java Time-Scale 对时间线的不同部分的定义略有不同,每一个都基于用作民用时间基础的共识国际时间尺度。每当国际商定的时间尺度被修改或替换时,必须为其定义一个新的 Java 时间尺度段。每个细分市场都必须满足以下要求:
- Java 时标应与基础国际民用时标紧密匹配;
- Java Time-Scale 应与每天中午的国际民用时标完全匹配;
- Java 时标应与国际民用时标具有精确定义的关系。
截至 2013 年,Java 时间尺度目前有两个部分。
对于从 1972 年 11 月 3 日(具体边界在下文讨论)直至另行通知的这段时间,一致的国际时间尺度是 UTC(有闰秒)。在这一部分中,Java Time-Scale 与 UTC-SLS 相同。这与没有闰秒的日子的 UTC 相同。在确实有闰秒的日子里,闰秒平均分布在一天的最后 1000 秒内,保持每天 86400 秒的精确外观。
对于 1972 年 11 月 3 日之前的部分,任意向后延伸,国际一致时间尺度定义为 UT1,应用于预测,相当于本初子午线(格林威治)上的(平均)太阳时.在这一部分中,Java 时间尺度与共识国际时间尺度相同。这两个段之间的确切边界是 1972-11-03T00:00 和 1972-11-04T12:00 之间 UT1 = UTC 的时刻。
使用 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 );
运行时,我们会看到偶数的结果,正好是 2 分钟、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.Date、Calendar 和 SimpleDateFormat。
Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。
要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。
使用符合JDBC 4.2 或更高版本的JDBC driver,您可以直接与您的数据库交换java.time 对象。不需要字符串也不需要 java.sql.* 类。
从哪里获得 java.time 类?
ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如Interval、YearWeek、YearQuarter 和more。