javadatejava-time2-digit-year

Localize string from `MonthDay` or `YearMonth` classes in java.time?


The java.time classes built into Java 8 and later offer the MonthDay and YearMonth classes. Their toString and parse methods use standard ISO 8601 formats (--MM-DD & YYYY-MM), which is wise.

For presentation to humans, the standard formats may not be suitable. Is there anyway to generate an automatically localized string to represent the values in objects of either MonthDay or YearMonth?

For example, in the United States users might typically want MM/DD for month-day and MM/YY for year-month. While in the UK users might want DD/MM for month-day.

Anyway to automate such variations by Locale rather than explicitly define formatting patterns?


I tried the following code, using a date-oriented localizing formatter.

Locale l = Locale.US;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate ( FormatStyle.SHORT ).withLocale ( l );

YearMonth ym = YearMonth.of ( 2017 , Month.JANUARY );
MonthDay md = MonthDay.of ( Month.JANUARY , 29 );

String outputYm = ym.format ( f );
String outputMd = md.format ( f );

That code fails, throwing an exception when used for either YearMonth or MonthDay.

For YearMonth:

Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfMonth
    at java.time.YearMonth.getLong(YearMonth.java:494)
    at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
    at java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormatterBuilder.java:2540)
    at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2179)
    at java.time.format.DateTimeFormatterBuilder$LocalizedPrinterParser.format(DateTimeFormatterBuilder.java:4347)
    at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2179)
    at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1746)
    at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1720)
    at java.time.YearMonth.format(YearMonth.java:1073)
    at javatimestuff.App.doIt(App.java:56)
    at javatimestuff.App.main(App.java:45)

For MonthDay:

Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: YearOfEra
    at java.time.MonthDay.getLong(MonthDay.java:451)
    at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
    at java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormatterBuilder.java:2540)
    at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2179)
    at java.time.format.DateTimeFormatterBuilder$LocalizedPrinterParser.format(DateTimeFormatterBuilder.java:4347)
    at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2179)
    at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1746)
    at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1720)
    at java.time.MonthDay.format(MonthDay.java:646)
    at javatimestuff.App.doIt(App.java:57)
    at javatimestuff.App.main(App.java:45)

Solution

  • See JDK-8168532. There needs an enhancement in the JDK to make this easy.

    It is possible to do the work outside the JDK, but it is a lot of work. You have to parse the CLDR XML files (which are interlinked and have many references). Then you extract the relevant localized patterns for MonthYear and YearMonth, Then those patterns can be used to create a DateTimeFormatter.

    Alternatively, you can hard code a map - Map<Locale, DateTimeFormatter> - based on your business needs.