mirror of
https://github.com/golang/go
synced 2024-11-23 07:00:05 -07:00
time: add Time.IsDST() to check if its Location is in Daylight Savings Time
Fixes #42102
Change-Id: I2cd2fdf67c794c3e99ed1c24786f7f779da73962
GitHub-Last-Rev: bbfa921357
GitHub-Pull-Request: golang/go#42103
Reviewed-on: https://go-review.googlesource.com/c/go/+/264077
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
Reviewed-by: Rob Pike <r@golang.org>
Trust: Emmanuel Odeke <emmanuel@orijtech.com>
This commit is contained in:
parent
d7cc2f1d7c
commit
a9b3c4bd06
@ -1152,7 +1152,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
|
|||||||
|
|
||||||
// Look for local zone with the given offset.
|
// Look for local zone with the given offset.
|
||||||
// If that zone was in effect at the given time, use it.
|
// If that zone was in effect at the given time, use it.
|
||||||
name, offset, _, _ := local.lookup(t.unixSec())
|
name, offset, _, _, _ := local.lookup(t.unixSec())
|
||||||
if offset == zoneOffset && (zoneName == "" || name == zoneName) {
|
if offset == zoneOffset && (zoneName == "" || name == zoneName) {
|
||||||
t.setLoc(local)
|
t.setLoc(local)
|
||||||
return t, nil
|
return t, nil
|
||||||
|
@ -440,7 +440,7 @@ func (t Time) abs() uint64 {
|
|||||||
if l.cacheZone != nil && l.cacheStart <= sec && sec < l.cacheEnd {
|
if l.cacheZone != nil && l.cacheStart <= sec && sec < l.cacheEnd {
|
||||||
sec += int64(l.cacheZone.offset)
|
sec += int64(l.cacheZone.offset)
|
||||||
} else {
|
} else {
|
||||||
_, offset, _, _ := l.lookup(sec)
|
_, offset, _, _, _ := l.lookup(sec)
|
||||||
sec += int64(offset)
|
sec += int64(offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -461,7 +461,7 @@ func (t Time) locabs() (name string, offset int, abs uint64) {
|
|||||||
name = l.cacheZone.name
|
name = l.cacheZone.name
|
||||||
offset = l.cacheZone.offset
|
offset = l.cacheZone.offset
|
||||||
} else {
|
} else {
|
||||||
name, offset, _, _ = l.lookup(sec)
|
name, offset, _, _, _ = l.lookup(sec)
|
||||||
}
|
}
|
||||||
sec += int64(offset)
|
sec += int64(offset)
|
||||||
} else {
|
} else {
|
||||||
@ -1114,7 +1114,7 @@ func (t Time) Location() *Location {
|
|||||||
// Zone computes the time zone in effect at time t, returning the abbreviated
|
// Zone computes the time zone in effect at time t, returning the abbreviated
|
||||||
// name of the zone (such as "CET") and its offset in seconds east of UTC.
|
// name of the zone (such as "CET") and its offset in seconds east of UTC.
|
||||||
func (t Time) Zone() (name string, offset int) {
|
func (t Time) Zone() (name string, offset int) {
|
||||||
name, offset, _, _ = t.loc.lookup(t.unixSec())
|
name, offset, _, _, _ = t.loc.lookup(t.unixSec())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1212,7 +1212,7 @@ func (t *Time) UnmarshalBinary(data []byte) error {
|
|||||||
|
|
||||||
if offset == -1*60 {
|
if offset == -1*60 {
|
||||||
t.setLoc(&utcLoc)
|
t.setLoc(&utcLoc)
|
||||||
} else if _, localoff, _, _ := Local.lookup(t.unixSec()); offset == localoff {
|
} else if _, localoff, _, _, _ := Local.lookup(t.unixSec()); offset == localoff {
|
||||||
t.setLoc(Local)
|
t.setLoc(Local)
|
||||||
} else {
|
} else {
|
||||||
t.setLoc(FixedZone("", offset))
|
t.setLoc(FixedZone("", offset))
|
||||||
@ -1302,6 +1302,12 @@ func Unix(sec int64, nsec int64) Time {
|
|||||||
return unixTime(sec, int32(nsec))
|
return unixTime(sec, int32(nsec))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsDST reports whether the time in the configured location is in Daylight Savings Time.
|
||||||
|
func (t *Time) IsDST() bool {
|
||||||
|
_, _, _, _, isDST := t.loc.lookup(t.Unix())
|
||||||
|
return isDST
|
||||||
|
}
|
||||||
|
|
||||||
func isLeap(year int) bool {
|
func isLeap(year int) bool {
|
||||||
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
||||||
}
|
}
|
||||||
@ -1377,13 +1383,13 @@ func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) T
|
|||||||
// The lookup function expects UTC, so we pass t in the
|
// The lookup function expects UTC, so we pass t in the
|
||||||
// hope that it will not be too close to a zone transition,
|
// hope that it will not be too close to a zone transition,
|
||||||
// and then adjust if it is.
|
// and then adjust if it is.
|
||||||
_, offset, start, end := loc.lookup(unix)
|
_, offset, start, end, _ := loc.lookup(unix)
|
||||||
if offset != 0 {
|
if offset != 0 {
|
||||||
switch utc := unix - int64(offset); {
|
switch utc := unix - int64(offset); {
|
||||||
case utc < start:
|
case utc < start:
|
||||||
_, offset, _, _ = loc.lookup(start - 1)
|
_, offset, _, _, _ = loc.lookup(start - 1)
|
||||||
case utc >= end:
|
case utc >= end:
|
||||||
_, offset, _, _ = loc.lookup(end)
|
_, offset, _, _, _ = loc.lookup(end)
|
||||||
}
|
}
|
||||||
unix -= int64(offset)
|
unix -= int64(offset)
|
||||||
}
|
}
|
||||||
|
@ -1465,3 +1465,39 @@ func TestConcurrentTimerResetStop(t *testing.T) {
|
|||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTimeIsDST(t *testing.T) {
|
||||||
|
ForceZipFileForTesting(true)
|
||||||
|
defer ForceZipFileForTesting(false)
|
||||||
|
|
||||||
|
tzWithDST, err := LoadLocation("Australia/Sydney")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not load tz 'Australia/Sydney': %v", err)
|
||||||
|
}
|
||||||
|
tzWithoutDST, err := LoadLocation("Australia/Brisbane")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not load tz 'Australia/Brisbane': %v", err)
|
||||||
|
}
|
||||||
|
tzFixed := FixedZone("FIXED_TIME", 12345)
|
||||||
|
|
||||||
|
tests := [...]struct {
|
||||||
|
time Time
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
0: {Date(2009, 1, 1, 12, 0, 0, 0, UTC), false},
|
||||||
|
1: {Date(2009, 6, 1, 12, 0, 0, 0, UTC), false},
|
||||||
|
2: {Date(2009, 1, 1, 12, 0, 0, 0, tzWithDST), true},
|
||||||
|
3: {Date(2009, 6, 1, 12, 0, 0, 0, tzWithDST), false},
|
||||||
|
4: {Date(2009, 1, 1, 12, 0, 0, 0, tzWithoutDST), false},
|
||||||
|
5: {Date(2009, 6, 1, 12, 0, 0, 0, tzWithoutDST), false},
|
||||||
|
6: {Date(2009, 1, 1, 12, 0, 0, 0, tzFixed), false},
|
||||||
|
7: {Date(2009, 6, 1, 12, 0, 0, 0, tzFixed), false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
got := tt.time.IsDST()
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("#%d:: (%#v).IsDST()=%t, want %t", i, tt.time.Format(RFC3339), got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -121,7 +121,7 @@ func FixedZone(name string, offset int) *Location {
|
|||||||
// the start and end times bracketing sec when that zone is in effect,
|
// the start and end times bracketing sec when that zone is in effect,
|
||||||
// the offset in seconds east of UTC (such as -5*60*60), and whether
|
// the offset in seconds east of UTC (such as -5*60*60), and whether
|
||||||
// the daylight savings is being observed at that time.
|
// the daylight savings is being observed at that time.
|
||||||
func (l *Location) lookup(sec int64) (name string, offset int, start, end int64) {
|
func (l *Location) lookup(sec int64) (name string, offset int, start, end int64, isDST bool) {
|
||||||
l = l.get()
|
l = l.get()
|
||||||
|
|
||||||
if len(l.zone) == 0 {
|
if len(l.zone) == 0 {
|
||||||
@ -129,6 +129,7 @@ func (l *Location) lookup(sec int64) (name string, offset int, start, end int64)
|
|||||||
offset = 0
|
offset = 0
|
||||||
start = alpha
|
start = alpha
|
||||||
end = omega
|
end = omega
|
||||||
|
isDST = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,6 +138,7 @@ func (l *Location) lookup(sec int64) (name string, offset int, start, end int64)
|
|||||||
offset = zone.offset
|
offset = zone.offset
|
||||||
start = l.cacheStart
|
start = l.cacheStart
|
||||||
end = l.cacheEnd
|
end = l.cacheEnd
|
||||||
|
isDST = zone.isDST
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,6 +152,7 @@ func (l *Location) lookup(sec int64) (name string, offset int, start, end int64)
|
|||||||
} else {
|
} else {
|
||||||
end = omega
|
end = omega
|
||||||
}
|
}
|
||||||
|
isDST = zone.isDST
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,12 +177,13 @@ func (l *Location) lookup(sec int64) (name string, offset int, start, end int64)
|
|||||||
offset = zone.offset
|
offset = zone.offset
|
||||||
start = tx[lo].when
|
start = tx[lo].when
|
||||||
// end = maintained during the search
|
// end = maintained during the search
|
||||||
|
isDST = zone.isDST
|
||||||
|
|
||||||
// If we're at the end of the known zone transitions,
|
// If we're at the end of the known zone transitions,
|
||||||
// try the extend string.
|
// try the extend string.
|
||||||
if lo == len(tx)-1 && l.extend != "" {
|
if lo == len(tx)-1 && l.extend != "" {
|
||||||
if ename, eoffset, estart, eend, ok := tzset(l.extend, end, sec); ok {
|
if ename, eoffset, estart, eend, eisDST, ok := tzset(l.extend, end, sec); ok {
|
||||||
return ename, eoffset, estart, eend
|
return ename, eoffset, estart, eend, eisDST
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,7 +248,7 @@ func (l *Location) firstZoneUsed() bool {
|
|||||||
// We call this a tzset string since in C the function tzset reads TZ.
|
// We call this a tzset string since in C the function tzset reads TZ.
|
||||||
// The return values are as for lookup, plus ok which reports whether the
|
// The return values are as for lookup, plus ok which reports whether the
|
||||||
// parse succeeded.
|
// parse succeeded.
|
||||||
func tzset(s string, initEnd, sec int64) (name string, offset int, start, end int64, ok bool) {
|
func tzset(s string, initEnd, sec int64) (name string, offset int, start, end int64, isDST, ok bool) {
|
||||||
var (
|
var (
|
||||||
stdName, dstName string
|
stdName, dstName string
|
||||||
stdOffset, dstOffset int
|
stdOffset, dstOffset int
|
||||||
@ -255,7 +259,7 @@ func tzset(s string, initEnd, sec int64) (name string, offset int, start, end in
|
|||||||
stdOffset, s, ok = tzsetOffset(s)
|
stdOffset, s, ok = tzsetOffset(s)
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", 0, 0, 0, false
|
return "", 0, 0, 0, false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// The numbers in the tzset string are added to local time to get UTC,
|
// The numbers in the tzset string are added to local time to get UTC,
|
||||||
@ -265,7 +269,7 @@ func tzset(s string, initEnd, sec int64) (name string, offset int, start, end in
|
|||||||
|
|
||||||
if len(s) == 0 || s[0] == ',' {
|
if len(s) == 0 || s[0] == ',' {
|
||||||
// No daylight savings time.
|
// No daylight savings time.
|
||||||
return stdName, stdOffset, initEnd, omega, true
|
return stdName, stdOffset, initEnd, omega, false, true
|
||||||
}
|
}
|
||||||
|
|
||||||
dstName, s, ok = tzsetName(s)
|
dstName, s, ok = tzsetName(s)
|
||||||
@ -278,7 +282,7 @@ func tzset(s string, initEnd, sec int64) (name string, offset int, start, end in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", 0, 0, 0, false
|
return "", 0, 0, 0, false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s) == 0 {
|
if len(s) == 0 {
|
||||||
@ -287,19 +291,19 @@ func tzset(s string, initEnd, sec int64) (name string, offset int, start, end in
|
|||||||
}
|
}
|
||||||
// The TZ definition does not mention ';' here but tzcode accepts it.
|
// The TZ definition does not mention ';' here but tzcode accepts it.
|
||||||
if s[0] != ',' && s[0] != ';' {
|
if s[0] != ',' && s[0] != ';' {
|
||||||
return "", 0, 0, 0, false
|
return "", 0, 0, 0, false, false
|
||||||
}
|
}
|
||||||
s = s[1:]
|
s = s[1:]
|
||||||
|
|
||||||
var startRule, endRule rule
|
var startRule, endRule rule
|
||||||
startRule, s, ok = tzsetRule(s)
|
startRule, s, ok = tzsetRule(s)
|
||||||
if !ok || len(s) == 0 || s[0] != ',' {
|
if !ok || len(s) == 0 || s[0] != ',' {
|
||||||
return "", 0, 0, 0, false
|
return "", 0, 0, 0, false, false
|
||||||
}
|
}
|
||||||
s = s[1:]
|
s = s[1:]
|
||||||
endRule, s, ok = tzsetRule(s)
|
endRule, s, ok = tzsetRule(s)
|
||||||
if !ok || len(s) > 0 {
|
if !ok || len(s) > 0 {
|
||||||
return "", 0, 0, 0, false
|
return "", 0, 0, 0, false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
year, _, _, yday := absDate(uint64(sec+unixToInternal+internalToAbsolute), false)
|
year, _, _, yday := absDate(uint64(sec+unixToInternal+internalToAbsolute), false)
|
||||||
@ -313,10 +317,15 @@ func tzset(s string, initEnd, sec int64) (name string, offset int, start, end in
|
|||||||
|
|
||||||
startSec := int64(tzruleTime(year, startRule, stdOffset))
|
startSec := int64(tzruleTime(year, startRule, stdOffset))
|
||||||
endSec := int64(tzruleTime(year, endRule, dstOffset))
|
endSec := int64(tzruleTime(year, endRule, dstOffset))
|
||||||
|
dstIsDST, stdIsDST := true, false
|
||||||
|
// Note: this is a flipping of "DST" and "STD" while retaining the labels
|
||||||
|
// This happens in southern hemispheres. The labelling here thus is a little
|
||||||
|
// inconsistent with the goal.
|
||||||
if endSec < startSec {
|
if endSec < startSec {
|
||||||
startSec, endSec = endSec, startSec
|
startSec, endSec = endSec, startSec
|
||||||
stdName, dstName = dstName, stdName
|
stdName, dstName = dstName, stdName
|
||||||
stdOffset, dstOffset = dstOffset, stdOffset
|
stdOffset, dstOffset = dstOffset, stdOffset
|
||||||
|
stdIsDST, dstIsDST = dstIsDST, stdIsDST
|
||||||
}
|
}
|
||||||
|
|
||||||
// The start and end values that we return are accurate
|
// The start and end values that we return are accurate
|
||||||
@ -324,11 +333,11 @@ func tzset(s string, initEnd, sec int64) (name string, offset int, start, end in
|
|||||||
// just the start and end of the year. That suffices for
|
// just the start and end of the year. That suffices for
|
||||||
// the only caller that cares, which is Date.
|
// the only caller that cares, which is Date.
|
||||||
if ysec < startSec {
|
if ysec < startSec {
|
||||||
return stdName, stdOffset, abs, startSec + abs, true
|
return stdName, stdOffset, abs, startSec + abs, stdIsDST, true
|
||||||
} else if ysec >= endSec {
|
} else if ysec >= endSec {
|
||||||
return stdName, stdOffset, endSec + abs, abs + 365*secondsPerDay, true
|
return stdName, stdOffset, endSec + abs, abs + 365*secondsPerDay, stdIsDST, true
|
||||||
} else {
|
} else {
|
||||||
return dstName, dstOffset, startSec + abs, endSec + abs, true
|
return dstName, dstOffset, startSec + abs, endSec + abs, dstIsDST, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,7 +596,7 @@ func (l *Location) lookupName(name string, unix int64) (offset int, ok bool) {
|
|||||||
for i := range l.zone {
|
for i := range l.zone {
|
||||||
zone := &l.zone[i]
|
zone := &l.zone[i]
|
||||||
if zone.name == name {
|
if zone.name == name {
|
||||||
nam, offset, _, _ := l.lookup(unix - int64(zone.offset))
|
nam, offset, _, _, _ := l.lookup(unix - int64(zone.offset))
|
||||||
if nam == zone.name {
|
if nam == zone.name {
|
||||||
return offset, true
|
return offset, true
|
||||||
}
|
}
|
||||||
|
@ -329,7 +329,7 @@ func LoadLocationFromTZData(name string, data []byte) (*Location, error) {
|
|||||||
} else if l.extend != "" {
|
} else if l.extend != "" {
|
||||||
// If we're at the end of the known zone transitions,
|
// If we're at the end of the known zone transitions,
|
||||||
// try the extend string.
|
// try the extend string.
|
||||||
if name, _, estart, eend, ok := tzset(l.extend, l.cacheEnd, sec); ok {
|
if name, _, estart, eend, _, ok := tzset(l.extend, l.cacheEnd, sec); ok {
|
||||||
l.cacheStart = estart
|
l.cacheStart = estart
|
||||||
l.cacheEnd = eend
|
l.cacheEnd = eend
|
||||||
// Find the zone that is returned by tzset,
|
// Find the zone that is returned by tzset,
|
||||||
|
@ -239,20 +239,21 @@ func TestTzset(t *testing.T) {
|
|||||||
off int
|
off int
|
||||||
start int64
|
start int64
|
||||||
end int64
|
end int64
|
||||||
|
isDST bool
|
||||||
ok bool
|
ok bool
|
||||||
}{
|
}{
|
||||||
{"", 0, 0, "", 0, 0, 0, false},
|
{"", 0, 0, "", 0, 0, 0, false, false},
|
||||||
{"PST8PDT,M3.2.0,M11.1.0", 0, 2159200800, "PDT", -7 * 60 * 60, 2152173600, 2172733200, true},
|
{"PST8PDT,M3.2.0,M11.1.0", 0, 2159200800, "PDT", -7 * 60 * 60, 2152173600, 2172733200, true, true},
|
||||||
{"PST8PDT,M3.2.0,M11.1.0", 0, 2152173599, "PST", -8 * 60 * 60, 2145916800, 2152173600, true},
|
{"PST8PDT,M3.2.0,M11.1.0", 0, 2152173599, "PST", -8 * 60 * 60, 2145916800, 2152173600, false, true},
|
||||||
{"PST8PDT,M3.2.0,M11.1.0", 0, 2152173600, "PDT", -7 * 60 * 60, 2152173600, 2172733200, true},
|
{"PST8PDT,M3.2.0,M11.1.0", 0, 2152173600, "PDT", -7 * 60 * 60, 2152173600, 2172733200, true, true},
|
||||||
{"PST8PDT,M3.2.0,M11.1.0", 0, 2152173601, "PDT", -7 * 60 * 60, 2152173600, 2172733200, true},
|
{"PST8PDT,M3.2.0,M11.1.0", 0, 2152173601, "PDT", -7 * 60 * 60, 2152173600, 2172733200, true, true},
|
||||||
{"PST8PDT,M3.2.0,M11.1.0", 0, 2172733199, "PDT", -7 * 60 * 60, 2152173600, 2172733200, true},
|
{"PST8PDT,M3.2.0,M11.1.0", 0, 2172733199, "PDT", -7 * 60 * 60, 2152173600, 2172733200, true, true},
|
||||||
{"PST8PDT,M3.2.0,M11.1.0", 0, 2172733200, "PST", -8 * 60 * 60, 2172733200, 2177452800, true},
|
{"PST8PDT,M3.2.0,M11.1.0", 0, 2172733200, "PST", -8 * 60 * 60, 2172733200, 2177452800, false, true},
|
||||||
{"PST8PDT,M3.2.0,M11.1.0", 0, 2172733201, "PST", -8 * 60 * 60, 2172733200, 2177452800, true},
|
{"PST8PDT,M3.2.0,M11.1.0", 0, 2172733201, "PST", -8 * 60 * 60, 2172733200, 2177452800, false, true},
|
||||||
} {
|
} {
|
||||||
name, off, start, end, ok := time.Tzset(test.inStr, test.inEnd, test.inSec)
|
name, off, start, end, isDST, ok := time.Tzset(test.inStr, test.inEnd, test.inSec)
|
||||||
if name != test.name || off != test.off || start != test.start || end != test.end || ok != test.ok {
|
if name != test.name || off != test.off || start != test.start || end != test.end || isDST != test.isDST || ok != test.ok {
|
||||||
t.Errorf("tzset(%q, %d, %d) = %q, %d, %d, %d, %t, want %q, %d, %d, %d, %t", test.inStr, test.inEnd, test.inSec, name, off, start, end, ok, test.name, test.off, test.start, test.end, test.ok)
|
t.Errorf("tzset(%q, %d, %d) = %q, %d, %d, %d, %t, %t, want %q, %d, %d, %d, %t, %t", test.inStr, test.inEnd, test.inSec, name, off, start, end, isDST, ok, test.name, test.off, test.start, test.end, test.isDST, test.ok)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user