Issue
I'm having this little piece of code:
import java.text.SimpleDateFormat;
import java.util.Date;
public class Main {
public static void main(String[] args) {
SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
System.out.println(format.format(new Date()));
}
}
With Java 8, the output is:
Tue, 16 Jul 2019 13:16:54 AEST
With Java 11:
Tue., 16 Jul. 2019 13:16:54 AEST
Why is there a difference and how should I modify my code to make it work evenly in both Java versions?
I was actually using Handlebars while finding this issue, and I could narrow it down to the Java version - but it would be cool to know how to use Handlebars to produce the same result for the same format string...)
Solution
TL;DR
Run your Java 9 and later (including Java 11) with the system property java.locale.providers
defined like this:
java -Djava.locale.providers=COMPAT,CLDR YourApp
Now output is without the dots, in the same format as on Java 8, for example:
Tue, 16 Jul 2019 14:24:15 AEST
CLDR
Java gets its locale data, including abbreviations used for days of the week and for months in different languages, from up to four sources. Up to Java 8 Java’s own locale data were the default. From Java 8 locale data from Unicode Common Locale Data Repository (CLDR; see links at the bottom) are included too, and from Java 9 they are the default. Java’s own data are still included and accessible by specifying COMPAT
in the above system property. We need to put it first in the string as the sources are tried in turn.
One might have expected that another (and perhaps even a nicer) solution would be to use CLDR in all Java versions. Curiously this doesn’t give us the same format on all Java versions in this case. Here is the output when setting the property to CLDR,JRE
(JRE is the old name for COMPAT, on Java 8 we need to use this instead).
On Java 8:
Tue, 16 Jul 2019 14:35:02 AEST
On Java 9 and 11:
Tue., 16 Jul. 2019 14:35:52 AEST
CLDR comes in versions, and not the same version is included with the different Java versions.
java.time
Here’s the snippet I have used for the above outputs.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
"EEE, dd MMM yyyy HH:mm:ss zzz", Locale.forLanguageTag("en-AU"));
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Australia/Sydney"));
System.out.println(now.format(formatter));
I am using and recommending java.time, the modern Java date and time API. The date-time classes that you used, SimpleDateFormat
and Date
, are long outdated and were always poorly designed, so I recommend avoiding them. On Java 8 and later there’s certainly no reason why we should use them, and java.time has been backported to Java 6 and 7 too.
Links
- CLDR - Unicode Common Locale Data Repository
- Wikipedia article: Common Locale Data Repository
- Use CLDR Locale Data by Default in Java Platform, Standard Edition Oracle JDK 9 Migration Guide
LocaleServiceProvider
documentation spelling out the possible locale data sources: CLDR, COMPAT and more.- Oracle tutorial: Date Time explaining how to use java.time.
Answered By - Ole V.V.
Answer Checked By - Candace Johnson (JavaFixing Volunteer)