Java 8 日期时间格式器行为异常

Java 8 Date time formatter misbehaving

我有以下代码抛出 DateTimeParseException:

String dateString = "Jul 20 09:32:46"
DateTimeFormatter formatter=DateTimeFormatter.ofPattern("L d HH:mm:ss");
return ZonedDateTime.parse(dateString, formatter);

根据 documentation,您会发现 Jul 是字符 L 的示例。

然而,异常信息是:

java.time.format.DateTimeParseException: Text 'Jul' could not be parsed at index 0

我错过了什么?

您在这里遇到了一些问题:

  1. 要正确解析 'Jul',您必须使用 MMM 而不是 L 解释了原因)。
  2. 您的日期字符串中没有年份。您不能创建没有年份的 ZonedDateTime。
  3. 如果是 Zoned 日期时间,它还必须包含时区信息,这不在您的日期字符串中。如果您不想使用时区,可以使用 LocalDateTime

这里有一些备选方案:

带时区:

String dateString = "Jul 20 2018 09:32:46+0000";
DateTimeFormatter formatter= DateTimeFormatter.ofPattern("MMM dd y H:mm:ssZ");
return ZonedDateTime.parse(dateString, formatter);

没有时区:

String dateString = "Jul 20 2018 09:32:46";
DateTimeFormatter formatter= DateTimeFormatter.ofPattern("MMM dd y H:mm:ss");
return LocalDateTime.parse(dateString, formatter);

是正确的。我将提供我的建议作为补充:要么改进您的字符串以包含年份和时区,要么构建一个格式化程序,可以在没有它们的情况下解析您当前的字符串。

改进字符串

    String dateString = "Jul 20 2018 09:32:46 America/Argentina/La_Rioja";
    DateTimeFormatter formatter 
            = DateTimeFormatter.ofPattern("LLL d uuuu HH:mm:ss VV", Locale.ROOT);
    System.out.println(ZonedDateTime.parse(dateString, formatter));

这会打印

2018-07-20T09:32:46-03:00[America/Argentina/La_Rioja]

相同的格式化程序还会将 Jul 20 2018 09:32:46 -08:30 解析为 2018-07-20T09:32:46-08:30 的 ZonedDateTime

第一个潜在问题是语言环境。如果“Jul”是英语,请提供英语语言环境,否则在使用 July 月份被称为其他语言的计算机上解析可能会失败。我建议您始终在格式化程序中指定语言环境。即使你最终选择 Locale.getDefault()。它仍然会告诉reader(和你自己)你做出了有意识的选择。

接下来the documentationML都可以给month as number/text并给出例子7; 07;七月;七月; J. 因此这一行显然是相关的:“Number/Text: 如果模式字母的数量为 3 或更多,请使用上面的文本规则。否则使用上面的数字规则。”由于“Jul”是文本,因此您需要 3 个或更多的模式字母。 “少于 4 个模式字母将使用缩写形式。” “Jul”很短,所以我们只需要三个字母。

无论我在格式模式字符串中使用 MMM 还是 LLL,上面的代码都适用于 Java 9.0.4。在 jdk1.8.0_131 中,它与 MMM 一起工作,但有趣的是在 LLL 上失败了,这可能是一个错误(在 Mac 上测试过)。有关 ML 之间预期差异的处理,请参阅 Juan Carlos Mendoza。

构建有效的格式化程序

    String dateString = "Jul 20 09:32:46";
    ZoneId zone = ZoneId.of("America/Argentina/La_Rioja");
    DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendPattern("LLL d HH:mm:ss")
            .parseDefaulting(ChronoField.YEAR, Year.now(zone).getValue())
            .toFormatter(Locale.ROOT)
            .withZone(zone);
    System.out.println(ZonedDateTime.parse(dateString, formatter));

这会将您问题中的字符串解析为 2018-07-20T09:32:46-03:00[America/Argentina/La_Rioja]。如果您想要的默认时区与我随机选择的不一致,请替换为您想要的默认时区。如果您不想要当前年份,也可以替换您想要的年份。

同样,我的 Java 8 需要 MMM 而不是 LLL