问题描述
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 使用 uk
或 uk-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 会检查这一点,如果不是这样,就会反对。
用乌克兰语写日期中的月份(适用于其他读者)
在写这个答案之前,我对用乌克兰语写日期一无所知。对于好奇的读者,我想通过我的搜索传递一些观察结果。
似乎使用了两种形式的月份名称:
- 主格,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(如果我理解正确,建议不要在日期中使用独立形式)
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