I would like to add the following details to @James_D’s correct answer:
Background: Most date and time libraries (Java.util.Calendar
in Java, see also .Net-DateTime or Date
in JavaScript or DateTime
in Perl) are based on the concept of a universal unique temporal type for all uses (in German there is the poetic expression “eierlegende Wollmilchsau”). There cannot be an unsupported field in this drawing. But the price is high: many temporal problems cannot be adequately managed with such an inflexible approach because it is difficult to find a common denominator for all types of temporal objects.
JSR-310 chose another way , which is to allow different time types consisting of specific sets of supported built-in field types. The natural consequence is that not all possible fields are supported by every type (and users can even define their own specialized fields). it is also possible ask programmatically each type object TemporalAccessor
for its specific set of supported fields. For LocalDate
we find:
•DAY_OF_WEEK
•ALIGNED_DAY_OF_WEEK_IN_MONTH
•ALIGNED_DAY_OF_WEEK_IN_YEAR
•DAY_OF_MONTH
•DAY_OF_YEAR
•Epoch_DAY
•ALIGNED_WEEK_OF_MONTH
•ALIGNED_WEEK_OF_YEAR
•MONTH_OF_YEAR
•PROLEPTIC_MONTH
•YEAR_OF_ERA
•YEAR
•ERA
There is no HOUR_OF_DAY field that explains the problem with UnsupportedTemporalTypeException
. And if we look at the JSR-310 – mapping of pattern symbols to fields we see that the symbol H is mapped to unsupported HOUR_OF_DAY:
/** Map of letters to fields. */
private static final Map FIELD_MAP = new HashMap<>();
static {
FIELD_MAP.put('G', ChronoField.ERA);
FIELD_MAP.put('y', ChronoField.YEAR_OF_ERA);
FIELD_MAP.put('u', ChronoField.YEAR);
FIELD_MAP.put('Q', IsoFields.QUARTER_OF_YEAR);
FIELD_MAP.put('q', IsoFields.QUARTER_OF_YEAR);
FIELD_MAP.put('M', ChronoField.MONTH_OF_YEAR);
FIELD_MAP.put('L', ChronoField.MONTH_OF_YEAR);
FIELD_MAP.put('D', ChronoField.DAY_OF_YEAR);
FIELD_MAP.put('d', ChronoField.DAY_OF_MONTH);
FIELD_MAP.put('F', ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH);
FIELD_MAP.put('E', ChronoField.DAY_OF_WEEK);
FIELD_MAP.put('c', ChronoField.DAY_OF_WEEK);
FIELD_MAP.put('e', ChronoField.DAY_OF_WEEK);
FIELD_MAP.put('a', ChronoField.AMPM_OF_DAY);
FIELD_MAP.put('H', ChronoField.HOUR_OF_DAY);
FIELD_MAP.put('k', ChronoField.CLOCK_HOUR_OF_DAY);
FIELD_MAP.put('K', ChronoField.HOUR_OF_AMPM);
FIELD_MAP.put('h', ChronoField.CLOCK_HOUR_OF_AMPM);
FIELD_MAP.put('m', ChronoField.MINUTE_OF_HOUR);
FIELD_MAP.put('s', ChronoField.SECOND_OF_MINUTE);
FIELD_MAP.put('S', ChronoField.NANO_OF_SECOND);
FIELD_MAP.put('A', ChronoField.MILLI_OF_DAY);
FIELD_MAP.put('n', ChronoField.NANO_OF_SECOND);
FIELD_MAP.put('N', ChronoField.NANO_OF_DAY);
}
This field mapping does not mean that the field is supported by the concrete type. The analysis takes place in several steps. Field mapping is only the first step. The second step is then to analyze a raw object of type TemporalAccessor
. Finally, analyze the delegates to the type of destination (here: LocalDate
) and let it decide if it accepts all the values of the fields in the parsed intermediate object.