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

标签 java date java-8 java-time

如何从不包含 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 处解析
  • 无法在索引 10 处解析“2014-04-23”

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

最佳答案

由于 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());

关于java - 如何使用默认区域解析 ZonedDateTime?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27293994/

相关文章:

java - 如果必须填充数组,我该如何使它们相等?

java - 将日历转换为日期 - 忽略时区

java - 如何从 Java 8 中的迭代器获取 n 个第一个值?

java - 获取流的最后一个元素的最有效方法

java - 微调器无需等待查看器的选择即可加载 Intent

java - 你如何在java中创建两个优先级队列数组?/Linux常量时间调度程序

java - 如何在 netty 中打开纯文本日志记录?

mysql - 如何在mysql中根据2个日期选择记录

java - 带有日期的负参数的奇怪结果

java - 在 Mac 上使用 JRE 打开 jar 文件