]> Cypherpunks repositories - gostls13.git/commitdiff
time: handle integer overflow in Sub
authorRick Arnold <rickarnoldjr@gmail.com>
Sat, 22 Jun 2013 01:07:57 +0000 (18:07 -0700)
committerRob Pike <r@golang.org>
Sat, 22 Jun 2013 01:07:57 +0000 (18:07 -0700)
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

src/pkg/time/time.go
src/pkg/time/time_test.go

index d291672af10a42a05fc47433d2b4a40884944083..6694ac5bec48ba57ee4bd41d82128b6d8d397a77 100644 (file)
@@ -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.
 //
@@ -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.
index a0ee37ae3b44b34e506bfc3c8557a3b098304b60..2caaf698b799ec4bf75af30c417ccca12a498c21 100644 (file)
@@ -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()