Skip to content

Commit

Permalink
time: add Time.ZoneBounds
Browse files Browse the repository at this point in the history
The method Location.lookup returns the "start" and "end" times bracketing seconds when that zone is in effect.

This CL does these things:

1. Exported the "start" and "end" times as time.Time form
2. Keep the "Location" of the returned times be the same as underlying time

Fixes #50062.

Change-Id: I88888a100d0fc68f4984a85c75a85a83aa3e5d80
Reviewed-on: https://go-review.googlesource.com/c/go/+/405374
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
  • Loading branch information
hopehook authored and gopherbot committed May 17, 2022
1 parent aedf298 commit 41b9d8c
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 0 deletions.
1 change: 1 addition & 0 deletions api/next/50062.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pkg time, method (Time) ZoneBounds() (Time, Time) #50062
18 changes: 18 additions & 0 deletions src/time/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -1141,6 +1141,24 @@ func (t Time) Zone() (name string, offset int) {
return
}

// ZoneBounds returns the bounds of the time zone in effect at time t.
// The zone begins at start and the next zone begins at end.
// If the zone begins at the beginning of time, start will be returned as a zero Time.
// If the zone goes on forever, end will be returned as a zero Time.
// The Location of the returned times will be the same as t.
func (t Time) ZoneBounds() (start, end Time) {
_, _, startSec, endSec, _ := t.loc.lookup(t.unixSec())
if startSec != alpha {
start = unixTime(startSec, 0)
start.setLoc(t.loc)
}
if endSec != omega {
end = unixTime(endSec, 0)
end.setLoc(t.loc)
}
return
}

// Unix returns t as a Unix time, the number of seconds elapsed
// since January 1, 1970 UTC. The result does not depend on the
// location associated with t.
Expand Down
74 changes: 74 additions & 0 deletions src/time/time_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1693,3 +1693,77 @@ func TestTimeWithZoneTransition(t *testing.T) {
}
}
}

func TestZoneBounds(t *testing.T) {
undo := DisablePlatformSources()
defer undo()
loc, err := LoadLocation("Asia/Shanghai")
if err != nil {
t.Fatal(err)
}

// The ZoneBounds of a UTC location would just return two zero Time.
for _, test := range utctests {
sec := test.seconds
golden := &test.golden
tm := Unix(sec, 0).UTC()
start, end := tm.ZoneBounds()
if !(start.IsZero() && end.IsZero()) {
t.Errorf("ZoneBounds of %+v expects two zero Time, got:\n start=%v\n end=%v", *golden, start, end)
}
}

// If the zone begins at the beginning of time, start will be returned as a zero Time.
// Use math.MinInt32 to avoid overflow of int arguments on 32-bit systems.
beginTime := Date(math.MinInt32, January, 1, 0, 0, 0, 0, loc)
start, end := beginTime.ZoneBounds()
if !start.IsZero() || end.IsZero() {
t.Errorf("ZoneBounds of %v expects start is zero Time, got:\n start=%v\n end=%v", beginTime, start, end)
}

// If the zone goes on forever, end will be returned as a zero Time.
// Use math.MaxInt32 to avoid overflow of int arguments on 32-bit systems.
foreverTime := Date(math.MaxInt32, January, 1, 0, 0, 0, 0, loc)
start, end = foreverTime.ZoneBounds()
if start.IsZero() || !end.IsZero() {
t.Errorf("ZoneBounds of %v expects end is zero Time, got:\n start=%v\n end=%v", foreverTime, start, end)
}

// Check some real-world cases to make sure we're getting the right bounds.
boundOne := Date(1990, September, 16, 1, 0, 0, 0, loc)
boundTwo := Date(1991, April, 14, 3, 0, 0, 0, loc)
boundThree := Date(1991, September, 15, 1, 0, 0, 0, loc)
makeLocalTime := func(sec int64) Time { return Unix(sec, 0) }
realTests := [...]struct {
giveTime Time
wantStart Time
wantEnd Time
}{
// The ZoneBounds of "Asia/Shanghai" Daylight Saving Time
0: {Date(1991, April, 13, 17, 50, 0, 0, loc), boundOne, boundTwo},
1: {Date(1991, April, 13, 18, 0, 0, 0, loc), boundOne, boundTwo},
2: {Date(1991, April, 14, 1, 50, 0, 0, loc), boundOne, boundTwo},
3: {boundTwo, boundTwo, boundThree},
4: {Date(1991, September, 14, 16, 50, 0, 0, loc), boundTwo, boundThree},
5: {Date(1991, September, 14, 17, 0, 0, 0, loc), boundTwo, boundThree},
6: {Date(1991, September, 15, 0, 50, 0, 0, loc), boundTwo, boundThree},

// The ZoneBounds of a local time would return two local Time.
// Note: We preloaded "America/Los_Angeles" as time.Local for testing
7: {makeLocalTime(0), makeLocalTime(-5756400), makeLocalTime(9972000)},
8: {makeLocalTime(1221681866), makeLocalTime(1205056800), makeLocalTime(1225616400)},
9: {makeLocalTime(2152173599), makeLocalTime(2145916800), makeLocalTime(2152173600)},
10: {makeLocalTime(2152173600), makeLocalTime(2152173600), makeLocalTime(2172733200)},
11: {makeLocalTime(2152173601), makeLocalTime(2152173600), makeLocalTime(2172733200)},
12: {makeLocalTime(2159200800), makeLocalTime(2152173600), makeLocalTime(2172733200)},
13: {makeLocalTime(2172733199), makeLocalTime(2152173600), makeLocalTime(2172733200)},
14: {makeLocalTime(2172733200), makeLocalTime(2172733200), makeLocalTime(2177452800)},
}
for i, tt := range realTests {
start, end := tt.giveTime.ZoneBounds()
if !start.Equal(tt.wantStart) || !end.Equal(tt.wantEnd) {
t.Errorf("#%d:: ZoneBounds of %v expects right bounds:\n got start=%v\n want start=%v\n got end=%v\n want end=%v",
i, tt.giveTime, start, tt.wantStart, end, tt.wantEnd)
}
}
}

0 comments on commit 41b9d8c

Please sign in to comment.