背景
今天接收到一个生产缺陷,客户的创建信息列表页,创建时间显示错误,比数据库的时间晚了八个小时;但是当第一次请求时,时间展示却是正常的,只有翻页或者筛选的时候才会出现异常。
问题分析
首先可以确定的是,数据库的创建时间是准确的,没有误差的;其次就是页面的第一次加载是正确现显示的,但是执行筛选或者翻页时就会出现日期误差;
之前页面的逻辑是我写的,为了配合页面的echarts组件展示,我将条件筛选查询和翻页查询都做成了ajax的页面请求,这样在筛选的时候,就不会刷新页面但又能更新数据了。
所以,目前的排查的主要方向是Ajax异步请求时,数据为什么会出现误差。
根据该方向,在网上确实找到了答案:
spring中对于@RestController或者@Controller+@ResponseBody
注解的接口方法的返回值默认是Json格式,
所以当对于date类型的数据,在返回浏览器端是会被spring-boot
默认的Jackson框架转换,而Jackson框架默认的时区GMT(相对于中国是少了8小时)。
问题解决
- 方案一:局部变量处理
这个方法针对于 当前页面的变量进行处理,虽然笨拙了点,但是也不失为一条解决策略;现在的问题是显示的日期比正确的晚了8个小时,那我可以直接在页面把这8个小时补上就可以了。可参考:
dateFormat = function (date, format) {
date = new Date(date);
var o = {
'M+' : date.getMonth() + 1, //month
'd+' : date.getDate(), //day
'H+' : date.getHours()+8, //hour+8小时
'm+' : date.getMinutes(), //minute
's+' : date.getSeconds(), //second
'q+' : Math.floor((date.getMonth() + 3) / 3), //quarter
'S' : date.getMilliseconds() //millisecond
};
if (/(y+)/.test(format))
format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp('(' + k + ')').test(format))
format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length));
return format;
}
-
方案二:全局处理
- 场景一:springMvc+spring
在springmvc.xml的配置文件中,添加对json属性的定制处理,指定时区即可,当然除此之外,还可以指定日期的格式化格式等。可在配置文件中添加如下配置的bean信息
<!--处理时区问题-->
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<!-- 处理responseBody 里面日期类型 -->
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" />
</bean>
</property>
<!-- 时区指定 -->
<property name="timeZone" value="GMT+8" />
<!-- 为null字段时不显示 -->
<property name="serializationInclusion">
<value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
</property>
</bean>
</property>
</bean>
- 场景二:springBoot项目
在配置文件application.properties文件中添加如下配置即可:
spring.jackson.time-zone=GMT+8
(再次惊叹springBoot的检查能力),如果需要对时间格式进行指定的话,再添加一个配置即可:
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
由于我们公司的这个子项目正好是由原始的spring项目重构为springBoot项目,所以以上两种场景我都做了验证,完全没问题。
总结
挺有意思的,第一次遇到这样的问题,刚开始还以为是数据库的时区问题呢,在这里记录一下,下面是两篇参考的博文。