记一次夏令时导致的fastJson序列化问题

443 阅读1分钟

大体情况

项目使用springboot + mybatis + fastJson 1.2.83

  • 出现问题的业务流程:

查询数据库存储过程中的数据,然后使用fastJson序列化到redis中缓存;

下次获取数据时,从redis中获取缓存数据,反序列化进行处理

存储过程中的数据字段类型都不固定,因此接收数据的类型为Map<String,Object>

问题

项目上线运行了一段时候后,发现会存在部分日期数据无法展示

通过排查后,发现是日期数据被序列化成了时间戳,反序列化后丢失原来的yyyy-MM-dd格式,无法解析导致

进一步排查,发现原始类型为java.sql.date,时区为+0900,再查看fastJson源码,发现如下逻辑:

Class<?> clazz = object.getClass();
long millis;
if (clazz == Date.class && !out.isEnabled(SerializerFeature.WriteDateUseDateFormat)) {
    millis = ((Date)object).getTime();
    TimeZone timeZone = serializer.timeZone;
    int offset = timeZone.getOffset(millis);
    if ((millis + (long)offset) % 86400000L == 0L && !SerializerFeature.isEnabled(out.features, features, SerializerFeature.WriteClassName)) {
        out.writeString(object.toString());
        return;
    }
}
  • 若没有手动指定序列化日期格式,并且时区和当前时区不相同,则会被转化成时间戳

原因

出问题的日期在1986-1991年之间,这段时间中国实行夏令时,时区往后一个小时,时区变成+0900; 导致和当前时区+0800不相同,出现问题

解决方案

序列化时指定日期序列化格式

由于业务场景问题,数据存在多种日期格式,指定格式会将所有date都格式化,因此不适应该场景

序列化之前将日期转化成字符串

最终选择该方案