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 float64→int64 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.
1e10h → 1e10 * 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.
Summary
ParseDurationindeadline.goreturns(MaxInt64, true)for durations that overflowtime.Durationinstead of rejecting them. Surfaced by the currently-failing testsTestParseDuration/very_large_hours_(overflow_test)andTestParseDuration/very_large_weeks(beeminder_test.go:2003/2006):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:
When
num * float64(unit)exceedsmath.MaxInt64, thefloat64→int64conversion saturates to a positivemath.MaxInt64(2562047h47m16.854775807s) rather than wrapping negative. Soduration < 0is never true and the function reports the value as valid.1e10h→1e10 * 3.6e12 ns = 3.6e22, far aboveMaxInt64(~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
float64and reject when it exceedsfloat64(math.MaxInt64):(Keep the existing
< 0check 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")andParseDuration("999999w")return(0, false).TestParseDurationpasses.