时间戳 大乌龙事件

123 阅读1分钟

场景

海外业务 数据库查询存储的事件和页面展示的事件不正确,数据库 create_time=2024-04-10 05:50:42 服务器地区UTC+3,国内页面展示 create_time=2024-04-10 10:50:42 相差了5个小时(国内东八区)

  • 流程 http请求->数据库查询 create_time=2024-04-10 05:50:42 -> json序列化 create_time=1712717442000时间戳
  • json序列化代码:
public class JacksonConfig {

    // 序列化LocalDateTime
    @Bean
    @Primary
    public ObjectMapper serializingObjectMapper(
            Jackson2ObjectMapperBuilder builder, JacksonProperties jacksonProperties) {
        ObjectMapper objectMapper = builder.build();

        // 把“忽略重复的模块注册”禁用,否则下面的注册不生效
        objectMapper.disable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS);

        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
        objectMapper.registerModule(javaTimeModule);

        // 重新设置为生效,避免被其他地方覆盖
        objectMapper.enable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS);

        return objectMapper;
    }

    // 序列化实现
    public static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
        @Override
        public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers)
                throws IOException {
            if (value != null) {
                long timestamp =
                        value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                gen.writeNumber(timestamp);
            }
        }
    }

    // 反序列化实现
    public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
        @Override
        public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext)
                throws IOException {
            long timestamp = p.getValueAsLong();
            if (timestamp > 0) {
                return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
            } else {
                return null;
            }
        }
    }
}

最终发现时间戳转换到日期的时候是根据时区来的,这个时候只要把本地时区改成东三区就可以显示正确,如果是东八区时区 就会转到北京时间来显示 所以会出现差异

image.png