kataras/iris · error

invalid offset hours: %d: %s

Error message

invalid offset hours: %d: %s

What it means

After parsing the hours of an unconventional '+HH:MM:SS' offset, adjustForUnconventionalOffset rejects hour values greater than 24, since a UTC offset hour component can never exceed 24. The parsed hours and the full offset string are included in the message.

Source

Thrown at x/jsonx/iso8601.go:241

func adjustForUnconventionalOffset(t time.Time, offset string) (time.Time, error) {
	sign := 1
	if offset[0] == '-' {
		sign = -1
	}
	offset = offset[1:]

	offsetParts := strings.Split(offset, ":")
	if len(offsetParts) != 3 {
		return time.Time{}, fmt.Errorf("invalid offset format: %s", offset)
	}

	hours, err := strconv.Atoi(offsetParts[0])
	if err != nil {
		return time.Time{}, fmt.Errorf("error parsing offset hours: %s: %w", offset, err)
	}

	if hours > 24 {
		return time.Time{}, fmt.Errorf("invalid offset hours: %d: %s", hours, offset)
	}

	minutes, err := strconv.Atoi(offsetParts[1])
	if err != nil {
		return time.Time{}, fmt.Errorf("error parsing offset minutes: %s: %w", offset, err)
	}
	if minutes > 60 {
		return time.Time{}, fmt.Errorf("invalid offset minutes: %d: %s", minutes, offset)
	}

	seconds, err := strconv.Atoi(offsetParts[2])
	if err != nil {
		return time.Time{}, fmt.Errorf("error parsing offset seconds: %s: %w", offset, err)
	}

	if seconds > 60 {
		return time.Time{}, fmt.Errorf("invalid offset seconds: %d: %s", seconds, offset)
	}

View on GitHub (pinned to 7bedaf55a0)

Solutions

  1. Correct the offset so its hour component is within 0–24 (e.g. '+03:00:00').
  2. Verify the producer is not swapping fields (e.g. putting a year or day value into the offset hours slot).
  3. Validate offsets at ingestion with a regex like ^[+-](0?[0-9]|1[0-9]|2[0-4]):[0-5]?[0-9]:[0-5]?[0-9]$ before parsing.

Example fix

// before
ParseISO8601("2024-01-02T15:04:05+30:00:00") // invalid offset hours: 30
// after
ParseISO8601("2024-01-02T15:04:05+03:00:00")
Defensive patterns

Strategy: validation

Validate before calling

func offsetHoursInRange(ts string) bool {
	i := strings.LastIndexAny(ts, "+-")
	if i < 0 { return false }
	parts := strings.Split(ts[i+1:], ":")
	if len(parts) != 3 { return false }
	h, err := strconv.Atoi(parts[0])
	return err == nil && h >= 0 && h <= 24
}

Try / catch

tt, err := jsonx.ParseISO8601(s)
if err != nil {
	if strings.Contains(err.Error(), "invalid offset hours") {
		return fmt.Errorf("timestamp %q has out-of-range UTC offset", s)
	}
	return err
}

Prevention

When it happens

Trigger: ParseISO8601 on a timestamp with an offset like '+30:00:00' or '+99:00:00' — digits parse fine but exceed the 24-hour bound.

Common situations: Malformed timestamps produced by buggy serializers (e.g. concatenating wrong fields); hand-built offset strings; tests with deliberately bogus offsets.

Related errors


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