func StartChore(c *Chore) error { Logger.Debug("Re-Starting: ", c.Name) expr := cronexpr.MustParse(c.Sched) if expr.Next(time.Now()).IsZero() { Logger.Debug("invalid schedule", c.Sched) c.State = fmt.Sprintf("NOT Scheduled (invalid Schedule: %s)", c.Sched) } else { Logger.Debug("valid Schedule: ", c.Sched) c.Next = expr.Next(time.Now()) dur := c.Next.Sub(time.Now()) if dur > 0 { Logger.Debug("valid duration: ", dur) Logger.Debug("testing timer.. ") if c.Timer == nil { Logger.Debug("creating new timer ") c.Timer = time.AfterFunc(dur, c.Trigger) // auto go-routine'd } else { Logger.Debug("pre-existing timer found, resetting to: ", dur) c.Timer.Reset(dur) // auto go-routine'd } c.State = fmt.Sprintf("Scheduled: %s", c.Next.String()) } else { Logger.Debug("invalid duration", dur) c.State = fmt.Sprintf("Halted. (invalid duration: %s)", dur) } } Logger.Debug("all set! Chore: ", c.Name, "scheduled at: ", c.Next) return nil }
func (c *Channel) ShouldSendNowGivenTime(currentTime time.Time) bool { // We assume these are already sent if c.TimeToNotify == "@immediately" { return false } // the additional -1s is to make sure that the range always had 1 second. // example: if you specify 01:00:00, the minute before and minute after will // be 01:00:00 and 01:01:00. The next time computed will be an hour from now // (in the hourly case). This means that there's a chance that this hour we // never send anything. With an additional second it minimize this risk. // // Also, double sending is minimized as only 1 goroutine sends and we use a // delivered flag in the db. minuteBefore := currentTime.Add(time.Duration(-currentTime.Second()-1) * time.Second) minuteAfter := minuteBefore.Add(time.Minute + time.Second) expression := cronexpr.MustParse(c.TimeToNotify) nextTime := expression.Next(minuteBefore) if nextTime.IsZero() { return false } // Operator overloading is so good. // return (minuteBefore <= nextTime <= currentTime) return (nextTime.After(minuteBefore) || nextTime.Equal(minuteBefore)) && (nextTime.Before(minuteAfter) || nextTime.Equal(minuteAfter)) }
func scheduleLoop() { jobs := make([]*Job, 0, len(config.Schedule)) for exprStr, cmd := range config.Schedule { expr := cronexpr.MustParse(exprStr) jobs = append(jobs, &Job{ expr, cmd, expr.Next(time.Now()), }) } go func() { for { <-time.Tick(time.Second) for _, j := range jobs { if time.Now().Before(j.next) { continue } log.Printf("Executing scheduled command '%s'...", j.cmd) execCmd(j.cmd) j.next = j.expr.Next(time.Now()) } } }() }
func TestZero(t *testing.T) { from, _ := time.Parse("2006-01-02", "2013-08-31") next := cronexpr.MustParse("* * * * * 1980").Next(from) if next.IsZero() == false { t.Error(`("* * * * * 1980").Next("2013-08-31").IsZero() returned 'false', expected 'true'`) } next = cronexpr.MustParse("* * * * * 2050").Next(from) if next.IsZero() == true { t.Error(`("* * * * * 2050").Next("2013-08-31").IsZero() returned 'true', expected 'false'`) } next = cronexpr.MustParse("* * * * * 2099").Next(time.Time{}) if next.IsZero() == false { t.Error(`("* * * * * 2014").Next(time.Time{}).IsZero() returned 'true', expected 'false'`) } }
func TestGenericFeedKilledNotificationSystem(t *testing.T) { fun := func(ctx *FeedContext) { ctx.N.Close() // easiest way to kill it } if err := runGenericFeedTest(t, fun, cronexpr.MustParse("@hourly"), 1*time.Second, 100000); err == nil { t.Fatal("killing notifier should have reported back an error") } }
func processCronRule(rule Rule, stop chan struct{}, outCh chan messages.Message, cronRoom string) { nextTime := cronexpr.MustParse(rule.When).Next(time.Now()) for { select { case <-stop: return default: if nextTime.Format("2006-01-02 15:04") == time.Now().Format("2006-01-02 15:04") { msgs := rule.Action() for _, msg := range msgs { msg.Room = cronRoom outCh <- msg } } nextTime = cronexpr.MustParse(rule.When).Next(time.Now()) time.Sleep(2 * time.Second) } } }
func (s *Scheduler) SubmitMultiple(newJobs []*pb.Job) { var jobs []*job for _, j := range newJobs { job := &job{ id: JobId(j.Id), expr: cronexpr.MustParse(j.When), } jobs = append(jobs, job) } s.queue <- jobs }
func TestGenericFeedFiredOnRefreshAllEvent(t *testing.T) { done := false fun := func(ctx *FeedContext) { if !done { done = true ctx.N.Publish(core.NtRefreshAll, 0) } } // expect 2 events (1 due to startup, 1 due to NtRefreshAll request) if err := runGenericFeedTest(t, fun, cronexpr.MustParse("@hourly"), 1*time.Second, 2); err != nil { t.Fatal(err) } }
// ExampleMustParse func ExampleMustParse() { t := time.Date(2013, time.August, 31, 0, 0, 0, 0, time.UTC) nextTimes := cronexpr.MustParse("0 0 29 2 *").NextN(t, 5) for i := range nextTimes { fmt.Println(nextTimes[i].Format(time.RFC1123)) // Output: // Mon, 29 Feb 2016 00:00:00 UTC // Sat, 29 Feb 2020 00:00:00 UTC // Thu, 29 Feb 2024 00:00:00 UTC // Tue, 29 Feb 2028 00:00:00 UTC // Sun, 29 Feb 2032 00:00:00 UTC } }
// verify the schedule and start the timer func (t *TimerCallback) Start() error { expr := cronexpr.MustParse(t.Schedule) if expr.Next(time.Now()).IsZero() { Logger.Debug("invalid schedule", t.Schedule) t.State = fmt.Sprintf("NOT Scheduled (invalid Schedule: %s)", t.Schedule) return fmt.Errorf("invalid schedule", t.Schedule) } t.Next = expr.Next(time.Now()) dur := t.Next.Sub(time.Now()) if dur > 0 { go t.Run(dur) } return nil }
func BenchmarkNext(b *testing.B) { exprs := make([]*cronexpr.Expression, benchmarkExpressionsLen) for i := 0; i < benchmarkExpressionsLen; i++ { exprs[i] = cronexpr.MustParse(benchmarkExpressions[i]) } from := time.Now() b.ResetTimer() for i := 0; i < b.N; i++ { expr := exprs[i%benchmarkExpressionsLen] next := expr.Next(from) next = expr.Next(next) next = expr.Next(next) next = expr.Next(next) next = expr.Next(next) } }
func (at *ActionPlan) GetNextStartTime(now time.Time) (t time.Time) { if !at.stCache.IsZero() { return at.stCache } i := at.Timing if i == nil || i.Timing == nil { return } // Normalize if i.Timing.StartTime == "" { i.Timing.StartTime = "00:00:00" } if len(i.Timing.Years) > 0 && len(i.Timing.Months) == 0 { i.Timing.Months = append(i.Timing.Months, 1) } if len(i.Timing.Months) > 0 && len(i.Timing.MonthDays) == 0 { i.Timing.MonthDays = append(i.Timing.MonthDays, 1) } at.stCache = cronexpr.MustParse(i.Timing.CronString()).Next(now) return at.stCache }
func TestNextN_every5min(t *testing.T) { expected := []string{ "Mon, 2 Sep 2013 08:45:00", "Mon, 2 Sep 2013 08:50:00", "Mon, 2 Sep 2013 08:55:00", "Mon, 2 Sep 2013 09:00:00", "Mon, 2 Sep 2013 09:05:00", } from, _ := time.Parse("2006-01-02 15:04:05", "2013-09-02 08:44:32") result := cronexpr.MustParse("*/5 * * * *").NextN(from, uint(len(expected))) if len(result) != len(expected) { t.Errorf(`MustParse("*/5 * * * *").NextN("2013-09-02 08:44:30", 5):\n"`) t.Errorf(` Expected %d returned time values but got %d instead`, len(expected), len(result)) } for i, next := range result { nextStr := next.Format("Mon, 2 Jan 2006 15:04:05") if nextStr != expected[i] { t.Errorf(`MustParse("*/5 * * * *").NextN("2013-09-02 08:44:30", 5):\n"`) t.Errorf(` result[%d]: expected "%s" but got "%s"`, i, expected[i], nextStr) } } }
func TestNextN(t *testing.T) { expected := []string{ "Sat, 30 Nov 2013 00:00:00", "Sat, 29 Mar 2014 00:00:00", "Sat, 31 May 2014 00:00:00", "Sat, 30 Aug 2014 00:00:00", "Sat, 29 Nov 2014 00:00:00", } from, _ := time.Parse("2006-01-02 15:04:05", "2013-09-02 08:44:30") result := cronexpr.MustParse("0 0 * * 6#5").NextN(from, uint(len(expected))) if len(result) != len(expected) { t.Errorf(`MustParse("0 0 * * 6#5").NextN("2013-09-02 08:44:30", 5):\n"`) t.Errorf(` Expected %d returned time values but got %d instead`, len(expected), len(result)) } for i, next := range result { nextStr := next.Format("Mon, 2 Jan 2006 15:04:15") if nextStr != expected[i] { t.Errorf(`MustParse("0 0 * * 6#5").NextN("2013-09-02 08:44:30", 5):\n"`) t.Errorf(` result[%d]: expected "%s" but got "%s"`, i, expected[i], nextStr) } } }
func main() { app := cli.NewApp() app.Version = "0.1.0" app.Name = "cronexpr" app.Usage = "convert cron expression and get next occurance" app.Flags = []cli.Flag{ cli.StringFlag{ Name: "unix, u", Value: "", Usage: "from specific unix timestamp", }, cli.StringFlag{ Name: "format, f", Value: "", Usage: "format options see http://strftime.org/", }, cli.StringFlag{ Name: "next, n", Value: "", Usage: "n next time stamps", }, cli.StringFlag{ Name: "utc", Value: "false", Usage: "n next time stamps", }, } app.Action = func(c *cli.Context) { cron := "" if len(c.Args()) > 0 { cron = c.Args()[0] } else { panic("missing cron expression") } from := time.Now() if c.String("unix") != "" { u, err := strconv.ParseInt(c.String("unix"), 10, 64) if err != nil { panic(err) } from = time.Unix(u, 0) } if c.BoolT("utc") { from = from.UTC() } if c.String("next") != "" { n, err := strconv.ParseInt(c.String("next"), 10, 64) if err != nil { panic(err) } result := cronexpr.MustParse(cron).NextN(from, uint(n)) for _, next := range result { out := strconv.FormatInt(next.Unix(), 10) if c.String("format") != "" { out = strftime.Format(c.String("format"), next) } fmt.Println(out) } } else { result := cronexpr.MustParse(cron).Next(from) out := strconv.FormatInt(result.Unix(), 10) if c.String("format") != "" { out = strftime.Format(c.String("format"), result) } fmt.Println(out) } } app.Run(os.Args) }
. "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gbytes" "github.com/onsi/gomega/gexec" ) var _ = Describe("Check", func() { var checkCmd *exec.Cmd BeforeEach(func() { checkCmd = exec.Command(checkPath) }) Context("MustParse", func() { It("can parse crontab expressions", func() { expr := cronexpr.MustParse("* * * * 0-5") Expect(expr).ToNot(BeNil()) }) }) Context("when a crontab expression is specified", func() { var request models.CheckRequest var response models.CheckResponse var session *gexec.Session BeforeEach(func() { request = models.CheckRequest{ Source: models.Source{ Location: "America/New_York", }, }
func TestGenericHourlyFeed(t *testing.T) { if err := runGenericFeedTest(t, nil, cronexpr.MustParse("@hourly"), 1*time.Second, 1); err != nil { t.Fatal(err) } }
func TestGenericFeedRefreshInterval(t *testing.T) { oncePerSecond := "* * * * * * *" if err := runGenericFeedTest(t, nil, cronexpr.MustParse(oncePerSecond), 3*time.Second, 2); err != nil { t.Fatal(err) } }
func BenchmarkParse(b *testing.B) { for i := 0; i < b.N; i++ { _ = cronexpr.MustParse(benchmarkExpressions[i%benchmarkExpressionsLen]) } }
func main() { flag.Parse() runtime.GOMAXPROCS(*threads) var cron *cronexpr.Expression if *update != "" { cron = cronexpr.MustParse(*update) } var db oui.DynamicDB url := "" fileName := "" var err error if strings.HasPrefix(*ouiFile, "http") { url = *ouiFile if url == "http" { url = "http://standards-oui.ieee.org/oui.txt" } log.Println("Downloading new Db from: " + url) db, err = oui.OpenHttp(url) if err != nil { log.Fatalf("Error downloading:%s", err.Error()) } } else { fileName = *ouiFile log.Println("Opening database from: " + fileName) db, err = oui.OpenFile(fileName) if err != nil { log.Fatalf("Error updating file:%s", err.Error()) } } log.Printf("Database generated at %s\n", db.Generated().Local().String()) // Start updater if needed. if cron != nil { go func() { for { // Sleep until next update next := cron.Next(time.Now()) log.Println("Next update: " + next.String()) time.Sleep(next.Sub(time.Now())) if url != "" { log.Println("Updating db from: " + url) err := oui.UpdateHttp(db, url) if err != nil { log.Printf("Error downloading update:%s", err.Error()) } else { log.Println("Updated Successfully") } } else { log.Println("Updating db with file: " + fileName) err := oui.UpdateFile(db, fileName) if err != nil { log.Printf("Error loading update:%s", err.Error()) } else { log.Println("Updated Successfully") } } } }() } // We dereference this to avoid a pretty big penalty under heavy load. prettyL := *pretty http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { var mac string var hw *oui.HardwareAddr // Prepare the response and queue sending the result. res := &Response{} defer func() { var j []byte var err error if prettyL { j, err = json.MarshalIndent(res, "", " ") } else { j, err = res.MarshalJSON() } if err != nil { log.Fatal(err) } w.Write(j) }() // Set headers if *originPolicy != "" { w.Header().Set("Access-Control-Allow-Origin", *originPolicy) } w.Header().Set("Content-Type", "application/json") w.Header().Set("Last-Modified", db.Generated().Format(http.TimeFormat)) // Find Mac mac = req.URL.Query().Get("mac") if mac == "" { mac = strings.Trim(req.URL.Path, "/") } hw, err := oui.ParseMac(mac) if err != nil { res.Error = err.Error() w.WriteHeader(http.StatusBadRequest) return } entry, err := db.LookUp(*hw) if err != nil { if err == oui.ErrNotFound { res.Error = "not found in db" w.WriteHeader(http.StatusNotFound) return } w.WriteHeader(http.StatusInternalServerError) res.Error = err.Error() return } res.Data = entry }) log.Println("Listening on " + *listen) log.Fatal(http.ListenAndServe(*listen, nil)) }
// Cron returns a CronSchedule using the cron expression giving as parameter of the function. func Cron(expression string) CronSchedule { expr := cronexpr.MustParse(expression) return CronSchedule{ Expression: expr, } }