Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LocalDate and DateTimeFormatter do not work correctly with Japanese locale

I've run across a strange issue when testing some method of mine and I seem to have been able to obtain a concrete example of my issue.

I am using the ja_JP_JP_#u-ca-japanese locale not able to parse a date using it's own date pattern defined by the locale.

I am wondering whether I am doing something wrong, or if this is a JDK bug.

Please note that in order to construct ja_JP_JP_#u-ca-japanese, you need to use new Locale("ja", "JP", "JP") as per excerpt from the Locale javadoc:

Special cases

For compatibility reasons, two non-conforming locales are treated as special cases. These are ja_JP_JP and th_TH_TH. These are ill-formed in BCP 47 since the variants are too short. To ease migration to BCP 47, these are treated specially during construction. These two cases (and only these) cause a constructor to generate an extension, all other values behave exactly as they did prior to Java 7.

Java has used ja_JP_JP to represent Japanese as used in Japan together with the Japanese Imperial calendar. This is now representable using a Unicode locale extension, by specifying the Unicode locale key ca (for "calendar") and type japanese. When the Locale constructor is called with the arguments "ja", "JP", "JP", the extension "u-ca-japanese" is automatically added.

Java has used th_TH_TH to represent Thai as used in Thailand together with Thai digits. This is also now representable using a Unicode locale extension, by specifying the Unicode locale key nu (for "number") and value thai. When the Locale constructor is called with the arguments "th", "TH", "TH", the extension "u-nu-thai" is automatically added.

The given test case that demonstrates the problem:

@Test
public void testJapaneseLocale() {
    LocalDate specificLocalDate = LocalDate.of(2014, 10, 2);
    Locale jpLocale = new Locale("ja", "JP", "JP");
    
    DateTimeFormatter jpDateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(jpLocale);
    String jpDate = specificLocalDate.format(jpDateTimeFormatter);
    
    String jpPattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.SHORT, null, Chronology.ofLocale(jpLocale), jpLocale);
    LocalDate jpLocalDate = LocalDate.parse(jpDate, DateTimeFormatter.ofPattern(jpPattern, jpLocale));
    
    assertEquals(specificLocalDate, jpLocalDate);
}

This code does work for any other normal locale, such as english, etc.

like image 647
skiwi Avatar asked Oct 02 '14 20:10

skiwi


People also ask

What is the Java time format DateTimeFormatter?

DateTimeFormatterBuilder Class in Java. DateTimeFormatterBuilder Class is a builder class that is used to create date-time formatters. DateTimeFormatter is used as a Formatter for printing and parsing date-time objects.

What is the role of the DateTimeFormatter type in Java?

Class DateTimeFormatter. Formatter for printing and parsing date-time objects. This class provides the main application entry point for printing and parsing and provides common implementations of DateTimeFormatter : Using predefined constants, such as ISO_LOCAL_DATE.

How do I parse LocalDateTime?

Parsing date and time To create a LocalDateTime object from a string you can use the static LocalDateTime. parse() method. It takes a string and a DateTimeFormatter as parameter. The DateTimeFormatter is used to specify the date/time pattern.


1 Answers

The round tripping ability does work, but it needs all the data to be made available.

In the first case, you need to specify both the locale and the chronology using the withLocale() and withChronology() methods:

LocalDate date = LocalDate.of(2014, 10, 2);
Locale jpLocale = new Locale("ja", "JP", "JP");
Chronology chrono = Chronology.ofLocale(jpLocale);
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
        .withLocale(jpLocale)
        .withChronology(chrono);
String jpDateStr = date.format(f);
LocalDate result = LocalDate.parse(jpDateStr, f);

The same is true of the second case, where using ofPattern() locks the locale, but not the chronology:

String pattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
    FormatStyle.SHORT, null, chrono, jpLocale);
DateTimeFormatter f = DateTimeFormatter.ofPattern(pattern, jpLocale)
    .withChronology(chrono);
LocalDate jpLocalDate = LocalDate.parse(jpDateStr, f);

Only when both locale and chronology are available and used will round tripping be possible. Your issue is based around trying to format using the Japanese locale, but inconsistently applying the Japanese chronology.

like image 64
JodaStephen Avatar answered Oct 23 '22 14:10

JodaStephen