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

问题描述

我收到此代码错误

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[]{...};
        }
}

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

解决方法

  • java.time

  • 月份名称的独立形式

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

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

您正在尝试解析乌克兰语中的日期时间字符串。我们会立即期望 Java 使用 ukuk-UA 语言环境开箱即用地执行此操作。 编辑:令我惊讶的是,您的字符串使用的是 Java 认为的 standalone 月份名称形式(似乎我不明白月份名称的独立形式是什么意思) .要在格式中指定这种形式,请在格式模式字符串中使用 LLLL 而不是 MMMM。此外,字符串中 Friday 的名称与 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. 所有格,Java 用作正常(非独立)形式,例如 січня 表示一月。也许“一月”可以用作翻译?这种形式通常以-ня (-nya?)
  3. 结尾

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

链接

,

java.time

Answer by Hajaj 看起来是正确的。但是问题和答案都使用了糟糕的日期时间类,这些类在几年前被 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 和 Java 16 对我而言)使用的主要默认语言环境定义集是 Unicode Common Locale Data Repository (CLDR)。回退定义可能是一个过时的特定于 Java 的集合,在旧版本的 Java 中作为主要集合捆绑在一起。我不知道哪一组语言环境定义在这里起作用,但我认为可以肯定地说 CLDR 涵盖了乌克兰语,因此必须在这里起作用。

正如我所说,我不会乌克兰语。但我怀疑您输入的月份名称在语言/语法/拼写方面不正确。

,

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

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

工作日字符串。例如:“Sunday”、“Monday”等8个字符串的数组,以Calendar.SUNDAY、Calendar.MONDAY等为索引,忽略weekdays[0]元素。


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

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