1

I have an API for JSON parsing which requires a DateTimeFormatter instance in order to parse date time strings to OffsetDateTime. However I always get an exception Unable to obtain ZoneOffset from TemporalAccessor: {},ISO resolved to 2021-08-17T13:26:49 of type java.time.format.Parsed The API uses OffsetDateTime.parse(String, DateFormatter).

// DateTimeFormatter instance to be provided to the API
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
// this is how the API uses the DateTimeFormatter instance
OffsetDateTime dateTime = OffsetDateTime.parse("20210817132649", formatter);

How do I have to create the DateTimeFormatter in order to deliver a ZoneOffset, so that the API is able to parse the DateTime correctly. The ZoneOffset may be UTC.

Stefan
  • 444
  • 5
  • 16
  • Yes, I tried: Unable to obtain OffsetDateTime from TemporalAccessor: {InstantSeconds=1629206809},ISO,Z resolved to 2021-08-17T13:26:49 – Stefan Oct 18 '21 at 14:44

3 Answers3

4

Update

I understood from you that you are using the API as follows:

apiClient.getJSON().setOffsetDateTimeFormat(DateTimeFormatter); 

and you do not have a parameter to pass timezone information. In this case, you can use DateTimeFormatter#withZone as shown below:

import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuuMMddHHmmss", Locale.ENGLISH)
                                        .withZone(ZoneOffset.UTC);

        OffsetDateTime odt = OffsetDateTime.parse("20210817132649", formatter);

        System.out.println(odt);
    }
}

Output:

2021-08-17T13:26:49Z

ONLINE DEMO

i.e. now, your call will be:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuuMMddHHmmss", Locale.ENGLISH)
                                .withZone(ZoneOffset.UTC);

apiClient.getJSON().setOffsetDateTimeFormat(formatter); 

Original answer

Your date-time string does not have a timezone offset. Parse it into a LocalDateTime and then apply the offset to it.

Demo:

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuuMMddHHmmss", Locale.ENGLISH);
        
        OffsetDateTime odt = LocalDateTime.parse("20210817132649", formatter)
                                .atOffset(ZoneOffset.UTC);
        
        System.out.println(odt);
    }
}

Output:

2021-08-17T13:26:49Z

ONLINE DEMO

The Z in the output is the timezone designator for zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours). You can specify a different timezone offset (e.g. ZoneOffset.of("+05:30")) as per your requirement.

In case you have ZoneId available

If you have ZoneId available, you should parse the given date-time string into a LocalDateTime and then apply the ZoneId to it to get a ZonedDateTime from which you can always obtain an OffsetDateTime. The best thing about a ZonedDateTime is that it has been designed to adjust the timezone offset automatically whereas an OffsetDateTime is used to represent a fixed timezone offset.

Demo:

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuuMMddHHmmss", Locale.ENGLISH);

        ZonedDateTime zdt = LocalDateTime.parse("20210817132649", formatter)
                                .atZone(ZoneId.of("Etc/UTC"));

        OffsetDateTime odt = zdt.toOffsetDateTime();

        System.out.println(odt);
    }
}

Output:

2021-08-17T13:26:49Z

ONLINE DEMO

You can specify a different ZoneId(e.g. ZoneId.of("Asia/Kolkata")) as per your requirement.

Learn more about the modern Date-Time API* from Trail: Date Time.


* If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring. Note that Android 8.0 Oreo already provides support for java.time.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
2

Well, the string you passed in does not contain zone information, while an OffsetDateTime requires zone information.

So you'll have to set a value for it.

You could use the DateTimeFormatterBuilder class, which then can be instructed to use some default value if a field is missing from the parsed information:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    .parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
    .appendPattern("yyyyMMddHHmmss")
    .toFormatter(Locale.ROOT);

You could also directly set an implied zone to the DateTimeFormatter:

DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(ZoneOffset.UTC);
MC Emperor
  • 22,334
  • 15
  • 80
  • 130
  • The DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(ZoneOffset.UTC); doesn't work. However with the DateTimeFormatterBuilder is was able to get the String parsed correctly – Stefan Oct 18 '21 at 14:57
1

Better use a ZonedDateTime - the difference being the Day Saving Time, and countries on with the same zone offset. Maybe using the default ZoneId.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138