地球是自西向东自转,东边比西边先看到太阳,东边的时间也比西边的早。东边时刻与西边时刻的差值不仅要以时计,而且还要以分和秒来计算,这给人们带来不便。
为了克服时间上的混乱,1884年在华盛顿召开的一次国际经度会议(又称国际子午线会议)上,规定将全球划分为24个时区(东、西各12个时区)。规定英国(格林尼治天文台旧址)为中时区(零时区)、东1—12区,西1—12区。每个时区横跨经度15度,时间正好是1小时。最后的东、西第12区各跨经度7.5度,以东、西经180度为界。每个时区的中央经线上的时间就是这个时区内统一采用的时间,称为区时,相邻两个时区的时间相差1小时。
例如,中国东8区的时间总比泰国东7区的时间早1小时,而比日本东9区的时间晚1小时。因此,出国旅行的人,必须随时调整自己的手表,才能和当地时间相一致。凡向西走,每过一个时区,就要把表拨慢1小时(比如2点拨到1点);凡向东走,每过一个时区,就要把表拨快1小时(比如1点拨到2点)。并且规定英国(格林尼治天文台旧址)为本初子午线,即零度经线。
以本初子午线的平子夜起算的平太阳时
英文:UT(Universal Time)
GMT:Greenwich Mean Time
格林尼治标准时间。这是以英国格林尼治天文台观测结果得出的时间,这是英国格林尼治当地时间,这个地方的当地时间过去被当成世界标准的时间。
也就是说它是世界计算时间和经度的起点。
1960年以前曾作为基本时间计量系统被广泛应用。由于地球自转速度变化的影响,它不是一种均匀的时间系统。后来世界时被原子时所取代,但在日常生活、天文导航、大地测量和宇宙飞行等方面仍属必需;同时,世界时反映地球自转速率的变化,是地球自转参数之一,仍为天文学和地球物理学的基本资料。
协调世界时是以原子时秒长为基础,在时刻上尽量接近于世界时的一种时间计量系统。
这套时间系统被应用于许多互联网和万维网的标准中,例如,网络时间协议就是协调世界时在互联网中使用的一种方式。
在军事中,协调世界时区会使用“Z”来表示。又由于Z在无线电联络中使用“Zulu”作代称,协调世界时也会被称为"Zulu time"。
中国大陆、中国香港、中国澳门、中国台湾、蒙古国、新加坡、马来西亚、菲律宾、西澳大利亚州的时间与UTC的时差均为+8,也就是UTC+8。
如今的标准时间——协调世界时(UTC)——由原子钟提供。 自1924年2月5日开始,格林尼治天文台每隔一小时会向全世界发放调时信息。而UTC是基于标准的GMT提供的准确时间。
国际原子时的准确度为每日数纳秒,而世界时的准确度为每日数毫秒。许多应用部门要求时间系统接近世界时UT,对于这种情况,一种称为协调世界时的折中时标于1972年面世。为确保协调世界时与世界时相差不会超过0.9秒,在有需要的情况下会在协调世界时内加上正或负闰秒。因此协调世界时与国际原子时之间会出现若干整数秒的差别,两者之差逐年积累,便采用跳秒(闰秒)的方法使协调时与世界时的时刻相接近,其差不超过1s。它既保持时间尺度的均匀性,又能近似地反映地球自转的变化。
为了方便,在不需要精确到秒的情况下,通常将GMT 和UTC 视作等同。但UTC
更加科学更加精确,它是以原子时为基础,在时刻上尽量接近世界时的一种时间计量系统。它的出现是现代社会对于精确计时的需要。
UT:Universal Time 世界时。根据原子钟计算出来的时间。
UTC:Coordinated Universal Time 协调世界时。因为地球自转越来越慢,每年都会比前一年多出零点几秒,每隔几年协调世界时组织都会给世界时+1秒,让基于原子钟的世界时和基于天文学(人类感知)的格林尼治标准时间相差不至于太大。并将得到的时间称为UTC,这是现在使用的世界标准时间。
协调世界时不与任何地区位置相关,也不代表此刻某地的时间,所以在说明某地时间时要加上时区也就是说GMT并不等于UTC,而是等于UTC+0,只是格林尼治刚好在0时区上。
GMT = UTC+0
地区:中国 北京 Beijing
时区:UTC/GMT +8.00 (东八区)
北京时间又称中国标准时间。
比格林威治时间(Greenwich Mean Time简称GMT)早8小时。
北京时间是由位于陕西西安的中国国家授时中心计算得出。
时区时差时间换算/世界时区划分时差查询
CST可以为如下4个不同的时区的缩写:
夏令时,表示为了节约能源,人为规定时间的意思。也叫夏时制,夏时令(Daylight Saving Time:DST),又称“日光节约时制”和“夏令时间”,在这一制度实行期间所采用的统一时间称为“夏令时间”。一般在天亮早的夏季人为将时间调快一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。各个采纳夏时制的国家具体规定不同。全世界有近110个国家每年要实行夏令时。
unix时间戳是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。
Unix时间戳(英文为Unix epoch, Unix time, POSIX time 或 Unix timestamp)
是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。 UNIX时间戳的0按照ISO 8601规范为 :1970-01-01T00:00:00Z.
一个小时表示为UNIX时间戳格式为:3600秒;一天表示为UNIX时间戳为86400秒,闰秒不计算。
在大多数的UNIX系统中UNIX时间戳存储为32位,这样会引发2038年问题或Y2038。
中文名unix时间戳外文名Unix epoch, Unix time, POSIX time又 称 Unix timestamp时
间1970年1月1日系 统UNIX内核系统
对于操作DB在两个不同时区的程序去存取同一个Datetime字段的话会出现大问题:
在同一时刻存入的“当前时间”会不一样。
另外一个情况是:在中国存到数据表里的 2020-12-12 13:39:00 在美国的服务器取出来为2020-12-12 13:39:00 看起来没什么问题;可是实际上美国中部现在时间为 2020-12-11 23:39:00 整整差了14个小时。
如果是一个国际秒杀活动的话在中国你可能已经拿到买的东西了而美国那边还在显示活动开始倒计时!
分布在不同时区的两个程序在传递Date类型数据时也可能会有意外的反应,因为同一个时间在传递另一个时区的Date对象中时会根据接收方的时区做相应的转换。
当前mysql默认的连接会话时区为美国中部
UTC-06:00
同时使用
一个JDBC连接加了东八区时区参数
的客户端和
一个没有加JDBC时区参数
的客户端查看同一条记录
另一个客户端使用默认时区
得到的结果为:
可以看出来create_time时差了8个小时
这就是时区不同所造成的影响
但这并不是错误,而是数据在时区机制的序列化的正常反应。
默认取系统的时区
我这里为东八区 CST 也就是 GMT+08:00
Date now = new Date();
System.out.println(now.toString())
Sat Dec 12 16:08:48 CST 2020
System.out.println(now.toGMTString())
12 Dec 2020 08:08:48 GMT
修改java程序全局默认的时区
这个会影响到Date类型序列化的日期偏移
TimeZone.setDefault(TimeZone.getTimeZone("CST"));
# 美国中部时区
TimeZone.setDefault(TimeZone.getTimeZone("GMT-06:00"));
Date类型的toString()不只是把自身属性打印出来这么简单
public String toString() {
// "EEE MMM dd HH:mm:ss zzz yyyy";
BaseCalendar.Date date = normalize();
StringBuilder sb = new StringBuilder(28);
int index = date.getDayOfWeek();
if (index == BaseCalendar.SUNDAY) {
index = 8;
}
convertToAbbr(sb, wtb[index]).append(' '); // EEE
convertToAbbr(sb, wtb[date.getMonth() - 1 + 2 + 7]).append(' '); // MMM
CalendarUtils.sprintf0d(sb, date.getDayOfMonth(), 2).append(' '); // dd
CalendarUtils.sprintf0d(sb, date.getHours(), 2).append(':'); // HH
CalendarUtils.sprintf0d(sb, date.getMinutes(), 2).append(':'); // mm
CalendarUtils.sprintf0d(sb, date.getSeconds(), 2).append(' '); // ss
TimeZone zi = date.getZone();
if (zi != null) {
sb.append(zi.getDisplayName(date.isDaylightTime(), TimeZone.SHORT, Locale.US)); // zzz
} else {
sb.append("GMT");
}
sb.append(' ').append(date.getYear()); // yyyy
return sb.toString();
}
@Deprecated
public String toGMTString() {
// d MMM yyyy HH:mm:ss 'GMT'
long t = getTime();
BaseCalendar cal = getCalendarSystem(t);
BaseCalendar.Date date =
(BaseCalendar.Date) cal.getCalendarDate(getTime(), (TimeZone)null);
StringBuilder sb = new StringBuilder(32);
CalendarUtils.sprintf0d(sb, date.getDayOfMonth(), 1).append(' '); // d
convertToAbbr(sb, wtb[date.getMonth() - 1 + 2 + 7]).append(' '); // MMM
sb.append(date.getYear()).append(' '); // yyyy
CalendarUtils.sprintf0d(sb, date.getHours(), 2).append(':'); // HH
CalendarUtils.sprintf0d(sb, date.getMinutes(), 2).append(':'); // mm
CalendarUtils.sprintf0d(sb, date.getSeconds(), 2); // ss
sb.append(" GMT"); // ' GMT'
return sb.toString();
}
我们阅读源代码得知Date类型的序列话是经过时区计算之后的结果。
-Duser.timezone=CST
tomcat可以将时区配置写到setenv.sh
或者catalina.sh的JAVA_OPTS
中
-Duser.timezone=CST
mysql的datetime字段并不存储时区信息。
我们的程序在mysql里存取Datetime的时候时间值取决于连接指定的时区
查看数据库会话时区命令
show VARIABLES like '%time_zone%'
全局参数system_time_zone
系统时区,在MySQL启动时会检查当前系统的时区并根据系统时区设置全局参数system_time_zone的值。
全局参数time_zone
用来设置每个连接会话的时区,默认为system时,使用全局参数system_time_zone的值。
上面两个配置并不是影响MySql自身的时区,它们指定的是程序连接到mysql的时候用的默认时区。
改为东八区
set time_zone=’+8:00’;
set global time_zone = ‘+8:00’;
flush privileges;
mysql驱动5.1版本要同时配置下面3个配置项连接服务器时区才会生效
useLegacyDatetimeCode=true
useTimezone=true
serverTimezone=Asia/Shanghai
修改jdbc连接设置,直接把连接的时区固定死,绕开mysql本身的时区。
就是在jdbc连接配置上添加以下3个配置即可:
useLegacyDatetimeCode=true&useTimezone=true&serverTimezone=GMT%2B8
分别为启用时区设置和设置连接服务的时区。
此时,jdbc操作都会使用自定义时区去进行时间处理。
8.0驱动没有useLegacyDatetimeCode
=true,useTimezone=true
这两个配置项了。
# date -R
Sat, 12 Dec 2020 14:15:06 +0800
# date
Sat Dec 12 14:15:20 CST 2020
表明服务器的时区是东八区
同时认为CST
为中国标准时间
springboot 默认的 jackson 序列化到 前端
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
(全局) 固定将日期时间按照东八区时区序列化
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
@JsonProperty("start_time")
private Date startTime;
设置单个字段的时区序列化
在java程序调用另一个java程序的接口时(或者叫后端接口)
此接口接收的参数有Date类型再序列化过程中会产生时区问题:即参数出现时差。
我们在条件不允许改变JVM的时区设置的时候也可以选择改变接口入参Date类型形参的序列化方案改纠正时区导致的时差问题。
接收入参的字段
// spring mvc 接口注解:指定入参的反序列化方式
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
// 字段的序列化格式
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date beginTime;
传参使用SimpleDataFormat将Date字段序列化为指定格式的字符串。
建议在数据库连接参数上都加上时区参数
建议在后端到前端的接口序列化指定时区
不建议使用CST来做时区,容易造成系统混淆