From 402d0c8f840b30aefa88a79eba1d41c0d551bd49 Mon Sep 17 00:00:00 2001 From: sunshineplan Date: Thu, 23 May 2024 19:48:15 +0800 Subject: [PATCH] scheduler: Use Next instead of First --- scheduler/clock.go | 65 +++++++++++++++++++++----------------- scheduler/clock_test.go | 66 +++++++++++++++++++++------------------ scheduler/complex.go | 20 ++++++------ scheduler/complex_test.go | 8 ++--- scheduler/misc.go | 4 --- scheduler/schedule.go | 20 ++++++------ scheduler/scheduler.go | 8 +++-- 7 files changed, 101 insertions(+), 90 deletions(-) diff --git a/scheduler/clock.go b/scheduler/clock.go index 74e29f1..e3da490 100644 --- a/scheduler/clock.go +++ b/scheduler/clock.go @@ -142,45 +142,52 @@ func (c Clock) IsMatched(t time.Time) bool { (!c.sec || c.Clock.Second() == sec) } -func (c Clock) First(t time.Time) time.Duration { - u, nc := AtClock(t.Clock()), new(Clock) +func (c Clock) Next(t time.Time) (next time.Time) { + year, month, day := t.Date() + var hour, min, sec int if c.sec { - nc.Second(c.Clock.Second()) + sec = c.Clock.Second() } else { - nc.Second(u.Clock.Second()) + sec = t.Second() } if c.min { - nc.Minute(c.Clock.Minute()) + min = c.Clock.Minute() } else { - nc.Minute(u.Clock.Minute()) + min = t.Minute() } if c.hour { - nc.Hour(c.Clock.Hour()) + hour = c.Clock.Hour() } else { - nc.Hour(u.Clock.Hour()) + hour = t.Hour() } - - if nc.Equal(u.Clock) { - return 0 - } - if !c.sec { - nc.Second(0) - } - if nc.Before(u.Clock) { + switch next = time.Date(year, month, day, hour, min, sec, 0, t.Location()); t.Truncate(time.Second).Compare(next) { + case 1: + if !c.sec { + next = next.Add(-time.Duration(sec) * time.Second) + } if !c.min { - if nc.Clock.Hour() == u.Clock.Hour() { - return nc.Add(time.Minute).Since(u.Clock) + if t.Hour() == hour { + if t := next.Add(time.Minute); t.Hour() == hour { + return t + } } - nc.Minute(0) + next = next.Add(-time.Duration(min) * time.Minute) } if !c.hour { - return nc.Add(time.Hour).Since(u.Clock) + return next.Add(time.Hour) + } + return next.AddDate(0, 0, 1) + case -1: + if !c.sec { + next = next.Add(-time.Duration(sec) * time.Second) + } + if !c.min && t.Hour() != hour { + next = next.Add(-time.Duration(min) * time.Minute) } - return nc.Add(24 * time.Hour).Since(u.Clock) - } else if !c.min && nc.Clock.Hour() != u.Clock.Hour() { - nc.Minute(0) + return + default: + return t } - return nc.Since(u.Clock) } func (c Clock) TickerDuration() time.Duration { @@ -232,17 +239,17 @@ func (s clockSched) IsMatched(t time.Time) bool { return (start.Equal(tc) || start.Before(tc) && end.After(tc) || end.Equal(tc)) && tc.Since(start.Clock)%s.d == 0 } -func (s clockSched) First(t time.Time) time.Duration { +func (s clockSched) Next(t time.Time) time.Time { if s.IsMatched(t) { - return 0 + return t } - start, end, tc := s.start.Clock, s.end.Clock, AtClock(t.Clock()).Clock + start, end := s.start.Clock, s.end.Clock for c := AtClock(t.Clock()); c.Compare(start) != -1 && c.Compare(end) != 1; c.Clock = c.Add(time.Second) { if s.IsMatched(c.Time()) { - return c.Since(tc) + return time.Date(t.Year(), t.Month(), t.Day(), c.Clock.Hour(), c.Clock.Minute(), c.Clock.Second(), 0, t.Location()) } } - return s.start.First(t) + return s.start.Next(t) } func (s clockSched) TickerDuration() time.Duration { diff --git a/scheduler/clock_test.go b/scheduler/clock_test.go index 395ee54..d41f37c 100644 --- a/scheduler/clock_test.go +++ b/scheduler/clock_test.go @@ -21,32 +21,38 @@ func TestClock(t *testing.T) { if res := s.TickerDuration(); res != 24*time.Hour { t.Errorf("expected 24h; got %v", res) } - if res := s.First(AtClock(6, 0, 0).Time()); res != time.Hour { - t.Errorf("expected 1h; got %v", res) + if res := s.Next(AtClock(6, 0, 0).Time()).Format("15:04:05"); res != "07:00:00" { + t.Errorf("expected 07:00:00; got %q", res) } } -func TestClockFirst(t *testing.T) { +func TestClockNext(t *testing.T) { + ct := AtClock(12, 30, 30).Time() for _, testcase := range []struct { clock *Clock + time string expected time.Duration }{ - {AtClock(0, 0, 0), 11*time.Hour + 29*time.Minute + 30*time.Second}, // 00:00:00 - {AtClock(-1, -1, -1), 0}, // 12:30:30 - {AtClock(-1, -1, 30), 0}, // 12:30:30 - {AtClock(-1, 30, -1), 0}, // 12:30:30 - {AtClock(12, -1, -1), 0}, // 12:30:30 - {AtClock(12, -1, 30), 0}, // 12:30:30 - {AtClock(-1, -1, 15), 45 * time.Second}, // 12:31:15 - {AtClock(-1, -1, 45), 15 * time.Second}, // 12:30:45 - {AtClock(-1, 15, -1), 44*time.Minute + 30*time.Second}, // 13:15:00 - {AtClock(-1, 45, -1), 14*time.Minute + 30*time.Second}, // 12:45:00 - {AtClock(6, -1, -1), 17*time.Hour + 29*time.Minute + 30*time.Second}, // 06:00:00+1 - {AtClock(18, -1, -1), 5*time.Hour + 29*time.Minute + 30*time.Second}, // 18:00:00 - {AtClock(6, -1, 15), 17*time.Hour + 29*time.Minute + 45*time.Second}, // 06:00:15+1 - {AtClock(18, -1, 45), 5*time.Hour + 30*time.Minute + 15*time.Second}, // 18:00:45 + {AtClock(0, 0, 0), "00:00:00", 11*time.Hour + 29*time.Minute + 30*time.Second}, + {AtClock(-1, -1, -1), "12:30:30", 0}, + {AtClock(-1, -1, 30), "12:30:30", 0}, + {AtClock(-1, 30, -1), "12:30:30", 0}, + {AtClock(12, -1, -1), "12:30:30", 0}, + {AtClock(12, -1, 30), "12:30:30", 0}, + {AtClock(-1, -1, 15), "12:31:15", 45 * time.Second}, + {AtClock(-1, -1, 45), "12:30:45", 15 * time.Second}, + {AtClock(-1, 15, -1), "13:15:00", 44*time.Minute + 30*time.Second}, + {AtClock(-1, 45, -1), "12:45:00", 14*time.Minute + 30*time.Second}, + {AtClock(6, -1, -1), "06:00:00", 17*time.Hour + 29*time.Minute + 30*time.Second}, // +1 + {AtClock(18, -1, -1), "18:00:00", 5*time.Hour + 29*time.Minute + 30*time.Second}, + {AtClock(6, -1, 15), "06:00:15", 17*time.Hour + 29*time.Minute + 45*time.Second}, // +1 + {AtClock(18, -1, 45), "18:00:45", 5*time.Hour + 30*time.Minute + 15*time.Second}, } { - if res := testcase.clock.First(AtClock(12, 30, 30).Time()); res != testcase.expected { + next := testcase.clock.Next(ct) + if res := next.Format("15:04:05"); res != testcase.time { + t.Errorf("%s expected %q; got %q", testcase.clock, testcase.time, res) + } + if res := next.Sub(ct); res != testcase.expected { t.Errorf("%s expected %v; got %v", testcase.clock, testcase.expected, res) } } @@ -74,18 +80,18 @@ func TestClockSchedule(t *testing.T) { s := ClockSchedule(AtHour(9).Minute(30), AtHour(15), 2*time.Minute) for _, testcase := range []struct { clock *Clock - duration time.Duration + time string expected bool }{ - {AtClock(9, 30, 0), 0, true}, - {AtClock(15, 0, 0), 0, true}, - {AtClock(13, 0, 0), 0, true}, - {ClockFromString("9:29"), time.Minute, false}, - {ClockFromString("9:31"), time.Minute, false}, - {ClockFromString("16:00:00"), 17*time.Hour + 30*time.Minute, false}, + {AtClock(9, 30, 0), "09:30:00", true}, + {AtClock(15, 0, 0), "15:00:00", true}, + {AtClock(13, 0, 0), "13:00:00", true}, + {ClockFromString("9:29"), "09:30:00", false}, + {ClockFromString("9:31"), "09:32:00", false}, + {ClockFromString("16:00:00"), "09:30:00", false}, } { - if res := s.First(testcase.clock.Time()); res != testcase.duration { - t.Errorf("%s expected %v; got %v", testcase.clock, testcase.duration, res) + if res := s.Next(testcase.clock.Time()).Format("15:04:05"); res != testcase.time { + t.Errorf("%s expected %q; got %q", testcase.clock, testcase.time, res) } if res := s.IsMatched(testcase.clock.Time()); res != testcase.expected { t.Errorf("%s expected %v; got %v", testcase.clock, testcase.expected, res) @@ -93,9 +99,9 @@ func TestClockSchedule(t *testing.T) { } } -func TestClockScheduleFirst(t *testing.T) { +func TestClockScheduleNext(t *testing.T) { s := ClockSchedule(ClockFromString("6:00"), ClockFromString("22:00"), time.Hour) - if res := s.First(AtClock(21, 59, 0).Time()); res != time.Minute { - t.Errorf("expected 1m; got %v", res) + if res := s.Next(AtClock(21, 59, 0).Time()).Format("15:04:05"); res != "22:00:00" { + t.Errorf("expected 22:00:00; got %q", res) } } diff --git a/scheduler/complex.go b/scheduler/complex.go index f53e98a..5cc0bd1 100644 --- a/scheduler/complex.go +++ b/scheduler/complex.go @@ -15,7 +15,7 @@ var ( type complexSched interface { IsMatched(time.Time) bool - First(time.Time) time.Duration + Next(time.Time) time.Time TickerDuration() time.Duration String() string @@ -60,16 +60,15 @@ func (s multiSched) IsMatched(t time.Time) bool { return false } -func (s multiSched) First(t time.Time) time.Duration { - var res time.Duration +func (s multiSched) Next(t time.Time) (next time.Time) { for i, sched := range s { if i == 0 { - res = sched.First(t) - } else if d := sched.First(t); d < res { - res = d + next = sched.Next(t) + } else if t := sched.Next(t); t.Before(next) { + next = t } } - return res + return } func (s multiSched) TickerDuration() time.Duration { @@ -140,11 +139,12 @@ func (s condSched) IsMatched(t time.Time) bool { return true } -func (s condSched) First(t time.Time) time.Duration { +func (s condSched) Next(t time.Time) time.Time { if len(s) == 1 { - return s[0].First(t) + return s[0].Next(t) } - return first(t, s.TickerDuration()) + d := s.TickerDuration() + return t.Add(d).Truncate(d) } func (s condSched) TickerDuration() time.Duration { diff --git a/scheduler/complex_test.go b/scheduler/complex_test.go index ab124a5..b878b96 100644 --- a/scheduler/complex_test.go +++ b/scheduler/complex_test.go @@ -10,8 +10,8 @@ func TestMultiSchedule(t *testing.T) { if d := s.TickerDuration(); d != time.Minute { t.Fatalf("expected 1m: got %s", d) } - if d := s.First(time.Date(2006, 1, 2, 0, 0, 0, 0, time.Local)); d != 5*time.Second { - t.Fatalf("expected 5s: got %s", d) + if res := s.Next(time.Time{}).Format("15:04:05"); res != "00:00:05" { + t.Fatalf("expected 00:00:05: got %q", res) } for _, testcase := range []struct { clock *Clock @@ -48,8 +48,8 @@ func TestConditionSchedule(t *testing.T) { if d := s.TickerDuration(); d != time.Second { t.Fatalf("expected 1s: got %s", d) } - if d := s.First(time.Date(2006, 1, 2, 3, 4, 5, 0, time.Local)); d != time.Second { - t.Fatalf("expected 1s: got %s", d) + if res := s.Next(time.Time{}).Format("15:04:05"); res != "00:00:01" { + t.Fatalf("expected 00:00:01: got %q", res) } for _, testcase := range []struct { clock *Clock diff --git a/scheduler/misc.go b/scheduler/misc.go index 26edb28..4edaca2 100644 --- a/scheduler/misc.go +++ b/scheduler/misc.go @@ -18,7 +18,3 @@ func gcd(a, b time.Duration) time.Duration { } return a } - -func first(t time.Time, d time.Duration) time.Duration { - return t.Add(d).Truncate(d).Sub(t) -} diff --git a/scheduler/schedule.go b/scheduler/schedule.go index 8b81cbe..3132163 100644 --- a/scheduler/schedule.go +++ b/scheduler/schedule.go @@ -9,7 +9,7 @@ import ( type Schedule interface { IsMatched(time.Time) bool - First(time.Time) time.Duration + Next(time.Time) time.Time TickerDuration() time.Duration String() string } @@ -77,11 +77,11 @@ func (s sched) IsMatched(t time.Time) bool { return false } -func (s sched) First(t time.Time) time.Duration { +func (s sched) Next(t time.Time) time.Time { if s.clock == nil { s.clock = &Clock{} } - return s.clock.First(t) + return s.clock.Next(t) } func (s sched) TickerDuration() time.Duration { @@ -138,11 +138,11 @@ func (s weekSched) IsMatched(t time.Time) bool { return false } -func (s weekSched) First(t time.Time) time.Duration { +func (s weekSched) Next(t time.Time) time.Time { if s.clock == nil { s.clock = &Clock{} } - return s.clock.First(t) + return s.clock.Next(t) } func (s weekSched) TickerDuration() time.Duration { @@ -217,11 +217,11 @@ func (s weekdaySched) IsMatched(t time.Time) bool { return false } -func (s weekdaySched) First(t time.Time) time.Duration { +func (s weekdaySched) Next(t time.Time) time.Time { if s.clock == nil { s.clock = &Clock{} } - return s.clock.First(t) + return s.clock.Next(t) } func (s weekdaySched) TickerDuration() time.Duration { @@ -281,11 +281,11 @@ func (s tickerSched) IsMatched(t time.Time) bool { return t.Truncate(time.Second).Sub(s.start)%s.d == 0 } -func (s tickerSched) First(t time.Time) time.Duration { +func (s tickerSched) Next(t time.Time) time.Time { if d := t.Sub(s.start); d > 0 { - return d % s.d + return t.Add(d % s.d) } - return s.start.Sub(t) + return s.start } func (s tickerSched) TickerDuration() time.Duration { diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index 376cfe6..0369efc 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -79,7 +79,7 @@ func (sched *Scheduler) init(d time.Duration) error { t := <-timer.C sched.sched.init(t) subscribeNotify(sched.notify) - sched.newTimer(sched.sched.First(t), d) + sched.newTimer(sched.sched.Next(t).Sub(t), d) return nil } return ErrAlreadyRunning @@ -119,7 +119,8 @@ func (sched *Scheduler) newTimer(first, duration time.Duration) { sched.ticker.Stop() sched.mu.Lock() defer sched.mu.Unlock() - sched.newTimer(sched.sched.First(time.Now()), duration) + t := time.Now() + sched.newTimer(sched.sched.Next(t).Sub(t), duration) return case <-sched.ctx.Done(): sched.ticker.Stop() @@ -137,7 +138,8 @@ func (sched *Scheduler) newTimer(first, duration time.Duration) { case <-sched.notify: sched.mu.Lock() if sched.timer.Stop() { - sched.timer.Reset(sched.sched.First(time.Now())) + t := time.Now() + sched.timer.Reset(sched.sched.Next(t).Sub(t)) } sched.mu.Unlock() case <-sched.ctx.Done():