Timestamps

UNIX 秒、TAI (SI) 秒、閏秒和真實世界程式碼

  • November 10, 2020

我最近讀了很多關於 UNIX Time 的文章,其中大部分內容不連貫,大部分內容自相矛盾。我正在嘗試協調 UNIX 時間(以下簡稱 UXT)、TAI 和 UTC 之間的轉換,為此,我需要正確理解 UXT。問題是,我似乎找不到其他人這樣做。

以下是我的最佳嘗試解釋,通過繁瑣的研究從無數來源重建。某處也是錯誤的。 我正在尋找對以下內容的整體分析和逐點驗證/反駁。本質上,修復以下內容以使其正常工作。


  1. TAI 是一個單調遞增的時間標準。它以 SI 秒為單位,忽略 DST 和閏秒。
  2. UTC 與 TAI 相同,但修正了整數閏 SI 秒(轉換為時間字元串將其反映為第 60 秒),以便在天文時間標準 UT1 的 0.9 SI 秒內。
  3. UXT 是自 1970-01-01 00:00:00 UTC 以來的*UNIX 秒數。*每天總是正好有 86400 秒。然而,UXT 與 UTC 相關。
  4. 這怎麼可能?好吧,UNIX 秒需要與 SI 秒不同,並且因為閏秒不是完全有規律的,所以 UNIX 秒不能是一個明確定義的時間長度。
  5. UNIX 規範第4.15 節中從 UTC 到 UXT 的轉換將不同的 UTC 時間別名為相同的 UXT 時間戳,有效地使 UNIX 秒與 SI 秒相同(UNIX 閏秒除外,它是兩個 SI 秒)

在實踐中,實際發生的情況各不相同。大多數電腦基於遠端伺服器進行同步,因此它們在同步期間隱式處理閏秒更新。 6. 所有這一切意味著,雖然每個單獨的 UXT 時間戳都可以輕鬆地轉換為/從 UTC 轉換(gmtime分別使用或 §4.15),但您無法真正使用它們進行算術運算來找出任何東西。特別是,difftime返回 UNIX seconds,所以你不能用它做任何事情,包括將它添加到不同的時間戳,除非你知道所有相關的閏秒在哪裡。

我想我明白到目前為止。

  1. 但是現在我們看看實際的程式碼,它根本沒有做任何事情。我可以理解人們使用測量持續時間difftime並且只是希望它足夠好(或者不知道有問題),但是計時庫也是錯誤的。

例如,libtai提供tai_now.c:7從 UXT 到 TAI 的轉換 ( ) 為:TAI := 4,611,686,018,427,387,914 + UXT. 由於 TAI 標記 SI 秒,而 UXT 標記 UNIX 秒,所以您不能這樣做。然而,由於libtai明確地處理閏秒,這是一個粗心的錯誤似乎是不合理的。

它不特定於libtai. 你到處都看到這種事情。

所以:第 1-6 點與第 7 點不一致。也就是說,大量現有程式碼與它所代表的時間標準相矛盾。 什麼地方出了錯?

好 。. . 未來的你在這裡,為一種封閉感而寫作。

在你寫了這個問題幾年後,但在今天之前的幾年,你寫了一個巨大的教程來解決這個問題。它構成了現代時間標準的完整引導,從 TAI 到 UTC 到偏移量和 UXT。


至於你的問題,看來你確實得到了第 1-6 點基本上是正確的。您的第 7 點可以概括為“軟體不可能在這方面做得這麼差,對吧?”

劇透警告:它可以。關於 libtai 的特別抱怨不是一個很好的例子——該庫包含閏秒,但僅通過組合適當的函式,而不僅僅是您查看的單個函式。這對我們來說感覺像是一個巨大的(並且明顯令人困惑的)缺陷,即使它最終得到了正確的答案(誰知道它是否正確),但它沒有用,因為這種事情非常普遍。不僅大多數軟體實際上會出錯,而且軟體會以不同的、不兼容的方式出錯!

哎呀,基本 UXT 原語的文件time(...)甚至都不清楚。標準 C 文件(ref.) , (ref.)遵循 POSIX 標準(ref.)的(部分),說它是自紀元以來的秒數。但是在 POSIX 標準(ref.)的其他地方,它不是秒數,而是從紀元開始的秒數,不包括閏秒,這是正確的表徵。這個想法是為了讓計算變得更簡單,但它顯然會讓計算變得更難,因為當你在數軸中引入不連續性時,減法不起作用。difftime(...)例如,任何使用 的程序都必然是不正確的。

這裡的關鍵心理模型是 SI 秒和 UNIX 秒是不同的。這在 POSIX 規範的另一個地方是最清楚的。(參考)。本節通過日期時間轉換將 UXT 與 UTC 掛鉤。UTC 包括閏秒,但本節指定天為 86 400 秒。我們該如何調和呢?關鍵的實現是一些 UNIX 秒是 2 SI 秒長,一些是 0(很明顯,絕大多數是 1)。還在迷茫嗎?去閱讀教程!現實世界的程式碼如此混亂,這並不奇怪。


您很遺憾無法將讀者指向程式碼。您編寫了自己的計時庫,但它既複雜又強大,而且還沒有真正投入生產。

我想,首先,呼叫time(...)以獲取 UNIX 秒的 POSIX-epoch-relative 計數,減去 220 924 790,並添加閏秒(從表中)以獲得 SI 秒的 TAI-epoch-relative 計數。反向執行以返回。(注意:TAI 時代有些模糊;我選擇了 1977-01-01 00:00:00 TAI(即 1976-12-31 23:59:45 UTC),因為這是標準化 TAI 的時代, JD TAI、TCB、TCG、TT 和 TDB (參考)。)

在 C++20 中,我們將擁有std::chrono::tai_clock1958-01-01 00:00:00 TAI(即 1957-12-31 23:59:50 UTC)。希望它是正確的,我們可以將這整個骯髒的業務大部分拋在腦後。

一個問題是大多數文件不使用能夠區分時間尺度而不會產生歧義的詞彙表。我建議http://www.ucolick.org/~sla/leapsecs/picktwo.html作為對歷史使用中有兩種不相關的秒數的問題的介紹——一種是日曆日的細分地球,並且是在特定參考系中測量的恆定持續時間。任何跨越 1970 年之前和之後日期的時間庫都試圖利用這兩種秒,最終提供的答案類似於聲稱提供 arcsin(-2) 的函式——也就是說,有所涉及的複雜性需要仔細解釋和定義究竟什麼被認為是重要的。

引用自:https://unix.stackexchange.com/questions/283164