Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate next scheduled time based on cron spec

What's an efficient way to calculate the next run time of an event given the current time and a cron spec?

I'm looking for something other than "loop through every minute checking if it matches spec".

Examples of specs might be:

  • Every month, on the 1st and 15 at 15:01
  • At 10,20,30,40,50 mins past the hour every hour

Python code would be lovely but psuedo code or high level description would also be appreciated.

[Update] Assume the spec is already parsed and is in some reasonable format.

like image 623
Parand Avatar asked Jan 06 '11 01:01

Parand


People also ask

What is the use of * * * * * In cron?

It is a wildcard for every part of the cron schedule expression. So * * * * * means every minute of every hour of every day of every month and every day of the week .

What is cron expression 0 * * * *?

Meaning of cron expression 0 * * * * *? I think it means the scheduler is expected to run every seconds.

How do I schedule a cron job every 12 hours?

->cron('0 */12 * * *'); This cron will run the scheduler at every 12 hours.

Can you run a cron job every 30 seconds?

But still, you can do some tricky configurations to run your script to run at every 30 seconds. In the above configuration, we have scheduled the script twice. The first cron runs every 1 minute and the second cron also starts at the same time but waits for 30 seconds before execution.


1 Answers

Just looking at it, I think you need to:

  • parse the chron spec to five arrays containing acceptable values for each field;
  • parse 'now' to a value for each field;
  • in order of minute, hour, {day-of-month OR day-of-week}, month-of year: find the lowest array value that matches or exceeds the current value, correcting for carry.

I don't know how to handle day-of-week and day-of-month simultaneously; I am sure there is a way, but on the other hand I don't think I've ever seen a spec that actually specified both. I think it would be sufficient to write a handler for either and throw an error if you receive both.

Edit: apparently if day-of-week and day-of-month are both specified, it is supposed to fire on both - ie if the rule is '15th, Wednesday' it will fire on every 15th and every Wednesday.

The croniter package does what you want:

import croniter
import datetime

now = datetime.datetime.now()
sched = '1 15 1,15 * *'    # at 3:01pm on the 1st and 15th of every month
cron = croniter.croniter(sched, now)

for i in range(4):
    nextdate = cron.get_next(datetime.datetime)
    print nextdate

prints

2011-01-15 15:01:00
2011-02-01 15:01:00
2011-02-15 15:01:00
2011-03-01 15:01:00

although it would be nice if it were written as an actual iterator. Maybe I've got my next project ;-)

like image 97
Hugh Bothwell Avatar answered Sep 17 '22 16:09

Hugh Bothwell