java.text.ParseException:无法解析的日期:“...”

java.text.ParseException: Unparseable date : "..."

我收到此代码的错误:

SimpleDateFormat sdf = new SimpleDateFormat("EEEE dd MMMM HH:mm yyyy",myDateFormatSymbols);
sdf.parse("понеділок 12 квітень 07:00 2021");

这是 "Monday 12 April 07:00 2021"。 问题是,无论何时我将日期从星期一更改为星期二 ("вівторок"),我都不会收到此错误,并且代码有效。 这是 myDateFormatSymbols:

的代码
private final static DateFormatSymbols myDateFormatSymbols = new DateFormatSymbols(){
        @Override
        public String[] getWeekdays(){
             return new String[]{"понеділок","вівторок", "середа", "четвер", "пятниця", "субота", "неділя"};
        }
        @Override
        public String[] getMonths() {
            return new String[]{...};
        }
}

所有月份和工作日都正常工作,似乎这个错误只发生在星期一。

您可以查看 DateFormatSymbols#weekdays 的 Javadoc,不幸的是,索引 0 处的元素总是被忽略。

我只是用一个空字符串替换它。

Weekday strings. For example: "Sunday", "Monday", etc. An array of 8 strings, indexed by Calendar.SUNDAY, Calendar.MONDAY, etc. The element weekdays[0] is ignored.


以下代码现在打印预期的答案

DateFormatSymbols myDateFormatSymbols = new DateFormatSymbols() {
    @Override
    public String[] getWeekdays() {
        return new String[]{"", "понеділок", "вівторок", "середа", "четвер", "пятниця", "субота", "неділя"};
    }

    @Override
    public String[] getMonths() {
        return new String[]{"квітень"};
    }
};

SimpleDateFormat sdf = new SimpleDateFormat("EEEE dd MMMM HH:mm yyyy", myDateFormatSymbols);
System.out.println(sdf.parse("понеділок 12 квітень 07:00 2021")); // Tue Jan 12 07:00:00 CET 2021

java.time

看起来是正确的。但是这个问题和那个答案都使用可怕的日期时间 类,几年前被 JSR 310 中定义的现代 java.time 类 所取代。

正在尝试问题中给出的原始输入。

String input = "понеділок 12 квітень 07:00 2021";
Locale locale = new Locale.Builder().setLanguage( "uk" ).setRegion( "UA" ).build();
DateTimeFormatter f = DateTimeFormatter.ofPattern( "EEEE dd MMMM HH:mm yyyy" ).withLocale( locale );
LocalDateTime ldt = LocalDateTime.parse( input , f );
System.out.println( "ldt = " + ldt );

看到 code run live at IdeOne.com,使用 Java 12.

根据您的输入,我得到 DateTimeParseException 提示“无法在索引 13 处解析文本‘понеділок 12 квітень 07:00 2021’”。这意味着您的月份名称有问题。

月份名称不正确?

我对Ukrainian language一无所知。因此,作为实验,我尝试了相反的方法,生成 文本而不是解析 文本。我得到月份名称的不同变体。

Locale locale = new Locale.Builder().setLanguage( "uk" ).setRegion( "UA" ).build();
DateTimeFormatter f = DateTimeFormatter.ofPattern( "EEEE dd MMMM HH:mm yyyy" ).withLocale( locale );

LocalDateTime ldt = LocalDateTime.of( 2021 , Month.APRIL , 12 , 7 , 0 );
String output = ldt.format( f );
LocalDateTime ldt2 = LocalDateTime.parse( output , f );

System.out.println( "ldt.toString() = " + ldt );
System.out.println( "output = " + output );
System.out.println( "ldt2.toString() = " + ldt2 );

看到这个code run live at IdeOne.com

结果:

ldt.toString() = 2021-04-12T07:00
output = понеділок 12 квітня 07:00 2021
ldt2.toString() = 2021-04-12T07:00

因此,您的数据发布者正在使用 Java 使用的当前语言环境定义所不期望的月份名称变体。现代 Java (Java 9 and later in general, and Java 16 here for me) is the Unicode Common Locale Data Repository (CLDR) 使用的主要默认语言环境定义集。后备定义可能是过时的 Java 特定集,在旧版本 Java。我不知道这里使用的是哪一组语言环境定义,但我认为可以肯定地说 CLDR 涵盖了乌克兰语,因此必须在这里使用。

正如我所说,我不懂乌克兰语。但我怀疑你输入的月份名称不正确 linguistically/grammatically/spelling-wise.

  • java.time

  • 月份名称的独立形式

  • DateTimeFormatterBuilder.appendText(TemporalField, Map<Long, String>)

我强烈建议您使用 java.time,现代 Java 日期和时间 API(链接在底部),因为你的日期和时间工作。

您正在尝试解析乌克兰语的日期时间字符串。我们会立即期望 Java 使用 ukuk-UA 区域设置开箱即用。 编辑: 令我惊讶的是,您的字符串使用的是 Java 认为 standalone 形式的月份名称(看来我还没有理解月份名称的独立形式是什么意思)。要在格式中指定此形式,请在格式模式字符串中使用 LLLL 而不是 MMMM。此外,您的字符串中星期五的名称与 Java 知道的名称不同(пʼятниця 带有撇号,正如 Basil Bourque 已经说过的那样,来自 CLDR)。正如您已经尝试过的那样,解决方案是指定您自己的日期名称。在 java.time 中,这是通过 DateTimeFormatterBuilder 及其两个参数 appendText 方法完成的。例如:

private static final Map<Long, String> DAY_NAMES = Map.of(1L, "понеділок", 2L, "вівторок",
        3L, "середа", 4L, "четвер", 5L, "пятниця", 6L, "субота", 7L, "неділя");
private static final Locale UKRAINIAN = Locale.forLanguageTag("uk-UA");
private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
        .appendText(ChronoField.DAY_OF_WEEK, DAY_NAMES)
        .appendPattern(" dd LLLL HH:mm uuuu")
        .toFormatter(UKRAINIAN);

现在有了格式化程序,解析就很简单了:

    String stringToParse = "понеділок 12 квітень 07:00 2021";
    LocalDateTime dateTime = LocalDateTime.parse(stringToParse, FORMATTER);
    System.out.println(dateTime);

输出:

2021-04-12T07:00

我发现我的代码比你的代码更易读,这就是代码的重要性。没有有趣的覆盖。没有从 0 开始的疯狂编号。星期一是一周的第一天,因为它在您的问题和乌克兰。

另外 java.time 默认执行更好的验证。当您的字符串表示 4 月 12 日是星期一时,java.time 会检查这一点,如果不是这样的话会反对。

用乌克兰语写日期中的月份(适用于其他读者)

在写这个答案之前,我对用乌克兰语写日期一无所知。对于好奇的读者,我想传递一些我搜索中的观察结果。

似乎使用了两种形式的月份名称:

  1. 主格,Java 称为独立形式,例如 січень 一月。这种形式通常以 -ень (-en).
  2. 结尾
  3. 属格,Java 用作正常(非独立)形式,例如 січня 一月。可能“一月”可以用作翻译?这种形式通常以 -ня (-nya?)
  4. 结尾

在互联网上我看到日期中使用了这两种形式。我不是很确定,但可能的趋势是:当月中没有日期时使用主格(独立)形式,有时在有日期时非正式地使用;当出现月份中的某天时,正式使用所有格。

链接