Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UnsupportedTemporalTypeException: Unsupported field: InstantSeconds

I have this code which is producing a timestamp and then parsing.

DateTimeFormatter formatter = 
    DateTimeFormatter
        .ofPattern("yyyyMMdd kk:HH:ss.SSSZ")
        .withLocale(Locale.getDefault())
        .withZone(ZoneId.systemDefault());

Instant now = Instant.now();

String formatted = formatter.format(now);
Instant parsed = formatter.parse(formatted, Instant::from);

When it runs, the last line produces an exception:

java.time.format.DateTimeParseException: Text '20180123 12:12:45.648-0500' could not be parsed: Unable to obtain Instant from TemporalAccessor: {SecondOfMinute=45, NanoOfSecond=648000000, OffsetSeconds=-18000, MilliOfSecond=648, MicroOfSecond=648000, HourOfDay=12},ISO,America/New_York resolved to 2018-01-23 of type java.time.format.Parsed

Caused by: java.time.DateTimeException: Unable to obtain Instant from TemporalAccessor: {SecondOfMinute=45, NanoOfSecond=648000000, OffsetSeconds=-18000, MilliOfSecond=648, MicroOfSecond=648000, HourOfDay=12},ISO,America/New_York resolved to 2018-01-23 of type java.time.format.Parsed

Caused by: java.time.temporal.UnsupportedTemporalTypeException: **Unsupported field: InstantSeconds**

I replace the formatter with DateTimeFormatter.ISO_INSTANT, it works correctly. The actual data produced are nearly identical. What is the disconnect?

ISO_INSTANT:  2018-01-23T16:51:25.516Z
My Format:    20180119 23:59:59.999-0800

I am required to use my format. What is the problem here?

like image 799
Steve Avatar asked Jan 23 '18 18:01

Steve


2 Answers

The problem is that your format does not completely represent an Instant because your format does not have a representation for minutes at all. The formatter can correctly go from Instant and output the result in your format because an Instant has all of the data that your format requires, but your format does not have everything that an Instant requires.

Try changing your pattern to yyyyMMdd kk:HH:mm:ss.SSS, and you will see that your code now works. Note the addition of mm.

If you absolutely require a minuteless pattern, you should make your own TemporalQuery to extract the information you require from the TemporalAccessor In this case, I simply set minutes to 0:

public class MyQuery implements TemporalQuery<Instant> {

    @Override
    public Instant queryFrom(TemporalAccessor temporal) {
        LocalDate ld = LocalDate.from(temporal);
        LocalTime lt = LocalTime.of(temporal.get(ChronoField.HOUR_OF_DAY), 0, temporal.get(ChronoField.SECOND_OF_MINUTE), temporal.get(ChronoField.NANO_OF_SECOND));
        return ZonedDateTime.of(ld, lt, ZoneId.systemDefault()).toInstant();
    }

}

We can then use this TemporalQuery like this:

public class Test {
    public static void main(String[] args) {
        DateTimeFormatter formatter = DateTimeFormatter
            .ofPattern("yyyyMMdd kk:HH:mm:ss.SSS")
            .withLocale(Locale.getDefault())
            .withZone(ZoneId.systemDefault());

        Instant now = Instant.now();

        String formatted = formatter.format(now);
        System.out.println(formatted);

        Instant ld = formatter.parse(formatted, new MyQuery());
    }
}
like image 100
IanGabes Avatar answered Oct 22 '22 04:10

IanGabes


I know that this is an old question but if you're looking for a short answer just add a locale and zone to the DateTimeFormatter, you may also use the default ones: .withLocale(Locale.getDefault()).withZone(ZoneId.systemDefault())

Here is an example of code:

    Instant now = Instant.now();
    System.out.println(now.toString());
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss").withLocale(Locale.getDefault()).withZone(ZoneId.systemDefault());
    System.out.println(formatter.format(now));

This code will use the current instant, output the sample, format it with the date time formatter and output the formatted instant.

like image 4
Francisco Solis Avatar answered Oct 22 '22 04:10

Francisco Solis