Search code examples
javatimezonelocalejava-timedateformatter

Java 8: DateTimeFormatter not translating timezone based on locale


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.


Solution

  • 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.

    Reminder: The LTS versions of Java are 8, 11, 17, & 21.

    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.