關于時間,每個程序員都應了解的事
注:英文原文由 @程序員的那些事 在微博推薦,感謝 @泉州一中-劉家昌 編譯完成初稿。在譯文基礎上,伯樂在線根據維基百科等資料有補充和改動。也感謝 @周金宇Jered 的翻譯。(如需轉載,請保留本段說明。)
一些關于時間的注解:
● UTC/協調世界時:又稱“世界標準時間”或“世界協調時間”,簡稱UTC(從英文“Coordinated Universal Time”/法文“Temps Universel Cordonné”而來),是最主要的世界時間標準,其以原子時秒長為基礎,在時刻上盡量接近于格林尼治平時。中國大陸稱之為“協調世界時”。臺灣稱之為“世界統一時間”。
● GMT(Greenwich Mean Time):是指位于英國倫敦郊區的皇家格林尼治天文臺的標準時間,因為本初子午線被定義在通過那里的經線。格林尼治時間(GMT)曾經作為標準時間使用,但因地球自轉不規則的原因,GMT 已不再作為現在的標準時間,現在的標準時間是由原子鐘報時的協調世界時(UTC)。
設于英國格林尼治皇家天文臺大門外的24小時制電子大鐘,顯示格林尼治標準時間。
● 其他時區都可寫為與 UTC 時間的偏移量。北京時間是 UTC+8(俗稱“東八區”)。例如某日的 UTC 00:00 是同日的北京時間 08:00。
● 夏令時并不影響 UTC。它只是當地政府關于改變其時區(與 UTC 的偏移量)的決定。 例如,GMT 在冬季是英國的國家時區,而夏季則選用英國夏令時(BST)。
● 閏秒:根據國際慣例,通過在每個 UTC 年的十二月,或者六月的最后一秒,引入一秒閏秒,來使 UTC 與物理現實(UT1,天文時間量度)保持 0.9 秒以內的差距。(UTC 完全是人類武斷的定義,而平均太陽日的長度正以非常緩慢的速度增加中)
● 閏秒并不需提前六個月宣布。這對于時長超過六個月的精密時間計劃是重大的問題。
● Unix 時間:亦或稱 POSIX 時間,以 Unix 紀元初(1970年1月1日0時0分0秒)至今的總秒數度量,不包括閏秒。 Unix 時間不受時區和夏令時的影響。
2038年1月19日3時14分07秒,32位系統的UNIX時間將會被重置
● 根據 POSIX.1 標準,Unix 時間應該通過重復前一秒處理閏秒。例如:
59.00 59.25 59.50 59.75 59.00 ← 重復 59.25 59.50 59.75 00.00 ← 增加 00.25
這一方法有利有弊:你無法重現一個閏秒,而且時間可能會倒退;但從另一個角度來說,每天的時間卻確切為 86,400 秒長,輸出人類可讀的時分秒之時,你并不需要列出一個過去和未來的所有閏秒表。
● ntpd
(Network Time Protocol daemon)在從上流服務器處接收到“閏秒位(leap bits)”后,應當完成這次時間重復。 但我也曾見過它違背這一標準:系統進入到未來的一秒,然后緩慢地偏移回正確的時間。
每個程序員都應了解的關于時間的事:
● 時區是一個表示層的問題! 你多數的代碼不應處理時區或本地時問題,請總使用 Unix 時間。
● 使用 Unix 時間作為度量,它是 UTC 時間 —— 易于獲取,而且沒有時區、夏令時(和閏秒)。
● 保存時間時使用 Unix 時間,它是一個整數。
● 如果你希望保存人類可讀的時間(例如在日志中),考慮將其與 Unix 時間共同保存, 而不是取代 Unix 時間。
● 顯示時間時,同時顯示所在時區——一個未知時區偏移量的時間是無用的。
● 系統時鐘并不精確。
● 你在一個網絡中? 那么請注意所有系統的時鐘都有不同的不精確性。
● 系統時鐘可以——或者會——因你所控制外的事情向后或向前跳躍。你的程序必須能夠處理這一問題。
● 每[實際]秒的[時鐘]滴答并不精確且易變。多數情況它會隨溫度而變化。
● 不要盲目地使用 gettimeofday()
。如果你需要一個單調遞增的時鐘,選擇clock_gettime()
.
● ntpd
可以通過以下兩種方式改變系統時間:
+跳躍: 直接將系統時鐘即時地向前或向后調整為正確時間。
+轉換: 修改系統時鐘的頻率,使系統時間慢慢地偏移為正確時間。
轉換這一方法破壞性較小,因而它較受歡迎。但只當修復小的時間誤差時此方法才有效。
值得一提的:
● 根據相對論,每個“觀察者”的“一秒鐘”都是不同的。遠程時鐘頻率相對一個“觀察者”來說,受到相對速度和重力的影響。GPS 衛星中的時間已根據相對論效應調整。
● MySQL(高于 4.x 和 5.x 的版本)將 DATETIME 類型保存為一個 “YYYY-MM-DD HH:MM:SS” 串的二進制編碼。其本身并不保存時差,使用時根據 @@session.time_zone
指示時區解釋。
mysql> insert into times values(now()); mysql> select unix_timestamp(t) from times; 1310128044 mysql> SET SESSION time_zone='+0:00'; mysql> select unix_timestamp(t) from times; 1310164044
如果你很介意這件事,那么使用 UNIX_TIMESTAMP() 和 FROM_UNIXTIME() 函數,將時間保存為數字。(UNIX_TIMESTAMP() 將參數時間串使用當前 time_zone 的設定解釋,并轉換為 Unix 時間數字,如果當地規定了夏令時,也將按夏令時解釋)
還有 TIMESTAMP 類型,將時間保存為 Unix 時間。它有不同的用法。