时间(一)之基础概念
时间是一种尺度,在物理定义是标量,借着时间,事件发生之先后可以按 过去-现在-未来 之序列得以确定(时间点),也可以衡量事件持续的期间以及事件之间和间隔长短(时间段) —— 维基百科
单位
时间的基本国际单位是秒。定义一秒为 铯-133原子 基态两个超精细能级间跃迁辐射振荡9,192,631,770周所持续的时间,其起点为世界时1958年的开始
时区
时区(Time Zone)是地球上的区域使用同一个时间定义。1884年在华盛顿召开国际经度会议时,为了克服时间上的混乱,规定将全球划分为24个时区(东西各 12 个时区)。造成时间上的混乱是由于世界各个国家位于地球不同位置上,因此不同国家,特别是东西跨度大的国家日出、日落时间必定有所偏差(这个偏差我们通常叫做时差)
理论时区
理论时区以被 15 整除的经线为中心,向东喜两侧延伸 7.5°,即每 15°划分为一个时区。理论时区的时间采用其中央经线(或标准经线)的地方时。所以每差一个时区,区时相差一个小时,相差多少个时区,就相差多少小时
法定时区
为了避开国界线,有的时区的形状并不规则,而且比较大的国家以国家内部行政分机型为时区界线。例如,中国跨5个时区,但为了使用方便简单并且全国统一使用一个区时,实际上在中国使用东8区的区时一般称为北京时间作为标准时间
GMT
格林尼治平均时间(英语:Greenwich Mean Time,GMT)是指位于英国伦敦郊区的皇家格林尼治天文台当地的平太阳时,因为本初子午线被定义为通过那里的经线。
自1924年2月5日开始,格林尼治天文台负责每隔一小时向全世界发放调时信息。
格林尼治标准时间的正午是指当平太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治平时 基于天文观测本身的缺陷,已经被原子钟报时的协调世界时(UTC)所取代。
UTC
协调世界时(英语:Coordinated Universal Time,法语:Temps Universel Coordonné,简称UTC)是最主要的世界时间标准,其以原子时秒长为基础,在时刻上尽量接近于格林威治标准时间。
中华人民共和国采用ISO 8601:2000的国家标准GB/T 7408-2005《数据元和交换格式 信息交换 日期和时间表示法》中亦称之为协调世界时。
协调世界时是世界上调节时钟和时间的主要时间标准,它与0度经线的平太阳时相差不超过1秒,并不遵守夏令时(由实验室用足够精确的铯原子钟导出的时间作为原子时,原子时的精确度极高,精度可以达到每2000万年才误差1秒)。协调世界时是最接近格林威治标准时间(GMT)的几个替代时间系统之一。对于大多数用途来说,UTC时间被认为能与GMT时间互换,但GMT时间已不再被科学界所确定。
DST
夏时制(英文:Daylight Saving Time),又称夏令时、日光节约时间,是一种在夏季月份牺牲正常的日出时间,而将时间调快的做法。通常使用夏时制的地区,会在接近春季开始的时候,将时间调快一小时,并在秋季调回正常时间。目前中国已经弃用DST
ISO-8601
国际标准ISO 8601,是国际标准化组织的日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》。目前是2004年12月1日发行的第三版“ISO8601:2004”以替代1998年的第一版“ISO8601:1998”与2000年的第二版“ISO8601:2000”。
计算机中的时间
JSR-310
JSR(Java Specification Requests)是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。310 是一个编号,在 JDK8 中通过这个标准提供了新的改进日期时间的 API
相信做 Java 开发,对于JDK 的时间 API(小于JDK8版本)肯定都是吐槽不少,主要问题体现在以下几个方面
-
最开始,Date 既要承载日期信息,又要做日期之间的转换,还要做不同日期格式的显示,职责较繁杂
而后 JDK1.1 开始,将三项职责分开了- 使用 Calendar 类实现日期和时间字段之间的转换
- 使用 DateFormat 类来格式化和分析日期字符串
- Date 只用来承载日期和时间信息
尽管已经区分了各自的职责,但在使用时任然是很不方便
-
谜之 year 和 month
1
2
3
4
5
6
7
8
9
10
11
12
13
14// 定义的月是 0-11表示 1-12 月
Date date = new Date(2020, 4, 8);
// 输出结果:Sat May 08 00:00:00 CST 3920
// 年竟然是 3920 = 2020 + 1900
// 月竟然是 May = 4 + 1
System.out.println(date);
Calendar calendar = Calendar.getInstance();
calendar.set(2020, 4, 8);
// 输出结果:Fri May 08 11:27:11 CST 2020
// 年输出:和预想一致
// 月输出:还和 Date 一样是输入月份 +1
System.out.println(calendar.getTime()); -
Date 与 Calendar 类中的所有属性是可变的,线程不安全
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 计算两个日期之间的天数
public static void main(String[] args) {
Calendar birth = Calendar.getInstance();
birth.set(1975, Calendar.MAY, 26);
Calendar now = Calendar.getInstance();
System.out.println(daysBetween(birth, now));
// 连续计算两个 Date 实例的话,第二次会取得 0,因为 Calendar 状态是可变的
System.out.println(daysBetween(birth, now)); // 显示 0?
}
public static long daysBetween(Calendar begin, Calendar end) {
long daysBetween = 0;
while(begin.before(end)) {
begin.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
return daysBetween;
}
JSR 310的规范领导者 Stephen Colebourne,同时也是 Joda-Time 的创建者,JSR 310是在Joda-Time的基础上建立的,参考了绝大部分的API
2038年问题
现时大部分使用UNIX的系统都是32位的,即它们会以32位有符号整数表示时间类型time_t。因此它可以表示136年的秒数。表示协调世界时间1901年12月13星期五20时45分52秒至2038年1月19日3时14分07秒(二进制:01111111 11111111 11111111 11111111,0x7FFF:FFFF),在下一秒二进制数字会是10000000 00000000 00000000 00000000(0x8000:0000),这是负数,因此各系统会把时间误解作1901年12月13日20时45分52秒(亦有可能回归到1970年)。这时可能会令软件发生问题,导致系统瘫痪
当前的解决方案
把系统由32位转为64位系统。在64位系统下,此时间最多可以表示到292,277,026,596年12月4日15时30分08秒
附录
- 时间 • 维基百科
- 时区 • 维基百科
- UNIX时间 • 维基百科
- ISO 8601 • 维基百科
- 格林尼治标准时间(GMT) • 维基百科
- 协调世界时(UTC) • 维基百科
- 计算机世界中的时间概念
- JSR 310: Date and Time API
- JSR310-新日期API(完结篇)-生产实战