Java 时间解析具有短日期名称的日期
Java Time parse Dates with short day names
我有以下德国日期:So, 18 Jul 2021 15:24:00 +0200
我无法使用 Java 时间解析它:
DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss Z", Locale.GERMANY)
.parse("So, 18 Jul 2021 15:24:00 +0200", Instant::from)
因为它抛出:Text 'So, 18 Jul 2021 15:24:00 +0200' could not be parsed at index 0
如果我要更改字符串以使其格式正确,它会起作用:
-So, 18 Jul 2021 15:24:00 +0200
+So., 18 Juli 2021 15:24:00 +0200
有什么神奇的模式可以解析上面的日期吗?
我在其他日期也遇到了同样的问题
LocalDateTime.parse("ven, 16/07/2021 - 09:49", DateTimeFormatter.ofPattern("EE, dd/MM/yyyy - HH:mm", Locale("fr")))
ven
必须是 ven.
LocalDateTime.parse("vr, 23 apr 2021 17:04:00", DateTimeFormatter.ofPattern("EE, dd MM yyyy HH:mm:ss", Locale("nl")))
apr
必须是04
(才能使用MM
)
为星期几指定您自己的缩写
根据 CLDR,德语星期几的缩写是用点写的。要让 Java 解析缩写缺少点的字符串,有两个明显的解决方案:
- 不要使用 CLDR。 Java 自己在 Java 8 和之前的缩写没有点,在较新的 Java 版本中仍然可用。
- 指定您自己的缩写。
因为你在法语方面也有类似的问题,Java自己的缩写也有点,我建议解决方案 1. 对你来说是不够的。因此,让我们深入研究解决方案 2。我下面的代码采用 CLDR 的缩写,例如 So.
,并从中删除尾随点,因此您得到例如字符串中的 So
。
Locale loc = Locale.GERMANY;
Map<Long,String> dowsWithoutDots = Arrays.stream(DayOfWeek.values())
.collect(Collectors.toMap(dow -> Long.valueOf(dow.getValue()),
dow -> dow.getDisplayName(TextStyle.SHORT, loc).replaceFirst("\.$", "")));
Map<Long,String> monthsWithoutDots = Arrays.stream(Month.values())
.collect(Collectors.toMap(m -> Long.valueOf(m.getValue()),
m -> m.getDisplayName(TextStyle.SHORT, loc).substring(0, 3)));
DateTimeFormatter germanWithoutDots = new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_WEEK, dowsWithoutDots)
.appendPattern(", dd ")
.appendText(ChronoField.MONTH_OF_YEAR, monthsWithoutDots)
.appendPattern(" yyyy HH:mm:ss Z")
.toFormatter(loc);
System.out.println(germanWithoutDots.parse("So, 18 Jul 2021 15:24:00 +0200", Instant::from));
代码段的输出是:
2021-07-18T13:24:00Z
删除最后一个点的月份缩写不起作用,因为正如您所观察到的,CLDR 的缩写是 Juli
,而您得到的是 Jul
。因此,我没有删除点,而是缩写为三个字符。您应该测试它是否适用于所有月份(包括 Mai)。
我还没有对法语和荷兰语进行相同的尝试,但应该可以。
如果您想尝试解决方案 1 的运气,完全绕过 CLDR,请参阅 。
现代日期时间 API 对模式非常讲究。因此,几乎不可能创建可用于解析所有类型字符串的单一模式。然而,DateTimeFormatter
的最大特点之一是它可以灵活地处理可选模式,使用方括号指定,例如以下演示使用 E, d [MMMM][MMM][M] u H:m:s Z
,它具有三个月份的可选模式。
演示:
import java.time.DateTimeException;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
Stream.of(
"So., 18 Juli 2021 15:24:00 +0200",
"ven., 16 avr. 2021 15:24:00 +0200",
"vr, 16 apr. 2021 15:24:00 +0200",
"vr, 16 07 2021 15:24:00 +0200"
).forEach(s -> {
Stream.of(
Locale.GERMANY,
Locale.FRANCE,
new Locale("nl", "NL")
).forEach( locale -> {
try {
System.out.println("Parsed '" + s + "' using the locale, " + locale + " => " + parseToInstant(s, locale));
}catch(DateTimeException e) {
//....
}
});
});
}
static Instant parseToInstant(String strDateTime, Locale locale) {
return DateTimeFormatter.ofPattern("E, d [MMMM][MMM][M] u H:m:s Z").withLocale(locale).parse(strDateTime,
Instant::from);
}
}
输出:
Parsed 'So., 18 Juli 2021 15:24:00 +0200' using the locale, de_DE => 2021-07-18T13:24:00Z
Parsed 'ven., 16 avr. 2021 15:24:00 +0200' using the locale, fr_FR => 2021-04-16T13:24:00Z
Parsed 'vr, 16 apr. 2021 15:24:00 +0200' using the locale, nl_NL => 2021-04-16T13:24:00Z
Parsed 'vr, 16 07 2021 15:24:00 +0200' using the locale, nl_NL => 2021-07-16T13:24:00Z
从 DateTimeFormatterBuilder
中了解有关日期时间模式的更多信息。
我有以下德国日期:So, 18 Jul 2021 15:24:00 +0200
我无法使用 Java 时间解析它:
DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss Z", Locale.GERMANY)
.parse("So, 18 Jul 2021 15:24:00 +0200", Instant::from)
因为它抛出:Text 'So, 18 Jul 2021 15:24:00 +0200' could not be parsed at index 0
如果我要更改字符串以使其格式正确,它会起作用:
-So, 18 Jul 2021 15:24:00 +0200
+So., 18 Juli 2021 15:24:00 +0200
有什么神奇的模式可以解析上面的日期吗?
我在其他日期也遇到了同样的问题
LocalDateTime.parse("ven, 16/07/2021 - 09:49", DateTimeFormatter.ofPattern("EE, dd/MM/yyyy - HH:mm", Locale("fr")))
ven
必须是ven.
LocalDateTime.parse("vr, 23 apr 2021 17:04:00", DateTimeFormatter.ofPattern("EE, dd MM yyyy HH:mm:ss", Locale("nl")))
apr
必须是04
(才能使用MM
)
为星期几指定您自己的缩写
根据 CLDR,德语星期几的缩写是用点写的。要让 Java 解析缩写缺少点的字符串,有两个明显的解决方案:
- 不要使用 CLDR。 Java 自己在 Java 8 和之前的缩写没有点,在较新的 Java 版本中仍然可用。
- 指定您自己的缩写。
因为你在法语方面也有类似的问题,Java自己的缩写也有点,我建议解决方案 1. 对你来说是不够的。因此,让我们深入研究解决方案 2。我下面的代码采用 CLDR 的缩写,例如 So.
,并从中删除尾随点,因此您得到例如字符串中的 So
。
Locale loc = Locale.GERMANY;
Map<Long,String> dowsWithoutDots = Arrays.stream(DayOfWeek.values())
.collect(Collectors.toMap(dow -> Long.valueOf(dow.getValue()),
dow -> dow.getDisplayName(TextStyle.SHORT, loc).replaceFirst("\.$", "")));
Map<Long,String> monthsWithoutDots = Arrays.stream(Month.values())
.collect(Collectors.toMap(m -> Long.valueOf(m.getValue()),
m -> m.getDisplayName(TextStyle.SHORT, loc).substring(0, 3)));
DateTimeFormatter germanWithoutDots = new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_WEEK, dowsWithoutDots)
.appendPattern(", dd ")
.appendText(ChronoField.MONTH_OF_YEAR, monthsWithoutDots)
.appendPattern(" yyyy HH:mm:ss Z")
.toFormatter(loc);
System.out.println(germanWithoutDots.parse("So, 18 Jul 2021 15:24:00 +0200", Instant::from));
代码段的输出是:
2021-07-18T13:24:00Z
删除最后一个点的月份缩写不起作用,因为正如您所观察到的,CLDR 的缩写是 Juli
,而您得到的是 Jul
。因此,我没有删除点,而是缩写为三个字符。您应该测试它是否适用于所有月份(包括 Mai)。
我还没有对法语和荷兰语进行相同的尝试,但应该可以。
如果您想尝试解决方案 1 的运气,完全绕过 CLDR,请参阅
现代日期时间 API 对模式非常讲究。因此,几乎不可能创建可用于解析所有类型字符串的单一模式。然而,DateTimeFormatter
的最大特点之一是它可以灵活地处理可选模式,使用方括号指定,例如以下演示使用 E, d [MMMM][MMM][M] u H:m:s Z
,它具有三个月份的可选模式。
演示:
import java.time.DateTimeException;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
Stream.of(
"So., 18 Juli 2021 15:24:00 +0200",
"ven., 16 avr. 2021 15:24:00 +0200",
"vr, 16 apr. 2021 15:24:00 +0200",
"vr, 16 07 2021 15:24:00 +0200"
).forEach(s -> {
Stream.of(
Locale.GERMANY,
Locale.FRANCE,
new Locale("nl", "NL")
).forEach( locale -> {
try {
System.out.println("Parsed '" + s + "' using the locale, " + locale + " => " + parseToInstant(s, locale));
}catch(DateTimeException e) {
//....
}
});
});
}
static Instant parseToInstant(String strDateTime, Locale locale) {
return DateTimeFormatter.ofPattern("E, d [MMMM][MMM][M] u H:m:s Z").withLocale(locale).parse(strDateTime,
Instant::from);
}
}
输出:
Parsed 'So., 18 Juli 2021 15:24:00 +0200' using the locale, de_DE => 2021-07-18T13:24:00Z
Parsed 'ven., 16 avr. 2021 15:24:00 +0200' using the locale, fr_FR => 2021-04-16T13:24:00Z
Parsed 'vr, 16 apr. 2021 15:24:00 +0200' using the locale, nl_NL => 2021-04-16T13:24:00Z
Parsed 'vr, 16 07 2021 15:24:00 +0200' using the locale, nl_NL => 2021-07-16T13:24:00Z
从 DateTimeFormatterBuilder
中了解有关日期时间模式的更多信息。