自学内容网 自学内容网

自定义日期转换配置

1.日期问题出现原因以及解决方案概述

1.图示

CleanShot 2024-10-26 at 19.47.19@2x

2.三种解决方案概述
1.对于表单数据 application/x-www-form-urlencoded

使用@InitBinder将日期JSON字符串转化为Java中的Date对象

2.对于JSON数据
1.使用@JsonFormat注解

使用@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”, timezone = “GMT+8”)注解指定反序列化和序列化的格式

2.自定义Jackson日期转换配置

2.解决方案

common-web-starter
1.目录

CleanShot 2024-10-26 at 19.54.33@2x

2.BaseController.java 使用@InitBinder解决表单数据的日期转换问题
package com.sunxiansheng.web.base;

import com.sunxiansheng.web.response.RespBeanEnum;
import com.sunxiansheng.web.response.Result;
import com.sunxiansheng.web.utils.DateUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;

import java.beans.PropertyEditorSupport;
import java.util.Date;

/**
 * Description: Controller层基类
 *
 * @Author sun
 * @Create 2024/10/26 09:48
 * @Version 1.0
 */
public class BaseController {

    /**
     * 将前台传递过来的日期格式的字符串,自动转化为Date类型
     * 注意:只对表单参数(application/x-www-form-urlencoded)或查询参数生效,对于json格式的请求不生效
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        // Date 类型转换
        binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) {
                setValue(DateUtils.parseDate(text));
            }
        });
    }

    /**
     * 成功
     *
     * @return
     */
    public Result<Object> ok() {
        return Result.ok();
    }

    /**
     * 失败
     *
     * @return
     */
    public Result<Object> fail() {
        return Result.fail();
    }

    /**
     * 失败,自定义消息
     *
     * @param message
     * @return
     */
    public Result<Object> fail(String message) {
        return Result.fail(message);
    }

    /**
     * 失败,自定义状态码和消息
     *
     * @param code
     * @param message
     * @return
     */
    public Result<Object> fail(int code, String message) {
        return Result.fail(code, message);
    }

    /**
     * 失败,自定义枚举
     *
     * @param respBeanEnum
     * @return
     */
    public Result<Object> fail(RespBeanEnum respBeanEnum) {
        return Result.fail(respBeanEnum);
    }
}
3.JacksonConfig.java 自定义Jackson日期转换配置
package com.sunxiansheng.web.config;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

/**
 * 自定义日期转换配置:优先级总结
 * <p>
 * 1.   @JsonFormat 注解:最高优先级。
 * 2.   自定义序列化和反序列化器:优先于全局配置,但在检测到 @JsonFormat 时会让位于注解逻辑。
 * 3.   jacksonObjectMapperBuilder.simpleDateFormat():默认全局配置,优先级最低。
 *
 * @author sunxiansheng
 */
@Configuration
@ConditionalOnClass(com.fasterxml.jackson.databind.ObjectMapper.class)
@AutoConfigureBefore(JacksonAutoConfiguration.class)
public class JacksonConfig {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customizer() {
        return jacksonObjectMapperBuilder -> {
            // 设置地区为中国,确保格式符合中文区域习惯
            jacksonObjectMapperBuilder.locale(Locale.CHINA);

            // 设置时区为系统默认时区,避免时区偏差
            jacksonObjectMapperBuilder.timeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));

            /**
             *  •  全局设置默认的日期格式,作用于 Date 类型的字段(不包括 LocalDate、LocalDateTime 等 Java 8 时间类)。
             *  •  只影响 Jackson 自动序列化和反序列化时的默认行为。
             *  •  如果在实体类的字段上没有使用 @JsonFormat 注解,Jackson 会采用这里设置的格式。
             */
            jacksonObjectMapperBuilder.simpleDateFormat("yyyy-MM-dd HH:mm:ss");

            // 注册自定义 JavaTimeModule 模块,处理 Java 8 日期时间类型和 Date 类型
            jacksonObjectMapperBuilder.modules(new JavaTimeModule());
        };
    }

    /**
     * JavaTimeModule 是一个自定义的 Jackson 模块,用于序列化和反序列化 Java 8 日期时间类型。
     * •    覆盖默认的 Jackson 序列化和反序列化行为,为 Date 类型字段提供自定义逻辑。
     * •    允许支持多个格式的反序列化,例如通过自定义的 CustomDateDeserializer。
     */
    public static class JavaTimeModule extends SimpleModule {
        public JavaTimeModule() {
            // 添加 LocalDateTime 类型的序列化和反序列化器,确保格式为 "yyyy-MM-dd HH:mm:ss"
            this.addSerializer(LocalDateTime.class,
                    new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            this.addDeserializer(LocalDateTime.class,
                    new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

            // 添加 LocalDate 类型的序列化和反序列化器,确保格式为 "yyyy-MM-dd"
            this.addSerializer(LocalDate.class,
                    new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            this.addDeserializer(LocalDate.class,
                    new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));

            // 添加 LocalTime 类型的序列化和反序列化器,确保格式为 "HH:mm:ss"
            this.addSerializer(LocalTime.class,
                    new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
            this.addDeserializer(LocalTime.class,
                    new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));

            // 添加对 Date 类型的自定义序列化和反序列化,这里会覆盖上面默认的Date序列化器配置
            this.addSerializer(Date.class,
                    new com.fasterxml.jackson.databind.ser.std.DateSerializer(false, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")));
            this.addDeserializer(Date.class, new CustomDateDeserializer());
        }
    }

    /**
     * 自定义的 Date 反序列化器,支持多种格式的日期字符串解析。
     */
    public static class CustomDateDeserializer extends JsonDeserializer<Date> {

        // 定义支持的日期格式列表
        private static final List<String> DATE_PATTERNS = Arrays.asList(
                "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
                "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
                "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"
        );

        @Override
        public Date deserialize(JsonParser p, com.fasterxml.jackson.databind.DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            String dateStr = p.getText().trim();
            for (String pattern : DATE_PATTERNS) {
                try {
                    // 尝试使用每个格式进行解析
                    return new SimpleDateFormat(pattern).parse(dateStr);
                } catch (ParseException e) {
                    // 如果解析失败,继续尝试下一个格式
                }
            }
            // 如果所有格式都不匹配,抛出异常
            throw new IOException("Invalid date format: " + dateStr);
        }
    }
}
4.DateUtils.java 日期转换工具类
package com.sunxiansheng.web.utils;

import org.apache.commons.lang3.time.DateFormatUtils;

import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.util.Date;

/**
 * 时间工具类
 */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {

    public static String YYYY = "yyyy";

    public static String YYYY_MM = "yyyy-MM";

    public static String YYYY_MM_DD = "yyyy-MM-dd";

    public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";

    public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";

    private static String[] parsePatterns = {
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};

    /**
     * 获取当前Date型日期
     *
     * @return Date() 当前日期
     */
    public static Date getNowDate() {
        return new Date();
    }

    /**
     * 获取当前日期, 默认格式为yyyy-MM-dd
     *
     * @return String
     */
    public static String getDate() {
        return dateTimeNow(YYYY_MM_DD);
    }

    public static final String getTime() {
        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
    }

    public static final String dateTimeNow() {
        return dateTimeNow(YYYYMMDDHHMMSS);
    }

    public static final String dateTimeNow(final String format) {
        return parseDateToStr(format, new Date());
    }

    public static final String dateTime(final Date date) {
        return parseDateToStr(YYYY_MM_DD, date);
    }

    public static final String parseDateToStr(final String format, final Date date) {
        return new SimpleDateFormat(format).format(date);
    }

    public static final Date dateTime(final String format, final String ts) {
        try {
            return new SimpleDateFormat(format).parse(ts);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 日期路径 即年/月/日 如2018/08/08
     */
    public static final String datePath() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyy/MM/dd");
    }

    /**
     * 日期路径 即年/月/日 如20180808
     */
    public static final String dateTime() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyyMMdd");
    }

    /**
     * 日期型字符串转化为日期 格式
     */
    public static Date parseDate(Object str) {
        if (str == null) {
            return null;
        }
        try {
            return parseDate(str.toString(), parsePatterns);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 获取服务器启动时间
     */
    public static Date getServerStartDate() {
        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
        return new Date(time);
    }

    /**
     * 计算相差天数
     */
    public static int differentDaysByMillisecond(Date date1, Date date2) {
        return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
    }

    /**
     * 计算时间差
     *
     * @param endDate   最后时间
     * @param startTime 开始时间
     * @return 时间差(天/小时/分钟)
     */
    public static String timeDistance(Date endDate, Date startTime) {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        // long ns = 1000;
        // 获得两个时间的毫秒时间差异
        long diff = endDate.getTime() - startTime.getTime();
        // 计算差多少天
        long day = diff / nd;
        // 计算差多少小时
        long hour = diff % nd / nh;
        // 计算差多少分钟
        long min = diff % nd % nh / nm;
        // 计算差多少秒//输出结果
        // long sec = diff % nd % nh % nm / ns;
        return day + "天" + hour + "小时" + min + "分钟";
    }

    /**
     * 增加 LocalDateTime ==> Date
     */
    public static Date toDate(LocalDateTime temporalAccessor) {
        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
        return Date.from(zdt.toInstant());
    }

    /**
     * 增加 LocalDate ==> Date
     */
    public static Date toDate(LocalDate temporalAccessor) {
        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
        ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
        return Date.from(zdt.toInstant());
    }
}
5.WebAutoConfiguration.java 导入JacksonConfig配置类

CleanShot 2024-10-26 at 19.56.25@2x

6.spring.factories 指定自动配置类

CleanShot 2024-10-26 at 19.57.13@2x


原文地址:https://blog.csdn.net/m0_64637029/article/details/145096742

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!