This is another general implementation without need for a third party library.
Disclaimer: This implementation works with UTC. For managing timezones it has to be modified.
Run a func
once a day at noon.
- Period:
time.Hour * 24
- Offset:
time.Hour * 12
Run a func
twice a day at 03:40 (00:00 + 03:40) and 15:40 (12:00 + 03:40).
- Period:
time.Hour * 12
- Offset:
time.Hour * 3 + time.Minute * 40
Updated (2020-01-28):
Changes:
context.Context
can be used for cancellation, makes it testable.
time.Ticker
removes the need for calculating the time of the next execution.
package main
import (
"context"
"time"
)
// Schedule calls function `f` with a period `p` offsetted by `o`.
func Schedule(ctx context.Context, p time.Duration, o time.Duration, f func(time.Time)) {
// Position the first execution
first := time.Now().Truncate(p).Add(o)
if first.Before(time.Now()) {
first = first.Add(p)
}
firstC := time.After(first.Sub(time.Now()))
// Receiving from a nil channel blocks forever
t := &time.Ticker{C: nil}
for {
select {
case v := <-firstC:
// The ticker has to be started before f as it can take some time to finish
t = time.NewTicker(p)
f(v)
case v := <-t.C:
f(v)
case <-ctx.Done():
t.Stop()
return
}
}
}
Original:
package main
import (
"time"
)
// Repeat calls function `f` with a period `d` offsetted by `o`.
func Repeat(d time.Duration, o time.Duration, f func(time.Time)) {
next := time.Now().Truncate(d).Add(o)
if next.Before(time.Now()) {
next = next.Add(d)
}
t := time.NewTimer(next.Sub(time.Now()))
for {
v := <-t.C
next = next.Add(d)
t.Reset(next.Sub(time.Now()))
f(v)
}
}