为什么我的日期时间解析尝试失败?

Why does my date time parsing attempt fail?

我正在用两种略有不同的格式解析同一日期,但遇到了一个我无法理解的错误。

解析标准ISO格式字符串成功:

    String s = "2018-04-17T22:57:29";
    LocalDateTime date = LocalDateTime.parse(s, DateTimeFormatter.ISO_DATE_TIME);  // OK

然而,当我添加 "Z" 后缀并使用 ISO_INSTANT 时:

    s = "2018-04-17T22:57:29Z";
    date = LocalDateTime.parse(s, DateTimeFormatter.ISO_INSTANT); // Fails

我得到以下异常:

Exception in thread "main" java.time.format.DateTimeParseException: Text '2018-04-17T22:57:29Z' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MicroOfSecond=0, NanoOfSecond=0, MilliOfSecond=0, InstantSeconds=1524005849},ISO of type java.time.format.Parsed
at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1920)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1855)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)
at iplus.fwk.manifest.Test.main(Test.java:37)
Caused by: java.time.DateTimeException: Unable to obtain LocalDateTime from TemporalAccessor: {MicroOfSecond=0, NanoOfSecond=0, MilliOfSecond=0, InstantSeconds=1524005849},ISO of type java.time.format.Parsed
at java.time.LocalDateTime.from(LocalDateTime.java:461)
at java.time.format.Parsed.query(Parsed.java:226)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
... 2 more
Caused by: java.time.DateTimeException: Unable to obtain LocalDate from TemporalAccessor: {MicroOfSecond=0, NanoOfSecond=0, MilliOfSecond=0, InstantSeconds=1524005849},ISO of type java.time.format.Parsed
at java.time.LocalDate.from(LocalDate.java:368)
at java.time.LocalDateTime.from(LocalDateTime.java:456)
... 4 more

我对ISO_INSTANT定义的解读 建议第二次解析应该成功。我做错了什么?

我认为正确的思考方式是这样的:概念上瞬间不是日期和时间。只是,一瞬间。它没有时区或偏移量,因此不能有日期和时间。由于 LocalDateTime 需要日期和时间,即时解析并不能满足您的需求。

是的,我知道:Instant 实现 使用自纪元以来的时间,而迄今为止最常见的纪元定义使用 UTC。我还知道您正在解析的字符串包含日期和一天中的时间,并且 DateTimeFormatter.ISO_INSTANTInstant.toString 都会产生相似的字符串。不过,这是界面,并没有告诉你瞬间的概念是什么。

My reading of the definition of ISO_INSTANT suggests that the second parse should succeed.

我知道定义可以这样读。不过,我认为这值得注意:

…the instant is converted from ChronoField.INSTANT_SECONDS and ChronoField.NANO_OF_SECOND…

虽然 nano of second 与 LocalDateTime 等其他日期时间类型共享,但上述两个字段不足以让您获得 LocalDateTime 的数据。所以这就是它为你打破的地方。要获得 LocalDateTime,我们需要假设一个时区或偏移量。尽管引用还在继续:

…using the UTC offset.

—这仅表示UTC偏移用于计算瞬间秒和纳秒,而不用于进一步处理。

我们也可能会尝试更仔细地研究您的错误消息:

Exception in thread "main" java.time.format.DateTimeParseException: Text '2018-04-17T22:57:29Z' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MicroOfSecond=0, NanoOfSecond=0, MilliOfSecond=0, InstantSeconds=1524005849},ISO of type java.time.format.Parsed

这也说明我们从解析中得到的只是瞬间秒数(即自纪元以来的秒数)和秒数的分数(以及 ISO 年表)。而且这还不足以获得 LocalDateTime.

您不是第一个(也不是最后一个)感到惊讶的人。不过,您观察到的行为是设计使然。

修复

ISO_INSTANT的定义还提到:

The ISO_OFFSET_DATE_TIME

所以这有效:

    String s = "2018-04-17T22:57:29Z";
    LocalDateTime date = LocalDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME);

结果是 2018-04-17T22:57:29(符合预期)。