Skip to content

ParseDuration fails to detect overflow for very large values (e.g. 1e10h, 999999w) #271

@narthur

Description

@narthur

Summary

ParseDuration in deadline.go returns (MaxInt64, true) for durations that overflow time.Duration instead of rejecting them. Surfaced by the currently-failing tests TestParseDuration/very_large_hours_(overflow_test) and TestParseDuration/very_large_weeks (beeminder_test.go:2003/2006):

ParseDuration("1e10h") valid = true, want false
ParseDuration("1e10h") = 2562047h47m16.854775807s, want 0s
ParseDuration("999999w") valid = true, want false
ParseDuration("999999w") = 2562047h47m16.854775807s, want 0s

These failures are timezone-independent (they reproduce in every TZ), which is why they were left out of the timezone-resilience fix in #270.

Root cause

The overflow guard only catches the wrap-to-negative case:

duration = time.Duration(num * float64(time.Hour))
...
// Check for overflow: time.Duration is int64 nanoseconds
if duration < 0 {
    return 0, false
}

When num * float64(unit) exceeds math.MaxInt64, the float64int64 conversion saturates to a positive math.MaxInt64 (2562047h47m16.854775807s) rather than wrapping negative. So duration < 0 is never true and the function reports the value as valid.

  • 1e10h1e10 * 3.6e12 ns = 3.6e22, far above MaxInt64 (~9.2e18) → saturates positive.
  • 999999w → similarly overflows → saturates positive.

Suggested fix

Detect the overflow on the floating-point product before/independently of the int64 conversion, e.g. compute the nanosecond product as float64 and reject when it exceeds float64(math.MaxInt64):

ns := num * float64(unitNanos) // unitNanos per the switch
if ns >= float64(math.MaxInt64) {
    return 0, false
}
duration := time.Duration(ns)

(Keep the existing < 0 check as a backstop.) The two failing test cases already encode the expected (0, false) behavior, so no test changes should be needed.

Acceptance criteria

  • ParseDuration("1e10h") and ParseDuration("999999w") return (0, false).
  • TestParseDuration passes.
  • Valid large-but-in-range durations (e.g. up to ~290 years) still parse successfully.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions