Java 8: DateTimeFormatter not translating timezone based on locale

Viewed 1355

I am using DateTimeFormatter to format date:

    ZonedDateTime date = ZonedDateTime.parse("2015-12-03T18:15:30+01:00[America/New_York]");

    DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL)
                .withLocale(Locale.FRENCH);

    System.out.println(formatter.format(date));

Code Output:

jeudi 3 décembre 2015 18 h 15 EST

How can I translate timezone as well from EST to French HNE (Heure Normale de l'Est)?

Any help will be appreciated.

3 Answers

tl;dr

➥ Upgrade beyond Java 8.

Java 9 and later, using the CLDR, now exhibits different localization behavior for some locales.

Running your code in Java 8 shows your observed results jeudi 3 décembre 2015 18 h 15 EST, while running in Java 9 and later shows your desired results jeudi 3 décembre 2015 à 18:15:30 heure normale de l’Est nord-américain.

Details

I tried your code as-is, except for the var name.

System.out.println( System.getProperty( "java.version" ) );

ZonedDateTime zdt = ZonedDateTime.parse( "2015-12-03T18:15:30-05:00[America/New_York]" );

DateTimeFormatter formatter =
        DateTimeFormatter
                .ofLocalizedDateTime( FormatStyle.FULL )
                .withLocale( Locale.FRENCH );

System.out.println( formatter.format( zdt ) );

Results:

10.0.2

jeudi 3 décembre 2015 à 18:15:30 heure normale de l’Est nord-américain

But you claim to have seen this result:

jeudi 3 décembre 2015 18 h 15 EST

Why the difference?

Java 10 versus Java 8

Well, I was running Java 10. Let's try Java 8 instead.

1.8.0_181

jeudi 3 décembre 2015 18 h 15 EST

Ah-ha! Now I see your results.

Erroneous offset

By the way, as discovered by Meno Hochschild, your example data "2015-12-03T18:15:30+01:00[America/New_York]" uses an incorrect offset-from-UTC of +01:00 for zone America/New_York. Should have been -05:00. I corrected your string for use in this Answer. See that Answer by Hochschild for more discussion.

CLDR

I suspect the difference is due to a major change made in Java 9, at least in the OpenJDK-based implementations of Java 9. In Java 9, OpenJDK switched from using its own definitions of localization data to outsourcing from the Unicode Consortium the Common Locale Data Repository (CLDR). See JEP 252: Use CLDR Locale Data by Default.

Among the many kinds of data provided is “Translations for timezones and example cities (or similar) for timezones”.

So no bugs, no problem. Localization is a complicated process where there is often more than one opinion about appropriate choices. You have found an example where the authors of the localization data bundled with OpenJDK 8 and earlier disagree with the authors at the Unicode Consortium. Or perhaps the cultural norms changed over time for that particular locale.

One great benefit of CLDR, besides a much richer set of locale data, is that the CLDR is the de facto standard source for locale data. The CLDR has been adopted by many other systems. So now Java apps will exhibit behavior identical to other systems, or nearly identical with some possible differences due to versioning.

And speaking of versioning, there (hopefully) is likely to be little churn in changes for most locales, as the Unicode Consortium has worked hard on this for many years now. The CLDR should be relatively complete and stable nowadays. While some Java apps jumping from Java <=8 to Java >=9 may see some major changes in behavior, as seen in this Question, going forward you should see very few surprises/changes.

Caveat: This entire discussion here relates to Java implementations based on OpenJDK. Nowadays, that means most of the vendors (Azul, AdoptOpenJDK.net, Amazon Corretto, IBM, Oracle JDK, and Oracle build of OpenJDK). But any Java implementation not built from OpenJDK is free to use an alternate source of localization data and behavior other than CLDR.

Workaround for Java 8

The CLDR is actually included in OpenJDK 8, per JEP 127, but is not used by default. What changed in OpenJDK 9 and later is that the CLDR was promoted by default to be the first locale data provider consulted, per JEP 252. See Answer by Meno Hochschild to make CLDR the primary locale data provider in 8.

Locale = language + culture

Tip: Try to avoid using merely “FRENCH” when specifying your locale. A locale is a combination of a human language plus a country code representing a set of cultural norms the determine issues such as capitalization, abbreviation, ordering of element (ex: year-month-day), and such. While Locale.FRENCH may fall back to using country such as France, I suggest you be explicit. Use Locale.FRANCE or Locale.CANADA_FRENCH for example.

For using the hundreds of languages and country codes not defined in the few Java constants, use the standard string codes as documented. For example, fr-FR for French in France.

Also, note that the CLDR has much more detailed and nuanced locale info, so you may now choose to use many variants and extensions (basically subcultures within the national culture) that were not defined in the old locale dataset. See Locale and the Unicode Consortium site for more info.

The question and other answers given so far only concentrate on the topic of zone name translations. But the main problem is the wrong time result of parsing. Now let's go into the details.

  • The combination of a fixed offset (UTC+01) and the zone identifier "America/New_York" is somehow strange because the zone identified by "America/New_York" only knows the offsets UTC-05 (and UTC-04 in summer).

  • Such an ambivalent and contradictious combination should be parsed with priority given to the offset. So the time "2015-12-03T18:15:30+01:00" is the same moment as "2015-12-03T12:15:30-05:00", but you have still parsed the same local time "18:15:30" in EST zone (with offset UTC-05) which is a heavy error. The associated JDK-Bug is solved but only for Java 9+, not yet for Java-8 backport.

  • About the topic of translations, Basil Bourque has said some correct things about the introduction of CLDR data as standard/default for Java 9 onwards. The change of default repository can be done in Java-8 by setting the system property to "java.locale.providers=CLDR,JRE". This configuration would probably solve your translation issue for Java-8, but the other problem of wrong parsed time still remains.

  • So my final recommendation is to upgrade to Java 9+.

You have to use the suitable Zone among global zones defined by Oracle corporation. TimeZone.getAvailableIDs() can be used to view all the available time zones as a String array. According to your requirement you can use Europe/Paris time zone. Define a new date with new time zone without changing the date as following,

ZonedDateTime dateParis = 
date.withZoneSameLocal( ZoneId.of( "Europe/Paris" ) );

Please refer following links for more details https://docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html#getAvailableIDs%28%29 https://garygregory.wordpress.com/2013/06/18/what-are-the-java-timezone-ids/ https://dzone.com/articles/deeper-look-java-8-date-and

Related