簡體   English   中英

(Oracle)Java JVM如何知道閏秒發生了什么?

[英]How does the (Oracle) Java JVM know a leap second is occurring?

閏秒將在2015年6月30日發生 。不同的操作系統似乎以不同的方式處理這種情況。 在我的特定情況下,我們運行的Red Hat 6.4系統具有嚴重依賴於時間的自定義Java(JDK 1.7)軟件。 根據最近我發現的一些Red Hat發布的信息 ,我們系統的NTP守護進程將確保操作系統通過重復23:59:59兩次自動處理閏秒。

我的問題是:如果我有一個長期運行的JDK 1.7進程,它是如何知道閏秒發生的? 我的意思是,Java最終如何知道IERS人們決定插入閏秒? 日期文檔似乎表明已經知道閏秒,但似乎沒有任何幫助。 我可以假設JDK,當構造適當的Date對象或調用Calendar.getInstance() ,它是否傳遞給底層操作系統的日期時間處理以獲得適當的“實際”時間值? (在我的情況下,聽起來它會重復第二次23:59:59,因為這就是操作系統將如何處理它)。

assylias 的回答是正確的。 本答案增加了一些由該答案評論引發的想法。 該評論涉及計算在安排閏秒的午夜時間的經過時間。

本答案也解決了原始問題,指出在實際應用中,閏秒問題沒有實際意義,被日期時間框架所忽略。

閏秒被忽略了

正如我所理解的那樣,所有常見的Java日期時間框架都忽略了閏秒。 這些包括:

  • java.time
  • 喬達時間
  • java.util.Date/.Calendar(現在已經過時了java.time和Joda-Time)

文件

以下是每個框架文檔的摘錄,顯示它們實際上忽略了Leap Seconds。 大膽的重點是我的。

Instant類的java.time文檔:

...鑒於上述精確計時的復雜性,此Java API定義了自己的時間尺度Java Time-Scale。

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常見問題

Joda-Time 不支持閏秒 通過編寫新的專業年表或對現有的ZonedChronology類進行一些增強,可以支持閏秒。 在任何一種情況下,Joda-Time的未來版本默認情況下都不會啟用閏秒。 大多數應用程序不需要它,並且可能會有額外的性能成本。

java.util.Date類doc

第二個由0到61的整數表示; 值60和61僅在閏秒發生,甚至僅在實際正確跟蹤閏秒的Java實現中發生

據我所知,OpenJDK和Oracle提供的實現跟蹤閏秒。 如果找到,請發布此類文檔。

計算經過時間時沒有閏秒

因此,這些框架在計算經過時間時不會報告額外的閏秒。

以下是一些示例代碼,用於計算2015年6月30日至7月1日安排閏秒時從午夜到分鍾之間的經過時間。 此代碼在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及更高版本中。 這些類取代了麻煩的舊遺留日期時間類,如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 ,和更多

這取決於你的jdk版本。 例如,如果您正在運行更新80,則可以查看發行說明

JDK 7u80包含IANA時區數據版本2015a。 有關更多信息,請參閱JRE軟件中的時區數據版本。

然后點擊時區數據版本的鏈接,找到2015a。 然后點擊TZ Updater version1.4.11的鏈接

新的閏秒2015-06-30 23:59:60 UTC根據IERS Bulletin C 49.(感謝Tim Parenti。)

它似乎之前沒有被包括在內,所以如果您運行的是舊版本的JDK 7,您可能無法進行調整。 有關內部工作原理的更多信息,請點擊此處

說實話,我從未測試過它在實踐中是如何運作的。

這些是我在RHEL 6.3上運行實時系統的實際操作,使用Oracle Java JDK 1.7.0_55-b13(即:過時的JDK)。 我添加了啟動的調試代碼,在2015年6月30日23:59 UTC的閏秒之前每0.5秒記錄一次新的DateTime(),然后在閏秒結束后啟動。 記錄如下(好奇的是,打印的雙號是自J2000紀元以來的秒數)

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的答案都表明會發生)。 我希望'新的Date()'可以達到底層操作系統(RHEL 6.3,據稱可以正確計算閏秒)來詢問當前時間。 情況似乎並非如此。

我沒有資源運行JDK版本和RHEL版本的任何其他組合來測試效果。 我目前最好的猜測是,Basil Bourque在當天的最后1000秒內發現閏秒的文檔適用於JDK 1.8及更新版本(因為它是Instant 8文檔的一部分,這是Java 8的一項功能)。 對於我的特殊情況,由於我的舊JDK 1.7.0_55沒有2015年的閏秒調整,我認為assylias的觀察適用。 也就是說,我們的實時流程現在提前1秒運行,不知道閏秒。

在這里學到的經驗? 如果您想確保它們考慮即將到來的閏秒,請確保您的實時系統已完全修補最新更新。 我將不得不做一些觀察和日志分析,但我們的特定系統現在可能比實時提前1秒運行,我們需要重新啟動我們的服務以便回退。

不要忘記閏秒發生在UT0的23:59:59,這是在世界各地的所有時區的同一時刻應用,所以如果你在區域時間加8,它將發生在23:59:59 - 8 = 15:59:59

由於“夏令時”而導致的任何本地時間更改都將被忽略。

以下是23:59:60然后00:00:00。

請注意,閏秒規范允許在12月或6月底插入+ - 1或2秒。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM