{"id":"d2da281b38e49eff","repo":"google/gson","slug":"mismatching-time-zone-indicator-timezoneid","errorCode":null,"errorMessage":"Mismatching time zone indicator: \" + timezoneId + \" given, resolves to \" + timezone.getID()","messagePattern":"Mismatching time zone indicator: \" \\+ timezoneId \\+ \" given, resolves to \" \\+ timezone\\.getID\\(\\)","errorType":"exception","errorClass":"IndexOutOfBoundsException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java","lineNumber":270,"sourceCode":"          // 18-Jun-2015, tatu: Looks like offsets only work from GMT, not UTC...\n          //    not sure why, but that's the way it looks. Further, Javadocs for\n          //    `java.util.TimeZone` specifically instruct use of GMT as base for\n          //    custom timezones... odd.\n          String timezoneId = \"GMT\" + timezoneOffset;\n          // String timezoneId = \"UTC\" + timezoneOffset;\n\n          timezone = TimeZone.getTimeZone(timezoneId);\n\n          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);","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java#L252-L288","documentation":"ISO8601Utils parses a +/- offset and constructs TimeZone.getTimeZone('GMT'+offset). The JDK silently falls back to GMT for unknown/invalid IDs, so Gson compares the returned ID to the requested one and throws IndexOutOfBoundsException when they differ even after stripping colons, indicating the offset was invalid (e.g. '+99:99').","triggerScenarios":"A date string whose numeric offset is out of range such as '+25:00', '-13:00', or '+99:99', causing the JDK to substitute GMT.","commonSituations":"Corrupt producer data, hand-built strings, timezone offsets with wrong sign or magnitude, or a serialization bug upstream.","solutions":["Correct the offset in the source to a valid ±HH:MM in the range -12:00 .. +14:00.","Pre-validate/normalize the offset before passing to Gson.","Register a custom date adapter that maps invalid offsets to a default zone."],"exampleFix":"// before\nDate d = gson.fromJson(\"\\\"2024-01-01T00:00:00+25:00\\\"\", Date.class);\n\n// after\nString fixed = source.replaceAll(\"([+\\\\-])([0-9]{2}):([0-9]{2})$\", m -> /* clamp to +/-14:00 */ \"+00:00\");\nDate d = gson.fromJson('\"' + fixed + '\"', Date.class);","handlingStrategy":"validation","validationCode":"boolean isValidOffset(String iso) {\n  java.util.regex.Matcher m = java.util.regex.Pattern.compile(\"([+\\\\-])(\\\\d{2}):(\\\\d{2})$\").matcher(iso == null ? \"\" : iso);\n  if (!m.find()) return true; // not an explicit offset\n  int hh = Integer.parseInt(m.group(2)), mm = Integer.parseInt(m.group(3));\n  return hh <= 14 && mm < 60;\n}","typeGuard":"static boolean hasPlausibleOffset(String iso) {\n  return iso != null && iso.matches(\".*[+\\\\-](0[0-9]|1[0-4]):[0-5][0-9]$\");\n}","tryCatchPattern":"try {\n  Date d = gson.fromJson(json, Date.class);\n} catch (JsonSyntaxException e) {\n  // if 'Mismatching time zone indicator', reject or clamp offset and retry\n}","preventionTips":["Validate offset magnitudes (HH 0-14, MM 0-59) before parsing.","Standardize on UTC ('Z') in producer data when possible.","Log and quarantine records with non-standard offsets."],"tags":["gson","date-parsing","iso8601","timezone","json"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}