3

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

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

在 IdeOne.com 上实时运行时,请查看此代码是否有效。

为什么它同时适用:小数点分隔符后的 6 位和 7 位数字,而不是 4、5 或 8 位数字?

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

4

4 回答 4

2

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

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

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

DateTimeFormatter 文档

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

于 2021-07-16T15:00:09.123 回答
2

tl;博士

您问:

如何创建适用于点后 4、5、6、7 数字的格式?

使用预定义的格式化程序,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") ;

请参阅在 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

如果好奇 OpenJDK 是如何ISO_INSTANT编写的,请查看源代码

于 2021-07-16T15:50:09.837 回答
0

正如 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”而不是偏移量?如果它的目的是能够准确地解析这些瞬间,那么将其更改为实际准确是您应该做的。

但是,如果它的限制性更强并且在奇怪的情况下失败,那么使用 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
于 2021-07-16T16:44:30.413 回答
-2

如果您想确保仅在 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");
于 2021-07-16T15:29:12.940 回答