1. 程式人生 > >程式設計師如何處理好時區問題

程式設計師如何處理好時區問題

寫國際化的程式,經常會遇到兩種問題:字元編碼、時間問題。今天我們就聊聊程式中如何處理時間問題。

首先,要了解一些基本的概念,只有對概念有清晰的掌握,才能明白解決方法。

基本概念

GMT時間:格林尼治標準時間(英語:Greenwich Mean Time,GMT)是指位於英國倫敦郊區的皇家格林尼治天文臺當地的平太陽時,因為本初子午線被定義為通過那裡的經線。

由於地球每天的自轉是有些不規則的,而且正在緩慢減速,因此格林尼治平時基於天文觀測本身的缺陷,已經被原子鐘報時的協調世界時(UTC)所取代。

UTC時間:協調世界時(英語:Coordinated Universal Time,法語:Temps Universel Coordonné,簡稱UTC)是最主要的世界時間標準,其以原子時秒長為基礎,在時刻上儘量接近於格林尼治標準時間。

對於大多數用途來說,UTC時間被認為能與GMT時間互換,但GMT時間已不再被科學界所確定。

UNIX時間戳:Unix time又叫POSIX time或UNIX Epoch time,是從UTC時間1970年1月1日起到現在的秒數,不考慮閏秒,一天有86400秒。

時區:時區是地球上的區域使用同一個時間定義。世界各個國家位於地球不同位置上,因此不同國家特別是東西跨度大的國家日出、日落時間必定有所偏差。這些偏差就是所謂的時差。

閏秒:閏秒是在協調世界時(UTC)中增加或減少一秒,使它與平太陽時貼近所做調整。在UTC時間中,有時會出現一分鐘有59秒或61秒。

夏令時:美國原本於每年4月的第一個星期日凌晨2時起至10月的最後一個星期日凌晨2時實施夏時制;但經美國國會2005年通過的能源法案,自2007年起延長夏時制,開始日期從每年4月的第一個星期日,提前到3月的第二個星期日,結束日期從每年10月的最後一個星期日,延後到11月的第一個星期日。美國夏時制實行與否,完全由各州各郡自己決定。

時間格式的標準:參考ISO_8601日期格式標準 https://zh.wikipedia.org/wiki/ISO_8601。例如:2004-05-03T17:30:08+08:00 在時間前面加上大些字母T,要標明偏移的時區時間。

概念解讀

通過上面的概念介紹瞭解到,GMT就是0時區的時間,以前是標準,但現在國際上已經用UTC取代他了。在寫程式時,可以認為UTC和GMT是等價的。為了嚴謹只需要關心UTC時間。

UNIX時間戳是程式中最常用的,他的特點是和UTC時間的1970年1月1日到現在的秒數,和時區無關,無論在地球上的那個角落,同一時刻,UNIX時間戳都是一樣的。是一個通用的時間偏移度量,計算每個時區當地時間時,都可以用時間戳推算出來。

不同時區的時間,都用UTC時間的偏移來計算。例如北京是東八區,比UTC時間快8個小時,所以計算北京時間,就在UTC時間的基礎上加8個小時實現。

我們在呼叫系統函式展示時間時,底層是根據UNIX時間戳轉換為UTC時間,再加上偏移的小時數,就得出了程式要用的當地時間。

UNIX時間戳可以對映到每個時區的當地時間,如果程式涉及到兩個時區的時間轉換,最好的方法是儲存UNIX時間戳,在使用的時候再做轉換。

在各種語言的函式庫中,都已經定義了時間時區轉換的函式。在使用時,還有一點要注意「時區偏移(time offset)」和「時區地區(time zone)」是兩個不同的概念。

偏移是一個數學上的值,直接能計算出時間。時區地區,會根據當地的法律規則,來得出最終的時間,混入了人為的規則。

例如:
在夏令時時,北京和紐約時差是12個小時,但是當夏令時結束時,北京和紐約的時差是11個小時。如果一直用固定的時間偏移,就會計算出錯。如果用指定的地區當引數,就會根據當地規則返回正確時間。

具體例子見程式碼:

<?php                                                                              

date_default_timezone_set('Asia/Shanghai');                                        
$d=strtotime("2018-11-04 13:00:00");                                               
echo "Beijing " . date("Y-m-d h:i:sa", $d) . "\n";                                 

date_default_timezone_set('America/New_York');                                     
echo "NewYork " . date("Y-m-d h:i:sa", $d) . "\n";                                 

echo "\n";                                                                         

date_default_timezone_set('Asia/Shanghai');                                        
$d=strtotime("2018-11-04 14:00:00");                                               
echo "Beijing " . date("Y-m-d h:i:sa", $d) . "\n";                                 

date_default_timezone_set('America/New_York');                                     
echo "NewYork " . date("Y-m-d h:i:sa", $d) . "\n";                                 

echo "\n";                                                                                                                                          

date_default_timezone_set('Asia/Shanghai');                                        
$d=strtotime("2018-11-04 15:00:00");                                               
echo "Beijing " . date("Y-m-d h:i:sa", $d) . "\n";                                 

date_default_timezone_set('America/New_York');                                     
echo "NewYork " . date("Y-m-d h:i:sa", $d) . "\n";                                 

輸出結果

Beijing 2018-11-04 01:00:00pm   //沒結束夏令時時,時差12個小時
NewYork 2018-11-04 01:00:00am

Beijing 2018-11-04 02:00:00pm   //夏令時切換,時差為11個小時
NewYork 2018-11-04 01:00:00am

Beijing 2018-11-04 03:00:00pm
NewYork 2018-11-04 02:00:00am

總結

  1. 涉及到多個時區的轉換,統一使用unix時間戳儲存或互動,或者使用帶有時區資訊的字串。
  2. 儘量在上層的程式碼層面修改時區配置,不要修改系統或軟體的配置,防止其他程式因為修改受到影響。

本質:時區概念是上層人為轉換的概念,程式的邏輯不要依賴於他,要有個統一的時刻值概念來衡量真實的時間(例如UNIX時間戳),然後在上層做轉換。

參考

https://zh.wikipedia.org/wiki/%E6%A0%BC%E6%9E%97%E5%B0%BC%E6%B2%BB%E6%A8%99%E6%BA%96%E6%99%82%E9%96%93

https://en.wikipedia.org/wiki/Unix_time

https://zh.wikipedia.org/wiki/%E5%8D%8F%E8%B0%83%E4%B8%96%E7%95%8C%E6%97%B6

https://zh.wikipedia.org/wiki/%E6%97%B6%E5%8C%BA

https://www.cnblogs.com/zihanxing/articles/6224263.html


轉載請註明來源: 程式設計師如何處理好時區問題 本文連結地址: https://www.owenzhang.net/blog/197.html 歡迎收聽公眾號