【问题标题】:java.text.ParseException: Unparseable date : "..."java.text.ParseException:无法解析的日期:“...”
【发布时间】:2021-07-05 19:03:13
【问题描述】:

我收到此代码错误:

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

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

【问题讨论】:

  • 我建议你不要使用SimpleDateFormatDate。这些类设计不佳且早已过时,尤其是前者,尤其是出了名的麻烦。而是使用来自java.time, the modern Java date and time APILocalDateTimeDateTimeFormatter
  • @OleV.V.实际上,我在 java.time 中尝试过但没有成功。我使用了乌克兰语的Locale(根据谷歌翻译的猜测):new Locale.Builder().setLanguage( "uk" ).setRegion( "UA" ).build();。我收到DateTimeParseException 说“无法在索引 13 处解析”(月份名称)。我粘贴了从谷歌翻译中复制的月份名称Квітень 的变体,将“四月”翻译成乌克兰语。我验证了понеділок = Monday = 2021-04-12。如果您可以看到已删除的答案,see mine。也许你可以试一试。
  • @k4rnaj1k 这个输入字符串是什么语言?当您的代码成功运行时,Locale 的作用是什么?
  • 我认为您正在尝试解析乌克兰语字符串,而您的字符串在 п'ятниця 中没有撇号(Frid​​ay ) Java 认为应该存在,这就是为什么您需要提供自己的日期名称。它是否正确?你创建myDateFormatSymbols还有其他原因吗?
  • 您使用的是哪个 Java 版本?特别是对于 Java 7、8 和 9,解决方案可能会有所不同。

标签: java simpledateformat java-time dayofweek datetime-parsing


【解决方案1】:
  • java.time

  • 月份名称的独立形式

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

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

您正在尝试解析乌克兰语的日期时间字符串。我们会立即期望 Java 使用 ukuk-UA 语言环境开箱即用地执行此操作。 编辑:令我惊讶的是,您的字符串正在使用 Java 认为的月份名称的 独立 形式(似乎我不明白月份名称的独立形式是什么意思) .要在格式化中指定此格式,请在格式模式字符串中使用 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. 结尾

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

链接

【讨论】:

  • 确实不错的答案!
【解决方案2】:

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 涵盖了乌克兰语,因此必须在这里起作用。

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

【讨论】:

  • 事实证明 квітень 是 April 的乌克兰名称的 独立 形式(根据 Java(和 CLDR))。我发现在互联网上的几个地方确认的日期中使用了独立表格。我很惊讶。我以为我已经理解 独立形式 的含义,但显然没有。无论如何,这似乎是正确的。
【解决方案3】:

您可以查看DateFormatSymbols#weekdays 的Javadoc,不幸的是,索引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

【讨论】:

  • 非常感谢您的回答。那么,正确的解决方案是在数组的开头添加“”?
  • 是的确实@k4rnaj1k 我已经添加了代码现在的样子
猜你喜欢
  • 2013-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多