1. 程式人生 > 其它 >通用時區:你應該知道的資料庫時區知識

通用時區:你應該知道的資料庫時區知識

摘要:我們深入瞭解一下通用時區資料庫的組織規則,以及時區和夏令時到底是如何維護的,GaussDB(DWS)中又是如何使用的。

本文分享自華為雲社群《你應該知道的時區知識之通用時區資料庫》,原文作者:leapdb 。

1. 背景介紹

接下來,我們深入瞭解一下通用時區資料庫的組織規則,以及時區和夏令時到底是如何維護的,GaussDB(DWS)中又是如何使用的。

2. 通用時區資料庫介紹

各地的時區和夏令時規則由各自的政府獨立管理,他們經常在有限的通知下進行變更。而且他們歷史資料和未來計劃也只是斷斷續續的記錄下來。通用時區資料庫試圖組織和整理這一領域相關的資料。

時區資料庫,通常稱為 tz, tzdata 或 zoneinfo,是一組包含大量程式碼和資料用來表示全球許多有代表性的地點的本地時間的歷史資訊,他會根據各個政體對時區邊界和夏令時規則的改變而不定期的更新。資料庫中每一個條目都代表這自1970年以來被廣泛認可的民用時鐘的時區資訊。該資料庫被很多專案引用,比如:the GNU C Library (used in GNU/Linux), Android, FreeBSD, NetBSD, OpenBSD, Chromium OS, Cygwin, MariaDB, MINIX, MySQL, webOS, AIX, BlackBerry 10, iOS, macOS, Microsoft Windows, OpenVMS, Oracle Database, 和 Oracle Solaris。GaussDB(DWS)同其它廣泛使用的軟體產品一樣,也是採用了IANA維護的通用時區資料。

該資料庫由David Olson創立,由Paul Eggert進行編輯和維護。因而有些地方也將其稱作Olson資料庫。它的顯著特色是由Paul Eggert設計的一套通用時區命名規則,每個時區按照“區域/位置”格式,得到一個獨有的名稱,例如“America/New_York”。英文地名中的空格用下劃線“_”代替,連詞符“-”只在英文地名本身包含時使用。時區資料庫目前普遍有兩個叫法 Olson時區資料庫或IANA時區資料庫。

奧爾森(Olson)的資料有所變化,部分原因是奧爾森(AD Olson)即將退休,部分原因是針對維護者的版權侵權提起了訴訟(現已撤銷)。2011年10月14日,國際網際網路名稱與名稱分配機構(IANA)接管了時區資料庫的維護工作。它會定期進行更新以反映各政治實體對時區邊界、UTC 差值和夏令時規則的更改。對tz的更新遵循BCP 175流程進行管理。

經常有一些國家變更時區規則,IANA每年都會發布最新的時區資料和解析原始碼庫。IANA提供三種方法對時區資料庫的訪問:

  1. https://www.iana.org/time-zones
  2. ftp://ftp.iana.org/tz/
  3. rsync://rsync.iana.org/tz/

時區資料庫中包含各大洲原始的時區定義的文字檔案和解析這些文字檔案的程式碼檔案。

2.1 通用時區資料庫原始碼

相關資料:

原始碼託管地址:https://github.com/eggert/tz
時區資料庫的介紹 https://data.iana.org/time-zones/tz-link.html
時區資料庫原理及使用 https://data.iana.org/time-zones/theory.html

下載方法:

mkdir tzdb
cd tzdb
wget https://www.iana.org/time-zones/repository/tzcode-latest.tar.gz #下載最新解析時區文字定義的程式碼檔案
wget https://www.iana.org/time-zones/repository/tzdata-latest.tar.gz #下載最新時區定義的文字檔案
gzip -dc tzcode-latest.tar.gz | tar -xf -
gzip -dc tzdata-latest.tar.gz | tar -xf -
#或者下載程式碼+資料的完整壓縮包
wget https://www.iana.org/time-zones/repository/tzdb-latest.tar.lz #下載程式碼+資料的完整時區資料庫
lzip -dc tzdb-latest.tar.lz | tar -xf -

程式碼結構:

時區資料庫中包含的時區定義檔案(文字檔案):
africa antarctica asia australasia europe northamerica southamerica

時區資料庫中包含的程式碼檔案(用於解析時區定義檔案)
asctime.c date.c difftime.c localtime.c strftime.c zdump.c zic.c

安裝目錄結構:

編譯完成後的安裝目錄結構如下

cd tzdb-2020a
make TOPDIR=$HOME/tzdir install

leap@gaussdb:~> ./sbin/tree -L 3 tzdir/ | more
tzdir/
├── etc
│   └── localtime    #本地時區檔案
└── usr
    ├── bin
    │   ├── tzselect #設定時區的工具
    │   └── zdump    #以文字展示某個時區變化歷史的工具
    ├── lib
    │   └── libtz.a  #解析時區檔案的靜態庫檔案
    ├── sbin
    │   └── zic      #時區編譯器,可將時區定義的文字檔案編譯成二進位制時區檔案
    └── share
        ├── man
        ├── zoneinfo #編譯好的各個時區檔案
        ├── zoneinfo-leaps
        └── zoneinfo-posix -> zoneinfo

#IANA的時間資料庫被the GNU C Library (used in GNU/Linux)採用,因此install後的目錄也按linux的系統目錄來組織。

程式碼中的zic.c是一個將時區定義的原始文字檔案解析成二進位制時區檔案的工具。如果一個軟體產品需要從IANA獲取最新時區資料的話,需要同時獲取原始碼和資料,並生成zic解析器將時區資料生成時區檔案給軟體產品使用。時區檔案的解析程式碼在localtime.c中

2.2 時區原始資料的規則及維護方法

時區資料庫的RAW檔案,是按照一定的規則組織的文字檔案。樣例如下:

該規則設計的很科學,能記錄時區及夏令時變更的歷史資訊。使用該通用時區庫能自動的轉換歷史時間,也因此該庫被廣泛採用。

這裡介紹一下莫斯科的時區定義

Zone  NAME              STDOFF  RULES  FORMAT   [UNTIL]
Zone Europe/Moscow       2:30:17 -      LMT     1880
                         2:30:17 -      MMT     1916 Jul  3 # Moscow Mean Time
                         2:31:19 Russia %s      1919 Jul  1  0:00u
                         3:00   Russia  %s      1921 Oct
                         3:00   Russia  MSK/MSD 1922 Oct
                         2:00   -       EET     1930 Jun 21
                         3:00   Russia  MSK/MSD 1991 Mar 31  2:00s
                         2:00   Russia  EE%sT   1992 Jan 19  2:00s
                         3:00   Russia  MSK/MSD 2011 Mar 27  2:00s
                         4:00   -       MSK     2014 Oct 26  2:00s
                         3:00   -       MSK
  • 1992年1月19日凌晨2點到2011年03月27日凌晨2點,採用東三區
  • 2011年3月27日凌晨2點到2014年10月26日凌晨2點,採用東四區
  • 2014年10月26日凌晨2點以後採用東三區

如何閱讀時區資料庫的RAW檔案https://data.iana.org/time-zones/tz-how-to.html
VSCode的Zoneinfo外掛可以通過語法高亮的方式檢視時區資料的RAW檔案

時區資料庫的基本功能是提供 時區資料原始檔案 和一個將 時區資料原始檔案 轉換成 時區檔案 的一個編譯器:zic。

可以通過 man zic 瞭解時區檔案編譯器(將時區定義文字檔案編譯成二進位制時區資料檔案)的使用

3. 通用時區資料庫使用

每個時區名的具體定義都存在於時區檔案中,比如:/etc/localtime。
作業系統支援的時區檔案儲存在/usr/share/zoneinfo/目錄下。
我們資料庫支援的時區檔案儲存在share/timezone目錄下。

時區檔案有統一約定的格式要求(來自IANA維護的時區資料庫),可以使用info tzfile檢視。時區檔案固定開頭結構:

struct tzhead
{
    char        tzh_magic[4];    /* TZ_MAGIC,固定在開頭的特徵字元"TZif"來標識時區檔案 */
    char        tzh_version[1]; /* '\0' or '2' or '3' as of 2013,版本資訊 */
    char        tzh_reserved[15];    /* reserved; must be zero */
    char        tzh_ttisutcnt[4];    /* coded number of trans. time flags 儲存在檔案中的UTC/local指示器數目*/
    char        tzh_ttisstdcnt[4];    /* coded number of trans. time flags 儲存在檔案中的standard/wall指示器數目*/
    char        tzh_leapcnt[4]; /* coded number of leap seconds 其值儲存在檔案中的leap second的數目*/
    char        tzh_timecnt[4]; /* coded number of transition times 其值儲存在檔案中的"變化時間"數目*/
    char        tzh_typecnt[4]; /* coded number of local time types 其值儲存在檔案中的"本地時間型別"數目(非零!)*/
    char        tzh_charcnt[4]; /* coded number of abbr. chars 儲存在檔案中的"時區簡寫符"數目*/
};

跟在上面這些頭部後的是tzh_timecnt 個"標準"位元組順序的四位元組 long 型別值, 以升序排序. 每個值均作為一個變化時間(就像 time(2) 的返回), 系統依賴這些值來計算本地時間變化.

而在此之後的是 tzh_timecnt 個 unsigned char 型別的一位元組值, 這些值指出了檔案中描述的多種"本地時間"型別中哪一個與具有相同索引的變化時間相關. 這些值可作為 ttinfo 結構陣列的索引.

而 ttinfo 結構在檔案中隨後就有定義, 描述如下:

struct ttinfo {
long tt_gmtoff;
int tt_isdst;
unsigned int tt_abbrind;
};
結構包括一個"標準"位元組順序的四位元組 long 型別值 tt_gmtoff,
以及一個一位元組的 tt_isdst
和一個一位元組的 tt_abbrind.
在每個結構裡, tt_gmtoff 給出了要被加到UTC的時間, 以秒為單位,
tt_isdst 表明 tm_isdst 是否可通過 localtime (3) 設定,
而 tt_abbrind 可作為時區簡寫符的陣列索引, 該陣列在檔案中跟在 ttinfo 結構後面.

這樣就有 tzh_leapcnt 個標準位元組順序的四位元組對, 每個四位元組對的第一個值給出一個leap second發生的時間, 就如 time(2) 的返回; 每個四位元組對的第二個值給出給定時間之後所實現的總的 leap second數. 四位元組對按時間的升序排序.

同樣有 tzh_ttisstdcnt 個standard/wall指示器, 每個儲存了一個一位元組值; 這些指示器指出了變化時間(與本地時間型別相關)是否被說明為standard time或者wall clock time, 以及當一個時區檔案被用於處理POSIX 格式時區環境變數時是否使用變化時間.

最後, 有 tzh_ttisgmtcnt 個UTC/local指示器, 每個儲存了一個一位元組值; 這些指示器指出了變化時間(與本地時間型別相關)是否被說明為UTC 或者local time, 以及當一個時區檔案被用於處理 POSIX格式時區環境變數時是否使用變化時間.

如果 tzh_timecnt 等於零或者時間引數比檔案記錄的第一個變化時間小的話, Localtime 就使用檔案中的第一個標準時間 ttinfo, 或者在沒有標準時間結構是就直接使用第一個 ttinfo 結構.

可以通過 man zdump dump二進位制時區資料檔案,瞭解如何檢視一個時區的變更歷史

./zdump -V Asia/Chongqing | more
Asia/Chongqing  Sat Dec 31 16:53:39 1927 UT = Sat Dec 31 23:59:59 1927 LMT isdst=0 gmtoff=25580
Asia/Chongqing  Sat Dec 31 16:53:40 1927 UT = Sat Dec 31 23:53:40 1927 LONT isdst=0 gmtoff=25200
Asia/Chongqing  Wed Apr 30 16:59:59 1980 UT = Wed Apr 30 23:59:59 1980 LONT isdst=0 gmtoff=25200
Asia/Chongqing  Wed Apr 30 17:00:00 1980 UT = Thu May  1 01:00:00 1980 CST isdst=0 gmtoff=28800
Asia/Chongqing  Sat May  3 15:59:59 1986 UT = Sat May  3 23:59:59 1986 CST isdst=0 gmtoff=28800
Asia/Chongqing  Sat May  3 16:00:00 1986 UT = Sun May  4 01:00:00 1986 CDT isdst=1 gmtoff=32400
Asia/Chongqing  Sat Sep 13 14:59:59 1986 UT = Sat Sep 13 23:59:59 1986 CDT isdst=1 gmtoff=32400
Asia/Chongqing  Sat Sep 13 15:00:00 1986 UT = Sat Sep 13 23:00:00 1986 CST isdst=0 gmtoff=28800
Asia/Chongqing  Sat Apr 11 15:59:59 1987 UT = Sat Apr 11 23:59:59 1987 CST isdst=0 gmtoff=28800
Asia/Chongqing  Sat Apr 11 16:00:00 1987 UT = Sun Apr 12 01:00:00 1987 CDT isdst=1 gmtoff=32400
Asia/Chongqing  Sat Sep 12 14:59:59 1987 UT = Sat Sep 12 23:59:59 1987 CDT isdst=1 gmtoff=32400
Asia/Chongqing  Sat Sep 12 15:00:00 1987 UT = Sat Sep 12 23:00:00 1987 CST isdst=0 gmtoff=28800
Asia/Chongqing  Sat Apr  9 15:59:59 1988 UT = Sat Apr  9 23:59:59 1988 CST isdst=0 gmtoff=28800
Asia/Chongqing  Sat Apr  9 16:00:00 1988 UT = Sun Apr 10 01:00:00 1988 CDT isdst=1 gmtoff=32400
Asia/Chongqing  Sat Sep 10 14:59:59 1988 UT = Sat Sep 10 23:59:59 1988 CDT isdst=1 gmtoff=32400
Asia/Chongqing  Sat Sep 10 15:00:00 1988 UT = Sat Sep 10 23:00:00 1988 CST isdst=0 gmtoff=28800
Asia/Chongqing  Sat Apr 15 15:59:59 1989 UT = Sat Apr 15 23:59:59 1989 CST isdst=0 gmtoff=28800
Asia/Chongqing  Sat Apr 15 16:00:00 1989 UT = Sun Apr 16 01:00:00 1989 CDT isdst=1 gmtoff=32400
Asia/Chongqing  Sat Sep 16 14:59:59 1989 UT = Sat Sep 16 23:59:59 1989 CDT isdst=1 gmtoff=32400
Asia/Chongqing  Sat Sep 16 15:00:00 1989 UT = Sat Sep 16 23:00:00 1989 CST isdst=0 gmtoff=28800
Asia/Chongqing  Sat Apr 14 15:59:59 1990 UT = Sat Apr 14 23:59:59 1990 CST isdst=0 gmtoff=28800
Asia/Chongqing  Sat Apr 14 16:00:00 1990 UT = Sun Apr 15 01:00:00 1990 CDT isdst=1 gmtoff=32400
Asia/Chongqing  Sat Sep 15 14:59:59 1990 UT = Sat Sep 15 23:59:59 1990 CDT isdst=1 gmtoff=32400
Asia/Chongqing  Sat Sep 15 15:00:00 1990 UT = Sat Sep 15 23:00:00 1990 CST isdst=0 gmtoff=28800
Asia/Chongqing  Sat Apr 13 15:59:59 1991 UT = Sat Apr 13 23:59:59 1991 CST isdst=0 gmtoff=28800
Asia/Chongqing  Sat Apr 13 16:00:00 1991 UT = Sun Apr 14 01:00:00 1991 CDT isdst=1 gmtoff=32400
Asia/Chongqing  Sat Sep 14 14:59:59 1991 UT = Sat Sep 14 23:59:59 1991 CDT isdst=1 gmtoff=32400
Asia/Chongqing  Sat Sep 14 15:00:00 1991 UT = Sat Sep 14 23:00:00 1991 CST isdst=0 gmtoff=28800

因為我國從1991年開始取消了夏令時,所以就不再變化。

4. GaussDB(DWS)中如何使用時區資料

為了國內使用者的使用方便,GaussDB(DWS)根據IANA的定義的語法規則,在內部幫助使用者定義了 Asia/Beijing 時區,其定義與 PRC 時區定義一致。位於按裝目錄下的 timezone/Asia/Beijing

檢視Asia/Beijing的具體定義:

./zdump -V Asia/Beijing
Asia/Beijing  Sat May  3 15:59:59 1986 UT = Sat May  3 23:59:59 1986 CST isdst=0 gmtoff=28800
Asia/Beijing  Sat May  3 16:00:00 1986 UT = Sun May  4 01:00:00 1986 CDT isdst=1 gmtoff=32400
Asia/Beijing  Sat Sep 13 14:59:59 1986 UT = Sat Sep 13 23:59:59 1986 CDT isdst=1 gmtoff=32400
Asia/Beijing  Sat Sep 13 15:00:00 1986 UT = Sat Sep 13 23:00:00 1986 CST isdst=0 gmtoff=28800
Asia/Beijing  Sat Apr 11 15:59:59 1987 UT = Sat Apr 11 23:59:59 1987 CST isdst=0 gmtoff=28800
Asia/Beijing  Sat Apr 11 16:00:00 1987 UT = Sun Apr 12 01:00:00 1987 CDT isdst=1 gmtoff=32400
Asia/Beijing  Sat Sep 12 14:59:59 1987 UT = Sat Sep 12 23:59:59 1987 CDT isdst=1 gmtoff=32400
Asia/Beijing  Sat Sep 12 15:00:00 1987 UT = Sat Sep 12 23:00:00 1987 CST isdst=0 gmtoff=28800
Asia/Beijing  Sat Apr  9 15:59:59 1988 UT = Sat Apr  9 23:59:59 1988 CST isdst=0 gmtoff=28800
Asia/Beijing  Sat Apr  9 16:00:00 1988 UT = Sun Apr 10 01:00:00 1988 CDT isdst=1 gmtoff=32400
Asia/Beijing  Sat Sep 10 14:59:59 1988 UT = Sat Sep 10 23:59:59 1988 CDT isdst=1 gmtoff=32400
Asia/Beijing  Sat Sep 10 15:00:00 1988 UT = Sat Sep 10 23:00:00 1988 CST isdst=0 gmtoff=28800
Asia/Beijing  Sat Apr 15 15:59:59 1989 UT = Sat Apr 15 23:59:59 1989 CST isdst=0 gmtoff=28800
Asia/Beijing  Sat Apr 15 16:00:00 1989 UT = Sun Apr 16 01:00:00 1989 CDT isdst=1 gmtoff=32400
Asia/Beijing  Sat Sep 16 14:59:59 1989 UT = Sat Sep 16 23:59:59 1989 CDT isdst=1 gmtoff=32400
Asia/Beijing  Sat Sep 16 15:00:00 1989 UT = Sat Sep 16 23:00:00 1989 CST isdst=0 gmtoff=28800
Asia/Beijing  Sat Apr 14 15:59:59 1990 UT = Sat Apr 14 23:59:59 1990 CST isdst=0 gmtoff=28800
Asia/Beijing  Sat Apr 14 16:00:00 1990 UT = Sun Apr 15 01:00:00 1990 CDT isdst=1 gmtoff=32400
Asia/Beijing  Sat Sep 15 14:59:59 1990 UT = Sat Sep 15 23:59:59 1990 CDT isdst=1 gmtoff=32400
Asia/Beijing  Sat Sep 15 15:00:00 1990 UT = Sat Sep 15 23:00:00 1990 CST isdst=0 gmtoff=28800
Asia/Beijing  Sat Apr 13 15:59:59 1991 UT = Sat Apr 13 23:59:59 1991 CST isdst=0 gmtoff=28800
Asia/Beijing  Sat Apr 13 16:00:00 1991 UT = Sun Apr 14 01:00:00 1991 CDT isdst=1 gmtoff=32400
Asia/Beijing  Sat Sep 14 14:59:59 1991 UT = Sat Sep 14 23:59:59 1991 CDT isdst=1 gmtoff=32400
Asia/Beijing  Sat Sep 14 15:00:00 1991 UT = Sat Sep 14 23:00:00 1991 CST isdst=0 gmtoff=28800

現在GaussDB(DWS)中所有關於時區的知識都介紹完了,希望從原理和維護角度的深入學習徹底掃除時區使用的疑惑,如果有相關問題歡迎在論壇中提問。

5. 總結

綜上,GaussDB(DWS)作為一款面向全球使用者的高效能分析型資料庫產品,對時區資料的支援也是符合工業界標準規範的。

想了解GuassDB(DWS)更多資訊,歡迎微信搜尋“GaussDB DWS”關注微信公眾號,和您分享最新最全的PB級數倉黑科技~

點選關注,第一時間瞭解華為雲新鮮技術~