{"id":"533b15b0e12889ad","repo":"google/gson","slug":"invalid-time-zone-indicator-timezoneindicator","errorCode":null,"errorMessage":"Invalid time zone indicator {timezoneIndicator}","messagePattern":"Invalid time zone indicator (.+?)","errorType":"exception","errorClass":"IndexOutOfBoundsException","httpStatus":null,"severity":"error","filePath":"extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java","lineNumber":204,"sourceCode":"          }\n        }\n      }\n\n      // extract timezone\n      String timezoneId;\n      if (date.length() <= offset) {\n        throw new IllegalArgumentException(\"No time zone indicator\");\n      }\n      char timezoneIndicator = date.charAt(offset);\n      if (timezoneIndicator == '+' || timezoneIndicator == '-') {\n        String timezoneOffset = date.substring(offset);\n        timezoneId = GMT_ID + timezoneOffset;\n        offset += timezoneOffset.length();\n      } else if (timezoneIndicator == 'Z') {\n        timezoneId = GMT_ID;\n        offset += 1;\n      } else {\n        throw new IndexOutOfBoundsException(\"Invalid time zone indicator \" + timezoneIndicator);\n      }\n\n      TimeZone timezone = TimeZone.getTimeZone(timezoneId);\n      if (!timezone.getID().equals(timezoneId)) {\n        throw new IndexOutOfBoundsException();\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);","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java#L186-L222","documentation":"Thrown internally by UtcDateTypeAdapter's parser when the character at the expected timezone position is not one of 'Z', '+', or '-'. The parser only accepts those three indicators; any other character (a letter, a digit, a space) is treated as an invalid indicator. This IndexOutOfBoundsException is caught and rethrown as a ParseException, then wrapped as JsonParseException by read().","triggerScenarios":"Date strings like \"2024-01-01T12:00:00X\", \"2024-01-01T12:00:00 UTC\", or \"2024-01-01T12:00:00GMT\" where a non-standard token sits at the timezone slot; corrupted or hand-edited timestamps; locale-specific formats that spell out the zone.","commonSituations":"Third-party APIs returning non-strict ISO-8601 (named zones like \"PST\", \"EST\", \"UTC\" spelled out); legacy systems emitting \"GMT\" literally; encoding issues inserting stray characters; copy-paste errors.","solutions":["Convert named/offset zones to the supported form: replace \"UTC\"/\"GMT\" with \"Z\", replace \"PST\" with \"-08:00\", etc., before parsing.","Use a lenient SimpleDateFormat or java.time parser that accepts zone names if you must consume such input, instead of UtcDateTypeAdapter.","Sanitize the input string with a regex/replace step that normalizes the trailing timezone token.","Validate the format against ^.*[Z+-]$ at the timezone position before handing off."],"exampleFix":"// before\nString json = \"\\\"2024-01-01T12:00:00 UTC\\\"\";\nDate d = gson.fromJson(json, Date.class); // throws: ' ' is invalid indicator\n\n// after\nString json = \"\\\"2024-01-01T12:00:00Z\\\"\";\nDate d = gson.fromJson(json, Date.class);","handlingStrategy":"validation","validationCode":"// Normalize named zones to offset form before parsing\nstatic String normalizeZone(String date) {\n  date = date.replaceAll(\"(?i)\\\\s*(UTC|GMT)$\", \"Z\");\n  // Map common named zones to offsets (extend as needed)\n  date = date.replaceAll(\"(?i)\\\\sPST$\", \"-08:00\");\n  date = date.replaceAll(\"(?i)\\\\sPDT$\", \"-07:00\");\n  if (!date.matches(\".*[Zz]$|.*/[+-]\\\\d{2}:?\\\\d{2}$\")) {\n    throw new IllegalArgumentException(\"Unrecognized timezone in: \" + date);\n  }\n  return date;\n}","typeGuard":"static boolean hasValidTimezoneIndicator(String date) {\n  if (date == null || date.isEmpty()) return false;\n  char c = date.charAt(date.length() - 1);\n  return c == 'Z' || c == 'z' || date.matches(\".*[+-]\\\\d{2}:?\\\\d{2}$\");\n}","tryCatchPattern":"try {\n  Date d = gson.fromJson(json, Date.class);\n} catch (JsonParseException e) {\n  Throwable c = e.getCause();\n  if (c != null && c.getMessage() != null && c.getMessage().contains(\"Invalid time zone indicator\")) {\n    // normalize the zone token and retry, or reject the payload\n  } else throw e;\n}","preventionTips":["Standardize producer output to strict ISO-8601 with Z or numeric offset only.","Map named timezones to offsets before parsing if you cannot change the producer.","Reject payloads with named-zone tokens at a validation boundary.","Consider java.time with a ZoneRulesProvider-based parser for named-zone support."],"tags":["date","iso8601","utc-adapter","parsing","invalid-timezone"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}