大家好,我是你们的建哥,一个一直站在技术一线的Leader。又到了一天最惬意的韶光,泡上一杯绿茶,跟着我一起回顾平时无法系统整理的知识。

今天来说说说时区和时刻。这个问题在新手刚接触的时分总是搞得似懂非懂,由于惬意的韶光总是时刻短,所以这儿仅仅把最简单搞混的当地来捋清楚。

GMT和UTC时刻差异与联系

GMT时刻

GMT(Greenwich Mean Time),格林威治时刻。这是英国的格林威治皇家地舆台为了海上霸权的扩张方案,在十七世纪就开端进行天体观测。为了地舆观测,选择了穿过伦敦格林威治地舆台子午仪中心的一条经线作为零度参阅线,这条线,简称格林威治子午线。

它规矩太阳每天通过位于英国伦敦郊区的皇家格林威治地舆台的时刻为正午12点。很明显,由于地球每天的自转是不规矩的(正在缓慢减速)所以,格林尼治时刻的准确度会越来越低,可是依照地球每天的自转而制定的时刻非常契合人的认知,所以说很适用。

1884年10月,在美国华盛顿召开了一个国际子午线会议,该会议将格林威治子午线设定为本初子午线,并将格林威治时刻作为国际基准时刻(UT, Universal Time)。由此也确定了全球24小时自然时区的区分,一切时区都以和GMT 之间的偏移量做为参阅。

时区概念

国际基准时刻时区界说如下:

从格林威治本初子午线起,经度每向东或许向西间隔15,就区分一个时区,在这个区域内,大家运用同样的规范时刻。全球共分为24个规范时区,相邻时区的时刻相差一个小时。

1972年之前,格林威治时刻(GMT)一直是国际时刻的规范。1972年之后,GMT不再是一个时刻规范了。

UTC

明显,无论是GMT时刻还是国际基准时刻,其实都不是很准确,那么不禁要问,找一个肯定准确的肯定时刻不就行了?

很明显,这不现实的。由于人类所能直接感知的时刻便是日升日落(地球自转),由于这种契合作息规矩才更简单被人接受和运用。想想全国际都用一种根据原子钟的准确时刻,有的当地是日已三竿,有的当地是漫漫黑夜,沟通起来岂不是愈加困难?

因而,为了既要准确,又要不违反实践的地舆规矩,UTC时刻诞生了。

UTC(Coodinated Universal Time),和谐国际时,又称国际一致时刻、国际规范时刻、国际和谐时刻。由于英文(CUT)和法文(TUC)的缩写不同,作为妥协,简称UTC。

UTC是现在全球通用的时刻规范,全球各地都同意将各自的时刻进行同步和谐。

UTC 时刻是通过平均太阳时(以格林威治时刻GMT为准)、地轴运动修正后的新时标以及以秒为单位的国际原子时所归纳精算而成:由于地球自转越来越慢,每年都会比前一年多出零点几秒,每隔几年和谐国际时安排都会给国际时+1秒,让根据原子钟的国际时和根据地舆学(人类感知)的格林威治规范时刻相差不至于太大。

所以,UTC与GMT基本上同等,可是假如要算时刻差,肯定是用UTC时刻愈加准确,此外,UTC时刻专指格林威治时刻,所以全国际其他当地的时刻都要加一个偏移量来表明。

UTC时刻的表明

如上文所说,UTC时刻要带上时区,那么很明显,UTC时刻能够表明如下:

榜首种:直接带上时区

类型 示例
日期:年 1997
日期:年月 1997-07
日期:年月日 1997-07-16
日期加小时和分钟 1997-07-16T19:20+01:00
日期加小时、分钟和秒钟 1997-07-16T19:20:30+01:00
日期加小时、分钟、秒钟和秒钟的小数部分 1997-07-16T19:20:30.45+01:00

其间:

  • YYYY = 四位数年份

  • MM = 月份,从 01 到 12 的两位数

  • DD = 几号,从 01 到 31 的两位数

  • T = 在日期后时刻前的文本值

  • hh = 小时,从 00 到 23 的两位数

  • mm = 分钟,从 00 到 59 的两位数

  • ss = 秒钟,从 00 到 59 的两位数

  • SSS = 一或多位数,表明秒的小数部分

  • 后边的+或许-表明时刻的偏移量。

第二种,不带时区,可是明确指出这是UTC时刻

假如不带时区指示符,那么,末尾应该带上一个“Z”,表明这是UTC时刻而非本地时刻。比如说:2015-08-03T07:21:33Z。转化成北京时刻便是2015-08-03 15:21:33

但其实,ISO其实规矩了很多的时刻格式,Java的DateTimeFormatter规矩如下的合法格式:

常量 阐明 示例
BASIC_ISO_DATE Basic ISO date ‘20111203’
ISO_LOCAL_DATE ISO Local Date ‘2011-12-03’
ISO_OFFSET_DATE ISO Date with offset ‘2011-12-03+01:00’
ISO_DATE ISO Date with or without offset ‘2011-12-03+01:00’; ‘2011-12-03’
ISO_LOCAL_TIME Time without offset ’10:15:30′
ISO_OFFSET_TIME Time with offset ’10:15:30+01:00′
ISO_TIME Time with or without offset ’10:15:30+01:00′; ’10:15:30′
ISO_LOCAL_DATE_TIME ISO Local Date and Time ‘2011-12-03T10:15:30’
ISO_OFFSET_DATE_TIME Date Time with Offset ‘2011-12-03T10:15:30+01:00’
ISO_ZONED_DATE_TIME Zoned Date Time ‘2011-12-03T10:15:30+01:00[Europe/Paris]’
ISO_DATE_TIME Date and time with ZoneId ‘2011-12-03T10:15:30+01:00[Europe/Paris]’
ISO_ORDINAL_DATE Year and day of year ‘2012-337’
ISO_WEEK_DATE Year and Week ‘2012-W48-6’
ISO_INSTANT Date and Time of an Instant ‘2011-12-03T10:15:30Z’
RFC_1123_DATE_TIME RFC 1123 / RFC 822 ‘Tue, 3 Jun 2008 11:05:30 GMT’

2.关于时区&偏移量

在JDK 8之前,Java运用java.util.TimeZone来表明时区。而在JDK8里分别运用了ZoneId表明时区ZoneOffset表明UTC的偏移量

值得强调的是,时区和偏移量在概念和实践作用上是有较大差异的,首要体现在:

  1. UTC偏移量仅仅记录了偏移的小时分钟而已,除此之外无任何其它信息。举个例子:+08:00的意思是比UTC时刻早8小时,没有地舆/时区意义,相应的-03:30代表的意思仅仅是比UTC时刻晚3个半小时

  2. 时区是特定于地区而言的,它和地舆上的地区(包括规矩)强绑定在一起。比如整个我国都叫东八区,纽约在西五区等等

我国没有夏令时,一切东八区对应的偏移量永远是+8;纽约有夏令时,因而它的偏移量可能是-4也可能是-5。

归纳来看,时区更好用。令人恼火的夏令时问题,若你运用UTC偏移量去表明那么就很麻烦,由于它可变:一年内的某些时期在原来基础上偏移量 +1,某些时期 -1;但若你运用ZoneId时区去表明就很便利喽,比如纽约是西五区,你在任何时分获取其当地时刻都是能得到正确答案的,由于它内置了对夏令时规矩的处理,也便是说啥时分+1啥时分-1时区自己门清,不需要API调用者关心。

假如将时区写死成偏移量,在没有时区规矩(没有夏令时)的国家不会存在问题,东八区和UTC+08:00作用永远相同。但在一些夏令时国家(如美国、法国等等),就只能根据时区去获取当地时刻,所以当你不了解当地规矩时,最好是运用时区而非偏移量。

ZoneId

它代表一个时区的ID,如Europe/Paris。它规矩了一些规矩可用于将一个Instant时刻戳转化为本地日期/时刻LocalDateTime。

上面说了时区ZoneId是包括有规矩的,实践上描述偏移量何时以及如何变化的实践规矩由java.time.zone.ZoneRules界说。ZoneId则仅仅一个用于获取底层规矩的ID。之所以采用这种方法,是由于规矩是由政府界说的,而且常常变化,而ID是稳定的

能够在这儿查看规范时区ID:规范时区ID

因而,要用ZoneId来作为时区。不能单纯用偏移量。

关于Unix时刻戳与Instant

Unix时刻(Unix Time),是核算机国际的时刻,也叫做POSIX时刻或纪元时刻(Epoch Time),是用来记录时刻的消逝,所以也常被叫做时刻戳。

界说为从1970-01-01T00:00:00开端消逝的秒数,不考虑闰秒。之后的时刻是正数,之前的是负数。

从界说能够看到,它只代表了从Unix纪元开端消逝的秒数,所以你身处地球上何处,这个时刻都是相同的。

一般Unix时刻都是准确到秒,但也有些当地Unix时刻是准确到毫秒的(比如MySQL 5.6.4之后开端支撑到微秒)

Unix时刻不是凭空产生的,他便是UTC时刻在核算机国际的表达。

Unix时刻戳是从1970年1月1日(UTC+00)开端所通过的秒数,不考虑闰秒。时刻戳表明的时刻是准确、稳定的。就连时刻+日期+时区也不可——时区不是稳定不变的,这是一个全球一致的肯定时刻。

因而,假如说咱们不想对时区进行各种转化,那么最好是存储Unix时刻戳,再转化成对应的UTC带时区时刻,或许直接存储UTC时刻。

Instant

Instant类返回的值核算从 1970 年 1 月 1 日(1970-01-01T00:00:00Z)榜首秒开端的时刻(准确到纳秒),也称为EPOCH。 发生在时期之前的瞬间具有负值,而且发生在时期后的瞬间具有正值,因而这个时刻就等于UTC时刻。

String output = instant.toString();

输出:

2023-02-20 T19:15:25.864Z

Instant简单理解为便是用一个对象来表明unix时刻戳。时刻等于当前的UTC时刻