全网仅此一篇,spring boot 2的各种出入参的时间戳与时间转换

1,443 阅读3分钟

需求

使用接口时,入参和返参的时间格式都是时间戳。接口内部用Date,LocalDateTime,LocalDate接收。本文不做原理解析。

例如

@PostMapping("/test/{pathDate}")
public Result<LocalDate> dateTest(@RequestParam LocalDate date,
                                      @RequestParam LocalDateTime dateTime,
                                      @RequestParam Date originalDate,
                                      @PathVariable("pathDate") LocalDateTime datePath,
                                      LocalDate localDate,
                                      @RequestBody Person person,
                                      Person person2) {
    System.out.println(date);
    System.out.println(dateTime);
    System.out.println(originalDate);
    System.out.println(localDate);
    System.out.println(datePath);
    System.out.println(person);
    System.out.println(person2);
    //返回结果
    return Result.ok(LocalDate.now());
}
@Data
public class Person {
    String name;
    Date birth1;
    LocalDateTime birth2;
    LocalDate birth3;
}
@Data
public class Result<T>
{
    public static final int SUCCESS = 200;
    public static final int ERROR = 500;
    private long timestamp;
    private String message;
    private int code;
    private T data;

    
    public Result(final int code, final String message, final T data) {
        this.timestamp = System.currentTimeMillis();
        this.code = code;
        this.message = message;
        this.data = data;
    }
    public Result() {
    }

    public Result(T data) {
        this.timestamp = System.currentTimeMillis();
        this.data = data;
    }

    public static Result ok() {
        Result result = new Result<>();
        result.setTimestamp(System.currentTimeMillis());
        result.setCode(200);
        result.setMessage("success");
        return result;
    }

    public static <T> Result<T> ok(T data) {
        Result<T> result = new Result<>(data);
        result.setTimestamp(System.currentTimeMillis());
        result.setCode(200);
        result.setMessage("success");
        result.setData(data);
        return result;
    }

    public static <T> Result<T> error(int code, String message) {
        Result result = new Result();
        result.setTimestamp(System.currentTimeMillis());
        result.setCode(code);
        result.setMessage(message);
        return result;
    }
    public static <T> Result<T> error(String message) {
        Result result = new Result();
        result.setTimestamp(System.currentTimeMillis());
        result.setCode(500);
        result.setMessage(message);
        return result;
    }
}

解决方案

按照如下的方式设置:

1. 对jackson的Date的序列化和反序列化处理

spring:
  jackson:
    serialization:
      WRITE_DATES_AS_TIMESTAMPS: true
    time-zone: GMT+8 # 时区修改为东8区

2. 对spring参数Date的序列化处理和非Date的处理

@Configuration
public class CustomDateConverterConfig {

    /**
     * 入参-形参、@RequestParam和@PathVariable时候-字符串转Date相关类
     */
    @Component
    public static class LocalDateConvert implements Converter<String, LocalDate> {
        @Override
        public LocalDate convert(String timestamp) {
            return DateUtil.date(Long.parseLong(timestamp)).toLocalDateTime().toLocalDate();
        }
    }

    /**
     * 入参-形参、@RequestParam和@PathVariable时候-字符串转Date相关类
     */
    @Component
    public static class LocalDateTimeConvert implements Converter<String, LocalDateTime> {
        @Override
        public LocalDateTime convert(String timestamp) {
            return DateUtil.date(Long.parseLong(timestamp)).toLocalDateTime();
        }
    }

    /**
     * 入参-形参、@RequestParam和@PathVariable时候-字符串转Date相关类
     */
    @Component
    public static class DateConvert implements Converter<String, Date> {
        @Override
        public Date convert(String timestamp) {
            return DateUtil.date(Long.parseLong(timestamp)).toJdkDate();
        }
    }

    /**
     * 反序列化LocalDateTime
     */
    @Component
    public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
        @Override
        public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext) throws IOException {
            Long timestamp = p.getLongValue();
            Instant instant = Instant.ofEpochMilli(timestamp);
            return LocalDateTime.ofInstant(instant, ZoneId.of("+8"));
        }
    }

    /**
     * 序列化LocalDateTime为时间戳
     */
    @Component
    public static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
        @Override
        public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeNumber(value.toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
        }
    }

    /**
     * 反序列化LocalDate
     */
    @Component
    public static class LocalDateDeserializer extends JsonDeserializer<LocalDate> {
        @Override
        public LocalDate deserialize(JsonParser p, DeserializationContext deserializationContext) throws IOException {
            Long timestamp = p.getLongValue();
            Instant instant = Instant.ofEpochMilli(timestamp);
            LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("+8"));
            LocalDate localDate = localDateTime.toLocalDate();
            return localDate;
        }
    }

    /**
     * 序列化LocalDate为时间戳
     */
    @Component
    public static class LocalDateSerializer extends JsonSerializer<LocalDate> {
        @Override
        public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeNumber(value.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
        }
    }
    /**
     * Json序列化和反序列化转换器,用于转换Post请求体中的json以及将我们的对象序列化为返回响应的json
     */
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        //不显示为null的字段
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        // 忽略不能转移的字符
        objectMapper.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
        // 过滤对象的null属性.
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        //忽略transient
        objectMapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);

        objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

        //LocalDateTime系列序列化和反序列化模块,继承自jsr310,我们在这里修改了日期格式
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        // LocalDateTime
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
        // LocalDate
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer());
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer());

        objectMapper.registerModule(javaTimeModule);
        return objectMapper;
    }
}

测试结果

请求数据

POST http://localhost:8080/test/1651929101123?date=1651929101123&localDate=1651929101123&dateTime=1651929101123&originalDate=1651929101123&name=test&birth1=1651929101123&birth2=1651929101123
Content-Type: application/json

{
  "name": "test",
  "birth1": 1651929101123,
  "birth2": 1651929101123,
  "birth3": 1651929101123
}

返回结果

{
  "timestamp": 1651929035396,
  "message": "success",
  "code": 200,
  "data": 1651852800000
}