google/gson · error · IndexOutOfBoundsException

Invalid time zone indicator '{}'

Error message

Invalid time zone indicator '{}'

What it means

Thrown by ISO8601Utils when the character at the timezone position is neither 'Z' nor '+'/'-'. After the date/time portion is consumed, the parser inspects date.charAt(offset); any other character (e.g. a space, a letter, end-of-field noise) is rejected as an invalid timezone indicator.

Source

Thrown at gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java:279

          String act = timezone.getID();
          if (!act.equals(timezoneId)) {
            /* 22-Jan-2015, tatu: Looks like canonical version has colons, but we may be given
             *    one without. If so, don't sweat.
             *   Yes, very inefficient. Hopefully not hit often.
             *   If it becomes a perf problem, add 'loose' comparison instead.
             */
            String cleaned = act.replace(":", "");
            if (!cleaned.equals(timezoneId)) {
              throw new IndexOutOfBoundsException(
                  "Mismatching time zone indicator: "
                      + timezoneId
                      + " given, resolves to "
                      + timezone.getID());
            }
          }
        }
      } else {
        throw new IndexOutOfBoundsException(
            "Invalid time zone indicator '" + timezoneIndicator + "'");
      }

      Calendar calendar = new GregorianCalendar(timezone);
      calendar.setLenient(false);
      calendar.set(Calendar.YEAR, year);
      calendar.set(Calendar.MONTH, month - 1);
      calendar.set(Calendar.DAY_OF_MONTH, day);
      calendar.set(Calendar.HOUR_OF_DAY, hour);
      calendar.set(Calendar.MINUTE, minutes);
      calendar.set(Calendar.SECOND, seconds);
      calendar.set(Calendar.MILLISECOND, milliseconds);

      pos.setIndex(offset);
      return calendar.getTime();
      // If we get a ParseException it'll already have the right message/offset.
      // Other exception types can convert here.
    } catch (IndexOutOfBoundsException | IllegalArgumentException e) {

View on GitHub (pinned to 310ac341f2)

Solutions

  1. Fix the producer to emit ISO8601 (use 'Z' or numeric offset, no named zones).
  2. Pre-process the string: strip named zones and convert to an offset using a known mapping.
  3. Register a custom TypeAdapter<Date> that parses the actual format (e.g. 'EEE, dd MMM yyyy HH:mm:ss zzz' for RFC 1123).
  4. Validate the format with a regex at the boundary and reject/log non-ISO8601 inputs.

Example fix

// before
// JSON: {"at":"2020-01-01T12:00:00 UTC"} -> invalid indicator ' '

// after (producer fix): {"at":"2020-01-01T12:00:00Z"}

// or custom adapter for RFC 1123 strings:
Gson g = new GsonBuilder()
  .registerTypeAdapter(Date.class, new TypeAdapter<Date>() {
    private final DateFormat f = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
    public Date read(JsonReader in) throws IOException {
      try { return f.parse(in.nextString()); }
      catch (ParseException e) { throw new JsonSyntaxException(e); }
    }
    public void write(JsonWriter out, Date v) throws IOException { out.value(f.format(v)); }
  }).create();
Defensive patterns

Strategy: validation

Validate before calling

private static final Pattern ISO =
  Pattern.compile("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$");
String raw = jsonNode.get("at").getAsString();
if (raw == null || !ISO.matcher(raw).matches()) {
  throw new IllegalArgumentException("Not ISO8601 with Z/offset: " + raw);
}

Try / catch

try {
  return gson.fromJson(json, Event.class);
} catch (JsonSyntaxException e) {
  if (e.getCause() instanceof ParseException
      && e.getCause().getMessage().contains("Invalid time zone indicator")) {
    // input uses named zone or wrong format; switch adapter or reject
    throw new IllegalArgumentException("Unsupported date format", e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Date string with a malformed trailing token: "2020-01-01T12:00:00 UTC" (space then letters), "2020-01-01T12:00:00X", or an unexpected separator. The parser expects exactly 'Z', '+', or '-' at that position.

Common situations: Human-readable timestamps with timezone names ("UTC", "EST"); RFC 1123 or RFC 822 date formats fed to a parser expecting ISO8601; copy-paste artifacts with trailing whitespace or newline; producers joining date and zone with a space.

Related errors


AI-assisted analysis of google/gson@310ac341f2 (2026-08-10). Data as JSON: /api/errors/951a8a085edb5f82. Report an issue: GitHub.