SpringBoot日期时间全局出入参格式化-3:全局Timestamp出入参

SpringBoot时间出入参格式化-3:全局Timestamp出入参

上一篇中使用的是全局使用字符串处理时间参数。本文提供第三种处理方式:使用全局时间戳方式处理入参时间,如入参:1657096088961,这种方式同全局字符串处理方式类似,置于两者用哪种,看个人所需。

  • 优点:参数上不需要加任何注解,即可全局统一入参格式&出参格式
  • 缺点:时间报文不可读
传送链接:

代码实现:

定义相关常量:TimeConstants

package com.vip.tools.format.timestamp.constant;

/**
 * 时间转换常量
 *
 * @author wgb
 * @date 2021/1/15 15:00
 */
public interface TimeConstants {
    /**
     * 默认时间格式正则
     */
    String LOCAL_TIME_REGULAR = "^\\d{1,2}:\\d{1,2}:\\d{1,2}$";
    /**
     * 时间戳正则
     */
    String TIMESTAMP_REGULAR = "^\\d{13}$";
    /**
     * 默认时间格式
     */
    String DEFAULT_TIME_FORMAT = "HH:mm:ss";
}

转换类核心代码:TimestampToDateConverter

import com.vip.tools.format.timestamp.constant.TimeConstants;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.Nullable;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.Date;

/**
 * 自定义类型转换, @RequestParam/@PathVariable的日期字符串转换对应日期类型
 *
 * @author wgb
 * @date 2021/01/14 15:57
 */
@Configuration
public class TimestampToDateConverter<T> {

    @Bean(name = "localDateTimeConverter")
    public Converter<String, LocalDateTime> localDateTimeConverter() {
        return new Converter<String, LocalDateTime>() {
            @Override
            public LocalDateTime convert(@Nullable String source) {
                if(StringUtils.isBlank(source)){
                    return null;
                }
                if(!source.matches(TimeConstants.TIMESTAMP_REGULAR)){
                    throw new IllegalArgumentException("Invalid Time Value of Type String:" + source);
                }
                return new Date(Long.parseLong(source)).toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime();
            }
        };
    }

    @Bean(name = "localDateConverter")
    public Converter<String, LocalDate> localDateConverter() {
        return new Converter<String, LocalDate>() {
            @Override
            public LocalDate convert(@Nullable String source) {
                if(StringUtils.isBlank(source)){
                    return null;
                }
                if(!source.matches(TimeConstants.TIMESTAMP_REGULAR)){
                    throw new IllegalArgumentException("Invalid Time Value of Type String:" + source);
                }
                return new Date(Long.parseLong(source)).toInstant().atOffset(ZoneOffset.of("+8")).toLocalDate();
            }
        };
    }

    @Bean(name = "localTimeConverter")
    public Converter<String, LocalTime> localTimeConverter() {
        return new Converter<String, LocalTime>() {
            @Override
            public LocalTime convert(@Nullable String source) {
                if(StringUtils.isBlank(source)){
                    return null;
                }
                if(!source.matches(TimeConstants.LOCAL_TIME_REGULAR)){
                    throw new IllegalArgumentException("Invalid Time Value of Type String:" + source);
                }
                return LocalTime.parse(source);
            }
        };
    }

    @Bean(name = "dateConverter")
    public Converter<String, Date> dateConverter() {
        return new Converter<String, Date>() {
            @Override
            @SneakyThrows
            public Date convert(@Nullable String source) {
                if (StringUtils.isBlank(source)) {
                    return null;
                }
                if (!source.matches(TimeConstants.TIMESTAMP_REGULAR)) {
                    throw new IllegalArgumentException("Invalid Time Value of Type String:" + source);
                }
                return new Date(Long.parseLong(source));
            }
        };
    }

}

Jackson参数转换器配置

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.vip.tools.format.timestamp.constant.TimeConstants;
import lombok.SneakyThrows;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Date;

/**
 * 使用官方自带的json格式类库
 * 可以处理多种格式化方式,这里只使用了时间格式化
 *
 * @author wgb
 * @date 2020/11/25 18:07
 */
public class JacksonHttpMessageConverter extends MappingJackson2HttpMessageConverter {

    public JacksonHttpMessageConverter() {
        handleDateTime();
    }

    private void handleDateTime() {
        ObjectMapper objectMapper = this.getObjectMapper();
        // Date出入参格式化
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // Java8时间格式化
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        // 出参格式化
        javaTimeModule.addSerializer(LocalDateTime.class, new MyLocalDateTimeSerializer());
        javaTimeModule.addSerializer(LocalDate.class, new MyLocalDateSerializer());
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(TimeConstants.DEFAULT_TIME_FORMAT)));
        // 入参格式化
        javaTimeModule.addDeserializer(LocalDateTime.class, new MyLocalDateTimeDeserializer());
        javaTimeModule.addDeserializer(LocalDate.class, new MyLocalDateDeserializer());
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TimeConstants.DEFAULT_TIME_FORMAT)));
        // 把“忽略重复的模块注册”禁用,否则下面的注册(LocalDateTime)不生效
        objectMapper.disable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS);
        objectMapper.registerModule(javaTimeModule);
        // 然后再设置为生效,避免被其他地方覆盖
        objectMapper.enable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS);
        // 设置格式化内容
        this.setObjectMapper(objectMapper);
    }

    /**
     * LocalDateTime序列化为时间戳
     */
    public static class MyLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
        @Override
        @SneakyThrows
        public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) {
            jsonGenerator.writeNumber(localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
        }
    }

    /**
     * LocalDate序列化时为时间戳
     */
    public static class MyLocalDateSerializer extends JsonSerializer<LocalDate> {
        @Override
        @SneakyThrows
        public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) {
            jsonGenerator.writeNumber(localDate.atStartOfDay(ZoneOffset.ofHours(8)).toInstant().toEpochMilli());
        }
    }

    /**
     * 时间戳反序列化为LocalDateTime
     */
    public static class MyLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
        @Override
        @SneakyThrows
        public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
            return LocalDateTime.ofEpochSecond(jsonParser.getLongValue() / 1000, 0, ZoneOffset.ofHours(8));
        }
    }

    /**
     * 时间戳反序列化为LocalDate
     */
    public static class MyLocalDateDeserializer extends JsonDeserializer<LocalDate> {
        @Override
        @SneakyThrows
        public LocalDate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
            return new Date(jsonParser.getLongValue()).toInstant().atOffset(ZoneOffset.of("+8")).toLocalDate();
        }
    }

}

WebMvcConfiguration

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
import java.util.List;

/**
 * WebMvcConfiguration
 *
 * @author wgb
 * @date 2020/11/25 16:45
 */
@Configuration
@RequiredArgsConstructor
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

    private final Converter<String, LocalDateTime> localDateTimeConverter;
    private final Converter<String, LocalDate> localDateConverter;
    private final Converter<String, LocalTime> localTimeConverter;
    private final Converter<String, Date> dateConverter;

    /**
     * 入参时间格式化
     */
    @Override
    protected void addFormatters(FormatterRegistry registry) {
        registry.addConverter(localDateTimeConverter);
        registry.addConverter(localDateConverter);
        registry.addConverter(localTimeConverter);
        registry.addConverter(dateConverter);
    }

    /**
     * extends WebMvcConfigurationSupport
     * 以下 spring-boot: jackson时间格式化 配置 将会失效
     * spring.jackson.time-zone=GMT+8
     * spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
     * 原因: 会覆盖 @EnableAutoConfiguration 关于 WebMvcAutoConfiguration 的配置
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.configureMessageConverters(converters);
        JacksonHttpMessageConverter converter = new JacksonHttpMessageConverter();
        converters.add(converter);
        // 添加二进制数组转换器:用以文件下载时二进制流的响应
        converters.add(new ByteArrayHttpMessageConverter());
    }

}

开始写测试代码

DTO
import lombok.Data;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;

/**
 * description
 *
 * @author wgb
 * @date 2021/1/14 15:57
 */
@Data
public class TinyDTO {
    /**
     * date
     */
    private Date date;

    /**
     * localDate
     */
    private LocalDate localDate;

    /**
     * localDateTime
     */
    private LocalDateTime localDateTime;

    /**
     * localTime
     */
    private LocalTime localTime;

    /**
     * 附加属性
     */
    private String name;

}

controller

import com.vip.tools.format.timestamp.dto.TinyDTO;
import com.vip.tools.normal.UuidUtils;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;

/**
 * 时间格式化 控制器
 *
 * @author wgb
 * @date 2021/1/15 13:25
 */
@RestController
@RequestMapping(value = "/timestamp_format")
public class TimestampDateFormatController {
    /**
     * 格式化测试
     *
     * @return
     */
    @PostMapping("/test")
    public TinyDTO format(@RequestParam Date date, @RequestParam LocalDate localDate, @RequestParam LocalDateTime localDateTime
            , @RequestParam LocalTime localTime, @RequestBody TinyDTO dto) {
        System.out.println(date);
        System.out.println(localDate);
        System.out.println(localDateTime);
        System.out.println(localTime);
        System.out.println(dto);
        return dto;
    }

}

测试

URL: http://localhost:9600/timestamp_format/test?date=1610595117250&localDate=1610595117250&localDateTime=1610595117250&localTime=12:00:23
Body参数:

{
	"date": 1610595117250,
	"localDate": 1610595117250,
	"localDateTime": 1610595117250,
	"localTime": "23:59:59",
	"name": "TEST"
}

控制台打印:

Thu Jan 14 11:31:57 CST 2021
2021-01-14
2021-01-14T11:31:57.250
12:00:23
TinyDTO(date=Thu Jan 14 11:31:57 CST 2021, localDate=2021-01-14, localDateTime=2021-01-14T11:31:57, localTime=23:59:59, name=TEST)

响应结果:

{
	"date": 1610595117250,
	"localDate": 1610553600000,
	"localDateTime": 1610595117000,
	"localTime": "23:59:59",
	"name": "TEST"
}

日期对象全局字符串出入参配置完成。
总结: 以上介绍了三种时间参数转换的方式,全注解方式是最不推荐的,推荐使用全局字符串转换方式,这样便于前端格式化日期格式,以及时间可阅读,全局时间戳转换方式按需使用。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值