解决LocalDateTime 时间格式中间带’T’返回的问题(小心踩坑)
解决LocalDateTime 时间格式中间带’T’返回的问题
前言:在一次工作开发过程中,LocalDateTime 类型的字段返回给前端带着T,然后被测试无情的提了一个bug,然后只能使用百度大法了,结果发现网上全是一堆相同的答案,且是无效无用的,只能自己实践整理一版有效的使用出来。
PS:要看自己的项目是数据序列化是不是用fastJson序列化(很重要,因为这个导致很多正常有效的都变无效了,我就是踩这个坑)
首先我们先了解Date和LocalDateTime 的源码返回格式
下面是Date的toString方法的源码,里面返回的字符格式就是日常使用的
public String toString() {
// "EEE MMM dd HH:mm:ss zzz yyyy";
BaseCalendar.Date date = normalize();
StringBuilder sb = new StringBuilder(28);
int index = date.getDayOfWeek();
if (index == BaseCalendar.SUNDAY) {
index = 8;
}
convertToAbbr(sb, wtb[index]).append(' '); // EEE
convertToAbbr(sb, wtb[date.getMonth() - 1 + 2 + 7]).append(' '); // MMM
CalendarUtils.sprintf0d(sb, date.getDayOfMonth(), 2).append(' '); // dd
CalendarUtils.sprintf0d(sb, date.getHours(), 2).append(':'); // HH
CalendarUtils.sprintf0d(sb, date.getMinutes(), 2).append(':'); // mm
CalendarUtils.sprintf0d(sb, date.getSeconds(), 2).append(' '); // ss
TimeZone zi = date.getZone();
if (zi != null) {
sb.append(zi.getDisplayName(date.isDaylightTime(), TimeZone.SHORT, Locale.US)); // zzz
} else {
sb.append("GMT");
}
sb.append(' ').append(date.getYear()); // yyyy
return sb.toString();
}
LocalDateTime的toString方法的源码,会把T放在年月日和时分秒的中间,所以会出现带’T’的字符串
@Override
public String toString() {
return date.toString() + 'T' + time.toString();
}
所以如果,后端想返回给前端不带’T’的时间格式,想省事就直接把LocalDateTime类型改为Date类型,然后在字段上加上注解即可
@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”,timezone=“GMT+8”)
但是,这不是我们想要的,如果我们就是想用LocalDateTime类型字段返回给前端,那应该怎么办呢。
一、使用字符串替(不推荐,很low)
前面我们可以知道,LocalDateTime源码是会自动把T加上去的,所以我们要做的比较简单,就是把T替换成空格即可
二、使用注解(处理局部实体对象返回)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
三、使用全局配置统一处理(对日期数据序列化和反序列化处理)
方式一:
import cn.hutool.core.date.DatePattern;
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.LocalDateTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
@Configuration
public class LocalDateTimeConfig {
@Bean
@Primary
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer(){
//日期转字符串
return builder -> builder.serializerByType(LocalDate.class,new LocalDateTimeSerializer(DatePattern.NORM_DATE_FORMATTER))
.serializerByType(LocalDateTime.class,new LocalDateTimeSerializer(DatePattern.NORM_DATETIME_FORMATTER))
.serializerByType(LocalTime.class,new LocalDateTimeSerializer(DatePattern.NORM_TIME_FORMATTER))
//字符串转日期
.deserializerByType(LocalDateTime.class,new LocalDateTimeDeserializer(DatePattern.NORM_DATETIME_FORMATTER))
.deserializerByType(LocalDate.class,new LocalDateDeserializer(DatePattern.NORM_DATE_FORMATTER))
.deserializerByType(LocalTime.class,new LocalTimeDeserializer(DatePattern.NORM_TIME_FORMATTER));
}
}
方式二:
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Configuration
public class LocalDateTimeConfig {
@Bean
public LocalDateTimeSerializer localDateTimeSerializer(){
return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer(){
return e -> e.serializerByType(LocalDateTime.class,localDateTimeSerializer());
}
}
四、总结(很重要,踩坑)
在我们实际生产项目中,后端返回给前端的数据都是做统一的格式化处理,例如用到fastjson进行格式化,此时有可能会导致上面的一些方式失效,例如下面就是用JSON.toJSONString将localDateTime日期转换,
public static String toString(Object data) {
return JSON.toJSONString(data, features);
}
/**
* 可以看到fastJson.toJSONString的序列化LocalDateTime的格式是带T的
*/
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
...
if (fieldType == LocalDateTime.class) {
int mask = SerializerFeature.UseISO8601DateFormat.getMask();
LocalDateTime dateTime = (LocalDateTime)object;
String format = serializer.getDateFormatPattern();
if (format == null) {
if ((features & mask) == 0 && !serializer.isEnabled(SerializerFeature.UseISO8601DateFormat)) {
int nano = dateTime.getNano();
if (nano == 0) {
format = "yyyy-MM-dd'T'HH:mm:ss";
} else if (nano % 1000000 == 0) {
format = "yyyy-MM-dd'T'HH:mm:ss.SSS";
} else {
format = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS";
}
} else {
format = "yyyy-MM-dd'T'HH:mm:ss";
}
}
....
}
}
如果如果上面的方法不生效的话,可以用下面的方法尝试下
使用@JSONField代替@JsonFormat
@JSONField(format = "yyyy-MM-dd HH:mm:ss")