java.time DateTimeFormatter pattern for timezone offset

Cheetah picture Cheetah · Jun 8, 2015 · Viewed 35.7k times · Source

I am trying to parse: 2014-05-02-10.45.05.993280-5:00 where the -5:00 is the offset from UTC. Using a java.time DateTimeFormatter in Java 8.

For the first bit I have the following: yyyy-MM-dd-HH.mm.ss.SSSSSS however, I can't figure out what the pattern should be to parse the offset also.

If I had the offset with 4 digits (-05:00) I could use: yyyy-MM-dd-HH.mm.ss.SSSSSSxxx, but this doesn't work for 3 digits.

Any ideas?

Answer

Meno Hochschild picture Meno Hochschild · Jun 8, 2015

Use capital letter X instead of x, hence XXX. The difference is that big X can recognize the input letter "Z" as UTC-Offset +00:00 while small pattern letter X cannot.

Suggested pattern:

yyyy-MM-dd-HH.mm.ss.SSSSSSXXX

Please be also aware of following JDK-bug:

java.time.format.DateTimeFormatter cannot parse an offset with single digit hour

UPDATE:

I have now tested the described workaround in the bug-log.

String input = "2014-05-02-10.45.05.993280-5:00";
DateTimeFormatter f = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd-HH.mm.ss.SSSSSS").parseLenient().appendOffset("+HH:MM", "Z").toFormatter();
System.out.println(f.parse(input, ZonedDateTime::from));

But it throws an exception:

Exception in thread "main" java.time.format.DateTimeParseException: Text '2014-05-02-10.45.05.993280-5:00' could not be parsed at index 26 at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1947) at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1849) at HelloWorld.main(HelloWorld.java:16)

So lenient parsing does not help either. So there are now only three options left for you:

  • Use workaround suggested by bug reporter: [...] workaround is to parse the date/time separately, use a hand coded parser for the offset and combine the LocalDateTime with the hand parsed offset. Not an easy work around.

  • Try your own specialized string preprocessing. If you have a fixed format then you can try to insert the zero-digit at position 26 (if the total input length is one digit too small).

  • Or you use an external library which can do this. My library Time4J (v4.0) can do that if you are willing to add an extra dependency. See this code:

String input = "2014-05-02-10.45.05.993280-5:00";
ZonalDateTime zdt =
    ZonalDateTime.parse(
        input,
        Moment.localFormatter("yyyy-MM-dd-HH.mm.ss.SSSSSSXXX", PatternType.CLDR));
System.out.println(zdt); // 2014-05-02T10:45:05,993280UTC-05:00
ZonedDateTime result = zdt.toTemporalAccessor();

Update: According to JDK-bug-status, the bug has been fixed for Java-9, but a backport for Java-8 does not seem to be available though.