后端返回日期少了一天的情景分析

580 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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 官方解释

image-20220426182721462

下面是2.9.0版本的更新记录,修复了上面的文档错误的bug:

image-20220426182816354

所以根据问题还是在于时区的设置问题;

总结

出现前后端日期不一致的情况,首先就是要查看时区设置是否正确;

我们国内,正确的时区设置如下:

@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date date;