人人都知道的时区,背后竟然有这么多奥秘

举报
leapdb 发表于 2021/02/11 20:16:41 2021/02/11
【摘要】 GaussDB(DWS)中关于时区相关知识及使用的介绍

1. 背景介绍

​ 时区同其它工业标准一样,标准化过程也是一个复杂而漫长的过程。GaussDB(DWS)作为一款面向全球用户的高性能分析型数据库产品,对时区的支持也是符合工业界标准规范的。下面从时区概念GaussDB(DWS)时区使用及原理,以及一些常见问题等方面介绍一下GaussDB(DWS)。
希望对大家了解GaussDB(DWS)和时区概念有所帮助。

2. 概念篇

​ 在以前全球国家都处于农业社会的时候,人们通过每天观察太阳的位置来决定时间,这就使得不同经度的地方有不同的时间。当时人们旅行主要靠走和马匹,不同地方时间不一致的问题没有那么突出。但是到了十九世纪随着火车的发明,人们一天旅行的距离一下子延长了很多,到不同的地方因此迫切需要一个通用的方法把各个地方的时间统一起来。1853年8月12日,美国东部罗德岛州,两辆火车迎头相撞,14人因此死亡。事故的原因在今天看来难以置信——两车工程师的手表差了2分钟。

1863年,首次使用时区的概念。时区通过设立一个区域的标准时间部分地解决了这个问题。
1870年代加拿大铁路工程师弗莱明首次提出全世界按统一标准划分时区。
1883年11月18日,美国铁路部门正式实施五个时区。
1884年华盛顿子午线国际会议正式通过采纳这种时区划分,称为世界标准时制度。因此,世界标准时区的诞生同其它全球标准一样也是有一个缓慢的发展过程。

2.1 名词解释

时区 时区是地球上的区域使用同一个时间定义。以前,人们通过观察太阳的位置(时角)决定时间,这就使得不同经度的地方的时间有所不同(地方时)。1863年,首次使用时区的概念。时区通过设立一个区域的标准时间部分地解决了这个问题。世界各个国家位于地球不同位置上,因此不同国家,特别是东西跨度大的国家日出、日落时间必定有所偏差。这些偏差就是所谓的时差。
格林尼治标准时间 GMT(Greenwich Mean Time)是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义为在那里通过的经线。由于地球在它的椭圆轨道里的运动速度不均匀,地球每天的自转是有些不规则的,而且正在缓慢减速。所以,格林尼治时间已经不再被作为标准时间使用。
世界协调时间 UTC(Coordinated Universal Time)是经过平均太阳时(以格林威治时间GMT为准)、地轴运动修正后的新时标以及以「秒」为单位的国际原子时所综合精算而成的时间。UTC比GMT来得更加精准。对于现行表款来说,GMT与UTC的功能与精确度是没有差别的。
夏日节约时间 DST(Daylight Saving Time)又称日光节约时制,在英国称为夏令时间(Summer Time)。是一种为节约能源而人为规定地方时间的制度,在这一制度实行期间所采用的统一时间称为“夏令时间”。一般在天亮较早的夏季人为将时间调快一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。各个采纳夏时制的国家规定不同。
时区表示法 如果时间是以协调世界时(UTC)表示,则在时间后面直接加上一个“Z”(不加空格)。“Z”是协调世界时中0时区的标志。因此,“09:30 UTC”就写作“09:30Z”或是“0930Z”。“14:45:15 UTC”则为“14:45:15Z”或“144515Z”。UTC时间也被叫做祖鲁时间,因为在北约音标字母中用“Zulu”表示“Z”。
UTC偏移量 UTC偏移量是协调世界时(UTC)和特定地点的日期与时间差异,其单位为小时和分钟。它通常以 ±[hh]:[mm]、±[hh][mm]、或 ±[hh]的格式显示。所以,如果被描述的时间比UTC早一小时(例如柏林的冬季时间),UTC的偏移量将是”+01:00”、”+0100”、或简单显示为”+01”。

各国时间调整

  1. 摩洛哥在2010年开始将夏令时开始时间从6月1日调整为5月2日,结束时间从8月21日调整为8月8日

  2. Tunisia从2010年开始废弃夏令时

  3. 麦夸里岛位于澳洲与南极洲之间决定从2010年4月4日开始不在跟随澳洲其它地区一起采用夏令时,由此产生新的时区Antarctica/Macquarie,简称MIST

  4. 朝鲜 - 2018年5月5日起调整时区,时区由 GMT+8:30 改为 GMT+9:00,因此平壤的标准时间于 2018年5月5日星期六 00:00:00 时间改为 00:30:00 拨快 30 分钟。当格林威治 GMT 标准时间是凌晨 0:00,平壤时间就是上午 9:00,与韩国和日本同一时区。

  5. 朝鲜 - 2015年8月15日起,弃用 GMT+9,“恢复”大韩帝国时代的 GMT+8:30 标准时间,新启用的时间比原来的时间慢30分钟,是世界上唯一正在使用这个时区的国家。当格林威治 GMT 是凌晨 0:00,平壤时间就是上午 8:30。

  6. 智利 - 2015年1月28日,智利採用了新的官方标准时间,他们把原来的标准时间和夏令时间合併成一个,永久採用夏令时制。新的官方时间在智利大陆改为 GMT-3,復活节岛则为 GMT-5。

  7. 俄罗斯 - 2014年10月最后一个星期日 (2014年10月26日) 凌晨开始,永久採用冬令时制,全国会拨慢一小时。因此莫斯科时间将由 GMT+4改为 GMT+3。

可见,不管是时区还是夏令时都会经常改变。

2.2 通用时间格式

主要参考Posix 1003.1 Section 8.3。操作系统中TZ环境变量代表时区信息,主要用于ctime、localtime、mktime等等。TZ时区名字有两种形式:时区名格式和POSIX时区格式。

2.2.1 时区名格式

:characters 该种形式以冒号开始,后面的字符处理与实现相关。linux上表示从某个文件读时区信息,例如TZ=":Pacific/Auckland"。

2.2.2 POSIX时区格式

不以冒号开始的格式都算此种格式,扩展开来如下,[]里的字段代表是可选的:std offset[dst[offset][,startdate[/time], enddate[/time]]]

格式:std offset[dst[offset][,startdate[/time], enddate[/time]]]

各字段含义如下:

  1. std && dst 代表标准时区和日光节约制时区,std是必须的,dst是可选的(有dst字段则代表支持夏令时)。这两个字段有两种格式:
    a. 一种带引用符号<>,引用符号内的字符串可以是字母、数字、+、-,解析时不包含引用符号<>;
    b. 另一种不带引用符号<>,字符串只能是字母。

字段长度大于等于3,小于等于TZNAME_MAX(linux是6),如果字符串长度不符合规定,解析规则未定义(linux不识别)。

  1. offset 代表本地时间加上多少能得到UTC时间。
    格式为hh[:mm[:ss]],0<=hh<=24,0<=mm&&ss<=59。hh是必须的,并且可以是1位,mm和ss是可选的。

​ 如果前面带了一个加号+,表示本初子午线以西。表示本时间加上多少时间才能得到UTC时间。加号+也可不带。
​ 如果前面带了一个减号-,表示本初子午线以东。表示本时间减去多少时间才能得到UTC时间。

​ std后面的offset是必须的,dst后面的offset是可选的,如果没有,则默认比标准时间提前一小时。

  1. rule 表示什么时候开始夏令时,什么时候结束夏令时,协议没有提到怎么处理没有rule的情况,从linux系统来看,没有rule默认按M3.2.0/02:00:00,M11.1.0/02:00:00处理。

​ 格式为date[/time],date[/time]。date有三种形式:
​ a. Jn,1 <= n <= 365,不包括闰年的2月29日;
​ b. n,0 <= n <= 365,包括闰年的2月29日;
​ c. Mm.n.d,m表示哪个月份(1 <= m <= 12),n表示一个月的第几周(1 <= n <= 5),5代表最后一个,d表示一周的第几天(0 <= d <= 6,从周日开始)。

  1. time 格式与offset相同,除了没有+和-,如果没带time字段,则默认为02:00:00。

​ 举例:"UTC-8:00:00DST-09:00:00,M3.2.0/02:00:00,M11.1.0/02:00:00"
​ 表示本地时间为UTC以东减8小时,支持夏令时且夏令时为UTC减9小时(及提前一小时),夏令时从3月第2周第0天凌晨2点开始,到11月第1周第0天凌晨2点结束。
​ CST6CDT,M3.2.0/2:00:00,M11.1.0/2:00:00

​ POSIX时区格式由于无法表示不使用公历的时间(比如希腊),因此并未被广泛采用。

2.2.3 通用时区数据库

各地的时区和夏令时规则由各自的政府独立管理,他们经常在有限的通知下进行变更。而且他们历史数据和未来计划也只是断断续续的记录下来。通用时区数据库试图组织和整理这一领域相关的数据。

时区数据库,通常称为 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同其它广泛使用的软件产品一样,也是采用了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维护的通用时区数据库由于每条记录都标识了明确的信息来源,有历史信息也能推测未来时间,被GNU C Library,BSD的采用而变的广泛使用。其实还存在其它的时区数据库,例如:微软时区数据库,HP-UX时区数据库,世界时间服务器(https://www.worldtimeserver.com/)和国际航空运输协会的《标准时间表信息手册》等。

3. 实战篇

3.1 GaussDB(DWS)时区的使用

系统表 pg_timezone_names 记录时区名称。

testdb=# select * from pg_timezone_names where utc_offset='08:00:00';
        name        | abbrev | utc_offset | is_dst
--------------------+--------+------------+--------
 Asia/Kuala_Lumpur  | +08    | 08:00:00   | f
 Asia/Brunei        | +08    | 08:00:00   | f
 Asia/Makassar      | WITA   | 08:00:00   | f
 Asia/Harbin        | CST    | 08:00:00   | f
 Asia/Ujung_Pandang | WITA   | 08:00:00   | f
 Asia/Chungking     | CST    | 08:00:00   | f
 Asia/Kuching       | +08    | 08:00:00   | f
 Asia/Taipei        | CST    | 08:00:00   | f
 Asia/Macau         | CST    | 08:00:00   | f
 Asia/Macao         | CST    | 08:00:00   | f
 Asia/Manila        | PST    | 08:00:00   | f
 Asia/Chongqing     | CST    | 08:00:00   | f
 Asia/Shanghai      | CST    | 08:00:00   | f
 Asia/Choibalsan    | +08    | 08:00:00   | f
 Asia/Ulaanbaatar   | +08    | 08:00:00   | f
 Asia/Irkutsk       | +08    | 08:00:00   | f
 Asia/Ulan_Bator    | +08    | 08:00:00   | f
 Asia/Hong_Kong     | HKT    | 08:00:00   | f
 Asia/Singapore     | +08    | 08:00:00   | f
 Australia/West     | AWST   | 08:00:00   | f
 Australia/Perth    | AWST   | 08:00:00   | f
 ROC                | CST    | 08:00:00   | f
 Etc/GMT-8          | +08    | 08:00:00   | f
 PRC                | CST    | 08:00:00   | f
 Singapore          | +08    | 08:00:00   | f
 Antarctica/Casey   | +08    | 08:00:00   | f
 Hongkong           | HKT    | 08:00:00   | f
(27 rows)

系统表 pg_timezone_abbrevs 记录了时区简称

testdb=# select * from pg_timezone_abbrevs where utc_offset='08:00:00';
 abbrev | utc_offset | is_dst
--------+------------+--------
 AWST   | 08:00:00   | f
 BNT    | 08:00:00   | f
 BORT   | 08:00:00   | f
 CCT    | 08:00:00   | f
 HKT    | 08:00:00   | f
 IRKST  | 08:00:00   | f
 IRKT   | 08:00:00   | f
 MYT    | 08:00:00   | f
 PHT    | 08:00:00   | f
 SGT    | 08:00:00   | f
 ULAT   | 08:00:00   | f
 WADT   | 08:00:00   | t
(12 rows)

通过设置session的环境变量 timezone 可以设置数据库数据入库和查询时的时区。为什么是session级?不用用户设置不同的时区,或者用户忘记之前设置的时区怎么办?

答:时间数据入库后数据库管理软件后台都会将时间转换为UTC的0时区时间,用户查询时根据用户设置的时区,再转换为相应时间给用户。因此用户设置的时区不影响数据存储,数据迁移时也不需要关心时区。

3.2 GaussDB(DWS)初始化时如何设定默认时区

​ 如果配置文件中未指定时区,则从操作系统当前环境变量 TZ 获取当前操作系统时区,且该时区是数据库内支持的时区,则直接使用操作系统时区。否则,根据操作系统当前时间构造一些日期来评判哪个时区更和操作系统匹配。
评判方法:

1)从今向后100年内采集一些时间戳

首先,探测当前年的一月和七月,这可以快速排除掉大量的不符合时区。
其次,探测当前年七月向后100年间的每周,可以有效排除掉夏令时不符合的时区。

tnow = time(NULL); //获取1970-01-01 00:00:00 UTC至今所经历过的秒数
tm_val = localtime(&tnow); //将从time()获取的时间戳,转为本地时间的tm结构格式
thisyear = tm_val->tm_year + 1900; //tm_year是自1900年起的年数,所以要加1900
time_needto_test = 0;

//先构造一个当年的1月15日,再调整到当前周的第一天 00:00:00
t = MakeTime(thisyear, 1, 15);
t -= (t % T_WEEK);
test_times[time_needto_test++] = t;

//构造一个当年的7月15日,再调整到当前周的第一天 00:00:00
t = MakeTime(thisyear, 7, 15);
t -= (t % T_WEEK);
test_times[time_needto_test++] = t;

//构造向后100年内每周的时间
while (tt.n_test_times < 52 * 100) {
	t -= T_WEEK;
	test_times[time_needto_test++] = t;
}

//总共构造了5202个探测时间

2)判断每个数据库内部时区与操作系统时区的匹配度

如果一个时间戳转为操作系统时区与转为待测试的数据库内部时区后的时间一致,则加一分。遇到一个不匹配则结束当前时区匹配。

首先,判断 "/etc/localtime" 中时区与前面测试时间的匹配度,如完全匹配则返回成功。

否则,判断数据库安装目录timezone下的每个时区文件与前面测试时间的匹配度,获取匹配度最高的时区。

bestscore = 0;  
for (获取每一个时区文件) {

    判断当前时区文件与探测时间的匹配度
    score = score_timezone(tzdirsub, tt);

    if (score > bestscore) { //当前时区文件匹配度 > 目前最大匹配度
        bestscore = score;
        bestzonename = tzdirsub;
    } else if (score == bestscore) { //如果当前时区文件匹配度与目前最大匹配度相等,则选择长度更短,字母排序更靠前的时区
      if ((strlen(tzdirsub) < strlen(bestzonename) ||
          (strlen(tzdirsub) == strlen(bestzonename) && strcmp(tzdirsub, bestzonename) < 0))
          strlcpy(bestzonename, tzdirsub, TZ_STRLEN_MAX + 1);
    }
}

3)如果所有数据库内部时区匹配完,最佳得分仍为0,则需要构造一个与操作系统时区一致的POSIX风格的时区,例如:“PSD8PDT”

4.冷知识篇

4.1 我国有哪些时区,都是怎么设置的?

公元1912年之前,中国各地并没有统一的标准时间。在封建时代,国家的标准历法由皇庭颁布,而中国传统历法同时依赖于日月两个天体的运动,并以实际天文观测为准,因此历时标准都以朝廷所在地(准确说,是钦天监的观测点)的经纬为准。

公元1912年,中华民国建立,首次设立并正式颁布时区划分将中国分为五个时区,并在1939年正式颁布。

  1. 昆仑时区(GMT+5:30)
  2. 新藏时区(GMT+6)
  3. 陇蜀时区(GMT+7)
  4. 中原标准时区(GMT+8)
  5. 长白时区(GMT+8:30)

详细介绍如下:

Asia/Harbin 也叫长白时区,主要代表黑龙江(不包括漠河),吉林地区的时间

# Zone  NAME            GMTOFF  RULES   FORMAT  [UNTIL]
# Changbai Time ("Long-white Time", Long-white = Heilongjiang area)
# Heilongjiang (except Mohe county), Jilin
Zone    Asia/Harbin     8:26:44 -       LMT     1928 # or Haerbin
                        8:30    -       CHAT    1932 Mar # Changbai Time
                        8:00    -       CST     1940
                        9:00    -       CHAT    1966 May
                        8:30    -       CHAT    1980 May
                        8:00    PRC     C%sT

Asia/Shanghai 也叫中原标准时区

# Zhongyuan Time ("Central plain Time")
# most of China
# Milne gives 8:05:56.7; round to nearest.
Zone    Asia/Shanghai   8:05:57 -       LMT     1928
                        8:00    Shang   C%sT    1949
                        8:00    PRC     C%sT

Asia/Chongqing 也叫胧蜀时区,主要代表广西,贵州,海南,宁夏,四川,陕西,云南,甘肃主要部分,内蒙古西部,青海西部,广东的德庆,恩平,开平,罗定,台山,新兴,阳春,阳江,郁南,云浮,为东七区。

# Long-shu Time (probably due to Long and Shu being two names of that area)
# Guangxi, Guizhou, Hainan, Ningxia, Sichuan, Shaanxi, and Yunnan;
# most of Gansu; west Inner Mongolia; west Qinghai; and the Guangdong
# counties Deqing, Enping, Kaiping, Luoding, Taishan, Xinxing,
# Yangchun, Yangjiang, Yu'nan, and Yunfu.
Zone    Asia/Chongqing  7:06:20 -       LMT     1928 # or Chungking
                        7:00    -       LONT    1980 May # Long-shu Time
                        8:00    PRC     C%sT

Asia/Urumqi 也叫新藏时区,主要代表甘肃的阿克赛哈族自治县,安西县,敦煌,肃北县,连江县,湛江市等等等。

# Xin-zang Time ("Xinjiang-Tibet Time")
# The Gansu counties Aksay, Anxi, Dunhuang, Subei; west Qinghai;
# the Guangdong counties  Xuwen, Haikang, Suixi, Lianjiang,
# Zhanjiang, Wuchuan, Huazhou, Gaozhou, Maoming, Dianbai, and Xinyi;
# east Tibet, including Lhasa, Chamdo, Shigaise, Jimsar, Shawan and Hutubi;
# east Xinjiang, including Urumqi, Turpan, Karamay, Korla, Minfeng, Jinghe,
# Wusu, Qiemo, Xinyan, Wulanwusu, Jinghe, Yumin, Tacheng, Tuoli, Emin,
# Shihezi, Changji, Yanqi, Heshuo, Tuokexun, Tulufan, Shanshan, Hami,
# Fukang, Kuitun, Kumukuli, Miquan, Qitai, and Turfan.
Zone    Asia/Urumqi     5:50:20 -       LMT     1928 # or Urumchi
                        6:00    -       URUT    1980 May # Urumqi Time
                        8:00    PRC     C%sT

Asia/Kashgar 也叫昆仑时区,主要代表西藏西部(普兰县,阿合奇县,疏附县,疏勒县),新疆西部(阿克苏,阿图什,伊宁,和田,策勒,洛浦,尼勒克,昭苏,特克斯,巩留,察布查尔,霍城,博乐,皮山,等等等)

Zone    Asia/Kashgar    5:03:56 -       LMT     1928 # or Kashi or Kaxgar
                        5:30    -       KAST    1940     # Kashgar Time
                        5:00    -       KAST    1980 May
                        8:00    PRC     C%sT

PRC指向中原标准时区

Link    Asia/Shanghai           PRC

1949年中华人民共和国成立,我国废除了原国民政府1939年设立的中原标准时区、昆仑时区、长白时区、陇蜀时区以及新藏时区,在全国统一使用(UTC+8),并命名为北京时间。
1950年后,全国除西藏、新疆外全部统一使用北京时间。当时新疆的地方人民代表大会为了方便群众,设定了乌鲁木齐时间:(UTC+6),如北京时间8点,乌鲁木齐时间则是6点。新的时区调整如下:

1)Asia/Harbin(长白时区),Asia/Chongqing(陇蜀时区),Asia/Shanghai(中原标准时区)时区名保留,定义都指向新定义的Asia/Shanghai(中国标准时区)。全国统一使用。

中原标准时区改名为北京时区,offset有调整

# Zone  NAME            STDOFF  RULES   FORMAT  [UNTIL]
# Beijing time, used throughout China; represented by Shanghai.
Zone    Asia/Shanghai   8:05:43 -       LMT     1901
                        8:00    Shang   C%sT    1949 May 28
                        8:00    PRC     C%sT

2)Asia/Kashgar(昆仑时区),Asia/Urumqi(新藏时区)时区名保留,定义都指向新定义的Asia/Urumqi(乌鲁木齐时区)。当地同时使用两个时区定义。

新藏时区改名为新疆时区offset有调整

# Xinjiang time, used by many in western China; represented by Ürümqi / Ürümchi
# / Wulumuqi.  (Please use Asia/Shanghai if you prefer Beijing time.)
Zone    Asia/Urumqi     5:50:20 -       LMT     1928
                        6:00    -       +06

被合并的时区

L Asia/Urumqi Asia/Kashgar 昆仑时区合入新藏时区
L Asia/Shanghai Asia/Harbin 长白时区合入中原标准时区
L Asia/Shanghai Asia/Chongqing 胧蜀时区合入中原标准时区
L Asia/Shanghai Asia/Chungking 胧蜀时区新增别名Chungking,也合入中原标准时区
L Asia/Shanghai PRC

4.1.2 为什么没有北京时区只有北京时间?

由于各种政治原因国家所在地区,国家名称和地区所属国家会时常发生变更,这给时区管理带来很多麻烦。例如:位于非洲东南部的内陆国家“斯威士兰王国”在2018年4月宣布将国名更改为“埃斯瓦蒂尼”;我们绝对熟悉的1997年7月1日,香港从英属殖民地回归到了我们伟大祖国的怀抱,所属国就进行了变更。因此,IANA的策略是对政治变更保持中立性,时区通常不与国家绑定,也并不要求每个国家或国家首都必须具有时区名称。

为了国内用户的使用方便,GaussDB(DWS)根据IANA的定义的语法规则,在内部帮助用户定义了 Asia/Beijing 时区,其定义与 PRC 时区定义一致。

4.1.3 时区定义是一成不变的吗?

时区如何定义如何管理是颇具争议的事情,并不存在绝对权威的时区数据库。一个国家或地区的时区信息变更属于独立事务,没有义务必须通知IANA。IANA时区数据库的权威性主要依赖时区数据库维护者对相关国家或地区的了解程度。

因此,在IANA的时区定义里记录了时区数据库维护者收集时区信息的来源,有时候并不是很客观。下面是IANA中关于我国时区信息的参照依据。

From Guy Harris:
From Bob Devine (1988-01-28): See TIME mag, 1986-02-17 p.52.
From U. S. Naval Observatory (1989-01-19): 
From Anthony Fok (2001-12-20): from some Taiwan websites
From Paul Eggert (2006-03-22): Devine's note about Time magazine
From Jesper Norgaard Welen (2006-07-14): http://www.astro.com/atlas
From Paul Eggert (2008-02-11): I just now checked Google News for western news sources

From David Cochrane (2014-03-26): https://content.time.com/time/magazine/article/0,9171,960684,00.html
From Luther Ma (2014-04-22): Guo's report regarding Xinjiang
From Paul Eggert (2014-06-30): http://www.sinkiang.gov.cn/service/ourworking (2014-04-22).
From Paul Eggert (2017-01-05):
  Guo Qing-sheng (National Time-Service Center, CAS, Xi'an 710600, China)
  (Zhongguo ke ji shi liao, 中国科技史料). 2003;24(1):5-9.
  http://oversea.cnki.net/kcms/detail/detail.aspx?filename=ZGKS200301000&dbname=CJFD2003
  Guo Qing-sheng (Shaanxi Astronomical Observatory, CAS, Xi'an 710600, China)
  An AP article "Shanghai Internat'l Area Little Changed"
From P Chan (2018-05-07):
  1986-04-12 http://www.zj.gov.cn/attach/zfgb/198608.pdf p.21-22
  1987-02-15 http://www.gov.cn/gongbao/shuju/1987/gwyb198703.pdf p.114
  1987-09-09 http://www.gov.cn/gongbao/shuju/1987/gwyb198721.pdf p.709
  1992-03-03 http://www.gov.cn/gongbao/shuju/1992/gwyb199205.pdf p.152
  http://data.people.com.cn/pic/101p/1988/04/1988041201.jpg

可见,2014年前IANA收集中国时区信息的来源还多是在西方的新闻,杂志,台湾的网站等等。2014年之后收集的信息来自中国的西安授时中心,国家门户网址等。

此外,因为很多国家的时区信息经常变更,IANA通用时区数据库中的时区数据会经常更新。GaussDB(DWS)作为一个面向全球的通用数据库产品,也会在每次发表新版本时从IANA同步最新的时区信息,确保用户的软件产品保持最新的时区信息。

4.1.4 为什么我们很少需要关注时区?

自从1992年取消夏令时以来,我们的时区信息从未变过,给我们的生产生活带来了极大的便利。国内用户也不再需要关心时区相关的问题。

5. 通用时区数据库介绍

各地的时区和夏令时规则由各自的政府独立管理,他们经常在有限的通知下进行变更。而且他们历史数据和未来计划也只是断断续续的记录下来。通用时区数据库试图组织和整理这一领域相关的数据。

时区数据库,通常称为 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提供三种方法对时区数据库的访问:

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

时区数据库中包含各大洲原始的时区定义的文本文件和解析这些文本文件的代码文件。

5.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中

5.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 了解时区文件编译器(将时区定义文本文件编译成二进制时区数据文件)的使用

6. 通用时区数据库使用

每个时区名的具体定义都存在于时区文件中,比如:/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年开始取消了夏令时,所以就不再变化。

7. 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=2880

8. 总结

综上,GaussDB(DWS)作为一款面向全球用户的高性能分析型数据库产品,对时区的支持也是符合工业界标准规范的。GaussDB(DWS)中时区的使用简单方便,默认时区的选择也比较科学合理。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

举报
请填写举报理由
0/200