[英]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日期時間框架都忽略了閏秒。 這些包括:
以下是每個框架文檔的摘錄,顯示它們實際上忽略了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 不支持閏秒 。 通過編寫新的專業年表或對現有的ZonedChronology類進行一些增強,可以支持閏秒。 在任何一種情況下,Joda-Time的未來版本默認情況下都不會啟用閏秒。 大多數應用程序不需要它,並且可能會有額外的性能成本。
第二個由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 8及更高版本中。 這些類取代了麻煩的舊遺留日期時間類,如java.util.Date
, Calendar
和SimpleDateFormat
。
現在處於維護模式的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的試驗場。 您可以在這里找到一些有用的類,比如Interval
, YearWeek
, YearQuarter
,和更多 。
這取決於你的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.