一次因JDK夏令时导致接口输出日期格式的时间与预期时间不一致的bug排查总结( 二 )


一次因JDK夏令时导致接口输出日期格式的时间与预期时间不一致的bug排查总结

文章插图
通过对比我们可以看到应用中的对应的用户生日"1988-07-29"刚好在中国的夏令时区间内,因为我们操作系统、数据库、JDK使用的都是 "Asia/" 时区,应该不会错,通过上图中debug结果我们也证实了结果是没问题的 。
继续往外排查业务层和接口层,定位到问题
项目使用的是 boot提供rest接口返回json报文,使用 默认的框架解析 。项目中有需要对外输出统一日期格式,对做了一下配置:
#jackson#日期格式化spring.jackson.date-format=yyyy-MM-dd HH:mm:ssspring.jackson.time-zone=GMT+8
我们通过查看 .java源码:
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package org.springframework.boot.autoconfigure.jackson;import com.fasterxml.jackson.annotation.JsonInclude.Include;import com.fasterxml.jackson.core.JsonParser.Feature;import com.fasterxml.jackson.databind.DeserializationFeature;import com.fasterxml.jackson.databind.MapperFeature;import com.fasterxml.jackson.databind.SerializationFeature;import java.util.EnumMap;import java.util.Locale;import java.util.Map;import java.util.TimeZone;import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(prefix = "spring.jackson")public class JacksonProperties {private String dateFormat;private String jodaDateTimeFormat;private String propertyNamingStrategy;private Map serialization = new EnumMap(SerializationFeature.class);private Map deserialization = new EnumMap(DeserializationFeature.class);private Map mapper = new EnumMap(MapperFeature.class);private Map parser = new EnumMap(Feature.class);private Map generator = new EnumMap(com.fasterxml.jackson.core.JsonGenerator.Feature.class);private Include defaultPropertyInclusion;private TimeZone timeZone = null;private Locale locale;public JacksonProperties() {}public String getDateFormat() {return this.dateFormat;}public void setDateFormat(String dateFormat) {this.dateFormat = dateFormat;}public String getJodaDateTimeFormat() {return this.jodaDateTimeFormat;}public void setJodaDateTimeFormat(String jodaDataTimeFormat) {this.jodaDateTimeFormat = jodaDataTimeFormat;}public String getPropertyNamingStrategy() {return this.propertyNamingStrategy;}public void setPropertyNamingStrategy(String propertyNamingStrategy) {this.propertyNamingStrategy = propertyNamingStrategy;}public Map getSerialization() {return this.serialization;}public Map getDeserialization() {return this.deserialization;}public Map getMapper() {return this.mapper;}public Map getParser() {return this.parser;}public Map getGenerator() {return this.generator;}public Include getDefaultPropertyInclusion() {return this.defaultPropertyInclusion;}public void setDefaultPropertyInclusion(Include defaultPropertyInclusion) {this.defaultPropertyInclusion = defaultPropertyInclusion;}public TimeZone getTimeZone() {return this.timeZone;}public void setTimeZone(TimeZone timeZone) {this.timeZone = timeZone;}public Locale getLocale() {return this.locale;}public void setLocale(Locale locale) {this.locale = locale;}}
得知 ..time-zone 属性操作的就是java.util. 。于是我们通过一段测试代码模拟转换过程:
package com.test;import java.sql.Date;import java.util.TimeZone;/*** @author alexpdh* @date 2019/07/17*/public class Test {public static void main(String[] args) {System.out.println("当前的默认时区为: " + TimeZone.getDefault().getID());Date date1 = Date.valueOf("1988-07-29");Date date2 = Date.valueOf("1983-07-29");System.out.println("在中国夏令时范围内的时间 date1=" + date1);System.out.println("正常东八区时间 date2=" + date2);//模拟 spring.jackson.time-zone=GMT+8 属性设置TimeZone zone = TimeZone.getTimeZone("GMT+8");TimeZone.setDefault(zone);System.out.println(TimeZone.getDefault().getID());Date date3 = date1;Date date4 = date2;System.out.println("转换后的在中国夏令时范围内的时间date3=" + date3);System.out.println("转换后的正常东八区时间 date4=" + date4);}}