From 21cb2ac858da5a45b3fc598625eafe9f41f8e593 Mon Sep 17 00:00:00 2001 From: Andreas Vogl Date: Thu, 22 Dec 2016 09:25:45 +0100 Subject: [PATCH 1/2] issue #69: added day_or option to change behavior when day-of-month and day-of-week is given cron defineds that day-of-month and day-of-week should be connected using OR, however, an AND connection would be expected by some users as it is implemented in fcron. This day_or option allows to switch between the behaviour --- src/croniter/croniter.py | 7 +++++-- src/croniter/tests/test_croniter.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/croniter/croniter.py b/src/croniter/croniter.py index aa8783d..7f49348 100644 --- a/src/croniter/croniter.py +++ b/src/croniter/croniter.py @@ -63,8 +63,10 @@ class croniter(object): bad_length = 'Exactly 5 or 6 columns has to be specified for iterator' \ 'expression.' - def __init__(self, expr_format, start_time=None, ret_type=float): + def __init__(self, expr_format, start_time=None, ret_type=float, day_or=True): self._ret_type = ret_type + self._day_or = day_or + if start_time is None: start_time = time() @@ -240,7 +242,8 @@ class croniter(object): raise TypeError("Invalid ret_type, only 'float' or 'datetime' " "is acceptable.") - if expanded[2][0] != '*' and expanded[4][0] != '*': + # exception to support day of month and day of week as defined in cron + if (expanded[2][0] != '*' and expanded[4][0] != '*') and self._day_or: bak = expanded[4] expanded[4] = ['*'] t1 = self._calc(self.cur, expanded, is_prev) diff --git a/src/croniter/tests/test_croniter.py b/src/croniter/tests/test_croniter.py index 6f8a47d..ac87b7c 100755 --- a/src/croniter/tests/test_croniter.py +++ b/src/croniter/tests/test_croniter.py @@ -145,6 +145,22 @@ class CroniterTest(base.TestCase): self.assertEqual(n3.day, 3) self.assertEqual(n3.year, 2010) + def testWeekDayDayAnd(self): + base = datetime(2010, 1, 25) + itr = croniter('0 0 1 * mon', base, day_or=False) + n1 = itr.get_next(datetime) + self.assertEqual(n1.month, 2) + self.assertEqual(n1.day, 1) + self.assertEqual(n1.year, 2010) + n2 = itr.get_next(datetime) + self.assertEqual(n2.month, 3) + self.assertEqual(n2.day, 1) + self.assertEqual(n2.year, 2010) + n3 = itr.get_next(datetime) + self.assertEqual(n3.month, 11) + self.assertEqual(n3.day, 1) + self.assertEqual(n3.year, 2010) + def testMonth(self): base = datetime(2010, 1, 25) itr = croniter('0 0 1 * *', base) From 02dba0c99798565eb3408f8e0ed6c082cfbcedd3 Mon Sep 17 00:00:00 2001 From: Andreas Vogl Date: Wed, 25 Jan 2017 09:21:56 +0100 Subject: [PATCH 2/2] updated readme --- README.rst | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index b210351..9eddcc8 100644 --- a/README.rst +++ b/README.rst @@ -30,7 +30,7 @@ A simple example:: >>> from croniter import croniter >>> from datetime import datetime >>> base = datetime(2010, 1, 25, 4, 46) - >>> iter = croniter('*/5 * * * *', base) # every 5 minites + >>> iter = croniter('*/5 * * * *', base) # every 5 minutes >>> print iter.get_next(datetime) # 2010-01-25 04:50:00 >>> print iter.get_next(datetime) # 2010-01-25 04:55:00 >>> print iter.get_next(datetime) # 2010-01-25 05:00:00 @@ -39,23 +39,40 @@ A simple example:: >>> print iter.get_next(datetime) # 2010-01-26 04:02:00 >>> print iter.get_next(datetime) # 2010-01-30 04:02:00 >>> print iter.get_next(datetime) # 2010-02-02 04:02:00 + >>> + >>> iter = croniter('2 4 1 * wed', base) # 04:02 on every Wednesday OR on 1st day of month + >>> print iter.get_next(datetime) # 2010-01-27 04:02:00 + >>> print iter.get_next(datetime) # 2010-02-01 04:02:00 + >>> print iter.get_next(datetime) # 2010-02-03 04:02:00 + >>> + >>> iter = croniter('2 4 1 * wed', base, day_or=False) # 04:02 on every 1st day of the month if it is a Wednesday + >>> print iter.get_next(datetime) # 2010-09-01 04:02:00 + >>> print iter.get_next(datetime) # 2010-12-01 04:02:00 + >>> print iter.get_next(datetime) # 2011-06-01 04:02:00 -All you need to know is how to use the constructor and the get_next +All you need to know is how to use the constructor and the ``get_next`` method, the signature of these methods are listed below:: - >>> def __init__(self, cron_format, start_time=time.time()) + >>> def __init__(self, cron_format, start_time=time.time(), day_or=True) -croniter iterates along with 'cron_format' from 'start_time'. -cron_format is 'min hour day month day_of_week', you can refer to -http://en.wikipedia.org/wiki/Cron for more details.:: +croniter iterates along with ``cron_format`` from ``start_time``. +``cron_format`` is **min hour day month day_of_week**, you can refer to +http://en.wikipedia.org/wiki/Cron for more details. The ``day_or`` +switch is used to control how croniter handles **day** and **day_of_week** +entries. Default option is the cron behaviour, which connects those +values using **OR**. If the switch is set to False, the values are connected +using **AND**. This behaves like fcron and enables you to e.g. define a job that +executes each 2nd friday of a month by setting the days of month and the +weekday. +:: >>> def get_next(self, ret_type=float) get_next calculates the next value according to the cron expression and -returns an object of type 'ret_type'. ret_type should be a 'float' or a -'datetime' object. +returns an object of type ``ret_type``. ``ret_type`` should be a ``float`` or a +``datetime`` object. -Supported added for get_prev method. (>= 0.2.0):: +Supported added for ``get_prev`` method. (>= 0.2.0):: >>> base = datetime(2010, 8, 25) >>> itr = croniter('0 0 1 * *', base)