持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
文章首发于个人博客:后端返回的日期少了一天 (javalover.cc)
问题描述
用的Jackson,调试时发现后端应该返回的是2022-02-08,但是实际上前端收到的是2022-02-07,比预期的少了一天;
- 数据库的数据格式为 date
- java实体类的日期格式为 java.util.Date
原因分析
根本原因:时区问题;
后端在实体类中的日期字段上,配置了Jackson的日期格式化:JsonFormat("yyyy-MM-dd"),如下所示:
@JsonFormat(pattern = "yyyy-MM-dd")
private Date date;
这里的JsonFormat没有配置时区,而Jackson默认的时区为 UTC协调时间时(比GMT格林威治时间准确,GMT已被淘汰),比中国时间少了8个小时;
所以这个时候,后端Jackson在格式化日期时,会先将数据库中查询到的2022-02-08 减去 8小时(因为数据库中查询到的时间为中国时区,而这里Jackson解析的时区为UTC),然后才返回前端;
所以前端接收的日期会少一天(准确地说,是少了8个小时,因为我这里的数据库存储的为Date日期格式,所以少一秒都会算到前一天);
解决办法
配置时区GMT+8,如下所示:
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date date;
延伸
关于Jackson的源码文档的错误问题:
在JsonFormat的源码文档中,写的是如果不设置时区,默认应该是选择系统所在地区的时区;
/**
* {@link java.util.TimeZone} to use for serialization (if needed).
* Special value of {@link #DEFAULT_TIMEZONE}
* can be used to mean "just use the default", where default is specified
* by the serialization context, which in turn defaults to system
* defaults ({@link java.util.TimeZone#getDefault()}) unless explicitly
* set to another locale.
*/
public String timezone() default DEFAULT_TIMEZONE;
而我们这里的系统时区应该是 Asia/Shanghai,运行下面的代码:
System.out.println(TimeZone.getDefault());
可以看到控制台打印如下:
sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=31,lastRule=null]
所以按理说,如果我们不设置时区,应该采用中国时区,但是为啥用了UTC标准时区呢?
通过查找相关资料,找到了官方的解释,说是文档错误,会修复,修复版本为2.9.0
以下是Jackson 官方解释:
下面是2.9.0版本的更新记录,修复了上面的文档错误的bug:
所以根据问题还是在于时区的设置问题;
总结
出现前后端日期不一致的情况,首先就是要查看时区设置是否正确;
我们国内,正确的时区设置如下:
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date date;