VictoriaMetrics/VictoriaMetrics · error

cannot parse minute from timezone offset %q: %w

Error message

cannot parse minute from timezone offset %q: %w

What it means

Fires in ParseTimeAt when the trailing timezone offset of a timestamp string (the last 6 characters, e.g. +05:30) has a sign and colon but its minute part (characters after the colon) is not a valid unsigned integer. ParseTimeAt cannot apply the offset, so the whole timestamp fails to parse.

Source

Thrown at lib/timeutil/time.go:50

// It returns unix timestamp in nanoseconds.
func ParseTimeAt(s string, currentTimestamp int64) (int64, error) {
	if s == "now" {
		return currentTimestamp, nil
	}
	sOrig := s
	tzOffset := int64(0)
	if len(sOrig) > 6 {
		// Try parsing timezone offset
		tz := sOrig[len(sOrig)-6:]
		if (tz[0] == '-' || tz[0] == '+') && tz[3] == ':' {
			isPlus := tz[0] == '+'
			hour, err := strconv.ParseUint(tz[1:3], 10, 64)
			if err != nil {
				return 0, fmt.Errorf("cannot parse hour from timezone offset %q: %w", tz, err)
			}
			minute, err := strconv.ParseUint(tz[4:], 10, 64)
			if err != nil {
				return 0, fmt.Errorf("cannot parse minute from timezone offset %q: %w", tz, err)
			}
			tzOffset = int64(hour*3600+minute*60) * 1e9
			if isPlus {
				tzOffset = -tzOffset
			}
			s = sOrig[:len(sOrig)-6]
		} else {
			if !strings.HasSuffix(s, "Z") {
				tzOffset = -GetLocalTimezoneOffsetNsecs()
			} else {
				s = s[:len(s)-1]
			}
		}
	}
	s = strings.TrimSuffix(s, "Z")
	if len(s) > 0 && (s[len(s)-1] > '9' || s[0] == '-') || strings.HasPrefix(s, "now") {
		// Parse duration relative to the current time
		s = strings.TrimPrefix(s, "now")

View on GitHub (pinned to 5079fb58f1)

Solutions

  1. Supply a complete RFC3339 offset, e.g. `2024-01-02T10:00:00+02:30`
  2. Fix the upstream producer that emits placeholder minutes
  3. Parse the timestamp yourself (time.Parse with the right layout) if inputs are nonstandard

Example fix

// before
ParseTimeAt("2024-01-02T10:00:00+02:MM", nil)
// after
ParseTimeAt("2024-01-02T10:00:00+02:30", nil)
Defensive patterns

Strategy: validation

Validate before calling

var tzRe = regexp.MustCompile(`[+-]([0-9]{2}):([0-9]{2})$`)
func validOffsetMinutes(s string) bool {
	m := tzRe.FindStringSubmatch(s)
	if m == nil {
		return false
	}
	_, err1 := strconv.ParseUint(m[1], 10, 64)
	_, err2 := strconv.ParseUint(m[2], 10, 64)
	return err1 == nil && err2 == nil
}

Type guard

func hasRFC3339Offset(s string) bool {
	_, err := time.Parse(time.RFC3339, s)
	return err == nil
}

Try / catch

ts, err := timeutil.ParseTimeMsec(s, nil)
if err != nil {
	log.Printf("skipping malformed timestamp %q: %v", s, err)
}

Prevention

When it happens

Trigger: ParseTimeAt / ParseTimeMsec with timestamps like `"2024-01-02T10:00:00+02:MM"` or `"...+02:--"` — the last 6 chars look like an offset but tz[4:] is not a valid digit string.

Common situations: Corrupted log timestamps; placeholders like `+HH:MM` never substituted with real digits; truncation/encoding issues mangling the tail of an RFC3339 string.

Related errors


AI-assisted analysis of VictoriaMetrics/VictoriaMetrics@5079fb58f1 (2026-09-03). Data as JSON: /api/errors/8564f04a4c7735e4. Report an issue: GitHub.