From 91bc75b4870308b668d497ff22eada75219c3c2e Mon Sep 17 00:00:00 2001 From: Shuo Date: Sun, 1 Mar 2020 02:32:32 +0000 Subject: [PATCH] time: optimize Time.ISOWeek MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit name old time/op new time/op delta ISOWeek-4 57.7ns ± 5% 27.9ns ±10% -51.54% (p=0.000 n=48+49) Fixes #37534 Change-Id: Ic4673ced44a4b0190018e87207743ed9500fb1e0 GitHub-Last-Rev: a376c57e83a99f8e8fde297335caa85215e7aead GitHub-Pull-Request: golang/go#36316 Reviewed-on: https://go-review.googlesource.com/c/go/+/212837 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/time/time.go | 70 ++++++++++++------------------------------- src/time/time_test.go | 7 +++++ 2 files changed, 26 insertions(+), 51 deletions(-) diff --git a/src/time/time.go b/src/time/time.go index 5dc9fa68ac..5fa09687e9 100644 --- a/src/time/time.go +++ b/src/time/time.go @@ -535,58 +535,26 @@ func absWeekday(abs uint64) Weekday { // 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, week int) { - year, month, day, yday := t.date(true) - wday := int(t.Weekday()+6) % 7 // weekday but Monday = 0. - const ( - Mon int = iota - Tue - Wed - Thu - Fri - Sat - Sun - ) - - // Calculate week as number of Mondays in year up to - // and including today, plus 1 because the first week is week 0. - // Putting the + 1 inside the numerator as a + 7 keeps the - // numerator from being negative, which would cause it to - // round incorrectly. - week = (yday - wday + 7) / 7 - - // The week number is now correct under the assumption - // that the first Monday of the year is in week 1. - // If Jan 1 is a Tuesday, Wednesday, or Thursday, the first Monday - // is actually in week 2. - jan1wday := (wday - yday + 7*53) % 7 - if Tue <= jan1wday && jan1wday <= Thu { - week++ + // According to the rule that the first calendar week of a calendar year is + // the week including the first Thursday of that year, and that the last one is + // the week immediately preceding the first calendar week of the next calendar year. + // See https://www.iso.org/obp/ui#iso:std:iso:8601:-1:ed-1:v1:en:term:3.1.1.23 for details. + + // weeks start with Monday + // Monday Tuesday Wednesday Thursday Friday Saturday Sunday + // 1 2 3 4 5 6 7 + // +3 +2 +1 0 -1 -2 -3 + // the offset to Thursday + abs := t.abs() + d := Thursday - absWeekday(abs) + // handle Sunday + if d == 4 { + d = -3 } - - // If the week number is still 0, we're in early January but in - // the last week of last year. - if week == 0 { - year-- - week = 52 - // A year has 53 weeks when Jan 1 or Dec 31 is a Thursday, - // meaning Jan 1 of the next year is a Friday - // or it was a leap year and Jan 1 of the next year is a Saturday. - if jan1wday == Fri || (jan1wday == Sat && isLeap(year)) { - week++ - } - } - - // December 29 to 31 are in week 1 of next year if - // they are after the last Thursday of the year and - // December 31 is a Monday, Tuesday, or Wednesday. - if month == December && day >= 29 && wday < Thu { - if dec31wday := (wday + 31 - day) % 7; Mon <= dec31wday && dec31wday <= Wed { - year++ - week = 1 - } - } - - return + // find the Thursday of the calendar week + abs += uint64(d) * secondsPerDay + year, _, _, yday := absDate(abs, false) + return year, yday/7 + 1 } // Clock returns the hour, minute, and second within the day specified by t. diff --git a/src/time/time_test.go b/src/time/time_test.go index 2fc23c4fee..ffbf92acbc 100644 --- a/src/time/time_test.go +++ b/src/time/time_test.go @@ -1348,6 +1348,13 @@ func BenchmarkDay(b *testing.B) { } } +func BenchmarkISOWeek(b *testing.B) { + t := Now() + for i := 0; i < b.N; i++ { + _, _ = t.ISOWeek() + } +} + func TestMarshalBinaryZeroTime(t *testing.T) { t0 := Time{} enc, err := t0.MarshalBinary() -- 2.48.1