我在传统应用程序中有代码和测试用例,可归纳如下:
@Test
public void testParseDate() throws ParseException {
String toParse = "Mo Aug 18 11:25:26 MESZ +0200 2014";
String pattern = "EEE MMM dd HH:mm:ss z Z yyyy";
DateFormat dateFormatter = new SimpleDateFormat(pattern, Locale.GERMANY);
Date date = dateFormatter.parse(toParse);
//skipped assumptions
}
此测试在Java 8及更低版本中通过。但是,随着Java 10向上,这将导致java.text.ParseException: Unparseable date: "Mo Aug 18 11:25:26 MESZ +0200 2014"
。
记录:
除了de_DE
之外,还会为区域设置抛出异常
de_CH
,de_AT
,de_LU
。
我知道日期格式为changed with JDK 9(JEP 252)。但是,我认为这是一个破坏向后兼容性的破坏性变化。摘录:
在JDK 9中,Unicode Consortium的公共区域设置数据存储库(CLDR)数据作为默认区域设置数据启用,因此您可以使用标准区域设置数据而无需任何进一步操作。
在JDK 8中,尽管CLDR区域设置数据与JRE捆绑在一起,但默认情况下不会启用它。
使用区域设置敏感服务(如日期,时间和数字格式)的代码可能会对CLDR区域设置数据产生不同的结果。
为一周中的某一天添加.
(Mo.
)会对此进行补偿并且测试将通过。但是,这不是旧数据的真正解决方案(以XML等序列化形式)。
检查此stackoverflow post,似乎该行为是德国语言环境的故意,可以通过java.locale.providers
模式指定COMPAT
来缓解此问题。但是,我不喜欢依赖于某些系统属性值的想法有两个原因:
我的问题是:
java.locale.providers
),这可能会在不同环境中被遗忘(应用程序服务器,独立jar,...)?答案 0 :(得分:18)
我不是说这是一个很好的解决方案,但它似乎是一种方法。
Map<Long, String> dayOfWeekTexts = Map.of(1L, "Mo", 2L, "Di",
3L, "Mi", 4L, "Do", 5L, "Fr", 6L, "Sa", 7L, "So");
Map<Long, String> monthTexts = Map.ofEntries(Map.entry(1L, "Jan"),
Map.entry(2L, "Feb"), Map.entry(3L, "Mär"), Map.entry(4L, "Apr"),
Map.entry(5L, "Mai"), Map.entry(6L, "Jun"), Map.entry(7L, "Jul"),
Map.entry(8L, "Aug"), Map.entry(9L, "Sep"), Map.entry(10L, "Okt"),
Map.entry(11L, "Nov"), Map.entry(12L, "Dez"));
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_WEEK, dayOfWeekTexts)
.appendLiteral(' ')
.appendText(ChronoField.MONTH_OF_YEAR, monthTexts)
.appendPattern(" dd HH:mm:ss z Z yyyy")
.toFormatter(Locale.GERMANY);
String toParse = "Mo Aug 18 11:25:26 MESZ +0200 2014";
OffsetDateTime odt = OffsetDateTime.parse(toParse, formatter);
System.out.println(odt);
ZonedDateTime zdt = ZonedDateTime.parse(toParse, formatter);
System.out.println(zdt);
在我的Oracle JDK 10.0.1上运行的输出:
2014-08-18T11:25:26+02:00
2014-08-18T11:25:26+02:00[Europe/Berlin]
然后,再也没有好的解决方案。
java.time
,现代Java日期和时间API,允许我们指定用于格式化和解析的字段的文本。因此,我将其用于星期和月份,指定与旧COMPAT或JRE语言环境数据一起使用的没有点的缩写。我使用Java 9 Map.of
和Map.ofEntries
来构建我们需要的地图。如果这也适用于Java 8,你必须找到一些其他的方法来填充这两张地图,我相信你这样做。
如果您确实需要老式的java.util.Date
(可能在遗留代码库中),请按以下方式进行转换:
Date date = Date.from(odt.toInstant());
System.out.println("As legacy Date: " + date);
我的时区输出(欧洲/哥本哈根,可能与您的大致一致):
As legacy Date: Mon Aug 18 11:25:26 CEST 2014
我想如果那是我,我会考虑这样做:
System.setProperty("java.locale.providers", "COMPAT,CLDR");
,这样在任何环境中都不会忘记它。 COMPAT语言环境数据自1.0以来一直存在(我相信,至少接近),因此很多代码依赖于它(不仅仅是你的)。在Java 9中,名称已从JRE更改为COMPAT。对我而言,这可能听起来像是一个保持数据相当长一段时间的计划。根据{{3}},它仍然可以在Java 11(下一个“长期支持”Java版本)中使用,并且没有弃用警告等。如果在将来的某个Java版本中将其删除,您可能很快就会发现可以在升级之前处理该问题。答案 1 :(得分:1)
提一下:SimpleDateFormat
是格式化BTW不是线程安全的日期的旧方法。从Java 8开始,有一些名为java.time
和java.time.format
的新包,您应该使用它们来处理日期。出于您的目的,您应该使用课程DateTimeFormatter。
答案 2 :(得分:0)
java 8中的格式化值是Fr Juni 15 00:20:21 MESZ +0900 2018 但它改为Fr. Juni 15 00:20:21 MESZ +0900 2018 EEE包括。这是兼容性问题,旧版本的代码在新版本中不起作用并不重要。(对不起翻译者)如果日期字符串是您的,则应为新版本用户添加点。或者让用户使用Java 8来使用您的软件。
它可以使软件变慢,使用substring方法也很好。
String toParse = "Mo Aug 18 11:25:26 MESZ +0200 2014";
String str = toParse.substring(0, 2) + "." + toParse.substring(2);
String pattern = "EEE MMM dd HH:mm:ss z Z yyyy";
DateFormat dateFormatter = new SimpleDateFormat(pattern, Locale.GERMANY);
System.out.println(dateFormatter.format(System.currentTimeMillis()));
Date date = dateFormatter.parse(str);
再次抱歉我的英语不好。
答案 3 :(得分:0)
这是一个有效但丑陋的解决方法。这很丑陋,因为您必须在自己的映射中重新定义所有单词,但您仍然拥有高效灵活的默认解析器的所有好处。
String dateString = "Mi Mai 09 09:17:24 2018";
Map<Long, String> dayOfWeekTexts =
Map.of(1L, "Mo", 2L, "Di", 3L, "Mi", 4L, "Do", 5L, "Fr", 6L, "Sa", 7L, "So");
Map<Long, String> monthTexts =
Map.ofEntries(
Map.entry(1L, "Jan"),
Map.entry(2L, "Feb"),
Map.entry(3L, "Mär"),
Map.entry(4L, "Apr"),
Map.entry(5L, "Mai"),
Map.entry(6L, "Jun"),
Map.entry(7L, "Jul"),
Map.entry(8L, "Aug"),
Map.entry(9L, "Sep"),
Map.entry(10L, "Okt"),
Map.entry(11L, "Nov"),
Map.entry(12L, "Dez"));
DateTimeFormatter dtf =
new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_WEEK, dayOfWeekTexts)
.appendLiteral(' ')
.appendText(ChronoField.MONTH_OF_YEAR, monthTexts)
.appendPattern(" dd HH:mm:ss yyyy")
.toFormatter(Locale.GERMAN);
LocalDateTime dateTime = LocalDateTime.parse(dateString, dtf);
稍作修改的答案