<时间-1>计算机世界中的时间概念
程序员文章站
2022-07-12 18:42:51
...
在计算机中表示时间是一个非常复杂的工程,若平时只是进行简单的时间推算,比如加减一个日期,Date和String之间格式化那几乎没有什么压力,也体会不到“时间”的复杂。复杂主要是牵涉到国际化,夏令时等因素。在使用编程语言(不仅仅java)进行时间相关的处理前,先了解一些背景大有必要。
1.UTC和GMT
整个地球分为二十四时区,每个时区都有自己的本地时间。在国际无线电通信场合,为了统一起见,使用一个统一的时间,称为通用协调时(UTC, Universal Time Coordinated)。
UTC是协调世界时(Universal Time Coordinated)英文缩写,是由国际无线电咨询委员会规定和推荐,并由国际时间局(BIH)负责保持的以秒为基础的时间标度。UTC相当于本初子午线(即经度0度)上的平均太阳时,过去曾用格林威治平均时(GMT)来表示(在不考虑闰秒的情况下,可以认为UTC和GMT一样),都与英国伦敦的本地时相同。.北京时间比UTC时间早8小时,以1999年1月1日0000UTC为例,UTC时间是零点,北京时间为1999年1月1日早上8点整。
GMT(Greenwich Mean Time)是格林尼治平均时间:由于地球轨道并非圆形,其运行速度又随着地球与太阳的距离改变而出现变化,因此视太阳时欠缺均匀性。视太阳日的长度同时亦受到地球自转轴相对轨道面的倾斜度所影响。为着要纠正上述的不均匀性,天文学家计算地球非圆形轨迹与极轴倾斜对视太阳时的效应。平太阳时就是指经修订后的视太阳时。在格林尼治子午线上的平太阳时称为世界时(UT0),又叫格林尼治平时(GMT)。
若以「世界标准时间」的角度来说,UTC比GMT来得更加精准。两者误差值必须保持在0.9秒以内,若大于0.9秒则由位于巴黎的国际地球自转事务*局发布闰秒,使UTC与地球自转周期一致。所以基本上UTC的本质强调的是比GMT更为精确的世界时间标准。
但是,在不需要十分精准的情况下,我们一般认为UTC和GMT时间是等价的。
一句话总结: UTC = GMT +/- 0.9
举例:
北京时区是东八区,领先UTC八个小时,在电子邮件信头的Date域记为+0800。如果在电子邮件的信头中有这么一行:
Date: Fri, 08 Nov 2002 09:42:22 +0800
说明信件的发送地的地方时间是二○○二年十一月八号,星期五,早上九点四十二分(二十二秒),这个地方的本地时领先UTC八个小时(+0800, 就是东八区时间)。电子邮件信头的Date域使用二十四小时的时钟,而不使用AM和PM来标记上下午。
以这个电子邮件的发送时间为例,如果要把这个时间转化为UTC,可以使用一下公式:
UTC + 时区 = 本地时间
时区东为正,西为负。在此,把东八区时区差记为 +0800,
UTC + (+0800) = 本地(北京)时间 (1)
那么,UTC = 本地时间(北京时间))- 0800 (2)
0942 - 0800 = 0142
即UTC是当天凌晨一点四十二分二十二秒。如果结果是负数就意味着是UTC前一天,把这个负数加上2400就是UTC在前一天的时间。例如,本地 (北京)时间是 0432 (凌晨四点三十二分),那么,UTC就是 0432 - 0800 = -0368,负号意味着是前一天, -0368 + 2400 = 2032,既前一天的晚上八点三十二分。
纽约的时区是西五区,比UTC落后五个小时,记为 -0500:
UTC + (-0500) = 纽约时间 (3)
UTC = 纽约时间 + 0500 (4)
把(2)式 - (4)式相比较,
UTC = 北京时间 - 0800 = 纽约时间 + 0500 (5)
即 北京时间 = 纽约时间 + 1300 (6)
即北京时间领先纽约时间十三个小时,由(6)式,
纽约时间 = 北京时间 - 1300 (7)
2.夏令时DST
所谓「夏日节约时间」Daylight Saving Time(简称D.S.T.),是指在夏天太阳升起的比较早时,将时钟拨快一小时,以提早日光的使用,在英国则称为夏令时间(Summer Time)。这个构想于1784年由美国班杰明•富兰克林提出来,1915年德国成为第一个正式实施夏令日光节约时间的国家,以削减灯光照明和耗电开支。 自此以后,全球以欧洲和北美为主的约70个国家都引用这个做法。目前被划分成两个时区的印度也正在商讨是否全国该统一实行夏令日光节约时间。欧洲手机上也 有很多GSM系统的基地台,除了会传送当地时间外也包括夏令日光节约时间,做为手机的时间标准,使用者可以自行决定要开启或关闭。这完全是一个人为的法令时间,可以人为的决定使用还是不使用。进夏令时时间要拨快一小时,出夏令时时间再拨回来。但这跟UTC或GMT完全没有关系。完全是人为行为。
如果考虑到夏令时,进夏令时时本地时间要比非夏令时快一小时,那上面的公式要变一下:
UTC + (时区+1) = DST
比如,在四月下旬,纽约换用夏令时,西五区(-0500)实际成为西四区的标准时间,成为 -0400
UTC +(-0400) = 纽约夏令时
3.CST, EST, PST等本地时间
本地时间(localtime)都是要考虑时区(timezone)的。
CST同时可以代表如下 4 个不同的时区:
Central Standard Time (USA) UT-6:00
Central Standard Time (Australia) UT+9:30
China Standard Time UT+8:00
Cuba Standard Time UT-4:00
在中国北京标准时间(忽略GMT和UTC的差异):GMT + 8 = UTC + 8 = CST,也就是说在中国地区,cst是转换后的本地时间。
比如在java中我们System.out.println("date:" + new Date());会输出:
date:Wed Jan 22 20:04:31 CST 2014
这里的cst就代表这是中国(jvm所取的时区被设置为+8区)现在的本地时间。是经过utc转换之后的。但如果计算机的时区改成美国 +9:30,则同样输出的是CST时间,但这个时间跟中国东八区不一样。也就是说,cst代表的是4个不同地区的本地时间,但具体是哪一个,要看是哪个时区。
此时修改计算机时区为(UTC+10:00)布里斯班,再次运行则输出:
date:Wed Jan 22 22:04:44 EST 2014
注意到cst变为est了,而且时间也相差了两个小时。
Est又是什么?
美国横跨西五区至西十区,共六个时区。每个时区对应一个标准时间,从东向西分别为东部时间(EST)(西五区时间)、中部时间([b]CST[/b])(西六区时间)、 山地时间(MST)(西七区时间)、太平洋时间(西部时间)(PST)(西八区时间)、阿拉斯加时间(AKST)(西九区时间)和夏威夷时间(HST) (西十区时间),按照“东早西晚”的规律,各递减一小时。
美国的时区界限并不完全按照经线划分,基本上照顾了各州的自然边界。不同的时区覆盖的州市大小、多少不同。东部时间(EST)包括大西洋沿岸及近大陆 的19个州和华盛顿特区,代表城市华盛顿、纽约。中部时间(CST)代表城市芝加哥、新奥尔良。山地时间(MST)代表城市盐湖城、丹佛,太平洋时间 (PST)包括太平洋沿岸的4个州,代表城市旧金山、洛杉矶、西雅图,阿拉斯加时间(AKST)只限于阿拉斯加。夏威夷时间(HST)只限于夏威夷。
CET表示Central European Time。中欧时间。
也就是说,这些都是本地时间。
Utc和gmt是和时区无关的时间,而本地时间(Dst,cst等)是与时区(或夏令时)紧密相关的。所以如果我们做的程序是国际化的,那么最好保存与时区无关的时间。
4.计算机时间系统
到这里就说到了计算机时间,计算机实际是保存的是自UTC时间 1970年1月1日经过的秒数。
首先注意的是,不同的时间系统关于秒长的定义是不一样的。
定义秒长:
•原子时 IAT(international atomic time)(即国际原子时):原子震荡,以精确的秒的定义为基础的时间
•恒星时 ST(sidereal time):以恒星为基础的相对于原子时不十分精准的时间
•太阳时 MT(mean solar time):地球自转,以太阳为基础的相对于原子时不十分精准的时间
时间系统:
•格林尼治标准时GMT 、世界时 UT(universal time):以本初子午线的平子夜起算的平太阳时
•历书时 ET(Ephemeris time):地球公转
•协调世界时 UTC:协调世界时是以原子时秒长为基础,在时刻上尽量接近于世界时的一种时间计量系统。
历史上使用的时间计量:
最开始,以地球自转为基准的时间计量系统的世界时被全世界统一使用。我们用的北京时,是东经120o的平太阳时。由于地球自转速度不均匀,导致用其测得的时间不均匀。
1960年起,人们开始以地球公转运动为基准来量度时间,用历书时系统代替世界时。历书时的秒长规定为1900年1月0日12时整回归年 长度的1/31 556 925.974 7,起始历元定在1900年1月0日12时。随着科技的发展,历书时的精度已不能满足需要, 1967年后,历书时被原子时所取代。原子时的时间单位在目 前来说是最精确的,但原子时不能确定时刻。为了得到既有准确时刻,又有精确秒长的时系,国际上规定了协调世界时。协调世界时的秒长与原子时秒长一致,在时 刻上尽量与世界时接近(其差值在全0.9秒以内)。
为什么是1970年1月1日?
最初计算机操作系统是32位,而时间也是用32位表示。
(Integer.MAX_VALUE);2147483647
Integer用32位表示,因此32位能表示的最大值是2147483647。另外1年365天的总秒数是31536000,2147483647/31536000 = 68.1。也就是说32位能表示的最长时间是68年,而实际上到2038年01月19日03时14分07秒,便会到达最大时间,过了这个时间点,所有32位操作系统时间便会变为
10000000 00000000 00000000 00000000
也就是1901年12月13日20时45分52秒,这样便会出现时间回归的现象,很多软件便会运行异常了。
最早出现的UNIX操作系统考虑到计算机产生的年代和应用的时限综合取了1970年1月1日作为UNIX TIME的纪元时间(开始时间),而java自然也遵循了这一约束。
至于时间回归的现象相信随着64为操作系统的产生逐渐得到解决,因为用64位操作
系统可以表示到292,277,026,596年12月4日15时30分08秒。
关于时间相关的背景就先介绍到这,后面再讨论java世界里时间相关的操作就方便多了。
1.UTC和GMT
整个地球分为二十四时区,每个时区都有自己的本地时间。在国际无线电通信场合,为了统一起见,使用一个统一的时间,称为通用协调时(UTC, Universal Time Coordinated)。
UTC是协调世界时(Universal Time Coordinated)英文缩写,是由国际无线电咨询委员会规定和推荐,并由国际时间局(BIH)负责保持的以秒为基础的时间标度。UTC相当于本初子午线(即经度0度)上的平均太阳时,过去曾用格林威治平均时(GMT)来表示(在不考虑闰秒的情况下,可以认为UTC和GMT一样),都与英国伦敦的本地时相同。.北京时间比UTC时间早8小时,以1999年1月1日0000UTC为例,UTC时间是零点,北京时间为1999年1月1日早上8点整。
GMT(Greenwich Mean Time)是格林尼治平均时间:由于地球轨道并非圆形,其运行速度又随着地球与太阳的距离改变而出现变化,因此视太阳时欠缺均匀性。视太阳日的长度同时亦受到地球自转轴相对轨道面的倾斜度所影响。为着要纠正上述的不均匀性,天文学家计算地球非圆形轨迹与极轴倾斜对视太阳时的效应。平太阳时就是指经修订后的视太阳时。在格林尼治子午线上的平太阳时称为世界时(UT0),又叫格林尼治平时(GMT)。
若以「世界标准时间」的角度来说,UTC比GMT来得更加精准。两者误差值必须保持在0.9秒以内,若大于0.9秒则由位于巴黎的国际地球自转事务*局发布闰秒,使UTC与地球自转周期一致。所以基本上UTC的本质强调的是比GMT更为精确的世界时间标准。
但是,在不需要十分精准的情况下,我们一般认为UTC和GMT时间是等价的。
一句话总结: UTC = GMT +/- 0.9
举例:
北京时区是东八区,领先UTC八个小时,在电子邮件信头的Date域记为+0800。如果在电子邮件的信头中有这么一行:
Date: Fri, 08 Nov 2002 09:42:22 +0800
说明信件的发送地的地方时间是二○○二年十一月八号,星期五,早上九点四十二分(二十二秒),这个地方的本地时领先UTC八个小时(+0800, 就是东八区时间)。电子邮件信头的Date域使用二十四小时的时钟,而不使用AM和PM来标记上下午。
以这个电子邮件的发送时间为例,如果要把这个时间转化为UTC,可以使用一下公式:
UTC + 时区 = 本地时间
时区东为正,西为负。在此,把东八区时区差记为 +0800,
UTC + (+0800) = 本地(北京)时间 (1)
那么,UTC = 本地时间(北京时间))- 0800 (2)
0942 - 0800 = 0142
即UTC是当天凌晨一点四十二分二十二秒。如果结果是负数就意味着是UTC前一天,把这个负数加上2400就是UTC在前一天的时间。例如,本地 (北京)时间是 0432 (凌晨四点三十二分),那么,UTC就是 0432 - 0800 = -0368,负号意味着是前一天, -0368 + 2400 = 2032,既前一天的晚上八点三十二分。
纽约的时区是西五区,比UTC落后五个小时,记为 -0500:
UTC + (-0500) = 纽约时间 (3)
UTC = 纽约时间 + 0500 (4)
把(2)式 - (4)式相比较,
UTC = 北京时间 - 0800 = 纽约时间 + 0500 (5)
即 北京时间 = 纽约时间 + 1300 (6)
即北京时间领先纽约时间十三个小时,由(6)式,
纽约时间 = 北京时间 - 1300 (7)
2.夏令时DST
所谓「夏日节约时间」Daylight Saving Time(简称D.S.T.),是指在夏天太阳升起的比较早时,将时钟拨快一小时,以提早日光的使用,在英国则称为夏令时间(Summer Time)。这个构想于1784年由美国班杰明•富兰克林提出来,1915年德国成为第一个正式实施夏令日光节约时间的国家,以削减灯光照明和耗电开支。 自此以后,全球以欧洲和北美为主的约70个国家都引用这个做法。目前被划分成两个时区的印度也正在商讨是否全国该统一实行夏令日光节约时间。欧洲手机上也 有很多GSM系统的基地台,除了会传送当地时间外也包括夏令日光节约时间,做为手机的时间标准,使用者可以自行决定要开启或关闭。这完全是一个人为的法令时间,可以人为的决定使用还是不使用。进夏令时时间要拨快一小时,出夏令时时间再拨回来。但这跟UTC或GMT完全没有关系。完全是人为行为。
如果考虑到夏令时,进夏令时时本地时间要比非夏令时快一小时,那上面的公式要变一下:
UTC + (时区+1) = DST
比如,在四月下旬,纽约换用夏令时,西五区(-0500)实际成为西四区的标准时间,成为 -0400
UTC +(-0400) = 纽约夏令时
3.CST, EST, PST等本地时间
本地时间(localtime)都是要考虑时区(timezone)的。
CST同时可以代表如下 4 个不同的时区:
Central Standard Time (USA) UT-6:00
Central Standard Time (Australia) UT+9:30
China Standard Time UT+8:00
Cuba Standard Time UT-4:00
在中国北京标准时间(忽略GMT和UTC的差异):GMT + 8 = UTC + 8 = CST,也就是说在中国地区,cst是转换后的本地时间。
比如在java中我们System.out.println("date:" + new Date());会输出:
date:Wed Jan 22 20:04:31 CST 2014
这里的cst就代表这是中国(jvm所取的时区被设置为+8区)现在的本地时间。是经过utc转换之后的。但如果计算机的时区改成美国 +9:30,则同样输出的是CST时间,但这个时间跟中国东八区不一样。也就是说,cst代表的是4个不同地区的本地时间,但具体是哪一个,要看是哪个时区。
此时修改计算机时区为(UTC+10:00)布里斯班,再次运行则输出:
date:Wed Jan 22 22:04:44 EST 2014
注意到cst变为est了,而且时间也相差了两个小时。
Est又是什么?
美国横跨西五区至西十区,共六个时区。每个时区对应一个标准时间,从东向西分别为东部时间(EST)(西五区时间)、中部时间([b]CST[/b])(西六区时间)、 山地时间(MST)(西七区时间)、太平洋时间(西部时间)(PST)(西八区时间)、阿拉斯加时间(AKST)(西九区时间)和夏威夷时间(HST) (西十区时间),按照“东早西晚”的规律,各递减一小时。
美国的时区界限并不完全按照经线划分,基本上照顾了各州的自然边界。不同的时区覆盖的州市大小、多少不同。东部时间(EST)包括大西洋沿岸及近大陆 的19个州和华盛顿特区,代表城市华盛顿、纽约。中部时间(CST)代表城市芝加哥、新奥尔良。山地时间(MST)代表城市盐湖城、丹佛,太平洋时间 (PST)包括太平洋沿岸的4个州,代表城市旧金山、洛杉矶、西雅图,阿拉斯加时间(AKST)只限于阿拉斯加。夏威夷时间(HST)只限于夏威夷。
CET表示Central European Time。中欧时间。
也就是说,这些都是本地时间。
Utc和gmt是和时区无关的时间,而本地时间(Dst,cst等)是与时区(或夏令时)紧密相关的。所以如果我们做的程序是国际化的,那么最好保存与时区无关的时间。
4.计算机时间系统
到这里就说到了计算机时间,计算机实际是保存的是自UTC时间 1970年1月1日经过的秒数。
首先注意的是,不同的时间系统关于秒长的定义是不一样的。
定义秒长:
•原子时 IAT(international atomic time)(即国际原子时):原子震荡,以精确的秒的定义为基础的时间
•恒星时 ST(sidereal time):以恒星为基础的相对于原子时不十分精准的时间
•太阳时 MT(mean solar time):地球自转,以太阳为基础的相对于原子时不十分精准的时间
时间系统:
•格林尼治标准时GMT 、世界时 UT(universal time):以本初子午线的平子夜起算的平太阳时
•历书时 ET(Ephemeris time):地球公转
•协调世界时 UTC:协调世界时是以原子时秒长为基础,在时刻上尽量接近于世界时的一种时间计量系统。
历史上使用的时间计量:
最开始,以地球自转为基准的时间计量系统的世界时被全世界统一使用。我们用的北京时,是东经120o的平太阳时。由于地球自转速度不均匀,导致用其测得的时间不均匀。
1960年起,人们开始以地球公转运动为基准来量度时间,用历书时系统代替世界时。历书时的秒长规定为1900年1月0日12时整回归年 长度的1/31 556 925.974 7,起始历元定在1900年1月0日12时。随着科技的发展,历书时的精度已不能满足需要, 1967年后,历书时被原子时所取代。原子时的时间单位在目 前来说是最精确的,但原子时不能确定时刻。为了得到既有准确时刻,又有精确秒长的时系,国际上规定了协调世界时。协调世界时的秒长与原子时秒长一致,在时 刻上尽量与世界时接近(其差值在全0.9秒以内)。
为什么是1970年1月1日?
最初计算机操作系统是32位,而时间也是用32位表示。
(Integer.MAX_VALUE);2147483647
Integer用32位表示,因此32位能表示的最大值是2147483647。另外1年365天的总秒数是31536000,2147483647/31536000 = 68.1。也就是说32位能表示的最长时间是68年,而实际上到2038年01月19日03时14分07秒,便会到达最大时间,过了这个时间点,所有32位操作系统时间便会变为
10000000 00000000 00000000 00000000
也就是1901年12月13日20时45分52秒,这样便会出现时间回归的现象,很多软件便会运行异常了。
最早出现的UNIX操作系统考虑到计算机产生的年代和应用的时限综合取了1970年1月1日作为UNIX TIME的纪元时间(开始时间),而java自然也遵循了这一约束。
至于时间回归的现象相信随着64为操作系统的产生逐渐得到解决,因为用64位操作
系统可以表示到292,277,026,596年12月4日15时30分08秒。
关于时间相关的背景就先介绍到这,后面再讨论java世界里时间相关的操作就方便多了。
下一篇: jvm 烂笔头