kataras/iris · error

ISO8601: %w

Error message

ISO8601: %w

What it means

ParseISO8601 parses strings in several ISO-8601 layouts (with/without milliseconds, Z suffix, or +hh:mm offsets, including unconventional hh:mm:ss offsets). When time.Parse (or offset adjustment) rejects the input, the underlying error is wrapped as "ISO8601: %w". It means the string is not in any of the supported ISO-8601 shapes.

Source

Thrown at x/jsonx/iso8601.go:107

	)

	/*
		// Check if the string contains a timezone offset after the 'T' character.
		hasOffset := strings.Contains(s, "Z") || (strings.Index(s, "+") > strings.Index(s, "T")) || (strings.Index(s, "-") > strings.Index(s, "T"))

		switch {
		case strings.HasSuffix(s, "Z"):
			tt, err = time.Parse(ISO8601LayoutWithTimezone, s)
		case hasOffset && strings.Contains(s, "."):
			tt, err = time.Parse(ISO8601ZUTCOffsetLayoutWithMicroseconds, s)
		case hasOffset:
			tt, err = parseWithOffset(s)
		default:
			tt, err = time.Parse(ISO8601Layout, s)
		}

		if err != nil {
			return ISO8601{}, fmt.Errorf("ISO8601: %w", err)
		}

		return ISO8601(tt), nil
	*/

	if idx := strings.LastIndexFunc(s, startUTCOffsetIndexFunc); idx > 18 { // should have some distance, with and without milliseconds
		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 {

View on GitHub (pinned to 7bedaf55a0)

Solutions

  1. Inspect the wrapped %w error (e.g. parsing time ... cannot parse) to see which part of the string failed and fix the producer to emit one of the supported layouts, e.g. time.Time.UTC().Format(time.RFC3339).
  2. Pre-parse with a custom time.Parse layout that matches the actual input, then convert to jsonx.ISO8601(tt).
  3. Normalize common variants before parsing (replace space with 'T', append ":00" to short offsets, or handle date-only strings explicitly).
  4. For UnmarshalJSON failures, use a *string or json.RawMessage field and decode manually with fallback layouts.

Example fix

// before: value "2024-01-02 10:30:00" (space instead of T)
var t jsonx.ISO8601
json.Unmarshal(b, &t) // ISO8601: parsing time ... cannot parse

// after
var raw string
json.Unmarshal(b, &raw)
normalized := strings.Replace(raw, " ", "T", 1)
tt, err := jsonx.ParseISO8601(normalized)
Defensive patterns

Strategy: validation

Validate before calling

func parseISO8601Safe(s string) (jsonx.ISO8601, error) {
    s = strings.TrimSpace(s)
    s = strings.Replace(s, " ", "T", 1)
    if s == "" || s == "null" {
        return jsonx.ISO8601{}, nil
    }
    if re := regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?$`); !re.MatchString(s) {
        return jsonx.ISO8601{}, fmt.Errorf("unsupported timestamp format: %q", s)
    }
    return jsonx.ParseISO8601(s)
}

Type guard

var isoLayout = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2}(:\d{2})?)?$`)

func looksLikeISO8601(s string) bool {
    return isoLayout.MatchString(strings.TrimSpace(s))
}

Try / catch

if err := json.Unmarshal(b, &ts); err != nil {
    var perr *time.ParseError
    if errors.As(err, &perr) && strings.HasPrefix(err.Error(), "ISO8601:") {
        // log raw value and fall back to a lenient custom-layout parser
    }
}

Prevention

When it happens

Trigger: Calling jsonx.ParseISO8601, or json.Unmarshal into a jsonx.ISO8601 field (UnmarshalJSON), or ISO8601.Scan on a string DB value, with a string whose format deviates: wrong layout like "02/01/2006 15:04", date-only "2024-01-02", out-of-range values (month 13), offsets whose parse fails (e.g. malformed +3:00 without zero padding, or a value like +030000), or an offset substring whose hour part is non-numeric.

Common situations: A third-party API changed its date format or sends date-only strings; frontend sends a locale-formatted date instead of toISOString(); a column migrated from timestamp to text now delivers values like "2024-01-02 10:00:00" (space instead of T); timezone offsets like "+03" (no minutes) that the layouts don't cover.

Related errors


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