google/gson · error · IllegalArgumentException
No time zone indicator
Error message
No time zone indicator
What it means
Thrown by Gson's ISO8601Utils.parse when a date string that contains a time component ('T') has no trailing timezone indicator. After parsing year/month/day/[hour/min/sec], the parser checks position; if the string ends without a 'Z' or +/- offset, the date is considered incomplete and IllegalArgumentException("No time zone indicator") is thrown, which ISO8601Utils rewraps into a ParseException.
Solutions
- Ensure the producer appends 'Z' (for UTC) or an explicit offset like '+00:00' to every date-time string.
- Register a custom TypeAdapter<Date> that uses SimpleDateFormat with a lenient pattern matching zoneless strings and a default TimeZone.
- If you cannot change the producer, pre-process the JSON to append a default zone before parsing.
- Switch the target field to java.time.LocalDateTime (with a custom adapter) if the value is genuinely zoneless.
Example fix
// before
public class Event { public Date at; }
// JSON: {"at":"2020-01-01T12:00:00"} -> 'No time zone indicator'
// after: producer emits UTC zone indicator
// {"at":"2020-01-01T12:00:00Z"}
// or custom adapter accepting zoneless as UTC:
Gson g = new GsonBuilder()
.registerTypeAdapter(Date.class, new TypeAdapter<Date>() {
private final DateFormat f = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
{ f.setTimeZone(TimeZone.getTimeZone("UTC")); }
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_Z =
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_Z.matcher(raw).matches()) {
// append 'Z' if the producer omits zone, or reject
raw = raw + "Z";
} Try / catch
try {
return gson.fromJson(json, Event.class);
} catch (JsonSyntaxException e) {
if (e.getCause() instanceof ParseException && e.getCause().getMessage().contains("No time zone indicator")) {
// append default zone and retry once
JsonObject patched = jsonNode.deepCopy();
patched.addProperty("at", patched.get("at").getAsString() + "Z");
return gson.fromJson(patched, Event.class);
}
throw e;
} Prevention
- Mandate ISO8601 with explicit zone ('Z' or offset) in producer contracts.
- Pin producer timestamp formatting through a single utility (e.g. Instant.toString).
- Contract-test that all date fields include a zone indicator.
- Consider java.time.Instant/OffsetDateTime typed fields with custom adapters for strict semantics.
When it happens
Trigger: Parsing a date-time string in ISO8601 form missing the timezone, e.g. "2020-01-01T12:00:00" (no trailing Z or offset), or "2020-01-01T12:00". This occurs when Gson deserializes into java.util.Date / java.util.Calendar using the default ISO format and the input omits the zone.
Common situations: Backends emitting 'local' datetimes without zone; databases returning TIMESTAMP without timezone; legacy formats like "yyyy-MM-dd'T'HH:mm:ss" produced by older Jackson/Joda configs; mismatches between producer (no zone) and Gson consumer (expects zone).
Related errors
- Invalid number
- Invalid time zone indicator
- Mismatching time zone indicator
- Failed parsing ' ' as SQL Date; at path
- Failed parsing ' ' as SQL Time; at path
AI-assisted analysis of google/gson@310ac341f2 (2026-08-10).
Data as JSON: /api/errors/259e4b186f2efdf8.
Report an issue: GitHub.
Appendix: source
Thrown at gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java:231
switch (parseEndOffset - offset) { // number of digits parsed
case 2:
milliseconds = fraction * 10;
break;
case 1:
milliseconds = fraction * 100;
break;
default:
milliseconds = fraction;
}
offset = endOffset;
}
}
}
}
// extract timezone
if (date.length() <= offset) {
throw new IllegalArgumentException("No time zone indicator");
}
TimeZone timezone = null;
char timezoneIndicator = date.charAt(offset);
if (timezoneIndicator == 'Z') {
timezone = TIMEZONE_UTC;
offset += 1;
} else if (timezoneIndicator == '+' || timezoneIndicator == '-') {
String timezoneOffset = date.substring(offset);
// When timezone has no minutes, we should append it, valid timezones are, for example:
// +00:00, +0000 and +00
timezoneOffset = timezoneOffset.length() >= 5 ? timezoneOffset : timezoneOffset + "00";
offset += timezoneOffset.length();
// 18-Jun-2015, tatu: Minor simplification, skip offset of "+0000"/"+00:00"
if (timezoneOffset.equals("+0000") || timezoneOffset.equals("+00:00")) {View on GitHub (pinned to 310ac341f2)