diff --git a/src/time/format.go b/src/time/format.go index 18a73c45ba..ea95f0be44 100644 --- a/src/time/format.go +++ b/src/time/format.go @@ -1174,6 +1174,37 @@ func leadingInt(s string) (x int64, rem string, err error) { return x, s[i:], nil } +// leadingFraction consumes the leading [0-9]* from s. +// It is used only for fractions, so does not return an error on overflow, +// it just stops accumulating precision. +func leadingFraction(s string) (x int64, scale float64, rem string) { + i := 0 + scale = 1 + overflow := false + for ; i < len(s); i++ { + c := s[i] + if c < '0' || c > '9' { + break + } + if overflow { + continue + } + if x > (1<<63-1)/10 { + // It's possible for overflow to give a positive number, so take care. + overflow = true + continue + } + y := x*10 + int64(c) - '0' + if y < 0 { + overflow = true + continue + } + x = y + scale *= 10 + } + return x, scale, s[i:] +} + var unitMap = map[string]int64{ "ns": int64(Nanosecond), "us": int64(Microsecond), @@ -1236,13 +1267,7 @@ func ParseDuration(s string) (Duration, error) { if s != "" && s[0] == '.' { s = s[1:] pl := len(s) - f, s, err = leadingInt(s) - if err != nil { - return 0, errors.New("time: invalid duration " + orig) - } - for n := pl - len(s); n > 0; n-- { - scale *= 10 - } + f, scale, s = leadingFraction(s) post = pl != len(s) } if !pre && !post { diff --git a/src/time/time_test.go b/src/time/time_test.go index fcc28ee99c..68236fd64d 100644 --- a/src/time/time_test.go +++ b/src/time/time_test.go @@ -840,6 +840,10 @@ var parseDurationTests = []struct { {"9223372036s854ms775us807ns", true, (1<<63 - 1) * Nanosecond}, // large negative value {"-9223372036854775807ns", true, -1<<63 + 1*Nanosecond}, + // huge string; issue 15011. + {"0.100000000000000000000h", true, 6 * Minute}, + // This value tests the first overflow check in leadingFraction. + {"0.830103483285477580700h", true, 49*Minute + 48*Second + 372539827*Nanosecond}, // errors {"", false, 0},