Skip to content

Commit

Permalink
Merge pull request #13 from kipe/issue_12
Browse files Browse the repository at this point in the history
Add support for day names and abbreviations
  • Loading branch information
kipe authored Sep 28, 2018
2 parents ad3470e + f90d078 commit b2de662
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 12 deletions.
51 changes: 41 additions & 10 deletions pycron/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
from datetime import datetime, timedelta
import calendar


DAY_NAMES = [x.lower() for x in calendar.day_name[6:] + calendar.day_name[:6]]
DAY_ABBRS = [x.lower() for x in calendar.day_abbr[6:] + calendar.day_abbr[:6]]
# Choice tuples, mainly designed to use with Django
MINUTE_CHOICES = [(str(x), str(x)) for x in range(0, 60)]
HOUR_CHOICES = [(str(x), str(x)) for x in range(0, 24)]
DOM_CHOICES = [(str(x), str(x)) for x in range(1, 32)]
MONTH_CHOICES = [(str(x), calendar.month_name[x]) for x in range(1, 13)]
DOW_CHOICES = [('0', calendar.day_name[6])] + [(str(x + 1), calendar.day_name[x]) for x in range(0, 6)]
DOW_CHOICES = [(str(i), day_name) for i, day_name in enumerate(DAY_NAMES)]


def _to_int(value, allow_daynames=False):
try:
return int(value)
except ValueError:
if not allow_daynames:
raise

for i, day_name in enumerate(DAY_NAMES):
if day_name == value:
return i
for i, day_abbr in enumerate(DAY_ABBRS):
if day_abbr == value:
return i

def _parse_arg(value, target):
raise ValueError('Failed to parse string to integer')


def _parse_arg(value, target, allow_daynames=False):
value = value.strip()

if value == '*':
Expand All @@ -21,7 +39,7 @@ def _parse_arg(value, target):
for value in values:
try:
# First, try a direct comparison
if int(value) == target:
if _to_int(value, allow_daynames=allow_daynames) == target:
return True
except ValueError:
pass
Expand All @@ -31,28 +49,41 @@ def _parse_arg(value, target):
if '/' in value:
# Allow divider in values, see issue #14
try:
start, tmp = [x.strip() for x in value.split('-')]
start = int(start)
end, step = [int(x) for x in tmp.split('/')]
start, tmp = [
x.strip()
for x in value.split('-')
]
start = _to_int(start)
end, step = [
_to_int(x.strip(), allow_daynames=allow_daynames)
for x in tmp.split('/')
]
except ValueError:
continue
else:
try:
start, end = [int(x.strip()) for x in value.split('-')]
start, end = [
_to_int(x.strip(), allow_daynames=allow_daynames)
for x in value.split('-')
]
except ValueError:
continue

# If target value is in the range, it matches
if target in range(start, end + 1, step):
return True

# Special cases, where the day names are more or less incorrectly set...
if allow_daynames and start > end:
return target in range(start, end + 6 + 1)

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 % int(interval) == 0:
if target % _to_int(interval, allow_daynames=allow_daynames) == 0:
return True

return False
Expand All @@ -75,7 +106,7 @@ def is_now(s, dt=None):
and _parse_arg(hour, dt.hour) \
and _parse_arg(dom, dt.day) \
and _parse_arg(month, dt.month) \
and _parse_arg(dow, 0 if weekday == 7 else weekday)
and _parse_arg(dow, 0 if weekday == 7 else weekday, True)


def has_been(s, since, dt=None):
Expand Down
84 changes: 84 additions & 0 deletions tests/test_day_names.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from datetime import datetime, timedelta
import pycron
from pytz import utc
import pendulum
import arrow
import udatetime
from delorean import Delorean


def test_day_names():
def run(now):
assert pycron.is_now('* * * * *', now)
assert pycron.is_now('* * * * thu', now)
assert pycron.is_now('* * * * thursday', now)
assert pycron.is_now('* * * * */thu', now)
assert pycron.is_now('* * * * */thursday', now)
assert pycron.is_now('* * * * sun,wed,thu', now)
assert pycron.is_now('* * * * sunday,wednesday,thursday', now)
assert pycron.is_now('* * * * wed', now) is False
assert pycron.is_now('* * * * wednesday', now) is False
assert pycron.is_now('* * * * */wed', now) is False
assert pycron.is_now('* * * * */wednesday', now) is False
assert pycron.is_now('* * * * sun,wed,sat', now) is False
assert pycron.is_now('* * * * sunday,wednesday,saturday', now) is False
assert pycron.DOW_CHOICES[now.isoweekday()][1] == 'thursday'
assert pycron.DOW_CHOICES[0][1] == 'sunday'
assert pycron.is_now('* * * * sun-thu', now)
assert pycron.is_now('* * * * sunday-thursday', now)
assert pycron.is_now('* * * * fri-sat', now) is False
assert pycron.is_now('* * * * friday-saturday', now) is False
# Special cases, where the day names are more or less incorrectly set...
assert pycron.is_now('* * * * thu-sun', now)
assert pycron.is_now('* * * * thursday-sunday', now)
assert pycron.is_now('* * * * wed-sun', now)
assert pycron.is_now('* * * * wednesday-sunday', now)
assert pycron.is_now('* * * * wed-mon', now)
assert pycron.is_now('* * * * wednesday-monday', now)
assert pycron.is_now('* * * * fri-sun', now) is False
assert pycron.is_now('* * * * friday-sunday', now) is False
assert pycron.is_now('* * * * fri-wed', now) is False
assert pycron.is_now('* * * * friday-wednesday', now) is False

now = datetime(2015, 6, 18, 16, 7)
run(now)
run(now.replace(tzinfo=utc))
run(pendulum.instance(now))
run(arrow.get(now))
run(udatetime.from_string(now.isoformat()))
run(Delorean(datetime=now, timezone='UTC').datetime)


def test_day_matching():
def run(now):
for i in range(0, 7):
# Test day matching from Sunday onwards...
now += timedelta(days=1)
assert pycron.is_now('* * * * %s' % (pycron.DAY_NAMES[i]), now)
assert pycron.is_now('* * * * %s' % (pycron.DAY_ABBRS[i]), now)
# Test weekdays
assert pycron.is_now('* * * * mon,tue,wed,thu,fri',
now) is (True if i not in [0, 6] else False)
assert pycron.is_now('* * * * monday,tuesday,wednesday,thursday,friday',
now) is (True if i not in [0, 6] else False)
assert pycron.is_now(
'* * * * mon-fri', now) is (True if i not in [0, 6] else False)
assert pycron.is_now(
'* * * * monday-friday', now) is (True if i not in [0, 6] else False)
assert pycron.is_now('* * * * mon,tue,wed,thu-fri',
now) is (True if i not in [0, 6] else False)
assert pycron.is_now('* * * * monday,tuesday,wednesday,thursday-friday',
now) is (True if i not in [0, 6] else False)
# Test weekends
assert pycron.is_now(
'* * * * sun,sat', now) is (True if i in [0, 6] else False)
assert pycron.is_now(
'* * * * sunday,saturday', now) is (True if i in [0, 6] else False)

now = datetime(2015, 6, 20, 16, 7)
run(now)
run(now.replace(tzinfo=utc))
run(pendulum.instance(now))
run(arrow.get(now))
run(udatetime.from_string(now.isoformat()))
run(Delorean(datetime=now, timezone='UTC').datetime)
4 changes: 2 additions & 2 deletions tests/test_dow.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ def run(now):
assert pycron.is_now('* * * * 3', now) is False
assert pycron.is_now('* * * * */3', now) is False
assert pycron.is_now('* * * * 0,3,6', now) is False
assert pycron.DOW_CHOICES[now.isoweekday()][1] == 'Thursday'
assert pycron.DOW_CHOICES[0][1] == 'Sunday'
assert pycron.DOW_CHOICES[now.isoweekday()][1] == 'thursday'
assert pycron.DOW_CHOICES[0][1] == 'sunday'
assert pycron.is_now('* * * * 0-4', now)
assert pycron.is_now('* * * * 5-6', now) is False

Expand Down

0 comments on commit b2de662

Please sign in to comment.