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 Overflow
🌐
Oracle
docs.oracle.com › javase › 8 › docs › api › java › time › format › DateTimeFormatterBuilder.html
DateTimeFormatterBuilder (Java Platform SE 8 )
October 20, 2025 - public DateTimeFormatterBuilder appendPattern(String pattern) Appends the elements defined by the specified pattern to the builder. All letters 'A' to 'Z' and 'a' to 'z' are reserved as pattern letters. The characters '#', '{' and '}' are reserved for future use.
🌐
Joda
joda.org › joda-time › apidocs › org › joda › time › format › DateTimeFormatterBuilder.html
DateTimeFormatterBuilder (Joda-Time 2.14.0 API)
public DateTimeFormatterBuilder appendPattern(String pattern) Calls upon DateTimeFormat to parse the pattern and append the results into this builder. Parameters: pattern - pattern specification · Returns: this DateTimeFormatterBuilder, for chaining · Throws: IllegalArgumentException - if ...
🌐
OpenJDK
cr.openjdk.org › ~sherman › threeten › old › javax › time › calendar › format › DateTimeFormatterBuilder.html
DateTimeFormatterBuilder (ThreeTen date and time API)
public DateTimeFormatterBuilder appendPattern(java.lang.String pattern) Appends the elements defined by the specified pattern to the builder. All letters 'A' to 'Z' and 'a' to 'z' are reserved as pattern letters.
🌐
Oracle
docs.oracle.com › javase › 8 › docs › api › java › time › format › DateTimeFormatter.html
DateTimeFormatter (Java Platform SE 8 )
1 week ago - DateTimeFormatterBuilder.appendPattern(String) public static DateTimeFormatter ofPattern(String pattern, Locale locale) Creates a formatter using the specified pattern and locale. This method will create a formatter based on a simple pattern of letters and symbols as described in the class ...
🌐
Oracle
docs.oracle.com › en › java › javase › 24 › docs › api › java.base › java › time › format › DateTimeFormatterBuilder.html
DateTimeFormatterBuilder (Java SE 24 & JDK 24)
April 15, 2025 - DateTimeFormatterBuilder · appendPattern · (String pattern) Appends the elements defined by the specified pattern to the builder. DateTimeFormatterBuilder · appendText · (TemporalField field) Appends the text of a date-time field to the formatter using the full text style.
🌐
Adobe Developer
developer.adobe.com › experience-manager › reference-materials › 6-5 › javadoc › org › joda › time › format › DateTimeFormatterBuilder.html
DateTimeFormatterBuilder (The Adobe AEM Quickstart and Web Application.)
public DateTimeFormatterBuilder appendPattern(java.lang.String pattern) Calls upon DateTimeFormat to parse the pattern and append the results into this builder. Parameters: pattern - pattern specification · Throws: java.lang.IllegalArgumentException - if the pattern is invalid ·
🌐
Microsoft Learn
learn.microsoft.com › en-us › dotnet › api › java.time.format.datetimeformatterbuilder.appendpattern
DateTimeFormatterBuilder.AppendPattern(String) Method (Java.Time.Format) | Microsoft Learn
[<Android.Runtime.Register("appendPattern", "(Ljava/lang/String;)Ljava/time/format/DateTimeFormatterBuilder;", "", ApiSince=26)>] member this.AppendPattern : string -> Java.Time.Format.DateTimeFormatterBuilder · pattern · String · the pattern to add, not null ·
🌐
JBoss
docs.jboss.org › jbossas › javadoc › 7.1.2.Final › org › joda › time › format › DateTimeFormatterBuilder.html
DateTimeFormatterBuilder (JBoss Application Server: Build 7.1.2.Final API)
April 25, 2018 - public DateTimeFormatterBuilder appendPattern(String pattern) Calls upon DateTimeFormat to parse the pattern and append the results into this builder. Parameters: pattern - pattern specification · Throws: IllegalArgumentException - if the pattern is invalid ·
Find elsewhere
🌐
Oracle
docs.oracle.com › en › java › javase › 23 › docs › api › java.base › java › time › format › DateTimeFormatterBuilder.html
DateTimeFormatterBuilder (Java SE 23 & JDK 23)
October 17, 2024 - DateTimeFormatterBuilder · appendPattern · (String pattern) Appends the elements defined by the specified pattern to the builder. DateTimeFormatterBuilder · appendText · (TemporalField field) Appends the text of a date-time field to the formatter using the full text style.
🌐
Waiting for Code
waitingforcode.com › home › java 8
Managing different date time formats with DateTimeFormatterBuilder on waitingforcode.com - articles about Java 8
July 5, 2020 - val parserOptionalMonthDays = new DateTimeFormatterBuilder() .appendValue(ChronoField.YEAR, 4) .optionalStart() .appendPattern("MM[dd]") .optionalEnd() .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1) .parseDefaulting(ChronoField.DAY_OF_MONTH, 1) .toFormatter() assert(LocalDate.parse("2020", parserOptionalMonthDays).toString == "2020-01-01") assert(LocalDate.parse("202005", parserOptionalMonthDays).toString == "2020-05-01") assert(LocalDate.parse("20200505", parserOptionalMonthDays).toString == "2020-05-05") It works if we have a field having the same format.
🌐
Oracle
docs.oracle.com › javase › jp › 11 › docs › api › java.base › java › time › format › DateTimeFormatterBuilder.html
DateTimeFormatterBuilder (Java SE 11 & JDK 11 )
January 10, 2025 - public DateTimeFormatterBuilder appendPattern​(String pattern) Appends the elements defined by the specified pattern to the builder. All letters 'A' to 'Z' and 'a' to 'z' are reserved as pattern letters. The characters '#', '{' and '}' are reserved for future use.
🌐
Tabnine
tabnine.com › home page › code › java › java.time.format.datetimeformatterbuilder
java.time.format.DateTimeFormatterBuilder.appendPattern java code examples | Tabnine
December 27, 2017 - @Override public Date unmarshal(String date) throws Exception { final LocalDateTime now = LocalDateTime.now(); final DateTimeFormatter parser = new DateTimeFormatterBuilder().appendPattern(DEFAULT_TIME_FORMAT) .parseDefaulting(ChronoField.YEAR, now.getYear()) .parseDefaulting(ChronoField.MONTH_OF_YEAR, now.getMonthValue()) .parseDefaulting(ChronoField.DAY_OF_MONTH, now.getDayOfMonth()) .parseDefaulting(ChronoField.MILLI_OF_SECOND, 0) .toFormatter(Locale.US); final LocalDateTime parsedDateTime = LocalDateTime.parse(date, parser); return Date.from(parsedDateTime.toInstant(ZONE_ID.getRules().getOffset(now))); }
🌐
GeeksforGeeks
geeksforgeeks.org › java › java-time-format-datetimeformatterbuilder-class-in-java
java.time.format.DateTimeFormatterBuilder Class in Java - GeeksforGeeks
July 23, 2025 - // Java program to illustrate DateTimeFormatterBuilder // Using optionalStart() and optionalEnd() Methods // Importing required libraries import java.io.*; import java.lang.*; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; // Main class public class GFG { // Main driver methods public static void main(String[] args) { // Creating an object of DateTimeFormatter class DateTimeFormatter parser = new DateTimeFormatterBuilder() .appendPattern("[yyyy][yyyyM
🌐
Oracle
docs.oracle.com › en › java › javase › 17 › docs › api › java.base › java › time › format › DateTimeFormatter.html
DateTimeFormatter (Java SE 17 & JDK 17)
January 20, 2026 - DateTimeFormatterBuilder.appendPattern(String) public static DateTimeFormatter ofPattern · (String pattern, Locale locale) Creates a formatter using the specified pattern and locale. This method will create a formatter based on a simple pattern of letters and symbols as described in the class ...
🌐
Js-joda
js-joda.github.io › js-joda › class › packages › core › src › format › DateTimeFormatter.js~DateTimeFormatter.html
DateTimeFormatter | js-joda
DateTimeFormatterBuilder#appendPattern(String) A query that provides access to the excess days that were parsed. This returns a singleton TemporalQuery that provides access to additional information from the parse. The query always returns a non-null period, with a zero period returned instead ...
🌐
ThreeTen
threeten.org › threetenbp › apidocs › org › threeten › bp › format › DateTimeFormatterBuilder.html
DateTimeFormatterBuilder (ThreeTen backport 1.7.3-SNAPSHOT API)
public DateTimeFormatterBuilder appendPattern​(String pattern) Appends the elements defined by the specified pattern to the builder. All letters 'A' to 'Z' and 'a' to 'z' are reserved as pattern letters. The characters '{' and '}' are reserved for future use.
Top answer
1 of 5
10

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.

2 of 5
5

I think it is better to use a DateTimeFormatterBuilder for that purpose. For the optional parts just use one of the follwing methods :

  1. OptionalStart() & OptionalEnd()
  2. 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.

🌐
University of San Francisco Computer Science
cs.usfca.edu › ~cs272 › javadoc › api › java.base › java › time › format › DateTimeFormatterBuilder.html
DateTimeFormatterBuilder (Java SE 17 & JDK 17)
DateTimeFormatterBuilder · appendPattern · (String pattern) Appends the elements defined by the specified pattern to the builder. DateTimeFormatterBuilder · appendText · (TemporalField field) Appends the text of a date-time field to the formatter using the full text style.