【发布时间】:2020-02-17 17:32:21
【问题描述】:
根据英国夏令时 (BST) / 夏令时 (DST) (https://www.gov.uk/when-do-the-clocks-change) 的规则:
- 在 3 月的最后一个星期日凌晨 1 点前进 1 小时(英国夏令时开始),
- 10 月最后一个星期日凌晨 2 点返回 1 小时(格林威治标准时间 GMT 开始 = 世界协调时间 UTC)。
在 2019 年,这种民间当地时间的变化发生在 3 月 31 日和 10 月 27 日,但日子每年都会略有变化。
类似的 DST 规则适用于中欧时间 CET(冬季)> CEST(夏季)检查 3 月/10 月的最后一个星期日 (https://www.timeanddate.com/time/change/denmark/copenhagen)。这些 BST/GMT 和 CET/CEST 规则的组合影响例如北海周边的所有国家。无论 BST/GMT,还是 CET/CEST,下面的 UTC 时间戳都应该是一样的。
我已经根据time.UTC 编写了以下代码,提供了 BST/GMT 的日期,但我想知道是否有更简单/更通用的方法来使用可能适用于 CET/ 的任意 time.Location CEST 和(理想情况下)任何 DST 规则。
- 检查文档(这里是https://golang.org/pkg/time/#FixedZone)似乎
time.FixedZone应该处理不变的时区,但是那里的offset是什么?如何使以下函数不受时区影响? - 有没有办法避免每月星期日出现
for循环? - 有没有办法避免基于 BST/GMT 或 CET/CEST 和月份的硬编码
map,以找出下一个时钟变化?
代码:
func beginOfMonth(year, month int, loc *time.Location) time.Time {
return time.Date(year, time.Month(month), 1, 0, 0, 0, 0, loc)
}
// https://www.gov.uk/when-do-the-clocks-change
func lastUTCSunday(year, month int) time.Time {
beginOfMonth := beginOfMonth(year, month, time.UTC)
// we can find max 5 sundays in a month
sundays := make([]time.Time, 5)
for d := 1; d <= 31; d++ {
currDay := beginOfMonth.Add(time.Duration(24*d) * time.Hour)
if currDay.Weekday().String() == "Sunday" {
sundays = append(sundays, currDay)
}
if currDay.Month() != beginOfMonth.Month() {
break
}
}
// check if last date is same month
if sundays[len(sundays)-1].Month() == beginOfMonth.Month() {
// the 5th sunday
return sundays[len(sundays)-1]
}
// if not like before, then we only have 4 Sundays
return sundays[len(sundays)-2]
}
// https://www.gov.uk/when-do-the-clocks-change
func MarchClockSwitchTime(year int) time.Time {
lastSunday := lastUTCSunday(year, int(time.March)) // month: 3
return time.Date(
year, lastSunday.Month(), lastSunday.Day(),
1, 0, 0, 0, // 1:00 AM
lastSunday.Location(),
)
}
// https://www.gov.uk/when-do-the-clocks-change
func OctoberClockSwitchTime(year int) time.Time {
lastSunday := lastUTCSunday(year, int(time.October)) // month: 10
return time.Date(
year, lastSunday.Month(), lastSunday.Day(),
2, 0, 0, 0, // 2:00 AM
lastSunday.Location(),
)
}
我还使用 GoConvey 编写了一些测试,这些测试应该验证这些基于星期日的奇怪夏令时 (DST) 规则,但它们仅适用于 2019 年和 2020 年。找到一种方法使此代码更通用是件好事。
func TestLastSunday(t *testing.T) {
Convey("Should find the last UTC Sunday of each month\n\n", t, func() {
for year := 2019; year <= 2020; year++ {
for month := 1; month <= 12; month++ {
lastUtcSunday := lastUTCSunday(year, month)
So(lastUtcSunday.Month(), ShouldEqual, time.Month(month))
So(lastUtcSunday.Weekday().String(), ShouldEqual, "Sunday")
So(lastUtcSunday.Year(), ShouldEqual, year)
So(lastUtcSunday.Day(), ShouldBeGreaterThanOrEqualTo, 28-7)
}
}
})
}
// https://www.gov.uk/when-do-the-clocks-change
func TestClockChange(t *testing.T) {
Convey("Should find the last UTC Sunday for the March switch\n\n", t, func() {
switch2019 := MarchClockSwitchTime(2019)
So(switch2019.Month(), ShouldEqual, time.March)
So(switch2019.Weekday().String(), ShouldEqual, "Sunday")
So(switch2019.Day(), ShouldEqual, 31)
So(switch2019.Location().String(), ShouldEqual, "UTC")
So(switch2019.Location().String(), ShouldEqual, time.UTC.String())
switch2020 := MarchClockSwitchTime(2020)
So(switch2020.Month(), ShouldEqual, time.March)
So(switch2020.Weekday().String(), ShouldEqual, "Sunday")
So(switch2020.Day(), ShouldEqual, 29)
So(switch2020.Location().String(), ShouldEqual, "UTC")
So(switch2020.Location().String(), ShouldEqual, time.UTC.String())
})
Convey("Should find the last UTC Sunday for the October switch\n\n", t, func() {
switch2019 := OctoberClockSwitchTime(2019)
So(switch2019.Month(), ShouldEqual, time.October)
So(switch2019.Weekday().String(), ShouldEqual, "Sunday")
So(switch2019.Day(), ShouldEqual, 27)
So(switch2019.Location().String(), ShouldEqual, "UTC")
So(switch2019.Location().String(), ShouldEqual, time.UTC.String())
switch2020 := OctoberClockSwitchTime(2020)
So(switch2020.Month(), ShouldEqual, time.October)
So(switch2020.Weekday().String(), ShouldEqual, "Sunday")
So(switch2020.Day(), ShouldEqual, 25)
So(switch2020.Location().String(), ShouldEqual, "UTC")
So(switch2020.Location().String(), ShouldEqual, time.UTC.String())
})
}
【问题讨论】:
-
不幸的是,所有这些详细信息都保存在
time包中未导出的字段中:golang.org/src/time/zoneinfo.go