Commit d9897096 authored by Volker Dobler's avatar Volker Dobler Committed by Rob Pike

time: add ISOWeek method to Time

As the ISO 8601 week number is untrivial to compute a new method
on *Time provides year and number of week.

R=golang-dev, rsc, r, r
CC=golang-dev
https://golang.org/cl/5316074
parent 2c39ca08
...@@ -237,3 +237,63 @@ func (t *Time) Weekday() int { ...@@ -237,3 +237,63 @@ func (t *Time) Weekday() int {
} }
return weekday return weekday
} }
// julianDayNumber returns the time's Julian Day Number
// relative to the epoch 12:00 January 1, 4713 BC, Monday.
func julianDayNumber(year int64, month, day int) int64 {
a := int64(14-month) / 12
y := year + 4800 - a
m := int64(month) + 12*a - 3
return int64(day) + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 - 32045
}
// startOfFirstWeek returns the julian day number of the first day
// of the first week of the given year.
func startOfFirstWeek(year int64) (d int64) {
jan01 := julianDayNumber(year, 1, 1)
weekday := (jan01 % 7) + 1
if weekday <= 4 {
d = jan01 - weekday + 1
} else {
d = jan01 + 8 - weekday
}
return
}
// dayOfWeek returns the weekday of the given date.
func dayOfWeek(year int64, month, day int) int {
t := Time{Year: year, Month: month, Day: day}
return t.Weekday()
}
// ISOWeek returns the time's year and week number according to ISO 8601.
// Week ranges from 1 to 53. Jan 01 to Jan 03 of year n might belong to
// week 52 or 53 of year n-1, and Dec 29 to Dec 31 might belong to week 1
// of year n+1.
func (t *Time) ISOWeek() (year int64, week int) {
d := julianDayNumber(t.Year, t.Month, t.Day)
week1Start := startOfFirstWeek(t.Year)
if d < week1Start {
// Previous year, week 52 or 53
year = t.Year - 1
if dayOfWeek(t.Year-1, 1, 1) == 4 || dayOfWeek(t.Year-1, 12, 31) == 4 {
week = 53
} else {
week = 52
}
return
}
if d < startOfFirstWeek(t.Year+1) {
// Current year, week 01..52(,53)
year = t.Year
week = int((d-week1Start)/7 + 1)
return
}
// Next year, week 1
year = t.Year + 1
week = 1
return
}
...@@ -478,6 +478,68 @@ func TestMinutesInTimeZone(t *testing.T) { ...@@ -478,6 +478,68 @@ func TestMinutesInTimeZone(t *testing.T) {
} }
} }
type ISOWeekTest struct {
year int64 // year
month, day int // month and day
yex int64 // expected year
wex int // expected week
}
var isoWeekTests = []ISOWeekTest{
{1981, 1, 1, 1981, 1}, {1982, 1, 1, 1981, 53}, {1983, 1, 1, 1982, 52},
{1984, 1, 1, 1983, 52}, {1985, 1, 1, 1985, 1}, {1986, 1, 1, 1986, 1},
{1987, 1, 1, 1987, 1}, {1988, 1, 1, 1987, 53}, {1989, 1, 1, 1988, 52},
{1990, 1, 1, 1990, 1}, {1991, 1, 1, 1991, 1}, {1992, 1, 1, 1992, 1},
{1993, 1, 1, 1992, 53}, {1994, 1, 1, 1993, 52}, {1995, 1, 2, 1995, 1},
{1996, 1, 1, 1996, 1}, {1996, 1, 7, 1996, 1}, {1996, 1, 8, 1996, 2},
{1997, 1, 1, 1997, 1}, {1998, 1, 1, 1998, 1}, {1999, 1, 1, 1998, 53},
{2000, 1, 1, 1999, 52}, {2001, 1, 1, 2001, 1}, {2002, 1, 1, 2002, 1},
{2003, 1, 1, 2003, 1}, {2004, 1, 1, 2004, 1}, {2005, 1, 1, 2004, 53},
{2006, 1, 1, 2005, 52}, {2007, 1, 1, 2007, 1}, {2008, 1, 1, 2008, 1},
{2009, 1, 1, 2009, 1}, {2010, 1, 1, 2009, 53}, {2010, 1, 1, 2009, 53},
{2011, 1, 1, 2010, 52}, {2011, 1, 2, 2010, 52}, {2011, 1, 3, 2011, 1},
{2011, 1, 4, 2011, 1}, {2011, 1, 5, 2011, 1}, {2011, 1, 6, 2011, 1},
{2011, 1, 7, 2011, 1}, {2011, 1, 8, 2011, 1}, {2011, 1, 9, 2011, 1},
{2011, 1, 10, 2011, 2}, {2011, 1, 11, 2011, 2}, {2011, 6, 12, 2011, 23},
{2011, 6, 13, 2011, 24}, {2011, 12, 25, 2011, 51}, {2011, 12, 26, 2011, 52},
{2011, 12, 27, 2011, 52}, {2011, 12, 28, 2011, 52}, {2011, 12, 29, 2011, 52},
{2011, 12, 30, 2011, 52}, {2011, 12, 31, 2011, 52}, {1995, 1, 1, 1994, 52},
{2012, 1, 1, 2011, 52}, {2012, 1, 2, 2012, 1}, {2012, 1, 8, 2012, 1},
{2012, 1, 9, 2012, 2}, {2012, 12, 23, 2012, 51}, {2012, 12, 24, 2012, 52},
{2012, 12, 30, 2012, 52}, {2012, 12, 31, 2013, 1}, {2013, 1, 1, 2013, 1},
{2013, 1, 6, 2013, 1}, {2013, 1, 7, 2013, 2}, {2013, 12, 22, 2013, 51},
{2013, 12, 23, 2013, 52}, {2013, 12, 29, 2013, 52}, {2013, 12, 30, 2014, 1},
{2014, 1, 1, 2014, 1}, {2014, 1, 5, 2014, 1}, {2014, 1, 6, 2014, 2},
{2015, 1, 1, 2015, 1}, {2016, 1, 1, 2015, 53}, {2017, 1, 1, 2016, 52},
{2018, 1, 1, 2018, 1}, {2019, 1, 1, 2019, 1}, {2020, 1, 1, 2020, 1},
{2021, 1, 1, 2020, 53}, {2022, 1, 1, 2021, 52}, {2023, 1, 1, 2022, 52},
{2024, 1, 1, 2024, 1}, {2025, 1, 1, 2025, 1}, {2026, 1, 1, 2026, 1},
{2027, 1, 1, 2026, 53}, {2028, 1, 1, 2027, 52}, {2029, 1, 1, 2029, 1},
{2030, 1, 1, 2030, 1}, {2031, 1, 1, 2031, 1}, {2032, 1, 1, 2032, 1},
{2033, 1, 1, 2032, 53}, {2034, 1, 1, 2033, 52}, {2035, 1, 1, 2035, 1},
{2036, 1, 1, 2036, 1}, {2037, 1, 1, 2037, 1}, {2038, 1, 1, 2037, 53},
{2039, 1, 1, 2038, 52}, {2040, 1, 1, 2039, 52},
}
func TestISOWeek(t *testing.T) {
// Selected dates and corner cases
for _, wt := range isoWeekTests {
dt := &Time{Year: wt.year, Month: wt.month, Day: wt.day}
y, w := dt.ISOWeek()
if w != wt.wex || y != wt.yex {
t.Errorf("got %d/%d; expected %d/%d for %d-%02d-%02d",
y, w, wt.yex, wt.wex, wt.year, wt.month, wt.day)
}
}
// The only real invariant: Jan 04 is in week 1
for year := int64(1950); year < 2100; year++ {
if y, w := (&Time{Year: year, Month: 1, Day: 4}).ISOWeek(); y != year || w != 1 {
t.Errorf("got %d/%d; expected %d/1 for Jan 04", y, w, year)
}
}
}
func BenchmarkSeconds(b *testing.B) { func BenchmarkSeconds(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
Seconds() Seconds()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment