kataras/iris · error
ISO8601: missing timezone offset
Error message
ISO8601: missing timezone offset
What it means
This error means the ISO8601 timestamp string handed to the parser has no timezone offset at all (no +HH:MM / -HH:MM suffix). The library's parseWithOffset path is only used when the string is expected to carry an offset, so finding none is treated as a hard parse failure rather than falling back to a zone-less time. It indicates the datetime part parsed OK positionally but the required offset signature ('+' or '-') is absent.
Source
Thrown at x/jsonx/iso8601.go:173
tt, err = time.Parse(ISO8601ZUTCOffsetLayoutWithoutMicroseconds, s)
}
}
} else if s[len(s)-1] == 'Z' {
tt, err = time.Parse(ISO8601LayoutWithTimezone, s)
} else {
tt, err = time.Parse(ISO8601Layout, s)
}
if err != nil {
return ISO8601{}, fmt.Errorf("ISO8601: %w", err)
}
return ISO8601(tt), nil
}
func parseWithOffset(s string) (time.Time, error) {
idx := strings.LastIndexFunc(s, startUTCOffsetIndexFunc)
if idx == -1 {
return time.Time{}, fmt.Errorf("ISO8601: missing timezone offset")
}
offsetText := s[idx:]
secondsEastUTC, err := parseOffsetToSeconds(offsetText)
if err != nil {
return time.Time{}, err
}
loc, ok := fixedEastUTCLocations[secondsEastUTC]
if !ok {
loc = time.FixedZone("", secondsEastUTC)
}
return time.ParseInLocation(ISO8601ZUTCOffsetLayoutWithoutMicroseconds, s, loc)
}
func parseOffsetToSeconds(offsetText string) (int, error) {
if len(offsetText) < 6 {View on GitHub (pinned to 7bedaf55a0)
Solutions
- Append a valid UTC offset to the input string, e.g. '2024-01-02T15:04:05' -> '2024-01-02T15:04:05+00:00' (or use the 'Z' suffix form '2024-01-02T15:04:05Z' so ParseISO8601 takes the Z-layout branch).
- If zone-less input is expected/allowed, do not route it through ParseISO8601 expecting an offset; pre-normalize the string (regex check for [+-]HH:MM) before calling, or parse with time.Parse(ISO8601Layout, s) directly.
- Fix upstream producers (API clients, DB column type 'timestamptz', serialization layer) so timestamps always include the offset.
- If the string is truncated, verify no middleware/logging pipeline is cutting the tail (min length for offset form is 25 chars: '2006-01-02T15:04:05+07:00').
Example fix
// before
var t jsonx.ISO8601
err := t.UnmarshalJSON([]byte("2024-01-02T15:04:05")) // ISO8601: missing timezone offset
// after
err := t.UnmarshalJSON([]byte("2024-01-02T15:04:05+00:00")) // ok
// or use the Z form:
err = t.UnmarshalJSON([]byte("2024-01-02T15:04:05Z")) Defensive patterns
Strategy: validation
Validate before calling
var isoOffsetRe = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?([+-]\d{2}:\d{2}|Z)$`)
func hasTimezone(s string) bool { return isoOffsetRe.MatchString(s) }
// call jsonx.ParseISO8601 only if hasTimezone(s); otherwise normalize by appending "+00:00" Type guard
func isOffsetForm(s string) bool {
i := strings.LastIndexAny(s, "+-")
return i > 18 && len(s)-i == 6
} Try / catch
tt, err := jsonx.ParseISO8601(s)
if err != nil {
if strings.Contains(err.Error(), "missing timezone offset") {
// fall back to naive layout
t0, perr := time.Parse(jsonx.ISO8601Layout, s)
if perr == nil {
tt = jsonx.ISO8601(t0)
err = nil
}
}
if err != nil {
return fmt.Errorf("parse timestamp %q: %w", s, err)
}
} Prevention
- Always emit timestamps with an explicit offset (+00:00 or Z) from producers
- Prefer Postgres 'timestamptz' over 'timestamp' for DB columns scanned into ISO8601
- Validate timestamp strings at API boundaries with a regex before unmarshaling
- Check string length >= 20 and presence of '+'/'-' after position 18 before assuming offset form
When it happens
Trigger: Calling jsonx.ParseISO8601 (or ISO8601.UnmarshalJSON / ISO8601.Scan) with a string that reaches parseWithOffset yet contains no '+' or '-' rune: e.g. '2024-01-02T15:04:05' with a code path that assumes an offset, or a truncated string like '2024-01-02T15:04:05' where the offset was stripped by upstream normalization.
Common situations: Frontends or third-party APIs sending naive local datetimes (no zone) into structs with jsonx.ISO8601 fields; database drivers returning 'timestamp without time zone' strings scanned into ISO8601; config files or CSV imports with datetime strings lacking the offset; truncation of the tail of a timestamp during logging/serialization.
Related errors
- ISO8601: %w
- ISO8601: invalid timezone offset length: %s
- ISO8601: invalid timezone offset sign: %c
- DayTime: unknown type of: %T
- ISO8601: invalid timezone format: %s
AI-assisted analysis of kataras/iris@7bedaf55a0 (2026-08-30).
Data as JSON: /api/errors/d7517f6ba9d1909d.
Report an issue: GitHub.