### 问题描述
想知道如何在以下场景中正确的使用LocalDateTime/LocalDate/LocalTime
项目前不久升级到Java8了, 新的业务模块都是采用Java8的语法或者API来实现, 比如Date
这个对象, 在Java8中, 我们就用了新的LocalDate
LocalDateTime
LocalTime
来替代.
今天在实现需求的时候, 需要修改一个RESTFul API接口, 新增了一个日期字段. 数据库中存储用的timestamp
, 代码中用的是LocalDateTime
, 格式为yyyy-MM-dd HH:mm:ss
.
但是前台展示以及编辑的时候, 用的是yyyy年MM月dd日
的格式.
将LocalDateTime
通过jackson转为yyyy年MM月dd日
是没有问题的.
但是将前端传回的字符串, 再通过jackson反序列化为LocalDateTime
对象时, jackson因为调用LocalDateTime
的实现导致报错.
因为jackson最终会使用
public static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter) {
Objects.requireNonNull(formatter, "formatter");
return formatter.parse(text, LocalDateTime::from);
}
这个方法, 其中LocalDateTime::from
的实现为
public static LocalDateTime from(TemporalAccessor temporal) {
if (temporal instanceof LocalDateTime) {
return (LocalDateTime) temporal;
} else if (temporal instanceof ZonedDateTime) {
return ((ZonedDateTime) temporal).toLocalDateTime();
} else if (temporal instanceof OffsetDateTime) {
return ((OffsetDateTime) temporal).toLocalDateTime();
}
try {
LocalDate date = LocalDate.from(temporal);
// 这一行因没有时间戳传入,导致报错
LocalTime time = LocalTime.from(temporal);
return new LocalDateTime(date, time);
} catch (DateTimeException ex) {
throw new DateTimeException("Unable to obtain LocalDateTime from TemporalAccessor: " +
temporal + " of type " + temporal.getClass().getName(), ex);
}
}
因为没有时间, 这就导致在运行
LocalTime time = LocalTime.from(temporal);
这行代码的时候, 程序就会报错, 最终Spring认定这个请求无效, 后台返回400
此外我也尝试使用jackson对LocalDate
的实现, 当然这种骚操作也是不行的.
@JsonSerialize(using = LocalDateSerializer.class)
@JsonDeserialize(using = LocalDateDeserializer.class)
private LocalDateTime someDateTime;
### 你期待的结果是什么?实际看到的错误信息又是什么?
用Date
太久了, 使用LocalDateTime
编程感觉有些许“水土不服”, 不知道各位有无方法能帮助一下我, 感谢!
其实吧,从你的问题描述来看,我觉得你对于
LocalDate
LocalDateTime
LocalTime
之间的区分以及Java8时间API的认识应该足够了,足够可以做一些简单使用的地步了,如果从我们旁观者角度来说,仅仅只是为了改你这个问题,其实之前回答的人都是可以的。比如上官元恒提到的直接把请求对象中的字段类型由
LocalDateTime
改为LocalDate
,这样肯定是可以的因为上官元恒在意的是请求报错的问题
而微凉提到的是
LocalDate
如何转换为LocalDateTime
,当然本质上LocalDate
和LocalDateTime
是不同,这种转换只是一家之言的转换方式,你可以根据自己的业务来做相应的修改或者就这样转也行总归微凉想提到的是前端只有年月日,但是你数据库是有时分秒的,所以他在意的是前端到入库这个转换过程,相当于对上官元恒的补充
两个回答的结合,应该就可以解决你的问题
但是可以感觉得出来,题主应该很不愿意把
LocalDateTime
改为LocalDate
。为啥呢?这样改代码改得多啊。因为由于数据库里是
timestamp
,所以多半实体对象中的类型就是LocalDateTime
,因此如果按照这两个回答的方式修改,肯定要多加一层转换了。加在业务层中的转换其实有时候很烦。要是不转换,直接用
jackson
全部搞定,岂不是美哉,其他代码都不用修改(当然都保不准请求对象就是实体对象都有可能,所以更不敢改,hhhhh)这也是为啥题主宁愿不改类型,硬是要用把
LocalDateDeserializer
塞到LocalDateTime
类型头上。当然我说这些不是埋汰题主哈。。我主要分析一下你的心理,希望能给你一个符合你心理预期的答案。
言归正传,不知道题主说硬使用
LocalDateDeserializer
这样的骚操作还是不行,这个不行的原因题主有注意么?我猜可能没注意,其实你使用

LocalDateDeserializer
这个反序列化操作是成功的,报错的根本原因是类型不匹配,也就是LocalDateDeserializer
其实最终把String -> LocalDate
,但是转换成功后的LocalDate
值需要被放到请求对象中啊,这个放置的操作用的是setter
方法,但由于类型不是LocalDateTime
么,所以反射操作失败了呗,这是才是失败的原因所以说
LocalDateDeserializer
是完全执行成功的,这种做法是可取的,只是呢跟我们后续的setter
操作不匹配而已。嘿嘿~ 所以你应该明白我想做啥了吧没错,咱们自己造一个
Deserializer
呗,让它返回类型为LocalDateTime
不就好么,最后用反射的setter
方法执行肯定也不会报错了啊因为官方是有一个
LocalDateTimeDeserializer
,所以我们取名就叫CustomLocalDateTimeDeserializer
,虽然咱们披着的名字里有LocalDateTime
,实际咱们处理的勾当跟LocalDateDeserializer
是一致的,只是根据LocalDateDeserializer
的处理之后呢,我们再按照题主自己的业务去转化为LocalDateTime
即可,我这里就简单采取微凉的方式,直接补齐00:00
好了最终
CustomLocalDateTimeDeserializer
的效果如下,代码我也放在一个github上吧,不然看起来很别扭(因为我加了很多注释说明,下面的代码去掉了注释)最后你使用的时候,当然就简单啦,直接
@JsonDeserialize
即可以上差不多就是全部答案,所以,拜了个拜o(^▽^)┛