It may be the reason that +0000 is not a zone id, but a zone offset.
the documentation offers this list:
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;
You may use appendOffset("+HHMM", "0000") (doc) or appendZoneOrOffsetId() (doc) instead of appendZoneId().
so your full formatter may look like the following
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.optionalStart()
.appendPattern(".SSS")
.optionalEnd()
.optionalStart()
.appendZoneOrOffsetId()
.optionalEnd()
.optionalStart()
.appendOffset("+HHMM", "0000")
.optionalEnd()
.toFormatter();
Further the way of creating a ZonedDateTime may influence if there is an exception or not. Therefore I'd recommend the following as this worked without any exceptions.
LocalDateTime time = LocalDateTime.parse("2013-09-20T07:00:33.123+0000", formatter);
ZonedDateTime zonedTime = time.atZone(ZoneId.systemDefault());
Answer from sailingthoms on Stack OverflowIt may be the reason that +0000 is not a zone id, but a zone offset.
the documentation offers this list:
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;
You may use appendOffset("+HHMM", "0000") (doc) or appendZoneOrOffsetId() (doc) instead of appendZoneId().
so your full formatter may look like the following
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.optionalStart()
.appendPattern(".SSS")
.optionalEnd()
.optionalStart()
.appendZoneOrOffsetId()
.optionalEnd()
.optionalStart()
.appendOffset("+HHMM", "0000")
.optionalEnd()
.toFormatter();
Further the way of creating a ZonedDateTime may influence if there is an exception or not. Therefore I'd recommend the following as this worked without any exceptions.
LocalDateTime time = LocalDateTime.parse("2013-09-20T07:00:33.123+0000", formatter);
ZonedDateTime zonedTime = time.atZone(ZoneId.systemDefault());
Did you try .appendPattern("ZZZ")? it would probably work!
You can build the pattern using DateTimeFormatterBuilder and reuse ISO_LOCAL_DATE and ISO_LOCAL_TIME constants:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral(" ")
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.appendPattern("[ Z z]")
.toFormatter();
ZonedDateTime dt = ZonedDateTime.parse(date, formatter);
The trick is that DateTimeFormatter.ISO_LOCAL_TIME handles the different number of digit used to represent milliseconds its own. From DateTimeFormatter.ISO_LOCAL_TIME JavaDoc:
This returns an immutable formatter capable of formatting and parsing the ISO-8601 extended local time format. The format consists of:
[..]
One to nine digits for the nano-of-second. As many digits will be output as required.
I think it is better to use a DateTimeFormatterBuilder for that purpose. For the optional parts just use one of the follwing methods :
- OptionalStart() & OptionalEnd()
- Append your whole optional pattern with appendOptional()
Here is an example :
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(""
+ "[yyyy-MM-dd HH:mm:ss.SSS Z z]"
+ "[yyyy-MM-dd HH:mm:ss.SS Z z]"
+ "[yyyy-MM-dd HH:mm:ss.S Z z]"
+ "[yyyy-MM-dd HH:mm:ss Z z]"
);
Also, you can create a dtf for each optional and append them with appendOptional() and the the DateTimeFormatterBuilder
for example :
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValue(HOUR_OF_DAY,2)
.optionalStart()
.appendValue(MINUTE_OF_HOUR,2)
.optionalEnd()
.optionalStart()
.appendValue(SECOND_OF_MINUTE,2)
.optionalEnd()
.toFormatter();
This code is not tested but try to build your optional pattern each time in a start/end optional blocks.