Skip to content

Commit 5fbf35d

Browse files
committed
time: be consistent about representation of UTC location in Time struct
In the zero Time, the (not user visible) nil *Location indicates UTC. In the result of t.UTC() and other ways to create times in specific zones, UTC is indicated by a non-nil *Location, specifically &utcLoc. This creates a representation ambiguity exposed by comparison with == or reflect.DeepEqual or the like. Change time.Time representation to use only nil, never &utcLoc, to represent UTC. This eliminates the ambiguity. Fixes #15716. Change-Id: I7dcc2c20ce6b073e1daae323d3e49d17d1d52802 Reviewed-on: https://go-review.googlesource.com/31144 Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 79c0362 commit 5fbf35d

File tree

3 files changed

+41
-15
lines changed

3 files changed

+41
-15
lines changed

src/time/format.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,12 +1021,12 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
10211021
// If that zone was in effect at the given time, use it.
10221022
name, offset, _, _, _ := local.lookup(t.sec + internalToUnix)
10231023
if offset == zoneOffset && (zoneName == "" || name == zoneName) {
1024-
t.loc = local
1024+
t.setLoc(local)
10251025
return t, nil
10261026
}
10271027

10281028
// Otherwise create fake zone to record offset.
1029-
t.loc = FixedZone(zoneName, zoneOffset)
1029+
t.setLoc(FixedZone(zoneName, zoneOffset))
10301030
return t, nil
10311031
}
10321032

@@ -1037,7 +1037,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
10371037
offset, _, ok := local.lookupName(zoneName, t.sec+internalToUnix)
10381038
if ok {
10391039
t.sec -= int64(offset)
1040-
t.loc = local
1040+
t.setLoc(local)
10411041
return t, nil
10421042
}
10431043

@@ -1046,7 +1046,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
10461046
offset, _ = atoi(zoneName[3:]) // Guaranteed OK by parseGMT.
10471047
offset *= 3600
10481048
}
1049-
t.loc = FixedZone(zoneName, offset)
1049+
t.setLoc(FixedZone(zoneName, offset))
10501050
return t, nil
10511051
}
10521052

src/time/time.go

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,18 @@ type Time struct {
5050
// loc specifies the Location that should be used to
5151
// determine the minute, hour, month, day, and year
5252
// that correspond to this Time.
53-
// Only the zero Time has a nil Location.
54-
// In that case it is interpreted to mean UTC.
53+
// The nil location means UTC.
54+
// All UTC times are represented with loc==nil, never loc==&utcLoc.
5555
loc *Location
5656
}
5757

58+
func (t *Time) setLoc(loc *Location) {
59+
if loc == &utcLoc {
60+
loc = nil
61+
}
62+
t.loc = loc
63+
}
64+
5865
// After reports whether the time instant t is after u.
5966
func (t Time) After(u Time) bool {
6067
return t.sec > u.sec || t.sec == u.sec && t.nsec > u.nsec
@@ -788,13 +795,13 @@ func Now() Time {
788795

789796
// UTC returns t with the location set to UTC.
790797
func (t Time) UTC() Time {
791-
t.loc = UTC
798+
t.setLoc(&utcLoc)
792799
return t
793800
}
794801

795802
// Local returns t with the location set to local time.
796803
func (t Time) Local() Time {
797-
t.loc = Local
804+
t.setLoc(Local)
798805
return t
799806
}
800807

@@ -805,7 +812,7 @@ func (t Time) In(loc *Location) Time {
805812
if loc == nil {
806813
panic("time: missing Location in call to Time.In")
807814
}
808-
t.loc = loc
815+
t.setLoc(loc)
809816
return t
810817
}
811818

@@ -846,7 +853,7 @@ const timeBinaryVersion byte = 1
846853
func (t Time) MarshalBinary() ([]byte, error) {
847854
var offsetMin int16 // minutes east of UTC. -1 is UTC.
848855

849-
if t.Location() == &utcLoc {
856+
if t.Location() == UTC {
850857
offsetMin = -1
851858
} else {
852859
_, offset := t.Zone()
@@ -907,11 +914,11 @@ func (t *Time) UnmarshalBinary(data []byte) error {
907914
offset := int(int16(buf[1])|int16(buf[0])<<8) * 60
908915

909916
if offset == -1*60 {
910-
t.loc = &utcLoc
917+
t.setLoc(&utcLoc)
911918
} else if _, localoff, _, _, _ := Local.lookup(t.sec + internalToUnix); offset == localoff {
912-
t.loc = Local
919+
t.setLoc(Local)
913920
} else {
914-
t.loc = FixedZone("", offset)
921+
t.setLoc(FixedZone("", offset))
915922
}
916923

917924
return nil
@@ -1104,7 +1111,9 @@ func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) T
11041111
unix -= int64(offset)
11051112
}
11061113

1107-
return Time{unix + unixToInternal, int32(nsec), loc}
1114+
t := Time{unix + unixToInternal, int32(nsec), nil}
1115+
t.setLoc(loc)
1116+
return t
11081117
}
11091118

11101119
// Truncate returns the result of rounding t down to a multiple of d (since the zero time).

src/time/time_test.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,8 @@ var defaultLocTests = []struct {
11171117

11181118
{"Truncate", func(t1, t2 Time) bool { return t1.Truncate(Hour).Equal(t2.Truncate(Hour)) }},
11191119
{"Round", func(t1, t2 Time) bool { return t1.Round(Hour).Equal(t2.Round(Hour)) }},
1120+
1121+
{"== Time{}", func(t1, t2 Time) bool { return (t1==Time{}) == (t2==Time{}) }},
11201122
}
11211123

11221124
func TestDefaultLoc(t *testing.T) {
@@ -1126,7 +1128,7 @@ func TestDefaultLoc(t *testing.T) {
11261128
t1 := Time{}
11271129
t2 := Time{}.UTC()
11281130
if !tt.f(t1, t2) {
1129-
t.Errorf("Default fail on fuction: %s", tt.name)
1131+
t.Errorf("Time{} and Time{}.UTC() behave differently for %s", tt.name)
11301132
}
11311133
}
11321134
}
@@ -1213,3 +1215,18 @@ func BenchmarkDay(b *testing.B) {
12131215
_ = t.Day()
12141216
}
12151217
}
1218+
1219+
func TestMarshalBinaryZeroTime(t *testing.T) {
1220+
t0 := Time{}
1221+
enc, err := t0.MarshalBinary()
1222+
if err != nil {
1223+
t.Fatal(err)
1224+
}
1225+
t1 := Now() // not zero
1226+
if err := t1.UnmarshalBinary(enc); err != nil {
1227+
t.Fatal(err)
1228+
}
1229+
if t1 != t0 {
1230+
t.Errorf("t0=%#v\nt1=%#v\nwant identical structures", t0, t1)
1231+
}
1232+
}

0 commit comments

Comments
 (0)