UnsupportedTemporalTypeException when formatting Instant to String
Asked Answered
M

8

369

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)
    ...
Makeshift answered 10/8, 2014 at 13:36 Comment(0)
A
517

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
Ascot answered 15/12, 2014 at 11:48 Comment(5)
Thank you!! BTW the exception "unsupported field" pointing to, e.g., year, is spectacularly obtuse. Maybe this situation should be detected and an exception pointing directly to the missing zone id in the Instant should be thrown!Phenacaine
More bizarrely, if you include .withZone (e.g., .withZone(ZoneId.of("Z")) ) and format a LocalDateTime, the zone is IGNORED! So as long as .withZone() is included, the same formatter can be used both for Instant and for LocalDateTime, without affecting the time shown for the latter.Garris
Why is it so difficult to accept the fact that Instant has a TimeZone which is GMT?Tengdin
@KorayTugay Because while pedantic "Instant is already GMT" comments might be true, they're far from helpful when facing an exception trace thrown because formatting an Instant without specifying a timezone does not work. It would have been nice if the formatter defaulted to GMT, but oh well.Wideopen
Now this gives you yyyy-MM-dd hh:mm:ss format: DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").withZone(ZoneId.of("Europe/Paris"));Ostiole
G
71
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()));

}
Glycolysis answered 3/2, 2018 at 10:50 Comment(3)
While this code may answer the question, providing additional context regarding how and why it solves the problem would improve the answer's long-term value.Yajairayajurveda
While this code snippet may solve the question, including an explanation really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion.Enalda
new Date().toInstant() may be replaced by Instant.now()Chemotaxis
T
52
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))
Tingly answered 7/3, 2019 at 22:58 Comment(2)
What do you mean by "some other language's time frameworks"? Will ISO_INSTANT.format() automatically truncate to seconds?Rett
Some frameworks expect only the time to go up to the seconds because they don't follow the full ISO convention and break when there's milliseconds as part of the input string.Tingly
D
29

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");
Drank answered 12/12, 2014 at 16:27 Comment(1)
nanoseconds, not millisecondsPouncey
M
24

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.

Middle answered 6/2, 2020 at 18:32 Comment(0)
B
0

I have same issue with @JsonFormat and lib 'jackson'. Need set timezone:

@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC")

In addition, to experiment with 'Instant' format you can write test:

@Test
void pattern() {
    Instant instant = Instant.now();

    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy MM dd")
            .withZone(ZoneId.of("UTC"));

    String formattedDate = dateTimeFormatter.format(instant);
    System.out.println(formattedDate);

Try change pattern "yyyy MM dd", "yyyy MMMM dd"...

Behistun answered 1/2 at 14:42 Comment(0)
P
-1

Or if you still want to use formatter created from pattern you can just use LocalDateTime instead of Instant:

LocalDateTime datetime = LocalDateTime.now();
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(datetime)
Protero answered 16/11, 2018 at 17:38 Comment(2)
Exception java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: YearOfEraPitchstone
OP: Instant. Answers: lOcAlDaTeTiMe. ClassicGaddi
C
-4
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");
String text = date.toString(formatter);
LocalDate date = LocalDate.parse(text, formatter);

I believe this might help, you may need to use some sort of localdate variation instead of instant

Chintzy answered 10/8, 2014 at 13:50 Comment(1)
It is really not about the Instant class.Hughes

© 2022 - 2024 — McMap. All rights reserved.