深入了解Java.Util.Date詳情
前言:
很少有類能像java.util.Date
那樣在堆棧溢出方面引起如此多的類似問題,有四個原因:
- 日期和時間工作基本上相當(dāng)復(fù)雜,而且充滿了拐彎抹角的情況。這是可以管理的,但你確實需要花一些時間來理解它。
java.util.Date
類在很多方面都很糟糕(詳情如下)。- 一般來說,開發(fā)人員對它的理解很差。
- 它被庫作者嚴(yán)重濫用,進一步加劇了混亂。
了解java.util.Date
最重要的事情為:
- 如果可能的話,你應(yīng)該避免。使用
java.time.*
如果可能的話,也可以使用ThreeTen Backport(基本上是舊版本的java.time)或Joda time(如果您還沒有使用java 8)。 - 如果您被迫使用它,請避免使用不推薦使用的成員。近20年來,它們中的大多數(shù)都被棄用了,這是有充分理由的。
- 如果您真的覺得必須使用不推薦的成員,請確保您真正理解他們。
- Date實例表示時間上的一個瞬間,而不是日期。重要的是,這意味著:
- 它沒有時區(qū)。
- 它沒有格式。
- 它沒有日歷系統(tǒng)。
現(xiàn)在,關(guān)于細節(jié)…
Java.Util.Date有什么問題?
java.util.Date
(從現(xiàn)在開始就是Date)是一種糟糕的類型,這解釋了為什么Java 1.1中有這么多的Date被棄用(但不幸的是,它仍然在使用)。
設(shè)計缺陷包括:
- 它的名字令人誤解:它不代表日期,它代表的是時間上的一個瞬間。因此,它應(yīng)該被稱為Instant,就像它的
java.time
一樣等效。 - 這是非最終結(jié)果:這鼓勵了java等繼承的不當(dāng)使用。
java.sql.Date
(表示日期,由于具有相同的短名稱,因此也容易混淆) - 它是可變的:日期/時間類型是由不可變類型有效建模的自然值。日期是可變的(例如通過
setTime
方法),這意味著勤奮的開發(fā)人員最終會在各地創(chuàng)建防御副本。 - 它在許多地方隱式使用系統(tǒng)本地時區(qū),包括
toString()
,這讓許多開發(fā)人員感到困惑。 - 它的月份編號是以0為基礎(chǔ)的,是從C復(fù)制的。這導(dǎo)致了很多很多錯誤。
- 它的年份編號是基于1900年的,也是從C中復(fù)制的。當(dāng)然,當(dāng)Java問世時,我們已經(jīng)有了這樣一個想法,即這對可讀性不利?
- 其方法的名稱不明確:
getDate()
返回月份的第幾天,getDay()
返回星期幾。給這些更具描述性的名字有多難? - 它是否支持閏秒是不明確的:“一秒由0到61之間的整數(shù)表示;值60和61只在閏秒中出現(xiàn),甚至僅在實際正確跟蹤閏秒的Java實現(xiàn)中出現(xiàn)。”我強烈懷疑大多數(shù)開發(fā)人員(包括我自己)都做了大量假設(shè),認(rèn)為
getSeconds()
的范圍實際上在0-59(包括0-59)之間。 - 沒有明顯的理由,它是寬大的:“在所有情況下,為這些目的而給出的方法的參數(shù)都不需要在指定的范圍內(nèi);例如,日期可以指定為1月32日,也可以解釋為2月1日。”這是否有用?
我可以發(fā)現(xiàn)更多的問題,但他們會越來越挑剔。這是一個非常豐富的列表。好的一面是:
- 它明確地表示一個值:時間上的一個瞬間,沒有相關(guān)的日歷系統(tǒng)、時區(qū)或文本格式,精確到毫秒。
不幸的是,開發(fā)人員對這一“好方面”的理解也很差。讓我們把它打開…
什么是“瞬間”
注意:在這篇文章的其余部分,我忽略了相對論和閏秒。它們對一些人來說非常重要,但對大多數(shù)讀者來說,它們只會帶來更多的困惑。
當(dāng)我談?wù)?ldquo;瞬間”時,我指的是可以用來識別什么時候發(fā)生了什么的概念。(這可能發(fā)生在將來,但最容易從過去的事情來考慮。)它獨立于時區(qū)和日歷系統(tǒng),因此多個使用“本地”時間表示的人可以用不同的方式談?wù)撍?/p>
讓我們用一個非常具體的例子來說明發(fā)生在不使用我們熟悉的時區(qū)的地方的事情:尼爾·阿姆斯特朗在月球上行走。月球行走開始于一個特定的時刻——如果來自世界各地的多個人同時觀看,他們會(幾乎)同時說“我現(xiàn)在可以看到它正在發(fā)生”。
如果你在休斯頓的任務(wù)控制中心觀看,你可能會想到那一刻是“1969年7月20日,CDT晚上9:56:20”。如果你在倫敦觀看,你可能會想到那一刻是“1969年7月21日,英國夏令時凌晨3:26:20”。如果你在利雅得觀看,你可能會認(rèn)為那一刻是“1389年7月7日上午5:56:20(+03)”(使用《古拉經(jīng)》日歷)。盡管不同的觀察者會在他們的時鐘上看到不同的時間,甚至不同的年份,但他們?nèi)匀粫紤]同一時刻。他們只是在應(yīng)用不同的時區(qū)和日歷系統(tǒng),將即時轉(zhuǎn)換為更人性化的概念。
那么,計算機如何表示實例呢?它們通常在一個特定的瞬間之前或之后存儲一定量的時間,而這個瞬間實際上是一個原點。許多系統(tǒng)使用Unix紀(jì)元,這是格里高歷中以UTC表示的1970年1月1日開始的午夜。這并不意味著紀(jì)元本質(zhì)上是“在”UTC中的——Unix紀(jì)元同樣可以定義為“1969年12月31日紐約下午7點的那一刻”。
Date
類使用“自Unix紀(jì)元以來的毫秒數(shù)”——這是getTime()
返回的值,由Date(long)
構(gòu)造函數(shù)或setTime()
方法設(shè)置。由于月球行走發(fā)生在Unix紀(jì)元之前,因此該值為負值:實際上是-14159020000
。
為了演示日期如何與系統(tǒng)時區(qū)交互,讓我們展示前面提到的三個時區(qū)–休斯頓(美國/芝加哥)、倫敦(歐洲/倫敦)和利雅得(亞洲/利雅得)。當(dāng)我們從歷元毫秒值構(gòu)建日期時,系統(tǒng)時區(qū)是什么并不重要,這根本不取決于本地時區(qū)。但如果我們使用Date.toString()
,它轉(zhuǎn)換為當(dāng)前默認(rèn)時區(qū)以顯示結(jié)果。更改默認(rèn)時區(qū)根本不會更改日期值。對象的內(nèi)部狀態(tài)完全相同。它仍然表示相同的瞬間,但toString()
、getMonth()
和getDate()
等方法將受到影響。
下面的示例代碼顯示:
import java.util.Date; import java.util.TimeZone; public class Test { public static void main(String[] args) { // The default time zone makes no difference when constructing // a Date from a milliseconds-since-Unix-epoch value Date date = new Date(-14159020000L); // Display the instant in three different time zones TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago")); System.out.println(date); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); System.out.println(date); TimeZone.setDefault(TimeZone.getTimeZone("Asia/Riyadh")); System.out.println(date); // Prove that the instant hasn't changed... System.out.println(date.getTime()); } }
輸出如下:
Sun Jul 20 21:56:20 CDT 1969
Mon Jul 21 03:56:20 GMT 1969
Mon Jul 21 05:56:20 AST 1969
-14159020000
這里的輸出中的“GMT”和“AST”縮寫非常不幸——java.util.TimeZone
并非在所有情況下都具有1970年之前值的正確名稱。不過時機已經(jīng)成熟。
常見問題
如何將Date日期轉(zhuǎn)換為其他時區(qū)?
你沒有——因為約會沒有時區(qū)。這是一個瞬間。不要被toString()
的輸出所愚弄。這會顯示默認(rèn)時區(qū)中的瞬間。這不是值的一部分。
如果代碼以日期作為輸入,則已經(jīng)發(fā)生了從“本地時區(qū)”到即時的任何轉(zhuǎn)換。(希望操作正確……)
如果您開始編寫一個帶有這樣簽名的方法,那么您并沒有幫助自己:
// A method like this is always wrong Date convertTimeZone(Date input, TimeZone fromZone, TimeZone toZone)
如何將Date日期轉(zhuǎn)換為其他格式?
你沒有——因為日期沒有格式。不要被toString()
的輸出所愚弄。始終使用相同的格式,如文檔所述。要以特定方式格式化日期,請使用合適的日期格式(可能是SimpleDataFormat
)——記住將時區(qū)設(shè)置為適合您使用的適當(dāng)區(qū)域。
到此這篇關(guān)于深入了解Java.Util.Date詳情的文章就介紹到這了,更多相關(guān)Java.Util.Date 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
利用Spring?boot+LogBack+MDC實現(xiàn)鏈路追蹤
這篇文章主要介紹了利用Spring?boot+LogBack+MDC實現(xiàn)鏈路追蹤,MDC?可以看成是一個與當(dāng)前線程綁定的哈希表,可以往其中添加鍵值對,下文詳細介紹需要的小伙伴可以參考一下2022-04-04重新啟動IDEA時maven項目SSM框架文件變色所有@注解失效
這篇文章主要介紹了重新啟動IDEA時maven項目SSM框架文件變色所有@注解失效,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03Java 其中翻轉(zhuǎn)字符串的實現(xiàn)方法
這篇文章主要介紹了Java 其中翻轉(zhuǎn)字符串的實現(xiàn)方法,需要的朋友可以參考下2014-02-02spring實現(xiàn)動態(tài)切換、添加數(shù)據(jù)源及源碼分析
這篇文章主要給大家介紹了關(guān)于spring實現(xiàn)動態(tài)切換、添加數(shù)據(jù)源及源碼分析的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09springboot dynamic多數(shù)據(jù)源demo以及常見切換、事務(wù)的問題
這篇文章主要介紹了springboot dynamic多數(shù)據(jù)源demo以及常見切換、事務(wù)的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07