你好呀,我是歪歪。
給大家分享一個我最近發現的有點意思,但是卵用不大的小知識點。
先給你搞個程式看一下:
public class MainTest {
public static void main(String[] args) throws Exception {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = simpleDateFormat.parse("1900-01-01 08:00:00");
System.out.println(simpleDateFormat.format(date));
}
}
你說上面的程式邏輯就是一個簡單的時間格式化,你說輸出結果是什麼?
只是需要瞟一眼就知道,肯定是輸出這個結果呀:
1900-01-01 08:00:00
但是,你把上面的程式拿出來,直接跑起來,你會發現輸出結果竟然是這樣的:
1900-01-01 08:05:43
當時就懵逼了。
我知道時差 8 小時,是因為有時區問題。
我知道時間差 1 小時,是因為有夏令時的原因。
但是這裡差了 5 分 43 秒,有零有整,就讓我有點摸不著頭腦了。
![](https://i.iter01.com/images/c536c8561a5d76d296229cc053b8348554f057b94fa91787a0553f6eb885f6c6.png)
上面這個案例就是一個讀者分享給我的,他們在資料庫裡面預設時間是 1900-01-01,再加上時區問題,剛好變成了 1900-01-01 08:00:00,於是在通過程式做資料遷移的時候就踩到了這個莫名其妙的時間問題。
![](https://i.iter01.com/images/0028080574cd453fe0b813ab122efe98efa6261c853934e417a241ca168ad6bb.jpg)
同時他還給我附送了一個關於這個 bug 的連結:
https://bugs.openjdk.java.net/browse/JDK-8266262
![](https://i.iter01.com/images/cef5aa7b29a7f6bc6bdc81f41405b021a1a404c76c302219134ad7c522b81f38.png)
我乍一看,這個 bug 還挺新的呢,屬於今年提出來的。
仔細又看了一眼發現是和之前的 bug 重複了:
![](https://i.iter01.com/images/9583fc21f4bae8ef7cdfe5cc4ac123447a1a03d6d783dce9b05b99e4bed12f18.png)
但是這裡提到了原因:
他說可以看一下這個連結https://www.timeanddate.com/time/zone/china/shanghai?year=1900
這裡面,在 1900 年的時候,發生了一個變化:
The timezone offset was UTC +8:05:43 hours all of the period.
雖然我沒太看明白具體是什麼意思,但是我看到了“5 分 43 秒”:
![](https://i.iter01.com/images/baf9901f112fdec011172b8c96455bc79e4a5941af097cc865cf684357292492.png)
我理解就是由於時區的變化,導致時間發生了重置。
接著我順藤摸瓜,在 stackoverflow 上找到了這個:
https://stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result
當時我就震驚了。
這個 10 年前被提出的問題居然已經被瀏覽過 746k 次了,非常熱門的問題了,我居然沒注意到過:
![](https://i.iter01.com/images/f32e96216e6c5487fa526ab506bffc916917d23f09fb828313f5a2c424679db6.png)
這個問題具體是這樣的:
![](https://i.iter01.com/images/8ecb030f110c4b473e2896eb99c648edaf2bd2fd8bb6721f866fdd2bef83d984.png)
你就大概瞟一眼,我給你翻譯翻譯。
提問者說,他發現 1927-12-31 23:54:07 到 1927-12-31 23:54:08 之間差了 353 秒,按理來說應該是 1 秒才對啊?
但是把時間改成下面這樣,又正常了:
String str3 = "1927-12-31 23:54:07";
String str4 = "1927-12-31 23:54:08";
我把程式粘出來你也可以跑一下,看看結果非常的神奇啊:
public static void main(String[] args) throws ParseException {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String str3 = "1927-12-31 23:54:07";
String str4 = "1927-12-31 23:54:08";
Date sDt3 = sf.parse(str3);
Date sDt4 = sf.parse(str4);
long ld3 = sDt3.getTime() /1000;
long ld4 = sDt4.getTime() /1000;
System.out.println(ld4-ld3);
}
但是當我跑了一遍之後,我發現:我去,說好的 353 秒呢?
跑出來怎麼是 1 秒呢,毫無毛病啊:
![](https://i.iter01.com/images/fd59b06af9fb153cb565f4cdeb46bc92797dcf0d7dcd1d671214f5517a80de94.png)
我甚至懷疑是 jdk 版本的問題,於是我換了 jdk 9,11,15 都跑了一下,都是 1 秒。
這就很奇怪了啊。
感覺這個問題提的就有問題啊。
![](https://i.iter01.com/images/d366f855cc3ee79b5c8d1a5d43cd4496a425c3ea38a98bdfd7ac9340833a4d5b.png)
但是當我讀了下面最高讚的答案之後,我才彷彿窺見了一點端倪。
這個回答比較長,我先全部截圖下來給你看看:
![](https://i.iter01.com/images/be1b154cc6e27b73b2d1dc3b1bb6fcea4dc58cfead8291bb0a3664639a71524a.png)
比較長的原因是作者修改了幾次回答。
為什麼會修改回答呢?
且往後看吧,一切的答案都藏在這裡面。
我選關鍵的給你說。
首先看第一段:
![](https://i.iter01.com/images/fb24350437de15e7f2b215a0cd56c75626d071171efa1d1f5a052ff48e9dc0b5.png)
他說(1927年) 12 月 31 日的時候,上海的時區發生了變化。
而關於 1927 年上海的詳細情況,他附上了一個超連結,這個超連結就是前面出現的網站,點進去之後是這樣的:
![](https://i.iter01.com/images/ef69df737fa42d193d24e931ed13e560328f4abb88a552aba3aaa44a48161262.png)
但是這個裡面顯示:
No further time changes in 1927 in Shanghai
翻譯過來就是:1927 年上海的時間沒有進一步變化。
這特麼就和他下面說的那一坨內容對不上了啊?
他下面說,在 1927 年底的午夜時分,時鐘往回走了 5 分 52 秒。因此,"1927-12-31 23:54:08"實際上發生了兩次,而 Java 取的是第二次的的時刻,因此存在差異。
看到這裡其實我都懵逼了,這玩意前後不符啊,於是我有接著開始搜尋。
直到我發現了這個:
https://coolshell.cn/articles/5075.html/comment-page-2#comments
這也是十年前的文章。
這裡面作者把當時網站截了個圖:
![](https://i.iter01.com/images/f75d6201820874387731d388e731c54d1fd66bfc56a7fe0a9a27c36552ecf7fb.png)
當年的截圖顯示:
在1927年12月31日23:59:59時,往後面的一秒應該是1928年1月1日 0:0:0,但是這個時間被往後調整了5分52秒,而成了,1927年12月31日的,23:54:08,於是,完成了352秒的穿越。
這說明了什麼?
說明資料發生了篡改,有人篡改了網頁上的資訊!
![](https://i.iter01.com/images/c2f439160246f8793468122905c419bb9c57b5d0d2b0896516518c7bcd293fc2.png)
到底是怎麼回事呢?
我們回到 stackoverflow 接著往下看:
![](https://i.iter01.com/images/54d45efb6a8b12de85e112356381db7442e49982cf523e7a4aca546275bce5c9.png)
這是他第一次修改回答,因為 History changes...
歷史發了變化了...
他這裡說,如果用 TZDB 的 2013a 版本的資料,原來的問題將不再表現出完全相同的行為。
在 2013a 中,結果將是 358 秒,過渡時間為 23:54:03,而不是 23:54:08。
他提到了一個 TZDB,這是個啥東西呢?
我也不知道,但是我搜尋了一下。
![](https://i.iter01.com/images/eec50bd267620cbd44fd6131e662301e2cbef803f7186a3ccf2b60d8c5e01138.png)
他應該說的是這個的東西。
https://www.iana.org/time-zones
看名字你也知道了,它是一個時區資料庫,裡面應該是維護的時區相關的資料。
也就是說,在這個時區資料庫裡面,用 2013a 版本的資料,前面的程式碼就是另外一種輸出了。
也就是說資料確實發生了變化。
而關鍵的回答在於下一次編輯:
![](https://i.iter01.com/images/6b67a9cc61c20a65c3a29fe51cac9500226d1340df36be155190732daf6cdff7.png)
History has changed again...
歷史再次發生了變化。
在個時區資料庫裡面,2014f 版本中,變化的時間已經移到了1900-12-31,現在只是一個 343 秒的變化。
343 秒?
不就是我們前面的 5 分 43 秒嗎?
![](https://i.iter01.com/images/617adc143fcf16dc450f56ade961be134b7745ef4aaf20f724750a4c6bbd64e9.png)
好了,現在時差能對上了,343 秒,但是時間還是沒對上啊。
我們的測試時間 1900-01-01 08:00:00,他這裡寫的時間是 1900-12-31。
差了整整一年呢?
好,我們看他最後一次編輯的內容:
![](https://i.iter01.com/images/aeff90f2d65cc514c5cce45848151c9e1fc21ef1e44728687f8318ec493e2fdb.png)
我個人理解他要表達的意思是這樣的。
Java 為了在時區上統一標準,所以來了個一刀切的政策。
統一的標準就是讓 UTC 時區下 1900 年之前的任何瞬間都是標準時間。
至於產生的時差嘛...
就在最開始的時候補上去吧。
所以,1900-01-01 00:00:00 加上 8 小時時差,是 1900-01-01 08:00:00,在這個基礎上預先加上 27 年後來自 1927-12-31 那個午夜由於時間回撥帶來的 343 秒。
1900-01-01 08:05:43,我個人認為就是這樣來的。
而前面 stackoverflow 裡面對應的那個程式,我們現在執行是輸出 1,但是在 10 年前,輸出結果確實是 353 。
就像我把程式改成這樣:
![](https://i.iter01.com/images/6cfd45ea3c6fd03e27359d217e96ff4ea9760e0545a4902ff13998b5b2038a3d.png)
最終的輸出結果不是 1,而是 -342。
時間,發生了“倒流”。
好了,又是一個沒啥卵用的知識點。
最後,再補充一個冷知識。
第一個是我在 jdk bug 列表裡面追溯了一下,能找到最早提出相關問題的時間是 2005 年:
https://bugs.openjdk.java.net/browse/JDK-6281408
![](https://i.iter01.com/images/c14e95ddff7423d6faa31fe12c7ef0371a227154e00b3398a5b789e342e31d7b.png)
在這個裡面,官方是這樣回覆的:
![](https://i.iter01.com/images/fa535cefe8efea55dd4d7a6449abb2ac5227d99c428623e979229e72fa79c15b.png)
這個問題不會被修復,以避免任何相容性問題。
意思就是:問題我知道了,但是這玩意不太好弄,bug 先變成 feature 吧,就先這樣吧。
別問,問就是有歷史原因在裡面。
最後說一句
好了,看到了這裡了,轉發、在看、點贊隨便安排一個吧,要是你都安排上我也不介意。寫文章很累的,需要一點正反饋。
給各位讀者朋友們磕一個了:
![](https://i.iter01.com/images/f90c97d3e52088ddb9b07d3c8bcb3033b3cc2894b66b915b02a9dd50290e8460.png)
本文已收錄自個人部落格,歡迎大家來玩。
https://www.whywhy.vip/