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 使用 uk
或 uk-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 会检查这一点,如果不是这样的话会反对。
用乌克兰语写日期中的月份(适用于其他读者)
在写这个答案之前,我对用乌克兰语写日期一无所知。对于好奇的读者,我想传递一些我搜索中的观察结果。
似乎使用了两种形式的月份名称:
- 主格,Java 称为独立形式,例如 січень 一月。这种形式通常以 -ень (-en).
结尾
- 属格,Java 用作正常(非独立)形式,例如 січня 一月。可能“一月”可以用作翻译?这种形式通常以 -ня (-nya?)
结尾
在互联网上我看到日期中使用了这两种形式。我不是很确定,但可能的趋势是:当月中没有日期时使用主格(独立)形式,有时在有日期时非正式地使用;当出现月份中的某天时,正式使用所有格。
链接
- Java SE 8 Date and Time: Why do we need a new date and time library?
- Oracle tutorial: Date Time 解释如何使用 java.time.
- How to Write Dates in Ukrainian + Useful Time Expressions(如果我理解正确,建议不要在日期中使用独立形式)
我收到此代码的错误:
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
正在尝试问题中给出的原始输入。
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 使用 uk
或 uk-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 会检查这一点,如果不是这样的话会反对。
用乌克兰语写日期中的月份(适用于其他读者)
在写这个答案之前,我对用乌克兰语写日期一无所知。对于好奇的读者,我想传递一些我搜索中的观察结果。
似乎使用了两种形式的月份名称:
- 主格,Java 称为独立形式,例如 січень 一月。这种形式通常以 -ень (-en). 结尾
- 属格,Java 用作正常(非独立)形式,例如 січня 一月。可能“一月”可以用作翻译?这种形式通常以 -ня (-nya?) 结尾
在互联网上我看到日期中使用了这两种形式。我不是很确定,但可能的趋势是:当月中没有日期时使用主格(独立)形式,有时在有日期时非正式地使用;当出现月份中的某天时,正式使用所有格。
链接
- Java SE 8 Date and Time: Why do we need a new date and time library?
- Oracle tutorial: Date Time 解释如何使用 java.time.
- How to Write Dates in Ukrainian + Useful Time Expressions(如果我理解正确,建议不要在日期中使用独立形式)