package sched import ( "time" ) var ( // zerotime is zero time.Time (unix epoch). zerotime = time.Time{} // emptytiming is a global timingempty to check against. emptytiming = timingempty{} ) // Timing provides scheduling for a Job, determining the next time // for given current time that execution is required. Please note that // calls to .Next() may alter the results of the next call, and should // only be called by the Scheduler. type Timing interface { Next(time.Time) time.Time } // timingempty is a 'zero' Timing implementation that always returns zero time. type timingempty struct{} func (timingempty) Next(time.Time) time.Time { return zerotime } // Once implements Timing to provide a run-once Job execution. type Once time.Time func (o *Once) Next(time.Time) time.Time { ret := *(*time.Time)(o) *o = Once(zerotime) // reset return ret } // Periodic implements Timing to provide a recurring Job execution. type Periodic time.Duration func (p Periodic) Next(now time.Time) time.Time { return now.Add(time.Duration(p)) } // PeriodicAt implements Timing to provide a recurring Job execution starting at 'Once' time. type PeriodicAt struct { Once Once Period Periodic } func (p *PeriodicAt) Next(now time.Time) time.Time { if next := p.Once.Next(now); !next.IsZero() { return next } return p.Period.Next(now) } // TimingWrap allows combining two different Timing implementations. type TimingWrap struct { Outer Timing Inner Timing // determined next times outerNext time.Time innerNext time.Time } func (t *TimingWrap) Next(now time.Time) time.Time { if t.outerNext.IsZero() { // Regenerate outermost next run time t.outerNext = t.Outer.Next(now) } if t.innerNext.IsZero() { // Regenerate innermost next run time t.innerNext = t.Inner.Next(now) } // If outer comes before inner, return outer if t.outerNext != zerotime && t.outerNext.Before(t.innerNext) { next := t.outerNext t.outerNext = zerotime return next } // Else, return inner next := t.innerNext t.innerNext = zerotime return next }