Format a date using the new date time API
Asked Answered
F

7

186

I was playing with the new date time API but when running this:

public class Test {         
    public static void main(String[] args){
        String dateFormatted = LocalDate.now()
                                        .format(DateTimeFormatter
                                              .ofPattern("yyyy-MM-dd HH:mm:ss"));
        System.out.println(dateFormatted);
    }
}

It throws:

Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay
    at java.time.LocalDate.get0(LocalDate.java:680)
    at java.time.LocalDate.getLong(LocalDate.java:659)
    at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
    at java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormatterBuilder.java:2543)
    at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2182)
    at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1745)
    at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1719)
    at java.time.LocalDate.format(LocalDate.java:1685)
    at Test.main(Test.java:23)

When looking at the source code of the LocalDate class, I see:

  private int get0(TemporalField field) {
        switch ((ChronoField) field) {
            case DAY_OF_WEEK: return getDayOfWeek().getValue();
            case ALIGNED_DAY_OF_WEEK_IN_MONTH: return ((day - 1) % 7) + 1;
            case ALIGNED_DAY_OF_WEEK_IN_YEAR: return ((getDayOfYear() - 1) % 7) + 1;
            case DAY_OF_MONTH: return day;
            case DAY_OF_YEAR: return getDayOfYear();
            case EPOCH_DAY: throw new UnsupportedTemporalTypeException("Invalid field 'EpochDay' for get() method, use getLong() instead");
            case ALIGNED_WEEK_OF_MONTH: return ((day - 1) / 7) + 1;
            case ALIGNED_WEEK_OF_YEAR: return ((getDayOfYear() - 1) / 7) + 1;
            case MONTH_OF_YEAR: return month;
            case PROLEPTIC_MONTH: throw new UnsupportedTemporalTypeException("Invalid field 'ProlepticMonth' for get() method, use getLong() instead");
            case YEAR_OF_ERA: return (year >= 1 ? year : 1 - year);
            case YEAR: return year;
            case ERA: return (year >= 1 ? 1 : 0);
        }
        throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
    }

As it described in the doc:

This method will create a formatter based on a simple pattern of letters and symbols as described in the class documentation.

And all these letters are defined.

So why DateTimeFormatter.ofPattern doesn't allow us to use some pattern letters?

Forepleasure answered 14/4, 2014 at 20:8 Comment(0)
G
353

LocalDate represents just a date, not a DateTime. So "HH:mm:ss" make no sense when formatting a LocalDate. Use a LocalDateTime instead, assuming you want to represent both a date and time.

Glynas answered 14/4, 2014 at 20:10 Comment(5)
How can I upvote this answer and downvote the fact there is both a LocalDate and a LocalDateTime object...Wold
I would like to just work with LocalTime, how do you perform formatting without running into this exception java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfWeekCitreous
Never mind: this works DateTimeFormatter.ofPattern("HH:mm:ss")Citreous
@samuelowino For me this worked: DateTimeFormatter.ofPattern("HH:mm").withZone(ZoneId.systemDefault()).format(...). <-- Adding the .withZone(...).Gahnite
I have used DateTime dateTime = DateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.RFC_1123_DATE_TIME.withLocale(defaultLocale).withZone(defaultTimeZone.toZoneId()); This gets me Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: OffsetSeconds As @isapir below, right: ZonedDateTime. Then it works for this as well. So I ended up with ZonedDateTime dateTime = ZonedDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.RFC_1123_DATE_TIME.withLocale(defaultLocale).withZone(defaultTimeZone.toZoneId());Parnassus
C
42

I would like to add following details to the correct answer of @James_D:

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 all-purpose unique temporal type (in German there is the poetic expression "eierlegende Wollmilchsau"). In this design there cannot be an unsupported field. But the price is high: Many time problems cannot be adequately handled with such an unflexible approach because it is hard to impossible to find a common denominator for all kinds of temporal objects.

JSR-310 has choosen another way, namely to allow different temporal types which consist of type-specific sets of supported built-in fields. The natural consequence is that not every possible field is supported by every type (and users can even define their own specialized fields). It is also possible to programmatically ask every object of type 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 which explains the problem of 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<Character, TemporalField> 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. Parsing happens in several steps. The field mapping is only the first step. The second step is then parsing to a raw object of type TemporalAccessor. And finally parsing delegates to the target type (here: LocalDate) and let it decide if it accepts all the field values in parsed intermediate object.

Commemorate answered 15/4, 2014 at 15:33 Comment(1)
en.wiktionary.org/wiki/eierlegende_Wollmilchsau (literally “egg-laying wool-milk-sow”) An all-in-one device or person which has (or claims to have) only positive attributes and which can (or attempts to) do the work of several specialized tools. :-)Macrobiotics
M
18

The right class for me was ZonedDateTime which includes both Time and Time Zone.

LocalDate doesn't have the Time information so you get a UnsupportedTemporalTypeException: Unsupported field: HourOfDay.

You can use LocalDateTime but then you don't have the Time Zone information so if you try to access that (even by using one of the predefined formatters) you will get a UnsupportedTemporalTypeException: Unsupported field: OffsetSeconds.

Melissamelisse answered 24/12, 2017 at 23:54 Comment(2)
This answer should be the accepted answer or should be added to the current accepted answer.Boondoggle
Inconvertible types; cannot cast 'java.time.LocalDateTime' to 'java.time.ZonedDateTime' :/Yetac
G
3

I had a similar problem with this code:

DateTimeFormatter.RFC_1123_DATE_TIME.format(new Date().toInstant())

java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfMonth

I solved it this way:

DateTimeFormatter.RFC_1123_DATE_TIME.format(
    ZonedDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault()))
Geomancy answered 12/4, 2022 at 15:38 Comment(0)
A
0

Kotlin 2022

fun Long.formatEpochMs(): String {
    val date = ZonedDateTime.ofInstant(Instant.ofEpochMilli(this), ZoneId.systemDefault())
    val pattern = "d, MMMM yyyy"
    return runCatching { DateTimeFormatter.ofPattern(pattern, Locale.ENGLISH).format(date) }
        .onFailure { Timber.e(it) }
        .getOrNull()
        .orEmpty()
}
Amundsen answered 27/9, 2022 at 15:31 Comment(0)
P
0

In Scala:

import org.joda.time.LocalDateTime
import org.joda.time.format.DateTimeFormat
                    
LocalDateTime.now.toString(DateTimeFormat.forPattern("yyyyMMddHHmmss"))
P answered 20/4, 2023 at 3:7 Comment(0)
H
0

This exception also happens when the using pattern for a LocalDate object is not correct. For example the pattern yyyy-mm-dd which has to be yyyy-MM-dd would result in Unsupported field: HourOfDay exception.

Haunch answered 1/7, 2023 at 14:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.