Home > Net >  Java DateTimeFormatter can't format its own parsed TemporalAccessor
Java DateTimeFormatter can't format its own parsed TemporalAccessor

Time:12-11

I'm creating a simply DateTimeFormatter, but it can't seem to read it's own output. The reason I need to use the DateTimeFormatter directly and not LocalDate.format or YearMonth.format is that the code needs to be generic enough to handle different instances of DateTimeFormatter with completely different fields. The code below fails with: java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: YearOfEra

DateTimeFormatter yearMonthFormatter = DateTimeFormatter.ofPattern("yyyyMM");
TemporalAccessor yearMonthTemp = yearMonthFormatter.parse("200102");
String formatted = yearMonthFormatter.format(yearMonthTemp); //fails here trying to format its own output

Is there any way to accomplish the above without having to know the contents of the datetime pattern? i.e. the string "yyyyMM" is not static, it will be passed as a parameter.

Edit: Dang, asked and solved in 40 minutes, on a Saturday.

CodePudding user response:

What is happening here is that when you parse the string, an additional resolution phase happens, and turns the yyyy format specifier representing the "Year Of Era" temporal field into a "Year" temporal field in the returned TemporalAccessor, and you end up with (yearMonthTemp.toString()):

{Year=2001, MonthOfYear=2},ISO

When you format, the yyyy format specifier expects to format a "Year Of Era" temporal field, but the temporal accessor doesn't have it, as you can clearly see above.

If one needed to change the format of a Temporal without knowing the formats head of time, eg from yyyyMM to MMM-yyyy, you would need two different DateTimeFormatters. The formatters would be completely compatible in terms of the fields that they parse and the temporal objects they produce.

If all you want to do is changing formats, you can set the resolver style to STRICT (by default this is SMART):

DateTimeFormatter.ofPattern("yyyyMM")
    .withResolverStyle(ResolverStyle.STRICT);

Though I cannot find documentation for this, I have found that this will prevent it from automatically changing "Year Of Era" to "Year" (which is typically denoted uuuu). From my testing, every parsed temporal field will be present in the result. Assuming that you have the same format specifiers in the formatting DateFormatter, it will get the same temporal fields successfully.

Note that this also means that you cannot easily get something like a YearMonth from this DateTimeFormatter, because YearMonth.parse/YearMonth.from expects a "Year". not "Year Of Era".

// does not work
System.out.println(YearMonth.from(yearMonthTemp));
  • Related