JAVA开发使用@JsonFormat注解,日期比实际日期少一天问题
前言
最近同事反馈一个问题,说是日期保存后未发生变化。刚开始以为是字段未对应或者是未保存成功,当我去进行排查的时候发现,发现数据保存没有问题。奇了怪了。
问题现象
库里日期数据保存正确,但是后台返回前台页面发现不正确。
排查过程
刚开始怀疑是数据未保存成功,经过测试发现数据正常保存进数据库中。
有转变方向根据Debug走了一遍数据查询过程,发现问题所在。
后台查询结果正确,但是经过controller层响应前台时,日期发生了变化。OK,问题定位,检查日期格式转换。
发现后台实体中日期转换使用@JsonFormat注解,但是转换格式没有发现问题
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
根据网上解决方案调整timezone 与 pattern 的顺序,未成功解决。
最后在一篇文章中找到答案并解决,特来记录这个有意思的小bug。
关于@JsonFormat注解
@JsonFormat注解,是在Jackson中定义的一个注解,是一个时间格式化注解。此注解用于属性上,作用是把DATE类型的数据转化成为我们想要的格式。
通俗点来说,是后台数据转换为指定日期格式返回给前台时使用的。
pattern(模式)
: 通过 pattern 属性,您可以指定日期和时间的格式。
例如,如果要将日期格式设置为"yyyy-MM-dd"
,可以使用@JsonFormat(pattern = "yyyy-MM-dd")
timezone(时区)
: 使用 timezone 属性可以指定日期和时间的时区。这对于确保正确地处理跨时区的日期数据很重要。locale(区域设置)
: 通过 locale 属性,您可以指定用于格式化的区域设置,以便支持不同的语言和地区。shape(形状)
: shape 属性定义了序列化后的日期表示形式。例如,您可以将日期表示为字符串或时间戳。with(特定类型的格式)
: 使用 with 属性,您可以为不同的 Java 类型指定不同的格式。这对于处理不同类型的日期数据非常有用。
注意:我们在格式化的时候要指定时区(timezone
),是因为@JsonFormat注解默认的时区为格林尼治时间,比中国时间(东八区是要小8个小时的)。
中国的默认时区是东八区。
时区概念
GMT:Greenwich Mean Time 格林尼治标准时间。这是以英国格林尼治天文台观测结果得出的时间,这是英国格林尼治当地时间,这个地方的当地时间过去被当成世界标准的时间。
UT:Universal Time 世界时。根据原子钟计算出来的时间。
UTC:Coordinated Universal Time 协调世界时。因为地球自转越来越慢,每年都会比前一年多出零点几秒,每隔几年协调世界时组织都会给世界时+1秒,让基于原子钟的世界时和基于天文学(人类感知)的格林尼治标准时间相差不至于太大。并将得到的时间称为UTC,这是现在使用的世界标准时间。
协调世界时不与任何地区位置相关,也不代表此刻某地的时间,所以在说明某地时间时要加上时区
也就是说GMT并不等于UTC,而是等于UTC+0,只是格林尼治刚好在0时区上。
GMT = UTC+0
格林尼治时间(GMT)、世界协调时间(UTC)和中国时间的关系
中国时间(Asia/Shanghai) = 格林尼治时间(GMT) + 8
格林尼治时间(GMT) = 世界协调时间(UTC) + 0
问题原因
中国在1986年至1991年实行过夏令时,即在这些年份北京时间比GMT提前9个小时。所以使用注解@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")看起来像是未起作用。
解决方案
使用下面的注解替换@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@JsonFormat(pattern = "yyyy-MM-dd", locale = "zh", timezone = "Asia/Shanghai")