java – Format a date using the new date and time API

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.

Leave a comment