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

2022-09-02 20:14:12

如何从不包含的字符串和其他字段进行解析?ZoneDateTimezone

以下是斯波克的重现测试:

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 处解析

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


答案 1

由于格式化程序需要区域或偏移信息,因此分析将失败。您必须制作一个具有区域信息和时间部分的可选部分的。对进行逆向工程并添加可选标记并不难。ISO_ZONED_DATE_TIMEDateTimeFormatterZonedDateTimeFormatter

然后,使用格式化程序的方法分析 。然后,对于次优解析结果,您可以使用所需的任何默认值创建。StringparseBest()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());

答案 2

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

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

请记住,存在一个错误,因此您需要8u20或更高版本才能使其完全正常工作。