{"record":{"id":"acad1435b6fe8988","repo":"google/gson","slug":"failed-parsing-as-sql-date-at-path","errorCode":null,"errorMessage":"Failed parsing '{}' as SQL Date; at path {}","messagePattern":"Failed parsing '(.+?)' as SQL Date; at path (.+?)","errorType":"exception","errorClass":"JsonSyntaxException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/sql/SqlDateTypeAdapter.java","lineNumber":69,"sourceCode":"\n  private final DateFormat format = new SimpleDateFormat(\"MMM d, yyyy\");\n\n  private SqlDateTypeAdapter() {}\n\n  @Override\n  public java.sql.Date read(JsonReader in) throws IOException {\n    if (in.peek() == JsonToken.NULL) {\n      in.nextNull();\n      return null;\n    }\n    String s = in.nextString();\n    synchronized (this) {\n      TimeZone originalTimeZone = format.getTimeZone(); // Save the original time zone\n      try {\n        Date utilDate = format.parse(s);\n        return new java.sql.Date(utilDate.getTime());\n      } catch (ParseException e) {\n        throw new JsonSyntaxException(\n            \"Failed parsing '\" + s + \"' as SQL Date; at path \" + in.getPreviousPath(), e);\n      } finally {\n        format.setTimeZone(originalTimeZone); // Restore the original time zone after parsing\n      }\n    }\n  }\n\n  @Override\n  public void write(JsonWriter out, java.sql.Date value) throws IOException {\n    if (value == null) {\n      out.nullValue();\n      return;\n    }\n    String dateString;\n    synchronized (this) {\n      dateString = format.format(value);\n    }\n    out.value(dateString);","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/google/gson/blob/310ac341f2f92a454b229bf21f70d2d18b2b6db7/gson/src/main/java/com/google/gson/internal/sql/SqlDateTypeAdapter.java#L51-L87","documentation":"Thrown by Gson's SqlDateTypeAdapter when its SimpleDateFormat(\"MMM d, yyyy\") fails to parse the JSON string into java.sql.Date. The default format expects a locale-specific month abbreviation (e.g. \"Jan 1, 2020\") in the adapter's locale; anything else (ISO date, epoch number, locale mismatch) causes ParseException wrapped as JsonSyntaxException.","triggerScenarios":"Deserializing a field of type java.sql.Date from a JSON string that does not match \"MMM d, yyyy\" in the running locale: e.g. \"2020-01-01\" (ISO), \"1577836800000\" (epoch millis as string), \"01/01/2020\", or a month abbreviation in a different locale than the JVM default.","commonSituations":"Mixing java.util.Date (ISO8601 default in Gson) with java.sql.Date (\"MMM d, yyyy\" default); servers running in different locales producing/expecting different month abbreviations; producers emitting ISO dates or epoch values for sql.Date fields; timezone differences shifting the day.","solutions":["Produce JSON strings in the format \"MMM d, yyyy\" (e.g. \"Jan 1, 2020\") matching the JVM default locale, OR switch the field to java.util.Date / java.time.LocalDate.","Register a custom TypeAdapter<java.sql.Date> with SimpleDateFormat(\"yyyy-MM-dd\") (or your actual format) and set its locale/timezone explicitly.","Pin the JVM locale or set the format's locale to match the producer to avoid month-abbreviation mismatches.","If the producer emits epoch millis, switch to a numeric adapter."],"exampleFix":"// before\npublic class Row { public java.sql.Date created; }\n// JSON: {\"created\":\"2020-01-01\"} -> fails (expects \"MMM d, yyyy\")\n\n// after: custom adapter for ISO dates\nGson g = new GsonBuilder()\n  .registerTypeAdapter(java.sql.Date.class, new TypeAdapter<java.sql.Date>() {\n    private final DateFormat f = new SimpleDateFormat(\"yyyy-MM-dd\");\n    { f.setTimeZone(TimeZone.getTimeZone(\"UTC\")); }\n    public java.sql.Date read(JsonReader in) throws IOException {\n      try { return new java.sql.Date(f.parse(in.nextString()).getTime()); }\n      catch (ParseException e) { throw new JsonSyntaxException(e); }\n    }\n    public void write(JsonWriter out, java.sql.Date v) throws IOException { out.value(f.format(v)); }\n  }).create();","handlingStrategy":"validation","validationCode":"// validate the expected format before parsing, or normalize\nprivate static final Pattern MMM = Pattern.compile(\"^[A-Za-z]{3} \\\\d{1,2}, \\\\d{4}$\");\nString raw = jsonNode.get(\"created\").getAsString();\nif (raw == null || !MMM.matcher(raw).matches()) {\n  // either reject, or convert ISO 'yyyy-MM-dd' -> 'MMM d, yyyy'\n  throw new IllegalArgumentException(\"Not 'MMM d, yyyy': \" + raw);\n}","typeGuard":null,"tryCatchPattern":"try {\n  return gson.fromJson(json, Row.class);\n} catch (JsonSyntaxException e) {\n  if (e.getMessage().contains(\"as SQL Date\")) {\n    // switch to a custom TypeAdapter<java.sql.Date> with the real format\n    throw new IllegalArgumentException(\"Bad SQL Date format\", e);\n  }\n  throw e;\n}","preventionTips":["Register a custom TypeAdapter<java.sql.Date> matching the producer's actual format.","Pin the format locale to avoid month-abbreviation mismatches across regions.","Prefer java.time.LocalDate for new code; reserve java.sql.Date for JDBC boundaries.","Contract-test round-trip of date values across locales/timezones."],"tags":["gson","date-parsing","sql-date","simple-date-format","locale","json-syntax-exception"],"backgroundTag":null,"analyzedSha":"310ac341f2f92a454b229bf21f70d2d18b2b6db7","analyzedAt":"2026-08-10T02:58:47.455Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}