kataras/iris · error

ISO8601: invalid timezone format: %s

Error message

ISO8601: invalid timezone format: %s

What it means

ParseISO8601 throws "ISO8601: invalid timezone format: <suffix>" when the string has an offset-looking sign after position 18, but the offset substring is structurally unusable: parseSignedOffset returned an inconsistent/zero length (idx+1 > idx+length) or the string ends before a complete offset could exist (len(s) <= idx+length+1). The offending suffix (everything from the +/- sign onward) is included verbatim in the message.

Source

Thrown at x/jsonx/iso8601.go:132

		length := parseSignedOffset(s[idx:])

		// Check if the offset is unconventional, e.g., -04:01:19
		if offset := s[idx:]; isUnconventionalOffset(offset) {
			mainPart := s[:idx]
			tt, err = time.Parse("2006-01-02T15:04:05.000000", mainPart)
			if err != nil {
				return ISO8601{}, fmt.Errorf("ISO8601: %w", err)
			}

			adjustedTime, parseErr := adjustForUnconventionalOffset(tt, offset)
			if parseErr != nil {
				return ISO8601{}, fmt.Errorf("ISO8601: %w", parseErr)
			}
			return ISO8601(adjustedTime), nil
		}

		if idx+1 > idx+length || len(s) <= idx+length+1 {
			return ISO8601{}, fmt.Errorf("ISO8601: invalid timezone format: %s", s[idx:])
		}

		offsetText := s[idx+1 : idx+length]
		offset, parseErr := strconv.Atoi(offsetText)
		if parseErr != nil {
			return ISO8601{}, fmt.Errorf("ISO8601: %w", parseErr)
		}

		// E.g. offset of +0300 is returned as 10800 which is - (3 * 60 * 60).
		secondsEastUTC := offset * 60 * 60

		// fmt.Printf("parsing %s with offset %s, secondsEastUTC: %d, using time layout: %s\n", s, offsetText, secondsEastUTC, ISO8601ZUTCOffsetLayoutWithMicroseconds)
		if loc, ok := fixedEastUTCLocations[secondsEastUTC]; ok { // Specific (fixed) zone.
			if strings.Contains(s, ".") {
				tt, err = time.ParseInLocation(ISO8601ZUTCOffsetLayoutWithMicroseconds, s, loc)
			} else {
				tt, err = time.ParseInLocation(ISO8601ZUTCOffsetLayoutWithoutMicroseconds, s, loc)
			}

View on GitHub (pinned to 7bedaf55a0)

Solutions

  1. Log/inspect the suffix printed in the error and re-emit the complete timestamp including the full offset (e.g. +03:00 not +).
  2. If the timestamp should be UTC, replace the broken suffix with "Z" or "+00:00" before parsing.
  3. Strip the incomplete offset entirely so the string matches the plain ISO8601Layout ("2024-01-02T15:04:05") when the offset is unknowable.
  4. Pre-validate the trailing offset with a regexp such as [+-]\d{2}(:\d{2})?$ before calling ParseISO8601.

Example fix

// before
t, err := jsonx.ParseISO8601("2024-01-02T15:04:05+") // ISO8601: invalid timezone format: +
// after
t, err := jsonx.ParseISO8601("2024-01-02T15:04:05+03:00")
Defensive patterns

Strategy: validation

Validate before calling

var completeOffsetRe = regexp.MustCompile(`[+-]\d{2}(:\d{2})?$`)

func hasCompleteOffset(s string) bool {
	i := strings.LastIndexAny(s, "+-")
	return i >= 0 && completeOffsetRe.MatchString(s[i:])
}

// usage
if !hasCompleteOffset(raw) {
	return fmt.Errorf("timestamp %q has truncated timezone offset", raw)
}

Type guard

func isTruncatedOffset(s string) bool {
	i := strings.LastIndexAny(s, "+-")
	return i >= 0 && i == len(s)-1 || (i >= 0 && !regexp.MustCompile(`[+-]\d{2}(:\d{2})?$`).MatchString(s[i:]))
}

Try / catch

t, err := jsonx.ParseISO8601(raw)
if err != nil {
	if strings.Contains(err.Error(), "invalid timezone format") {
		// recover: treat as UTC or re-fetch the full timestamp from source
		return jsonx.ParseISO8601(strings.TrimRight(raw, "+-") + "Z")
	}
	return err
}

Prevention

When it happens

Trigger: Calling ParseISO8601 (or UnmarshalJSON/Scan) with strings like "2024-01-02T15:04:05+" (bare sign), "2024-01-02T15:04:05+2" (truncated offset), "2024-01-02T15:04:05xyz+03" (offset truncated by a trailing char), or any input where the trailing +/- region is not a complete +hh / +hh:mm style offset.

Common situations: Truncated timestamps from log tailing or fixed-width column reads; JSON payloads cut off mid-string; query parameters or config values where the offset was accidentally stripped (e.g. shell splitting on '+'); spreadsheet/CSV exports that mangle '+' characters.

Related errors


AI-assisted analysis of kataras/iris@7bedaf55a0 (2026-08-30). Data as JSON: /api/errors/2c2bc49620b3659e. Report an issue: GitHub.