GMT?UTC?CST? 進(jìn)來聊聊時區(qū)(in Java)

先來看看幾種常見的日期格式:

2021-03-25 20:00:00
2021-03-25T20:00:00+0800
2021-03-25T20:00:00+08:00
Thu Mar 25 20:00:00 CST 2021
2021-03-25T20:00:00Z

第一種肯定都不陌生,后面的也很常見,但是里面的TZCST、+0800分別是什么意思呢?

T很簡單,代表時間,國際標(biāo)準(zhǔn)規(guī)定的日期時間組合法,用T放在時間內(nèi)容前,代表Time,而最后的部分,代表時區(qū)。

Timezone(時區(qū))

什么是時區(qū)?

??世界各國位于地球不同位置上,因此不同國家,特別是東西跨度大的國家日出、日落時間必定有所偏差。這些偏差就是所謂的時差。

??火車鐵路與其他交通和通訊工具的發(fā)展,以及全球化貿(mào)易的推動,在19世紀(jì)催生了統(tǒng)一時間標(biāo)準(zhǔn)的需求,時區(qū)由此誕生。

??時區(qū)是地球上的區(qū)域使用同一個時間定義。理論時區(qū)采用其中央經(jīng)線(或標(biāo)準(zhǔn)經(jīng)線),以被15整除的經(jīng)線為中心,向東西兩側(cè)延伸7.5度,即每15°劃分一個時區(qū)。所以每差一個時區(qū),區(qū)時相差一個小時。
??具體就不展開說了,百科內(nèi)容隨處可見,這里貼個圖吧:


image.png

??說完時區(qū),還要提到其他幾個概念:

GMT - 格林尼治平均時間(Greenwich Mean Time,GMT)

??是指位于英國倫敦郊區(qū)的皇家格林尼治天文臺當(dāng)?shù)氐钠教枙r,因為本初子午線被定義為通過那里的經(jīng)線。

??自1924年2月5日開始,格林尼治天文臺負(fù)責(zé)每隔一小時向全世界發(fā)放調(diào)時信息。

??格林尼治標(biāo)準(zhǔn)時間的正午是指當(dāng)平太陽橫穿格林尼治子午線時(也就是在格林尼治上空最高點(diǎn)時)的時間。由于地球每天的自轉(zhuǎn)是有些不規(guī)則的,而且正在緩慢減速,因此格林尼治平時基于天文觀測本身的缺陷,已經(jīng)被原子鐘報時的協(xié)調(diào)世界時(UTC)所取代。

UTC - 協(xié)調(diào)世界時(Coordinated Universal Time)

??是最主要的世界時間標(biāo)準(zhǔn),其以原子時秒長為基礎(chǔ),在時刻上盡量接近于格林威治標(biāo)準(zhǔn)時間。

??協(xié)調(diào)世界時是世界上調(diào)節(jié)時鐘和時間的主要時間標(biāo)準(zhǔn),它與0度經(jīng)線的平太陽時相差不超過1秒,并不遵守夏令時。協(xié)調(diào)世界時是最接近格林威治標(biāo)準(zhǔn)時間(GMT)的幾個替代時間系統(tǒng)之一。對于大多數(shù)用途來說,UTC時間被認(rèn)為能與GMT時間互換,但GMT時間已不再被科學(xué)界所確定。

??如今,格林威治時間僅僅是一個時區(qū)名字,主要被非洲和西歐的一些國家使用。

CST - 中國標(biāo)準(zhǔn)時間: China Standard Time (utc+8)

??CST是眾多地區(qū)時區(qū)簡稱中的一個,在國內(nèi)特指中國標(biāo)準(zhǔn)時間,但是國外就不是了,還存在其他地區(qū)也在使用同樣的時區(qū)簡稱,比如:

??中原標(biāo)準(zhǔn)時間,Chungyuan Standard Time
??澳洲中部時間,Central Standard Time (Australia)
??北美中部時區(qū),Central Standard Time (North America)
??古巴標(biāo)準(zhǔn)時間,Cuba Standard Time

??所以,為了避免歧義,現(xiàn)在多用CTT來指代中國時區(qū)。

??除了CST、CTT,還存在多種地區(qū)時區(qū)簡稱,因為容易造成沖突引發(fā)歧義,時區(qū)簡稱方式正在逐步被替代,不推薦大家過多使用。

??那在JAVA中有哪些時區(qū)相關(guān)的內(nèi)容?

??先來看一組代碼:

LocalDateTime.now();  //2021-03-26T09:49:40.417744200
ZonedDateTime.now();  //2021-03-26T09:50:05.880025400+08:00[Asia/Shanghai]
Instant.now();  //2021-03-26T01:50:15.187036100Z
new Date();   //Fri Mar 26 09:50:25 CST 2021

??觀察代碼可以看到,除了LocalDateTime.now()的toString()內(nèi)容沒有攜帶時區(qū)信息外,其他三種常見時間類的輸出內(nèi)容都帶有時區(qū),而LocalDateTime,顧名思義,其實只是使用了系統(tǒng)默認(rèn)時區(qū),所以這些常見的時間類的運(yùn)作,都離不了時區(qū)的概念。
??實際上,它們是通過以下幾個類,來記錄和展示時區(qū)內(nèi)容的:

Java中一些時區(qū)相關(guān)Class

Class From version
java.util.TimeZone; 1.1
java.time.ZoneId; 1.8
java.time.ZoneOffset extends ZoneId; 1.8

由于TimeZone類的作用已經(jīng)在1.8之后被新添加的時區(qū)類替代,這里就不講了

ZoneId

這里偷懶,直接貼一下類注釋:

??A ZoneId is used to identify the rules used to convert between an Instant and a LocalDateTime. There are two distinct types of ID:
??1. Fixed offsets - a fully resolved offset from UTC/Greenwich, that uses the same offset for all local date-times
??2. Geographical regions - an area where a specific set of rules for finding the offset from UTC/Greenwich apply

??里面提到說,ZoneId主要是用來做LocalDateTime和Instant的轉(zhuǎn)換處理的,因為LocalDateTime使用了系統(tǒng)時區(qū)直接生成時間,對象信息內(nèi)不包含時區(qū)信息,而Instant需要包含時區(qū)信息,所以在轉(zhuǎn)換時需要指定時區(qū)。
??而ZoneId的使用上有兩種方式,第一種是固定偏移量,也就是跟UTC/GMT去比較,直接寫+-多少小時多少分鐘;第二種是使用特定的地區(qū)名,具體如下:

ZoneId.of(“xxx”);

  1. Z 或者+、-開頭,如of(“Z”)、of(“+8”)、of(“-03:30”),即ZoneOffset格式
  2. 以”UTC”、”GMT”、”UT”開頭,后跟+-,如of(“GMT+8”)
  3. 以洲名+地區(qū)名稱組成,如Asia/Shanghai、Asia/Taipei

??另外,如“CST”、“CTT”格式的時區(qū)簡稱方式不再被支持,因為部分簡稱存在沖突(如CST),并且無法對夏令時提供支持,所以ZoneId類中還提供了對時區(qū)簡稱轉(zhuǎn)地區(qū)名的方法:

ZoneId.SHORT_IDS.get("CTT"); //Asia/Shanghai
ZoneId.SHORT_IDS.get("CST"); //America/Chicago

??所有舊時區(qū)簡稱列表如下:

EST - -05:00
HST - -10:00
MST - -07:00
ACT - Australia/Darwin
AET - Australia/Sydney
AGT - America/Argentina/Buenos_Aires
ART - Africa/Cairo
AST - America/Anchorage
BET - America/Sao_Paulo
BST - Asia/Dhaka
CAT - Africa/Harare
CNT - America/St_Johns
CST - America/Chicago
CTT - Asia/Shanghai
EAT - Africa/Addis_Ababa
ECT - Europe/Paris
IET - America/Indiana/Indianapolis
IST - Asia/Kolkata
JST - Asia/Tokyo
MIT - Pacific/Apia
NET - Asia/Yerevan
NST - Pacific/Auckland
PLT - Asia/Karachi
PNT - America/Phoenix
PRT - America/Puerto_Rico
PST - America/Los_Angeles
SST - Pacific/Guadalcanal
VST - Asia/Ho_Chi_Minh

??而支持的地區(qū)名有幾百種,此處就不一一列出了,可通過ZoneRulesProvider類查看:

image.png

ZoneOffset

??同樣先摘錄一下官方注釋:

??A time-zone offset from Greenwich/UTC, such as +02:00.
??A time-zone offset is the amount of time that a time-zone differs from Greenwich/UTC. This is usually a fixed number of hours and minutes.

?&emsp可以看到,它主要使用偏移量來配置時區(qū),使用方式如下:

ZoneOffset.of(“xxx”);
Z - for UTC
+h
+hh
+hh:mm
-hh:mm
+hhmm
-hhmm
+hh:mm:ss
-hh:mm:ss
+hhmmss
-hhmmss

??具體到代碼里面的使用,舉例如下:

Instant.now();  //2021-03-25T12:03:25.281066900Z
Instant.now().atZone(ZoneId.systemDefault());   //2021-03-25T20:05:19.977884400+08:00[Asia/Shanghai]
Instant.now().atZone(ZoneId.of("America/Chicago"));  //2021-03-25T07:06:57.531644100-05:00[America/Chicago]
LocalDateTime.now().atZone(ZoneId.of("America/Chicago"));   //2021-03-25T20:07:49.940338900-05:00[America/Chicago]
LocalDateTime.now().atZone(ZoneId.of("+8"));    //2021-03-25T20:07:32.583907500+08:00
LocalDateTime.now().atZone(ZoneOffset.of("+8"));   //2021-03-25T20:08:21.963101+08:00

??看起來,使用offset方式明顯更方便快捷一些,是不是只要記得當(dāng)前時區(qū)對應(yīng)的偏移量,需要換算時區(qū)的時候使用+-N帶入時區(qū)信息計算就可以了呢?

??答案是不是,因為有另外一個概念影響,叫夏令時:

夏令時( daylight saving time )

??看命名可知,Save的是daylight時間,而夏天的daylight來的更早一些,所以要早起早睡,要在夏天到來時把時鐘調(diào)快一些時間(一小時)。

??施行的初衷是為了節(jié)約能源,合理利用日光,但會讓報時工作變得更加復(fù)雜,并且會擾亂旅行、計費(fèi)、紀(jì)錄保存、醫(yī)療設(shè)備、重機(jī)設(shè)備...與睡眠模式的運(yùn)作。

注:中國曾在1986-1991期間實施過夏令時。

??因此可知,如果是中國這樣的全部地區(qū)一個時區(qū),并且沒有夏令時的還好,不然的話,還要按照時間去切換+N-1,豈不煩死?

??這時候就體現(xiàn)出用地區(qū)名方式時區(qū)的好了,此處直接上代碼展示一下:

加拿大 . 紐芬蘭,每年3月第2個周日開始夏令時并把時鐘往前調(diào)1小時,每年11月第1個周日結(jié)束夏令時并把時鐘往后調(diào)1小時。

ZoneId zoneId = ZoneId.of("Canada/Newfoundland");
LocalDateTime.of(2020,1,1,10,0,0).toInstant(ZoneOffset.of("+8")).atZone(zoneId);   
//2019-12-31T22:30-03:30[Canada/Newfoundland]
LocalDateTime.of(2020,4,1,10,0,0).toInstant(ZoneOffset.of("+8")).atZone(zoneId);
//2020-03-31T23:30-02:30[Canada/Newfoundland]

??可以看到,同樣的時分秒,只因一個在夏天一個不是,換算成紐芬蘭時區(qū)的時候就出現(xiàn)了差異,是不是很有趣呢?


Other Things:

??我們使用jdbc連接mysql的時候,經(jīng)常需要在url上配置serverTimezone=CTT,這是為什么呢?

??這題我會!很明顯這是配置時區(qū)的,使用CTT是為了CST有沖突,對吧?

??沒錯!而且這個沖突其實發(fā)生的很搞笑,通過mysql查詢show variables like '%time_zone%';,一般能看到如下結(jié)果:

image.png

??可以看到time_zone默認(rèn)配置了系統(tǒng)時區(qū),那么為什么服務(wù)器都部署在國內(nèi),且服務(wù)器時區(qū)都設(shè)置了+8,為什么還是有問題呢?
??原因就在mysql的connector處理連接請求時,監(jiān)測到time_zone為System,就會去取得system_time_zone中的CST,此時就有問題了,因為mysql中的CST代表中國標(biāo)準(zhǔn)時區(qū),而java中卻被識別為了美國中部時區(qū),從+8變成了-6,一正一負(fù)就差了14小時?。∷砸丛趍ysql中指定具體時區(qū)為+8,要么就得在連接參數(shù)中指定了。

??另外來看一看時間戳,在Java中常見的獲取毫秒級時間戳的方式有:

1. System.currentTimeMillis()
2. Instant.now().getEpochSecond()
Returns:  the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
3. new Date().getTime()
Returns:
the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this date

??雖然在注釋中看到這幾種都是拿當(dāng)前時間與1970-1-1 00:00:00去比較,但是前兩個比較的是UTC,后面一個比較的是GMT,不過其實都一樣啦。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容