新项目里使用LocalDateTime遇到了一些坑,总结一下,本文章以全局配置方式解决了以下问题ヽ( ̄▽ ̄)ノ:
0、概念介绍:前端参数到后端先反序列化解析后执行controller内方法,后端数据出controller先序列化后给前端,具体相关概念请咨询战略合作伙伴(ノ゚∀゚)ノ 百度一下
1、数据库和后端的日期差:请在数据库链接上加上&serverTimezone=Asia/Shanghai。
2、后端无法接收数据库参数:大概率是数据库连接池包版本太低了,不支持LocalDateTime,我的是druid,看下边4.的包版本。
3、前端post请求到后端参数无法解析、差8个小时,看下边的1.-4.的反序列化
4、返回前端带T等各种奇葩格式统一格式化,看下边1.-4.的序列化
5、前端get请求到后端参数无法解析,看下边5.
6、讨厌全局配置?保证包的新鲜度下,看下边6.
7、时间戳的话,请改写反序列化方法~~我没用到哦 (=´ω`=)
2022-02-21新增:
8、遇到了个超级奇葩问题:后端远程访问后端接口,以实体类list做参数,同一个实体类list接,不知道由于有空值参数还是啥原因,在接收的时候LocalDateTime那个字段值丢失了,而且还会带空值进入反序列化方法里,这里给一个解决方法:用 com.alibaba.fastjson 的JSON.parseObject(JSON.toJSONString(你的实体类列表), List.class)作为参数发送出去,它会把所有null都干掉而且丢参问题就没有了。
2022-03-08新增:
9、综合8经测试发现:RestTemplate远程访问接收返回的数据的时候并不走咱们自定义的反序列化配置,因为咱们没告诉它走。。解决方案见新帖吧,因为把整个方法改写了一下:LocalDateTime无法解析新的解决方案

后续还有的话,再更新。
如果有问题或者有问题,欢迎留言!我会及时更新和回复哒(^_−)☆~

  1. LocalDateFormatConfig.java --将序列化和反序列化丢到jackson里
package com.zoomlgd.config;

import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

/**
 * @author DepengZhang
 * @date 2021年12月03日 18:41
 */
@Configuration
public class LocalDateFormatConfig {

	/**
	 * Date格式化字符串 yyyy-MM-dd
	 */
	private static final String DATE_FORMAT = DatePattern.NORM_DATE_PATTERN;

	/**
	 * Time格式化字符串 HH:mm:ss
	 */
	private static final String TIME_FORMAT = DatePattern.NORM_TIME_PATTERN;

	@Bean
	@Primary
	public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
		return builder -> {
			builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer())
					.serializerByType(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)))
					.serializerByType(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(TIME_FORMAT)))
					.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer())
					.deserializerByType(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_FORMAT)))
					.deserializerByType(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TIME_FORMAT)));
		};
	}

}

  1. LocalDateTimeDeserializer.java --反序列化定制类
package com.zoomlgd.config;

import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import org.apache.commons.lang.StringUtils;

import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;

/**
 * @author DepengZhang
 * @date 2021年12月11日 0:08
 */
public class LocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {
	/**
	 * DateTime格式化字符串 yyyy-MM-dd HH:mm:ss
	 */
	private static final String DATETIME_FORMAT = DatePattern.NORM_DATETIME_PATTERN;
	private static final ZoneId DEFAULT_ZONE_ID = ZoneId.of("UTC");
	private static final ZoneId SYSTEM_ZONE_ID = ZoneId.of("Asia/Shanghai");
	private static final String T = "T";
	private static final String Z = "Z";

	public LocalDateTimeDeserializer() {
		super(LocalDateTime.class);
	}

	@Override
	public LocalDateTime deserialize(final JsonParser parser,
									 final DeserializationContext context) throws IOException {
		String value = parser.getValueAsString();
		// 处理标准格式的时间
		if (StringUtils.isNotBlank(value) && value.length() == DATETIME_FORMAT.length()) {
			if (!value.contains(T) || !value.endsWith(Z)) {
				StringBuilder str = new StringBuilder(value);
				str.replace(10, 11, T);
				str.append(Z);
				return LocalDateTime.ofInstant(Instant.parse(str.toString()), DEFAULT_ZONE_ID);
			}
		}
		// 处理UTC格式的时间
		return LocalDateTime.ofInstant(Instant.parse(value), DEFAULT_ZONE_ID)
				.atZone(DEFAULT_ZONE_ID).withZoneSameInstant(SYSTEM_ZONE_ID).toLocalDateTime();
	}
}


  1. LocalDateTimeSerializer.java --序列化定制
package com.zoomlgd.config;

import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

/**
 * @author DepengZhang
 * @date 2021年12月11日 0:03
 */
public class LocalDateTimeSerializer extends StdSerializer<LocalDateTime> {

	/**
	 * DateTime格式化字符串 yyyy-MM-dd HH:mm:ss
	 */
	private static final String DATETIME_FORMAT = DatePattern.NORM_DATETIME_PATTERN;

	public LocalDateTimeSerializer() {
		super(LocalDateTime.class);

	}

	@Override
	public void serialize(final LocalDateTime value,
						  final JsonGenerator generator,
						  final SerializerProvider provider) throws IOException {
		if (value != null) {
			generator.writeString(value.format(DateTimeFormatter.ofPattern(DATETIME_FORMAT)));
		} else {
			generator.writeNull();
		}
	}

}

  1. 使用的包,这里多了一个druild包,因为低版本的不支持LocalDateTime
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.17</version>
        </dependency>
        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.18</version>
        </dependency>
  1. GlobalExceptionHandler.java --为了支持Get的参数,还需要加上一个专门的配置方法
    // 2021-12-29添加判空逻辑- -之前忘记加了
package com.zoomlgd.config;

import cn.hutool.core.date.DatePattern;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;

import java.beans.PropertyEditorSupport;
import java.time.*;
import java.time.format.DateTimeFormatter;

/**
 * @author DepengZhang
 * @date 2021年12月23日 17:06
 */
@ControllerAdvice
public class GlobalExceptionHandler {

	/**
	 * Date格式化字符串 yyyy-MM-dd
	 */
	private static final String DATE_FORMAT = DatePattern.NORM_DATE_PATTERN;

	/**
	 * Time格式化字符串 HH:mm:ss
	 */
	private static final String TIME_FORMAT = DatePattern.NORM_TIME_PATTERN;

	/**
	 * DateTime格式化字符串 yyyy-MM-dd HH:mm:ss
	 */
	private static final String DATETIME_FORMAT = DatePattern.NORM_DATETIME_PATTERN;
	private static final ZoneId DEFAULT_ZONE_ID = ZoneId.of("UTC");
	private static final ZoneId SYSTEM_ZONE_ID = ZoneId.of("Asia/Shanghai");
	private static final String T = "T";
	private static final String Z = "Z";

	@InitBinder
	protected void initBinder(WebDataBinder binder) {
		binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
			@Override
			public void setAsText(String text) throws IllegalArgumentException {
				if (StringUtils.isNotBlank(text)) {
					setValue(LocalDate.parse(text, DateTimeFormatter.ofPattern(DATE_FORMAT)));
				} else {
					setValue(null);
				}
			}
		});
		binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport() {
			@Override
			public void setAsText(String text) throws IllegalArgumentException {
				if (StringUtils.isNotBlank(text)) {
					if (text.length() == DATETIME_FORMAT.length()) {
						if (!text.contains(T) || !text.endsWith(Z)) {
							StringBuilder str = new StringBuilder(text);
							str.replace(10, 11, T);
							str.append(Z);
							setValue(LocalDateTime.ofInstant(Instant.parse(str.toString()), DEFAULT_ZONE_ID));
						}
					} else {
						setValue(LocalDateTime.ofInstant(Instant.parse(text), DEFAULT_ZONE_ID)
								.atZone(DEFAULT_ZONE_ID).withZoneSameInstant(SYSTEM_ZONE_ID).toLocalDateTime());
					}
				} else {
					setValue(null);
				}
			}
		});
		binder.registerCustomEditor(LocalTime.class, new PropertyEditorSupport() {
			@Override
			public void setAsText(String text) throws IllegalArgumentException {
				if (StringUtils.isNotBlank(text)) {
					setValue(LocalTime.parse(text, DateTimeFormatter.ofPattern(TIME_FORMAT)));
				} else {
					setValue(null);
				}
			}
		});
	}
}


  1. 如果你不想用全局配置解决以上问题,可以参考这里ヾ(✿゚▽゚)ノ
  • yml配置文件全局序列化返回格式,使用上边全局配置应该是失效的,感兴趣可以自己测试一下。
spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  • 在实体类字段上加注释,DateTimeFormate管反序列化,JsonFormat管序列化。
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime yourDate;
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐