在Java中,如何獲取不同時區的當前時間?
你知道這道題的正確答案應該如何回答嗎?背后的原理又是什么呢?
然后,緊接著,我又提出了以下問題:
為什么以下代碼無法得到美國時間。(在東八區的計算機上)
System.out.println(Calendar.getInstance(TimeZone.getTimeZone(“America/Los_Angeles”)).getTime());
接下來,本文就圍繞這兩個問題,來帶領讀者一起學習一下哪些和Java中的時間有關的概念。
時區
前面提到了時區,可能很多讀者不知道什么是時區,先來簡單介紹一下。
時區是地球上的區域使用同一個時間定義。以前,人們通過觀察太陽的位置(時角)決定時間,這就使得不同經度的地方的時間有所不同(地方時)。1863年,首次使用時區的概念。時區通過設立一個區域的標準時間部分地解決了這個問題。
世界各個國家位于地球不同位置上,因此不同國家,特別是東西跨度大的國家日出、日落時間必定有所偏差。這些偏差就是所謂的時差。
為了照顧到各地區的使用方便,又使其他地方的人容易將本地的時間換算到別的地方時間上去。有關國際會議決定將地球表面按經線從東到西,劃成一個個區域,并且規定相鄰區域的時間相差1小時。在同一區域內的東端和西端的人看到太陽升起的時間最多相差不過1小時。當人們跨過一個區域,就將自己的時鐘校正1小時(向西減1小時,向東加1小時),跨過幾個區域就加或減幾小時。這樣使用起來就很方便。
世界時區
現今全球共分為24個時區。由于實用上常常1個國家,或1個省份同時跨著2個或更多時區,為了照顧到行政上的方便,常將1個國家或1個省份劃在一起。所以時區并不嚴格按南北直線來劃分,而是按自然條件來劃分。例如,中國幅員寬廣,差不多跨5個時區,但為了使用方便簡單,實際上在只用東八時區的標準時即北京時間為準。
格林威治時間
前面提到了,時區通過設立一個區域的標準時間部分地解決了不同地方看到的太陽位置不一樣而無法定義時間的問題。那么這個標準時間是什么呢?
前面還提到。中國位于東八區,一般是用GMT+8來表示東八區這個時區。那么,看起來GMT就是這個所謂的標準時間。GMT是個什么東西呢?為什么要在他的基礎上+8來表示東八區呢?
GMT,是Greenwich Mean Time的縮寫,及格林尼治(格林威治)平時,是指位于英國倫敦郊區的皇家格林尼治天文臺當地的平太陽時,因為本初子午線被定義為通過那里的經線。
格林威治子午線
自1924年2月5日開始,格林尼治天文臺負責每隔一小時向全世界發放調時信息。國際天文學聯合會于1928年決定,將由格林威治平子夜起算的平太陽時作為世界時,也就是通常所說的格林威治時間
一般使用GMT+8表示中國的時間,是因為中國位于東八區,時間上比格林威治時間快8個小時。
北京時間還可以用CST表示,即China Standard Time,又名中國標準時間,是中國的標準時間。當格林威治時間為凌晨0:00時,中國標準時間正好為上午8:00。
所以,有等式:CST=GMT +8 小時
時間戳
前面提到了全世界各個時區的時間可能都是不一樣的,那么有沒有一個什么樣的辦法可以不受時區的限制,可以精確的表示時間呢。
其實是有的,這個方法就是時間戳。
時間戳(timestamp),一個能表示一份數據在某個特定時間之前已經存在的、 完整的、 可驗證的數據,通常是一個字符序列,唯一地標識某一刻的時間。
時間戳是指格林威治時間1970年01月01日00時00分00秒起至現在的總秒數。
有了時間戳,無論我們深處哪個時區,從格林威治時間1970年01月01日00時00分00秒到現在這一時刻的總秒數應該是一樣的。所以說,時間戳是一份能夠表示一份數據在一個特定時間點已經存在的完整的可驗證的數據。
1970-01-01
不知道大家有沒有注意到一個比較特殊的時間,1970-01-01,相信每一個開發者對這個時間都并不陌生。一般如果軟件系統中出現這個時間的時候,代表著出現了網絡故障、線上bug等。
?
微信手機充值Bug
當有些計算機存儲或者傳輸時間戳出錯時,這個時間戳就會取默認值。而在計算機中,默認值通常是 0。
當 Timestamp 為 0,就表示時間(GMT)1970年1月1日0時0分0秒。中國使用北京時間,處于東 8 區,相應就是早上 8 點。因此在中國這邊,時間出錯了,就經常會顯示成 1970年1月1日 08:00。
System.out.println(new Date(0)); //Thu Jan 01 08:00:00 CST 1970
當我們在Java代碼中使用new Date(0)來創建時間的時候,得到的結果就是Thu Jan 01 08:00:00 CST 1970,既1970年1月1日 上午08點整。
Date
前面提到了java.util.Java中的Date類,這個類通常用來表示時間。你可以通過getTime()方法訪問java.util.Date實例的日期和時間,比如像這樣:
Date date = new Date(); long time = date.getTime();
以上代碼,其實得到的就是時間戳,在源碼中也有明確的表述:
?所以,我們就可以認為java.util.Java其實表示的就是從格林威治1970年1月1日零點到現在這一時刻的總秒數。
從Date的源碼中也可以看到,Date中是不包含時區有關的信息的,因為時間戳和時區沒有關系。
那么,如果想要把一個時間戳轉換成不同時區的時間輸出應該怎么做呢?
顯示不同時區的時間
想要把時間戳轉換成對應時區的時間,總要有個地方可以獲取時區吧。其實,我們的計算機中是有時區相關的信息的。
無論我們使用的是哪種操作系統的電腦,都是可以查看時間的,而一般情況下,我們拿到的電腦都會展示中國時間,那是因為操作系統中已經設置了一個默認時區。
其實,Java中的時區信息也是從操作系統中取到的,默認情況下會使用操作系統的時區。
當我們使用System.out.println來輸出一個時間的時候,他會調用Date類的toString方法,而該方法會讀取操作系統的默認時區來進行時間的轉換。
主要代碼如上。也就是說如果我們想要通過System.out.println輸出一個Date類的時候,輸出美國洛杉磯時間的話,就需要想辦法把defaultTimeZone改為America/Los_Angeles,這個方法就是:
TimeZone.setDefault(TimeZone.getTimeZone(“America/Los_Angeles”));
所以,當我們想要輸出美國洛杉磯時間時,可以選擇這種方式:
TimeZone.setDefault(TimeZone.getTimeZone(“America/Los_Angeles”)); Date date =new Date();System.out.println(date);
還有一種方式,就是通過SimpleDateFormat來處理,這種方式我們在為什么阿里巴巴禁止把SimpleDateFormat定義為static類型的中也介紹過。這里就不再展開了。
接下來,我們再回到文章開始的那個問題:
為什么以下代碼無法得到美國時間。(在東八區的計算機上)
System.out.println(Calendar.getInstance(TimeZone.getTimeZone(“America/Los_Angeles”)).getTime());
其實答案前面也已經說過了,我們通過查看Date.toString的源碼,發現在輸出的過程中該方法只會去獲取系統的默認時區,只有修改了默認時區才會顯示該時區的時間。
但是,通過閱讀Calendar的源碼,我們可以發現,getInstance方法雖然有一個參數可以傳入時區,但是并沒有將默認時區設置成傳入的時區。
而在Calendar.getInstance.getTime后得到的時間只是一個時間戳,其中未保留任何和時區有關的信息,所以,在輸出時,還是顯示的是當前系統默認時區的時間。
Java 8與時區
了解Java8 的朋友可能都知道,Java8提供了一套新的時間處理API,這套API比以前的時間處理API要友好的多。
Java8 中加入了對時區的支持,帶時區的時間為分別為:ZonedDate、ZonedTime、ZonedDateTime。其中每個時區都對應著 ID,地區ID都為 “{區域}/{城市}”的格式,如Asia/Shanghai、America/Los_Angeles等。
在Java8中,直接使用以下代碼即可輸出美國洛杉磯的時間:
總結
世界上有很多時區,不同的時區的時間不一樣,中國使用東八區的時間作為標準時間。美國自東海岸至西海岸橫跨西五區至西十區,共六個時區。
所謂東八區,一般表示成GMT+8,這里的GMT指的是格林威治時間。計算機中經常使用時間戳來表示時間,時間戳指的就是當前時間舉例格林威治的1970-01-01 00:00:00的總秒數。
而Java中的Date類中是不包含時區信息的,在使用System.out.println打印Date的時候,回調用Date.toString方法,該方法會獲取系統的默認時區來轉換時間。
在Java8中可以使用ZonedTime、ZonedDate和ZonedDateTime來表示帶有時區信息的時間。
LocalDateTime now = LocalDateTime.now(ZoneId.of(“America/Los_Angeles”)); System.out.println(now);
評論
查看更多