I have code and a test-case in a legacy application, which can be summarized as follows:
@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 }
This test passes in Java 8 and below. However with Java 10 upwards this leads to a java.text.ParseException: Unparseable date: "Mo Aug 18 11:25:26 MESZ +0200 2014"
.
For the record: Besides de_DE
, the exception is also thrown for the locales de_CH
, de_AT
, de_LU
.
I am aware of the fact, that Date formatting was changed with JDK 9 (JEP 252). However, I consider this to be a disruptive change breaking backwards compatibility. Excerpted:
In JDK 9, the Unicode Consortium's Common Locale Data Repository (CLDR) data is enabled as the default locale data, so that you can use standard locale data without any further action.
In JDK 8, although CLDR locale data is bundled with the JRE, it isn’t enabled by default.
Code that uses locale-sensitive services such as date, time, and number formatting may produce different results with the CLDR locale data.
Adding a .
for the day of the week (Mo.
) compensates for this and test would pass. However, this is not a real solution for old data (in serialized form such as XML).
Checking this stackoverflow post, it seems that the behaviour is intentional for the German locale and can be mitigated by specifying java.locale.providers
with COMPAT
mode. However, I do not like the idea to rely on some system property value for two reasons as it might:
My question is:
java.locale.providers
), which may be forgotten in different environments (application servers, standalone jars, ...) ?DateTimeFormatter is a replacement for the old SimpleDateFormat that is thread-safe and provides additional functionality.
Class SimpleDateFormat. SimpleDateFormat is a concrete class for formatting and parsing dates in a locale-sensitive manner. It allows for formatting (date -> text), parsing (text -> date), and normalization. SimpleDateFormat allows you to start by choosing any user-defined patterns for date-time formatting.
It is mostly used where we need to display or utilize the date and time functionality of Java. Both of these classes are present in com. text package. DateFormat is used for formatting a date into String based on specific locale that is provided as input.
I don’t say it’s a nice solution, but it seems to be a way through.
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);
Output running on my Oracle JDK 10.0.1:
2014-08-18T11:25:26+02:00 2014-08-18T11:25:26+02:00[Europe/Berlin]
Then again, no nice solution may exist.
java.time
, the modern Java date and time API, allows us to specify texts to use for fields for both formatting and parsing. So I exploit that for both day of week and for month, specifying the abbreviations without dot that were used with the old COMPAT or JRE locale data. I have used the Java 9 Map.of
and Map.ofEntries
for building the maps we need. If this is to work in Java 8 too, you must find some other way to populate the two maps, I trust you to do that.
If you do need an old-fashioned java.util.Date
(likely in a legacy code base), convert like this:
Date date = Date.from(odt.toInstant()); System.out.println("As legacy Date: " + date);
Output in my time zone (Europe/Copenhagen, probably roughly agrees with yours):
As legacy Date: Mon Aug 18 11:25:26 CEST 2014
I am thinking that if that were me, I’d consider proceeding this way:
System.setProperty("java.locale.providers", "COMPAT,CLDR");
so it won’t be forgot in any environment. The COMPAT locale data have been around since 1.0 (I believe, at least close), so a lot of code out there depends on it (not only yours). The name was changed from JRE to COMPAT in Java 9. To me this may sound like a plan to keep the data around for quite a while still. According to the early access documentation it will still be available in Java 11 (the next “long term support” Java version) and with no deprecation warning or the like. And should it be removed in some future Java version, you will probably be able to find out soon enough that you can deal with the problem before upgrading.Just to mention: SimpleDateFormat
is an old way to format dates which BTW is not thread safe. Since Java 8 there are new packages called java.time
and java.time.format
and you should use those to work with dates. For your purposes you should use class DateTimeFormatter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With