时间出入参格式化-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"
}

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容