java.time DateTimeFormatter pattern for timezone offset
Asked Answered
L

3

29

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?

Lucic answered 8/6, 2015 at 13:45 Comment(0)
F
26

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.

Frater answered 8/6, 2015 at 14:5 Comment(2)
The pattern does not work...but the link to the bug looks like exactly my issue. I tried the builder suggestion, but that didn't appear to work either.Lucic
@Lucic You are right. Sorry that you suffer from the bug. Please see also my update if you can use any of the alternative workarounds.Frater
S
20

All the answers are good. The java8+ have these patterns for parsing and formatting timezone: V, z, O, X, x, Z.

Here's they are, for parsing, according to rules from the documentation :

   Symbol  Meaning                     Presentation      Examples
   ------  -------                     ------------      -------
   V       time-zone ID                zone-id           America/Los_Angeles; Z; -08:30
   z       time-zone name              zone-name         Pacific Standard Time; PST
   O       localized zone-offset       offset-O          GMT+8; GMT+08:00; UTC-08:00;
   X       zone-offset 'Z' for zero    offset-X          Z; -08; -0830; -08:30; -083015; -08:30:15;
   x       zone-offset                 offset-x          +0000; -08; -0830; -08:30; -083015; -08:30:15;
   Z       zone-offset                 offset-Z          +0000; -0800; -08:00;

But how about formatting? Here's a sample for a date (assuming ZonedDateTime) that show these patters behavior for different formatting patters:

// The helper function:
static void printInPattern(ZonedDateTime dt, String pattern) {
    System.out.println(pattern + ": " + dt.format(DateTimeFormatter.ofPattern(pattern)));
}        

// The date:
String strDate = "2020-11-03 16:40:44 America/Los_Angeles";
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss zzzz");
ZonedDateTime dt = ZonedDateTime.parse(strDate, format);
// 2020-11-03T16:40:44-08:00[America/Los_Angeles]

// Rules:
// printInPattern(dt, "V");     // exception!
printInPattern(dt, "VV");       // America/Los_Angeles
// printInPattern(dt, "VVV");   // exception!
// printInPattern(dt, "VVVV");  // exception!
printInPattern(dt, "z");        // PST
printInPattern(dt, "zz");       // PST
printInPattern(dt, "zzz");      // PST
printInPattern(dt, "zzzz");     // Pacific Standard Time
printInPattern(dt, "O");        // GMT-8
// printInPattern(dt, "OO");    // exception!
// printInPattern(dt, "OO0");   // exception!
printInPattern(dt, "OOOO");     // GMT-08:00
printInPattern(dt, "X");        // -08
printInPattern(dt, "XX");       // -0800
printInPattern(dt, "XXX");      // -08:00
printInPattern(dt, "XXXX");     // -0800
printInPattern(dt, "XXXXX");    // -08:00
printInPattern(dt, "x");        // -08
printInPattern(dt, "xx");       // -0800
printInPattern(dt, "xxx");      // -08:00
printInPattern(dt, "xxxx");     // -0800
printInPattern(dt, "xxxxx");    // -08:00
printInPattern(dt, "Z");        // -0800
printInPattern(dt, "ZZ");       // -0800
printInPattern(dt, "ZZZ");      // -0800
printInPattern(dt, "ZZZZ");     // GMT-08:00
printInPattern(dt, "ZZZZZ");    // -08:00

In the case of positive offset the + sign character is used everywhere(where there is - now) and never omitted.

This well works for new java.time types. If you're about to use these for java.util.Date or java.util.Calendar - not all going to work as those types are broken(and so marked as deprecated, please don't use them)

Sedition answered 1/2, 2021 at 0:43 Comment(1)
The FORMATTING of the symbol is what was causing me issues. Thank you for this detailed example!Navada
O
8

Offset for SimpleDateFormat sign is Z check Java7 or Java8 SimpleDateFormat API.

Then, your parser format for String

2014-05-02-10.45.05.993280-5:00

must be:

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

UPDATE: for DateTimeFormatter the offset Patterns for Formatting and Parsing are:

O       localized zone-offset       offset-O          GMT+8; GMT+08:00; UTC-08:00;
X       zone-offset 'Z' for zero    offset-X          Z; -08; -0830; -08:30; -083015; -08:30:15;
x       zone-offset                 offset-x          +0000; -08; -0830; -08:30; -083015; -08:30:15;
Z       zone-offset                 offset-Z          +0000; -0800; -08:00;

Your desired pattern in DateTimeFormatter is X.

Ocelot answered 8/6, 2015 at 13:50 Comment(3)
I am using a DateTimeFormatter and adding a Z does not appear ot workLucic
You linked to Java 7 twice. Java 8 API is here.Daddylonglegs
I tried variations of this and cant get it to print the format "+00:00" on a GMT time. It will only print "+0000" . On the other hand, if the timezone is offset by at least 1, then DateTimeFormatter.ISO_OFFSET_DATE_TIME will print the former desired pattern.Sheepfold

© 2022 - 2024 — McMap. All rights reserved.