无法在索引 20 处解析文本“2021-06-22T18:27:03.5577Z”

Text '2021-06-22T18:27:03.5577Z' could not be parsed at index 20

我有几个代码片段。其中一些有效,有些无效,但我不明白为什么。

DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z'");  //(7 positions after last dor)

TIME_FORMATTER.parse("2021-06-22T18:27:03.5577Z")//Broken 4 
TIME_FORMATTER.parse("2021-06-22T18:27:03.55770Z")//Broken 5
TIME_FORMATTER.parse("2021-06-22T18:27:03.557700Z")//Working 6 
TIME_FORMATTER.parse("2021-06-22T18:27:03.5577000Z")//Working 7 
TIME_FORMATTER.parse("2021-06-22T18:27:03.55770000Z")//Broken 8

running live at IdeOne.com.

时,查看此代码是否有效

为什么它适用于:decimal separator 之后的 6 和 7 位数字,但不适用于 4、5 或 8 位数字?

如何创建适用于 4、5、6、7 或 8 位数字的格式化程序?

您可以在模式中使用可选格式来解析预期格式。对于您的具体情况,以下工作。

DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.[SSSSSSSS][SSSSSSS][SSSSS][SSSS]'Z'");

文档中不是很清楚,但可选部分的顺序很重要。

DateTimeFormatter documentation

此外,这种形式的解析会影响性能,建议使用 DateTimeFormatterBuilder

如果您想确保仅在 UTC 时区中传递日期并且仅使用 'Z' 文字,并且如果时区不同则预期结果失败,则此代码有效解决方案:

    new DateTimeFormatterBuilder()
        .append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"))
        .appendFraction(ChronoField.MICRO_OF_SECOND, 4, 7, true)
        .appendLiteral("Z")
        .toFormatter().parse("2021-06-22T18:27:03.5577Z");

tl;博士

您问的是:

How to create Format which will work for 4,5,6,7 numbers after point ?

使用预定义的格式化程序,DateTimeFormatter.ISO_INSTANT

DateTimeFormatter.ISO_INSTANT.parse("2021-06-22T18:27:03.5577Z")

从不忽略 Z

切勿在格式化模式中将 Z 括起来。那封信承载着重要的信息,而不是单纯的装饰。 该字母表示与 UTC 的零时-分-秒偏移量。您在 Z 周围的单引号表示应该预期该字母然后将其忽略。

忽略偏移量时,您只剩下日期和时间。日期和时间不足以代表一个时刻。我们无法知道您的输入是指东京的 6:30 下午、图卢兹的 6:30 下午,还是托莱多的 6:30 下午——所有这些时刻都相隔几个小时。

对于时间轴上的一个点,我们需要第三部分,偏移量或时区的上下文。

java.time.Instant.parse

您输入的文本符合 java.time 中默认使用的 ISO 8601 标准。因此无需指定格式模式。

简单地将输入解析为 Instant 对象。 Instant 表示在 UTC 中看到的时刻,偏移量为零。

Instant.parse 方法使用常量 DateTimeFormatter.ISO_INSTANT 中预定义的格式化程序。

Instant instant4 = Instant.parse("2021-06-22T18:27:03.5577Z") ;
Instant instant5 = Instant.parse("2021-06-22T18:27:03.55770Z") ;
Instant instant6 = Instant.parse("2021-06-22T18:27:03.557700Z") ;
Instant instant7 = Instant.parse("2021-06-22T18:27:03.5577000Z") ;
Instant instant8 = Instant.parse("2021-06-22T18:27:03.55770000Z") ;

看到这个 code run live at IdeOne.com

2021-06-22T18:27:03.557700Z
2021-06-22T18:27:03.557700Z
2021-06-22T18:27:03.557700Z
2021-06-22T18:27:03.557700Z

如果想知道 ISO_INSTANT 是如何用 OpenJDK 编写的,see the source code

正如 Basil 所说,您可能应该使用 DateTimeFormatter.ISO_INSTANT 作为您的格式,这会导致所有成功和更准确的结果:

Success: 2021-06-22T18:27:03.5577Z      {InstantSeconds=1624386423, MilliOfSecond=557, MicroOfSecond=557700, NanoOfSecond=557700000},ISO
Success: 2021-06-22T18:27:03.55770Z     {InstantSeconds=1624386423, MilliOfSecond=557, MicroOfSecond=557700, NanoOfSecond=557700000},ISO
Success: 2021-06-22T18:27:03.557700Z        {InstantSeconds=1624386423, MilliOfSecond=557, MicroOfSecond=557700, NanoOfSecond=557700000},ISO
Success: 2021-06-22T18:27:03.5577000Z       {InstantSeconds=1624386423, MilliOfSecond=557, MicroOfSecond=557700, NanoOfSecond=557700000},ISO
Success: 2021-06-22T18:27:03.55770000Z      {InstantSeconds=1624386423, MilliOfSecond=557, MicroOfSecond=557700, NanoOfSecond=557700000},ISO

但真正的问题是,“对这个格式化程序有什么期望”?它 似乎 应该是解析瞬间,但没有文档我们不确定。它真的意味着在即时支持的某些日期格式上失败,它真的意味着期待文字 'Z' 而不是偏移量吗?如果它的目的是能够准确地解析这些瞬间,那么将其更改为 actually be accurate 是你应该做的。

但是如果它意味着更严格并且在奇怪的情况下失败那么使用 aksappy 的想法可能是可行的方法,因为他的格式与您现有的格式保持接近但允许您明确添加到模式以匹配新的您要匹配的格式。


我希望您的小数点后六位数字的成功是您的 java.time 或提供此 API.

的任何库中的错误

运行 OpenJDK 16.0.1:

public static void main(String[] args) {
    DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z'");
    String[] dates = {
            "2021-06-22T18:27:03.5577Z",
            "2021-06-22T18:27:03.55770Z",
            "2021-06-22T18:27:03.557700Z",
            "2021-06-22T18:27:03.5577000Z", 
            "2021-06-22T18:27:03.55770000Z",
    };
    for (String date : dates) {
        try {
            System.out.println("Success: " + date + "\t\t" + TIME_FORMATTER.parse(date));
        } catch (Exception e) {
            System.out.println("Failure: " + date);
        }
    }
}

根据您的格式,我得到了符合我预期的以下结果:

Failure: 2021-06-22T18:27:03.5577Z      Text '2021-06-22T18:27:03.5577Z' could not be parsed at index 20
Failure: 2021-06-22T18:27:03.55770Z     Text '2021-06-22T18:27:03.55770Z' could not be parsed at index 20
Failure: 2021-06-22T18:27:03.557700Z        Text '2021-06-22T18:27:03.557700Z' could not be parsed at index 20
Success: 2021-06-22T18:27:03.5577000Z       {},ISO resolved to 2021-06-22T18:27:03.557700
Failure: 2021-06-22T18:27:03.55770000Z      Text '2021-06-22T18:27:03.55770000Z' could not be parsed at index 27