【问题标题】:Java Time parse Dates with short day namesJava Time 解析具有短日期名称的日期
【发布时间】:2021-10-06 14:09:20
【问题描述】:

我有以下德国日期:So, 18 Jul 2021 15:24:00 +0200

我无法使用 Java Time 解析它:

DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss Z", Locale.GERMANY)
  .parse("So, 18 Jul 2021 15:24:00 +0200", Instant::from)

当它抛出时:Text 'So, 18 Jul 2021 15:24:00 +0200' could not be parsed at index 0

如果我要将字符串更改为正确格式,它可以工作:

-So, 18 Jul 2021 15:24:00 +0200
+So., 18 Juli 2021 15:24:00 +0200

有什么神奇的模式可以解析上述日期吗?


我在其他日期也遇到了同样的问题

  • LocalDateTime.parse("ven, 16/07/2021 - 09:49", DateTimeFormatter.ofPattern("EE, dd/MM/yyyy - HH:mm", Locale("fr")))
    • ven 必须是 ven.
  • LocalDateTime.parse("vr, 23 apr 2021 17:04:00", DateTimeFormatter.ofPattern("EE, dd MM yyyy HH:mm:ss", Locale("nl")))
    • apr 必须是 04(才能使用 MM

【问题讨论】:

  • DateTimeFormatter 用于解析有效日期,不用于清理错误输入。在进入格式化程序之前清理您的输入
  • 我同意,您不需要日期名称,因为您有完整的日期。
  • @JoakimDanielson 验证通常是一件好事。
  • "apr 必须是 07"?那不应该是04吗?

标签: java date datetime dayofweek date-parsing


【解决方案1】:

现代的日期时间 API 对模式非常讲究。因此,几乎不可能创建一个可用于解析所有类型字符串的单一模式。然而,DateTimeFormatter 的最大特点之一是它可以灵活地使用可选模式,使用方括号指定,例如下面的演示使用E, d [MMMM][MMM][M] u H:m:s Z,它具有三个可选的月份模式。

演示:

import java.time.DateTimeException;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.stream.Stream;


public class Main {
    public static void main(String[] args) {
        Stream.of(
                "So., 18 Juli 2021 15:24:00 +0200",
                "ven., 16 avr. 2021 15:24:00 +0200",
                "vr, 16 apr. 2021 15:24:00 +0200",
                "vr, 16 07 2021 15:24:00 +0200"
        ).forEach(s -> {
            Stream.of(
                    Locale.GERMANY,
                    Locale.FRANCE,
                    new Locale("nl", "NL")
            ).forEach( locale -> {
                try {
                    System.out.println("Parsed '" + s + "' using the locale, " + locale + " => " + parseToInstant(s, locale));
                }catch(DateTimeException e) {
                    //....
                }
            });
        });
    }

    static Instant parseToInstant(String strDateTime, Locale locale) {
        return DateTimeFormatter.ofPattern("E, d [MMMM][MMM][M] u H:m:s Z").withLocale(locale).parse(strDateTime,
                Instant::from);
    }
}

输出:

Parsed 'So., 18 Juli 2021 15:24:00 +0200' using the locale, de_DE => 2021-07-18T13:24:00Z
Parsed 'ven., 16 avr. 2021 15:24:00 +0200' using the locale, fr_FR => 2021-04-16T13:24:00Z
Parsed 'vr, 16 apr. 2021 15:24:00 +0200' using the locale, nl_NL => 2021-04-16T13:24:00Z
Parsed 'vr, 16 07 2021 15:24:00 +0200' using the locale, nl_NL => 2021-07-16T13:24:00Z

ONLINE DEMO

DateTimeFormatterBuilder 了解有关日期时间模式的更多信息。

【讨论】:

    【解决方案2】:

    指定您自己的星期几的缩写

    根据 CLDR,德语星期几的缩写用点书写。要让 Java 解析缩写词缺少点的字符串,有两种明显的解决方案:

    1. 不要使用 CLDR。 Java 自己的 Java 8 及之前的缩写没有点,在较新的 Java 版本中仍然可用。
    2. 指定您自己的缩写。

    由于您在使用法语时遇到了类似的问题,Java 自己的缩写也有点,我建议解决方案 1. 对您来说是不够的。因此,让我们深入研究解决方案 2。我下面的代码采用 CLDR 的缩写,例如 So.,并从中删除尾随的点,因此您可以在字符串中获得例如 So

        Locale loc = Locale.GERMANY;
        Map<Long,String> dowsWithoutDots = Arrays.stream(DayOfWeek.values())
                .collect(Collectors.toMap(dow -> Long.valueOf(dow.getValue()),
                        dow -> dow.getDisplayName(TextStyle.SHORT, loc).replaceFirst("\\.$", "")));
        Map<Long,String> monthsWithoutDots = Arrays.stream(Month.values())
                .collect(Collectors.toMap(m -> Long.valueOf(m.getValue()),
                        m -> m.getDisplayName(TextStyle.SHORT, loc).substring(0, 3)));
        DateTimeFormatter germanWithoutDots = new DateTimeFormatterBuilder()
                .appendText(ChronoField.DAY_OF_WEEK, dowsWithoutDots)
                .appendPattern(", dd ")
                .appendText(ChronoField.MONTH_OF_YEAR, monthsWithoutDots)
                .appendPattern(" yyyy HH:mm:ss Z")
                .toFormatter(loc);
        
        System.out.println(germanWithoutDots.parse("So, 18 Jul 2021 15:24:00 +0200", Instant::from));
    

    sn-p 的输出是:

    2021-07-18T13:24:00Z

    对于月份的缩写,删除最后一个点不起作用,因为正如您所观察到的,CLDR 的缩写是 Juli,您在其中得到了 Jul。因此,我没有删除点,而是缩写为三个字符。您应该测试它是否适用于所有月份(包括Mai)。

    我没有尝试过法语和荷兰语,但应该可以。

    如果您想通过解决方案 1 试试运气,完全绕过 CLDR,请参阅 JDK dateformatter parsing DayOfWeek in German locale, java8 vs java9

    【讨论】:

      猜你喜欢
      • 2014-04-08
      • 2014-01-21
      • 1970-01-01
      • 2010-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-21
      相关资源
      最近更新 更多