在 Java 中无法从西班牙语日期字符串转换为 LocalDate

Cannot convert from Spanish date string to LocalDate in Java

您好,我是来处理 Locale 格式化程序语言的 DateTimeFormatter 问题的,让我向您介绍一下:

观察到堆栈跟踪错误:

Exception in thread "main" java.time.format.DateTimeParseException: Text '28-dic-2017' could not be parsed at index 3
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2051)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1953)
    at java.base/java.time.LocalDate.parse(LocalDate.java:429)
    at com.examen.Presentation.Principal.parseDate(Principal.java:28)
    at com.examen.Presentation.Principal.main(Principal.java:19)

最小的、可重现的代码示例:

public static LocalDate parseDate(String fecha) {

    Locale loc = new Locale("es", "ES");
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy", loc);
    LocalDate date = LocalDate.parse(fecha, formatter);

    return date;
}

我在尝试来自语言环境的不同事物和方法时遇到了同样的错误,而 DateTimeFormatter 甚至不知道我在做什么,但我认为这显然不可行。

对于西班牙语,DateTimeFormatter 期望缩写的月份以句点结束。像这样:28-dic.-2017

如果所有你的日期都是这种格式,你可以解析输入字符串以添加句点,然后将它发送到DateTimeFormatter:

public static LocalDate parseDate(String fecha) {
    int firstDash = fecha.indexOf('-')+1;
    int secondDash = fecha.indexOf('-', fecha.indexOf('-')+1);
    String month = fecha.substring(firstDash, secondDash);
    String dateStr = fecha.substring(0,firstDash)+month+"."+fecha.substring(secondDash);
    Locale loc = new Locale("es", "ES");
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy", loc);
    LocalDate date = LocalDate.parse(dateStr, formatter);

    return date;
}

如果格式有时有句点,如果不存在则需要添加句点:

public static LocalDate parseDate(String fecha) {
    String dateStr;
    if (fecha.indexOf(".")!=-1) {
        dateStr = fecha;
    } else {
        int firstDash = fecha.indexOf('-')+1;
        int secondDash = fecha.indexOf('-', fecha.indexOf('-')+1);
        String month = fecha.substring(firstDash, secondDash);
        dateStr = fecha.substring(0,firstDash)+month+"."+fecha.substring(secondDash);
    }
    System.out.println(dateStr);
    Locale loc = new Locale("es", "ES");
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy", loc);
    LocalDate date = LocalDate.parse(dateStr, formatter);

    return date;
}

输出:

2017-12-28

在另一个答案中geocodezip 已经解释了您尝试失败的原因。这里有两个我认为比那个答案中的更优雅的解决方案建议。

Java 语言环境数据提供者

Java 最多可以从四个来源获取其区域设置数据,包括不同语言的月份缩写。由于 Java 9 默认情况下它更喜欢从 CLDR(Unicode 通用语言环境数据存储库)获取它们。在 CLDR 中,西班牙语月份缩写带有点(或 整点 ),如其他答案中所述,例如 dic. 表示 diciembre。另一个来源是自早期版本以来 Java 内置的语言环境数据(在 Java 8 之前是默认设置)。这里的西班牙月份缩写没有点(你的数据可能是由程序 运行 在 Java 8 或更早的时候生成的,我不知道)。这些区域设置数据被称为 COMPAT 以与早期的 Java 版本兼容。启动程序时,可以在命令行中指定 Java 应该优先使用 COMPAT 而不是 CLDR,例如:

java -Djava.locale.providers=COMPAT,CLDR YourJavaMainClass

这将使您现有的代码接受 dic 和其他不带点的月份缩写。

这是 JVM 的全局设置,因此它还会导致您的程序和其他程序中任何位置的任何区域设置敏感操作 运行 在同一 JVM 中使用来自 [=62] 的旧区域设置数据=] 8. 三思而后行。

DateTimeFormatterBuilder.appendText()

因为您知道您的格式始终是 28-dic-2017,您还可以通过明确指定在输入数据中使用哪个月份缩写来使自己完全独立于任何语言环境数据。

private static Map<Long, String> getMonthAbbreviations() {
    return Map.ofEntries(
            Map.entry(1L, "ene"),
            Map.entry(2L, "feb"),
            Map.entry(3L, "mar"),
            Map.entry(4L, "abr"),
            Map.entry(5L, "may"),
            Map.entry(6L, "jun"),
            Map.entry(7L, "jul"),
            Map.entry(8L, "ago"),
            Map.entry(9L, "sep"),
            Map.entry(10L, "oct"),
            Map.entry(11L, "nov"),
            Map.entry(12L, "dic"));
}

private static final DateTimeFormatter inputDateFormatter = new DateTimeFormatterBuilder()
        .appendPattern("dd-")
        .appendText(ChronoField.MONTH_OF_YEAR, getMonthAbbreviations())
        .appendPattern("-uuuu")
        .toFormatter();

此格式化程序独立于区域设置并解析您的日期字符串:

    String fecha = "28-dic-2017";
    LocalDate date = LocalDate.parse(fecha, inputDateFormatter);
    System.out.println(date);

输出为:

2017-12-28

解析问题的提示:先尝试格式化

提示:当解析不起作用时,您可以先尝试格式化,以了解要解析的字符串应该是什么样子,并与您尝试解析的字符串进行比较。例如:

    Locale loc = new Locale("es", "ES");
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy", loc);
    
    System.out.println("Want to parse: 28-dic-2017");
    System.out.println("Formatted:     "
            + LocalDate.of(2017, Month.DECEMBER, 28).format(formatter));
    System.out.println("Want to parse: 29-sep-2017");
    System.out.println("Formatted:     "
            + LocalDate.of(2017, Month.SEPTEMBER, 29).format(formatter));
Want to parse: 28-dic-2017
Formatted:     28-dic.-2017
Want to parse: 29-sep-2017
Formatted:     29-sept.-2017

现在我们至少可以一开始就知道哪里出了问题。

文档链接