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