Hi!
*/n on the day of month and month fields counts from 0, but those two fields start
at 1.
import pycron
from datetime import datetime as dt
print([d for d in range(1, 32) if pycron.is_now('0 0 */7 * *', dt(2026, 1, d))])
# [7, 14, 21, 28], expecting [1, 8, 15, 22, 29]
print([m for m in range(1, 13) if pycron.is_now('0 0 1 */5 *', dt(2026, m, 1))])
# [5, 10], expecting [1, 6, 11]
Minutes are fine since that field really does start at 0.
The clearest way to see it is that */7 is just short for 1-31/7, and the long
form gives the right answer:
pycron.is_now('0 0 */7 * *', dt(2026, 1, 1)) # False
pycron.is_now('0 0 1-31/7 * *', dt(2026, 1, 1)) # True
I think it is this line, target % interval == 0 assumes the field starts at 0,
while the a-b/n branch just above uses range(start_int, end + 1, step) which is
why the long form works:
|
if "/" in _value: |
|
v, interval = [x.strip() for x in _value.split("/")] |
|
# Not sure if applicable for every situation, but just to make sure... |
|
if v != "*": |
|
continue |
|
# If the remainder is zero, this matches |
|
if target % _to_int(interval, allow_daynames=allow_daynames) == 0: |
|
return True |
_parse_arg does not know which field it is parsing so it cannot know the minimum.
Passing one in and using (target - min_value) % interval == 0 gives the right
answers for both fields and leaves minutes and hours alone.
It would change when existing jobs fire though, so maybe worth a changelog note.
Can do a pull request with tests if you want.
Version: pycron 3.2.0, Python 3.13
Hi!
*/non the day of month and month fields counts from 0, but those two fields startat 1.
Minutes are fine since that field really does start at 0.
The clearest way to see it is that
*/7is just short for1-31/7, and the longform gives the right answer:
I think it is this line,
target % interval == 0assumes the field starts at 0,while the
a-b/nbranch just above usesrange(start_int, end + 1, step)which iswhy the long form works:
pycron/pycron/__init__.py
Lines 97 to 104 in 8a17626
_parse_argdoes not know which field it is parsing so it cannot know the minimum.Passing one in and using
(target - min_value) % interval == 0gives the rightanswers for both fields and leaves minutes and hours alone.
It would change when existing jobs fire though, so maybe worth a changelog note.
Can do a pull request with tests if you want.
Version: pycron 3.2.0, Python 3.13