Java 中 String 转 LocalDateTime 出现错误

Java 中 String 转 LocalDateTime 出现错误

场景

在 Java 中使用 LocalDateTime 解析 String 失败

代码如下

1
2
final LocalDateTime result = LocalDateTime.parse("2000-01-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
log.info("result: {}", result);

抛出异常

1
java.time.format.DateTimeParseException: Text '2000-01-01' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 2000-01-01 of type java.time.format.Parsed

吾辈也在 SegmentFault 上提出了这个问题,然而直到写出这篇记录时然而没有人告诉吾辈答案。。。

解决

先转换为 LocalDate 再二次转换

吾辈首先找到了一种笨方法

  1. 先解析为 LocalDate
  2. LocalDate 转换为 LocalDateTime
1
2
3
final LocalDateTime localDateTime = LocalDate.parse("2018-12-11", DateTimeFormatter.ISO_DATE).atStartOfDay();
assertThat(localDateTime)
.isNotNull();

使用 DateTimeFormatter 先解析,然后转换为 LocalDateTime

  1. 使用 DateTimeFormatter.ISO_DATE 解析文本并得到 TemporalAccessor 对象
  2. 使用 temporalAccessor.get 方法获取指定属性
  3. 使用 LocalDateTime.of 构造一个 LocalDateTime 对象
1
2
3
4
5
6
7
8
9
10
11
final TemporalAccessor temporalAccessor = DateTimeFormatter.ISO_DATE.parse("2018-12-11");
final LocalDateTime localDateTime = LocalDateTime.of(
secureGet(temporalAccessor, ChronoField.YEAR),
secureGet(temporalAccessor, ChronoField.MONTH_OF_YEAR),
secureGet(temporalAccessor, ChronoField.DAY_OF_MONTH),
secureGet(temporalAccessor, ChronoField.HOUR_OF_DAY),
secureGet(temporalAccessor, ChronoField.MINUTE_OF_HOUR),
secureGet(temporalAccessor, ChronoField.SECOND_OF_MINUTE),
secureGet(temporalAccessor, ChronoField.NANO_OF_SECOND)
);
log.info("localDateTime: {}", localDateTime);

secureGet 是吾辈自定义的一个工具方法,具体看下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 安全获取时间的某个属性
*
* @param temporalAccessor 需要获取的时间对象
* @param chronoField 需要获取的属性
* @return 时间的值,如果无法获取则默认为 0
*/
private static int secureGet(TemporalAccessor temporalAccessor, ChronoField chronoField) {
if (temporalAccessor.isSupported(chronoField)) {
return temporalAccessor.get(chronoField);
}
return 0;
}

使用 DateTimeFormatterBuilder 构建器

吾辈在 StackOverflow 找到了一个好的方法

  1. 使用 DateTimeFormatterBuilder 构建 DateTimeFormatter 对象
  2. 赋予可选匹配项默认值(非常重要
  3. 使用 LocalDateTime.parse 进行解析
1
2
3
4
5
6
7
8
9
10
final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd[['T'hh][:mm][:ss]]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.parseDefaulting(ChronoField.MILLI_OF_SECOND, 0)
.toFormatter();
final LocalDateTime localDateTime = LocalDateTime.parse("2018-12-11", formatter);
assertThat(localDateTime)
.isNotNull();

最后一种方法满足了吾辈的需求,所以,也便是在这里记录一下啦