如何使用默认区域解析ZonedDateTime?

如何从不包含zone和其他字段的字符串解析ZoneDateTime

这是在Spock中重现的测试:

 import spock.lang.Specification import spock.lang.Unroll import java.time.ZoneId import java.time.ZoneOffset import java.time.ZonedDateTime import java.time.format.DateTimeFormatter @Unroll class ZonedDateTimeParsingSpec extends Specification { def "DateTimeFormatter.ISO_ZONED_DATE_TIME parsing incomplete date: #value #expected"() { expect: ZonedDateTime.parse(value, DateTimeFormatter.ISO_ZONED_DATE_TIME) == expected where: value | expected '2014-04-23T04:30:45.123Z' | ZonedDateTime.of(2014, 4, 23, 4, 30, 45, 123_000_000, ZoneOffset.UTC) '2014-04-23T04:30:45.123+01:00' | ZonedDateTime.of(2014, 4, 23, 4, 30, 45, 123_000_000, ZoneOffset.ofHours(1)) '2014-04-23T04:30:45.123' | ZonedDateTime.of(2014, 4, 23, 4, 30, 45, 123_000_000, ZoneId.systemDefault()) '2014-04-23T04:30' | ZonedDateTime.of(2014, 4, 23, 4, 30, 0, 0, ZoneId.systemDefault()) '2014-04-23' | ZonedDateTime.of(2014, 4, 23, 0, 0, 0, 0, ZoneId.systemDefault()) } } 

前两个测试通过,其他所有测试都失败了DateTimeParseException:

  • ‘2014-04-23T04:30:45.123’无法在索引23处解析
  • ‘2014-04-23T04:30’无法在索引16处解析
  • ‘2014-04-23’无法在索引10处解析

如何使用设置为默认值的时间和区域解析不完整的日期?

由于ISO_ZONED_DATE_TIME格式化程序需要区域或偏移量信息,因此解析失败。 您必须创建一个DateTimeFormatter ,它包含区域信息和时间部分的可选部分。 逆向工程ZonedDateTimeFormatter并添加可选标签并不太难。

然后使用parseBest()方法解析String 。 然后,对于次优的解析结果,您可以使用您想要的任何默认值创建ZonedDateTime

 DateTimeFormatter formatter = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_DATE) .optionalStart() // time made optional .appendLiteral('T') .append(ISO_LOCAL_TIME) .optionalStart() // zone and offset made optional .appendOffsetId() .optionalStart() .appendLiteral('[') .parseCaseSensitive() .appendZoneRegionId() .appendLiteral(']') .optionalEnd() .optionalEnd() .optionalEnd() .toFormatter(); TemporalAccessor temporalAccessor = formatter.parseBest(value, ZonedDateTime::from, LocalDateTime::from, LocalDate::from); if (temporalAccessor instanceof ZonedDateTime) { return ((ZonedDateTime) temporalAccessor); } if (temporalAccessor instanceof LocalDateTime) { return ((LocalDateTime) temporalAccessor).atZone(ZoneId.systemDefault()); } return ((LocalDate) temporalAccessor).atStartOfDay(ZoneId.systemDefault()); 

格式化程序有一个withZone() 方法 ,可以调用它来提供缺少的时区。

 ZonedDateTime.parse( value, DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(ZoneId.systemDefault())) 

请记住,有一个错误,所以你需要8u20或更高版本才能完全运行。