UnsupportedTemporalTypeException when formatting Instant to String

311,286

Solution 1

Time Zone

To format an Instant a time-zone is required. Without a time-zone, the formatter does not know how to convert the instant to human date-time fields, and therefore throws an exception.

The time-zone can be added directly to the formatter using withZone().

DateTimeFormatter formatter =
    DateTimeFormatter.ofLocalizedDateTime( FormatStyle.SHORT )
                     .withLocale( Locale.UK )
                     .withZone( ZoneId.systemDefault() );

If you specifically want an ISO-8601 format with no explicit time-zone (as the OP asked), with the time-zone implicitly UTC, you need

DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(ZoneId.from(ZoneOffset.UTC))

Generating String

Now use that formatter to generate the String representation of your Instant.

Instant instant = Instant.now();
String output = formatter.format( instant );

Dump to console.

System.out.println("formatter: " + formatter + " with zone: " + formatter.getZone() + " and Locale: " + formatter.getLocale() );
System.out.println("instant: " + instant );
System.out.println("output: " + output );

When run.

formatter: Localized(SHORT,SHORT) with zone: US/Pacific and Locale: en_GB
instant: 2015-06-02T21:34:33.616Z
output: 02/06/15 14:34

Solution 2

public static void main(String[] args) {

    DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
            .withZone(ZoneId.systemDefault());

    System.out.println(DATE_TIME_FORMATTER.format(new Date().toInstant()));

}

Solution 3

DateTimeFormatter.ISO_INSTANT.format(Instant.now())

This saves you from having to convert to UTC. However, some other language's time frameworks may not support the milliseconds so you should do

DateTimeFormatter.ISO_INSTANT.format(Instant.now().truncatedTo(ChronoUnit.SECONDS))

Solution 4

The Instant class doesn't contain Zone information, it only stores timestamp in milliseconds from UNIX epoch, i.e. 1 Jan 1070 from UTC. So, formatter can't print a date because date always printed for concrete time zone. You should set time zone to formatter and all will be fine, like this :

Instant instant = Instant.ofEpochMilli(92554380000L);
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT).withLocale(Locale.UK).withZone(ZoneOffset.UTC);
assert formatter.format(instant).equals("07/12/72 05:33");
assert instant.toString().equals("1972-12-07T05:33:00Z");

Solution 5

Instants are already in UTC and already have a default date format of yyyy-MM-dd. If you're happy with that and don't want to mess with time zones or formatting, you could also toString() it:

Instant instant = Instant.now();
instant.toString()
output: 2020-02-06T18:01:55.648475Z


Don't want the T and Z? (Z indicates this date is UTC. Z stands for "Zulu" aka "Zero hour offset" aka UTC):

instant.toString().replaceAll("[TZ]", " ")
output: 2020-02-06 18:01:55.663763


Want milliseconds instead of nanoseconds? (So you can plop it into a sql query):

instant.truncatedTo(ChronoUnit.MILLIS).toString().replaceAll("[TZ]", " ")
output: 2020-02-06 18:01:55.664

etc.

Share:
311,286
Dag
Author by

Dag

Hi, I'm a passionate java software developer/architect, java user group leader, electronic music enthusiast and martial arts sportsman.

Updated on July 08, 2022

Comments

  • Dag
    Dag almost 2 years

    I'm trying to format an Instant to a String using the new Java 8 Date and Time API and the following pattern:

    Instant instant = ...;
    String out = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(instant);
    

    Using the code above I get an exception which complains about an unsupported field:

    java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: YearOfEra
        at java.time.Instant.getLong(Instant.java:608)
        at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
        ...