时区问题
PostgreSQ中,timstamp是不带时区的类型,timestamp with zone是带时区的类型;函数CURRENT_TIMESTAMP得到的是timestamp with zone的当前时间;函数LOCALTIMESTAMP得到的是timestamp类型的时间。
那么你知道,将CURRENT_TIMESTAMP赋值给一个timstamp类型的字段会发生什么吗?
时区?偏移量?
在搞清楚数据库和代码中的时区表达之前,必须掌握好基础知识。
你能分清GMT、UTC、夏令时、时区等概念吗?
对于上面四个概念,下面这篇文章算是讲的比较清楚。
简单来说,GMT就是以前的时间标准,以格林威治的时间作为世界标准时间,其它各个时区以此为参考增加或减少小时数;UTC是新的世界时间标准,与GMT不同,GMT是单纯以格林威治这个地方作为标准时参考,UTC是结合了平均太阳时、地轴运动修正、国际原子钟综合得到的精确结果;夏令时,是指在天亮比较早的夏季人为将时间调早一个小时,我们对它不是很熟悉是因为我国1991年开始就已经废除了下令时;时区,理论来说,按照经度划分,每15度为一个时区,全球总共24个时区,每个时区相差一个小时,为啥是理论呢?这是因为实际还会考虑政治因素,很过国家一个国家跨多个时区,但他们都使用一个时区,所以时区不是严格按照经度划分的。
格林威治在哪里,看下面这张图。
你能分清+08:00、Asia/Shanghai、GMT-这几种表达的区别吗?
先说Asia/Shanghai,这是以地区命名的地区标准时,叫做CST;+8:00,指的是东八区。现在这个年份来说,二者表述一样,但是前者还包含一些历史时间,比如91年之前,我国实行过一段时间夏令时,如果将时区设置为Asia/Shanghai,那么在获取91年之前的时间时候就会有所差异。
其实地区标准时是非常有必要的,比如我们的日历中,使用地区标准是必须的,91年之前的时间你肯定想要显示当时的计算结果。
LocalDateTime/ZoneOffsetDateTime/ZonedDateTime
认识它们:
LocalDateTime、OffsetDateTime、ZonedDateTime互转,这一篇绝对喂饱你
ZoneOffset和Zoned的区别,通过解释Asia/Shanghai和+8:00的区别,想必已经了解了。
理解上述的几个关键点
LocalDateTime不带时区,不带时区不是说它的时间就是UTC时间,而是说它就是一个时间字面量,不能够标识时间线上的某个时间。核心是理解Local这个概念,即当地时间,“当地”,与时区无关。
它不能表示时间线上的点,因此无法获取其epoch值,要获取时必须指定时区变成时间线上点才行。
ZoneOffsetDateTime,即以偏移量表示的时间。
ZonedDateTime,即以地区时间表示的时间,即我们常说的时区。
ZoneOffsetDateTime与ZonedDateTime区别
- 前者可以自由设置偏移量,后者无法设置偏移量,只能根据时区来设置
- 后者能够很好得支持夏令时,前者不行
为什么它们之间能够相互转换?
- LocalDateTime不能直接转换为其它两种类型,除非它指定是在哪个时区的本地时间。这也能够理解,本地时间+时区,不就成了通用时间了吗。
- 偏移量和地区时间如何转换呢?前者转后者比较好做,因为时区包含了偏移量。
数据库应该使用带时区还是不带时区的类型?
当然是带时区的类型啦。
如果不带时区,是无法表示时间线上的某个点的;就会出现,如果数据库的时区设置在东八区,则生成的时间就是东八区的本地时间;如果设置在0时区,则生成的就是0时区的本地时间;二者是不一样的。也就是说,生成的时间在时间线上的位置会随着数据库时区的变化而变化,这显然是不合理的。
如果使用带时区的类型,只不过是在显示该日期时会有所差别,但实际上的时间是没有问题的。
其实,理论上最好的方式是存储epoch值,这样就不用管各种不同的数据库之间时间类型的差异了。