使用 ZonedDateTime 解析日期时出现 DateTimeParseException

DateTimeParseException while parsing date using ZonedDateTime

我有以下程序,看起来 ZonedDateTime 无法解析日期字符串。我应该使用不同的日期格式还是不同的库来解析?

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

class Scratch {
    public static void main(String[] args) {
        final String inputDate = "2022-03-12T03:59:59+0000Z";
        ZonedDateTime.parse(inputDate, DateTimeFormatter.ISO_DATE_TIME).toEpochSecond();
    }
}

Exception in thread "main" java.time.format.DateTimeParseException: Text '2022-03-12T03:59:59+0000Z' could not be parsed, unparsed text found at index 19
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2053)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1952)
    at java.base/java.time.ZonedDateTime.parse(ZonedDateTime.java:599)
    at Scratch.main(scratch_29.java:7)

Process finished with exit code 1

这不是 ISO_DATE_TIME 格式。这将需要类似 2022-03-12T03:59:59+0000 的东西(没有 'Z')。有效的格式化程序类似于:

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;


class Scratch {
    public static void main(String[] args) {
        final String inputDate = "2022-03-12T03:59:59+0000Z";

        DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                .parseCaseInsensitive()
                .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
                .optionalStart()
                .appendPattern(".SSS")
                .optionalEnd()
                .optionalStart()
                .appendZoneOrOffsetId()
                .optionalEnd()
                .optionalStart()
                .appendOffset("+HHMM", "0000")
                .optionalEnd()
                .optionalStart()
                .appendLiteral('Z')
                .optionalEnd()
                .toFormatter();

        long epochSecond = ZonedDateTime.parse(inputDate, formatter).toEpochSecond();

        System.out.println("epochSecond is " + epochSecond);
    }
}

源自 。您可以在一个地方创建该格式化程序并再次使用它。

tl;博士

您的输入恰好是 java.time.Instant class 默认使用的标准 IS0 8601 格式的变体。修复您的输入字符串,然后解析。

Instant.parse( "2022-03-12T03:59:59+0000Z".replace( "+0000Z" , "Z" ) )

或者,重新格式化:

Instant.parse
( 
    "2022-03-12T03:59:59+0000Z"
    .replace( "+0000Z" , "Z" ) 
)

IdeOne.com 查看此代码 运行。

2022-03-12T03:59:59Z

但最好的解决方案是在交换 date-time 值时仅使用 ISO 8601 格式。

详情

ZonedDateTime这里不合适

ZonedDateTime 是您输入的错误 class。您的输入有一个 Z,它是零 hours-minutes-seconds 与 UTC 的偏移量的标准缩写。但没有指明时区,只是一个偏移量。所以使用 OffsetDateTimeInstant 而不是 ZonedDateTime.

+0000Z 多余

+0000 也表示偏移量为零 hours-minutes-seconds。这与 Z 的含义相同。所以这部分是多余的。我建议您对数据的发布者进行有关标准 ISO 8601 格式的培训。无需发明输入中看到的格式。

字符串操作而不是自定义格式化程序

如果您的所有输入都具有相同的 +0000Z 结尾,我建议您清理传入数据而不是定义格式模式。

String input = "2022-03-12T03:59:59+0000Z".replace( "+0000Z" , "Z" ) ;
Instant instant = Instant.parse( input ) ;

其他格式

你以后:

"2017-01-04T12:30:00-05:00" , "2017-03-20T22:05:00Z", etc. So I cannot assume all my inputs to have the same ending of +0000Z.

这两种格式都是标准的 ISO 8601 格式。两者都可以解析为 OffsetDateTime 对象 as-is,无需更改。

所以我仍然认为最简单的方法是,考虑到您可能的输入范围,首先进行 replace 字符串操作,如果您的其他两种格式到达,这将不起作用。然后将所有三个变体解析为 OffsetDateTime 个对象。有时程序员倾向于 over-think 一个问题,over-engineer 一个复杂的解决方案,一个简单的解决方案就足够了。

示例代码:

OffsetDateTime odt1 = OffsetDateTime.parse( "2022-03-12T03:59:59+0000Z".replace( "+0000Z" , "Z" ) ) ;
OffsetDateTime odt2 = OffsetDateTime.parse( "2017-01-04T12:30:00-05:00".replace( "+0000Z" , "Z" ) ) ;
OffsetDateTime odt3 = OffsetDateTime.parse( "2017-03-20T22:05:00Z".replace( "+0000Z" , "Z" ) ) ;

2022-03-12T03:59:59Z

2017-01-04T12:30-05:00

2017-03-20T22:05Z

看到这个code run live at IdeOne.com

解析时陷入 DateTimeParseException 以检测另一种意外格式。

当然,最好的解决方案是教育您的数据发布者一致使用 ISO 8601 格式,例如后两个示例,同时避免第一个示例的古怪格式.

从纪元开始计数

我建议不要将时间跟踪为 count-since-epoch。这样的计数本质上是模棱两可的 error-prone。相反,请使用标准 ISO 8601 格式的文本。但如果你坚持要数的话,就在这里。

要计算自 1970-01-01T00:00Z 纪元参考以来的秒数,从 OffsetDateTime 中提取 Instant 并查询。

long secondsSinceEpoch = odt.toInstant().getEpochSecond() ;