{"record":{"id":"951a8a085edb5f82","repo":"google/gson","slug":"invalid-time-zone-indicator","errorCode":null,"errorMessage":"Invalid time zone indicator '{}'","messagePattern":"Invalid time zone indicator '(.+?)'","errorType":"exception","errorClass":"IndexOutOfBoundsException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java","lineNumber":279,"sourceCode":"          String act = timezone.getID();\n          if (!act.equals(timezoneId)) {\n            /* 22-Jan-2015, tatu: Looks like canonical version has colons, but we may be given\n             *    one without. If so, don't sweat.\n             *   Yes, very inefficient. Hopefully not hit often.\n             *   If it becomes a perf problem, add 'loose' comparison instead.\n             */\n            String cleaned = act.replace(\":\", \"\");\n            if (!cleaned.equals(timezoneId)) {\n              throw new IndexOutOfBoundsException(\n                  \"Mismatching time zone indicator: \"\n                      + timezoneId\n                      + \" given, resolves to \"\n                      + timezone.getID());\n            }\n          }\n        }\n      } else {\n        throw new IndexOutOfBoundsException(\n            \"Invalid time zone indicator '\" + timezoneIndicator + \"'\");\n      }\n\n      Calendar calendar = new GregorianCalendar(timezone);\n      calendar.setLenient(false);\n      calendar.set(Calendar.YEAR, year);\n      calendar.set(Calendar.MONTH, month - 1);\n      calendar.set(Calendar.DAY_OF_MONTH, day);\n      calendar.set(Calendar.HOUR_OF_DAY, hour);\n      calendar.set(Calendar.MINUTE, minutes);\n      calendar.set(Calendar.SECOND, seconds);\n      calendar.set(Calendar.MILLISECOND, milliseconds);\n\n      pos.setIndex(offset);\n      return calendar.getTime();\n      // If we get a ParseException it'll already have the right message/offset.\n      // Other exception types can convert here.\n    } catch (IndexOutOfBoundsException | IllegalArgumentException e) {","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/google/gson/blob/310ac341f2f92a454b229bf21f70d2d18b2b6db7/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java#L261-L297","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix the producer to emit ISO8601 (use 'Z' or numeric offset, no named zones).","Pre-process the string: strip named zones and convert to an offset using a known mapping.","Register a custom TypeAdapter<Date> that parses the actual format (e.g. 'EEE, dd MMM yyyy HH:mm:ss zzz' for RFC 1123).","Validate the format with a regex at the boundary and reject/log non-ISO8601 inputs."],"exampleFix":"// before\n// JSON: {\"at\":\"2020-01-01T12:00:00 UTC\"} -> invalid indicator ' '\n\n// after (producer fix): {\"at\":\"2020-01-01T12:00:00Z\"}\n\n// or custom adapter for RFC 1123 strings:\nGson g = new GsonBuilder()\n  .registerTypeAdapter(Date.class, new TypeAdapter<Date>() {\n    private final DateFormat f = new SimpleDateFormat(\"EEE, dd MMM yyyy HH:mm:ss z\", Locale.US);\n    public Date read(JsonReader in) throws IOException {\n      try { return f.parse(in.nextString()); }\n      catch (ParseException e) { throw new JsonSyntaxException(e); }\n    }\n    public void write(JsonWriter out, Date v) throws IOException { out.value(f.format(v)); }\n  }).create();","handlingStrategy":"validation","validationCode":"private static final Pattern ISO =\n  Pattern.compile(\"^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}(\\\\.\\\\d+)?(Z|[+-]\\\\d{2}:\\\\d{2})$\");\nString raw = jsonNode.get(\"at\").getAsString();\nif (raw == null || !ISO.matcher(raw).matches()) {\n  throw new IllegalArgumentException(\"Not ISO8601 with Z/offset: \" + raw);\n}","typeGuard":null,"tryCatchPattern":"try {\n  return gson.fromJson(json, Event.class);\n} catch (JsonSyntaxException e) {\n  if (e.getCause() instanceof ParseException\n      && e.getCause().getMessage().contains(\"Invalid time zone indicator\")) {\n    // input uses named zone or wrong format; switch adapter or reject\n    throw new IllegalArgumentException(\"Unsupported date format\", e);\n  }\n  throw e;\n}","preventionTips":["Reject named timezones ('UTC','EST') at ingestion; require numeric offsets.","Validate date strings with an ISO8601 regex at the boundary.","Use a custom TypeAdapter for any non-ISO format you must support.","Document the exact date contract for producers."],"tags":["gson","date-parsing","iso8601","timezone","index-out-of-bounds"],"backgroundTag":null,"analyzedSha":"310ac341f2f92a454b229bf21f70d2d18b2b6db7","analyzedAt":"2026-08-10T02:58:47.455Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}