Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
time: handle integer overflow in Sub
If time.Sub results in a value that won't fit in a Duration (int64),
return either the min or max int64 value as appropriate.

Fixes #5011.

R=golang-dev, bradfitz, r, rsc
CC=golang-dev
https://golang.org/cl/10328043
  • Loading branch information
rickar authored and robpike committed Jun 22, 2013
1 parent dd3a3cf commit fc0b5ef
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 2 deletions.
20 changes: 18 additions & 2 deletions src/pkg/time/time.go
Expand Up @@ -424,6 +424,11 @@ func (t Time) YearDay() int {
// largest representable duration to approximately 290 years.
type Duration int64

const (
minDuration Duration = -1 << 63
maxDuration Duration = 1<<63 - 1
)

// Common durations. There is no definition for units of Day or larger
// to avoid confusion across daylight savings time zone transitions.
//
Expand Down Expand Up @@ -611,10 +616,21 @@ func (t Time) Add(d Duration) Time {
return t
}

// Sub returns the duration t-u.
// Sub returns the duration t-u. If the result exceeds the maximum (or minimum)
// value that can be stored in a Duration, the maximum (or minimum) duration
// will be returned.
// To compute t-d for a duration d, use t.Add(-d).
func (t Time) Sub(u Time) Duration {
return Duration(t.sec-u.sec)*Second + Duration(t.nsec-u.nsec)
d := Duration(t.sec-u.sec)*Second + Duration(t.nsec-u.nsec)
// Check for overflow or underflow.
switch {
case u.Add(d).Equal(t):
return d // d is correct
case t.Before(u):
return minDuration // t - u is negative out of range
default:
return maxDuration // t - u is positive out of range
}
}

// Since returns the time elapsed since t.
Expand Down
34 changes: 34 additions & 0 deletions src/pkg/time/time_test.go
Expand Up @@ -1327,6 +1327,40 @@ func TestLoadFixed(t *testing.T) {
}
}

const (
minDuration Duration = -1 << 63
maxDuration Duration = 1<<63 - 1
)

var subTests = []struct {
t Time
u Time
d Duration
}{
{Time{}, Time{}, Duration(0)},
{Date(2009, 11, 23, 0, 0, 0, 1, UTC), Date(2009, 11, 23, 0, 0, 0, 0, UTC), Duration(1)},
{Date(2009, 11, 23, 0, 0, 0, 0, UTC), Date(2009, 11, 24, 0, 0, 0, 0, UTC), -24 * Hour},
{Date(2009, 11, 24, 0, 0, 0, 0, UTC), Date(2009, 11, 23, 0, 0, 0, 0, UTC), 24 * Hour},
{Date(-2009, 11, 24, 0, 0, 0, 0, UTC), Date(-2009, 11, 23, 0, 0, 0, 0, UTC), 24 * Hour},
{Time{}, Date(2109, 11, 23, 0, 0, 0, 0, UTC), Duration(minDuration)},
{Date(2109, 11, 23, 0, 0, 0, 0, UTC), Time{}, Duration(maxDuration)},
{Time{}, Date(-2109, 11, 23, 0, 0, 0, 0, UTC), Duration(maxDuration)},
{Date(-2109, 11, 23, 0, 0, 0, 0, UTC), Time{}, Duration(minDuration)},
{Date(2290, 1, 1, 0, 0, 0, 0, UTC), Date(2000, 1, 1, 0, 0, 0, 0, UTC), 290*365*24*Hour + 71*24*Hour},
{Date(2300, 1, 1, 0, 0, 0, 0, UTC), Date(2000, 1, 1, 0, 0, 0, 0, UTC), Duration(maxDuration)},
{Date(2000, 1, 1, 0, 0, 0, 0, UTC), Date(2290, 1, 1, 0, 0, 0, 0, UTC), -290*365*24*Hour - 71*24*Hour},
{Date(2000, 1, 1, 0, 0, 0, 0, UTC), Date(2300, 1, 1, 0, 0, 0, 0, UTC), Duration(minDuration)},
}

func TestSub(t *testing.T) {
for i, st := range subTests {
got := st.t.Sub(st.u)
if got != st.d {
t.Errorf("#%d: Sub(%v, %v): got %v; want %v", i, st.t, st.u, got, st.d)
}
}
}

func BenchmarkNow(b *testing.B) {
for i := 0; i < b.N; i++ {
t = Now()
Expand Down

0 comments on commit fc0b5ef

Please sign in to comment.