The problem
You have a periodic task in Celery defined with a crontab(...) schedule, and you want to calculate the next time it's supposed to run.
Example: you want to find out when crontab(hour=12, minute=0) will trigger next after now.
Simple, right? There’s croniter library, which seems to be designed to solve this exact problem. Just use it, right?
Well.
First mistake: trying croniter with crontab
So my first instinct was to use croniter like this:
from celery.schedules import crontab from croniter import croniter from datetime import datetime schedule = crontab(hour=12, minute=0) cron = croniter(schedule, datetime.now()) next_run = cron.get_next(datetime) Boom 💥 doesn’t work. Because Celery’s crontab is not a string and croniter expects a string like "0 12 * * *":
AttributeError: 'crontab' object has no attribute 'lower' And no, crontab() does not have a nice .as_cron_string() method either.
So now you’re stuck parsing crontab's internal fields (._orig_minute, ._orig_hour, etc) just to reconstruct a string - and it starts to smell like overengineering for something that should be simple.
The right way (which I learned too late)
Turns out Celery’s crontab (and all schedules derived from celery.schedules.BaseSchedule) already has a method for this:
from datetime import datetime from celery.schedules import crontab schedule = crontab(hour=12, minute=0) now = datetime.now() # `now` is datetime.datetime(2025, 6, 11, 0, 16, 58, 484085) next_run = now + schedule.remaining_delta(now)[1] # `next_run` is datetime.datetime(2025, 6, 11, 12, 0) That’s it. You don’t need croniter at all. Celery knows how to calculate the delta to the next run. It just doesn’t shout about it in the docs.
Summary
- don’t reinvent Celery’s scheduling logic - it already has what you need;
-
crontabis not a cron string, don’t try to treat it like one; - use
.remaining_delta(last_run)to calculate when the next run will happen.
Hope this saves someone the 2 hours I wasted trying to do it the wrong way.
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments. Some comments have been hidden by the post's author - find out more