1.问题记录
Mysql中数据如下,都是Date类型,实体对应数据类型也为Date
在业务中拿到的数据为:
转成json后:
两相对比,发现转成JSON后的时间数据相较于Mysql中存储的数据少了8小时
2.原因分析
可以发现,8小时正好为两个时区相差的时间。经过资料的搜索发现如下的原因
2.1 Mysql中存在着时区的概念
首先通过show variables like '%time_zone%'命令查询出Mysql的时区,得到结果如下
system_time_zone:mysql启动的时候,mysql服务所在服务器(主机)的时区,windows系统返回为空,linux系统一般会返回CST
time_zone:当前会话使用的时区,可以在配置文件中指定默认值,如果不指定,默认值是SYSTEM,表示使用system_time_zone指向的时区
像我这样的情况则是表示Mysql使用的时区为CST(CST可以同时表示美国,澳大利亚,中国,古巴四个国家的标准时间)
PS:建议直接使用
set global time_zone = '+8:00'修改mysql全局时区为北京时间或者直接在mysql的配置文件my.ini中加上default-time_zone = '+8:00'
2.2 Jackson也有时区概念
Jackson是SpringBoot默认的Json视图转换框架。因为通常我们都会使用统一封装的方式,将code,data等数据封装成实体,交由SpringBoot框架自动转成Json,而SpringBoot就是默认使用JackSon进行对象序列化。
/**
* Value that indicates that default {@link java.util.TimeZone}
* (from deserialization or serialization context) should be used:
* annotation does not define value to use.
*<p>
* NOTE: default here does NOT mean JVM defaults but Jackson databindings
* default, usually UTC, but may be changed on <code>ObjectMapper</code>.
*/
public final static String DEFAULT_TIMEZONE = "##default";
注释已经说明默认情况下会将 时区设置为UTC 注意:Jackson反序列化时间类型的底层实际上调用的是Java的 SimpleDateFormat#parse() 方法,而JVM中的时区则会根据你的操作系统来获取,所以JVM认为你的时区应该是 GMT+8 时区
也就是说,在并没有其他配置的情况下,Jackson进行序列化操作时,默认的时区为UTC
2.3 各个时区缩写的含义(GMT,UTC,DST,CST,CET)及关系
UTC: Universal Time Coordinated 协调世界时,又称世界标准时间
GMT: Greenwich Mean Time 格林尼治平均时
CET:Central European Time 欧洲中部时间
CST时间: 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
关系:
UTC=GMT
CET=UTC/GMT + 1小时
CST=UTC/GMT +8 小时
CST=CET+7
3.问题总结
在第2点的分析中可以得知,我的数据库默认时区为CST,而在实体序列化成JSON时默认的时区为UTC。再由CST = UTC + 8这个关系,就可以明白为什么会出现1中的情况。
4.解决方式
4.1 修改mysql的时区
直接使用set global time_zone = '+8:00'修改mysql全局时区为北京时间或者直接在mysql的配置文件my.ini中加上default-time_zone = '+8:00'
4.2 在每个带有日期类型的字段上加上@JsonFormat
@NotNull
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
@JsonProperty("start_time")
private Date contractEndDate;
4.3 在启动类或者带有@Configration注解的类中配置bean
@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
return jacksonObjectMapperBuilder ->
jacksonObjectMapperBuilder.timeZone(TimeZone.getTimeZone("GMT+8"));
}
4.4 在配置文件中配置Jackson相关配置(推荐和4.1一起使用)
spring:
jackson:
date-format: yyyy-MM-dd #如果使用字符串表示,用这行设置格式,可以自由设定
time-zone: GMT+8 #设置时区
参考资料: