diff --git a/crontab/_crontab.py b/crontab/_crontab.py index 5266fad..83a87cc 100644 --- a/crontab/_crontab.py +++ b/crontab/_crontab.py @@ -321,24 +321,24 @@ class CronTab(object): else: # we are going forwards _test = lambda: self.matchers.year < future.year - to_test = 0 - while to_test < 6: - incr = increments[to_test] - ch = False - inc = None - while not self._test_match(to_test, future): - inc = incr(future, self.matchers) + + # Start from the year and work our way down. Any time we increment a + # higher-magnitude value, we reset all lower-magnitude values. This + # gets us performance without sacrificing correctness. Still more + # complicated than a brute-force approach, but also orders of + # magnitude faster in basically all cases. + to_test = 5 + while to_test >= 0: + if not self._test_match(to_test, future): + inc = increments[to_test](future, self.matchers) future += inc - ch = True + for i in xrange(0, to_test): + future = increments[6+i](future, inc) if _test(): return None - if ch: - for i in xrange(0, to_test-1): - future = increments[6+i](future, inc) - to_test = 0 + to_test = 5 continue - - to_test += 1 + to_test -= 1 # verify the match match = [self._test_match(i, future) for i in xrange(6)] diff --git a/setup.py b/setup.py index 4fd7506..ed3853b 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ with open('README') as f: setup( name='crontab', - version='.16', + version='0.17', description='Parse and use crontab schedules in Python', author='Josiah Carlson', author_email='josiah.carlson@gmail.com', diff --git a/tests/test_crontab.py b/tests/test_crontab.py index 24ed5a5..61084b1 100644 --- a/tests/test_crontab.py +++ b/tests/test_crontab.py @@ -38,12 +38,12 @@ class TestCrontab(unittest.TestCase): n = datetime.datetime.utcfromtimestamp(ce.next(t945, delta=False)) assert n == datetime.datetime(2013, 1, 1, 10, 0), n p = datetime.datetime.utcfromtimestamp(ce.previous(t945, delta=False)) - assert p == datetime.datetime(2012, 12, 31, 15, 30) + assert p == datetime.datetime(2012, 12, 31, 15, 45), p n = datetime.datetime.utcfromtimestamp(ce.next(s1245, delta=False)) - assert n == datetime.datetime(2013, 1, 7, 10, 0) + assert n == datetime.datetime(2013, 1, 7, 10, 0), n p = datetime.datetime.utcfromtimestamp(ce.previous(s1245, delta=False)) - assert p == datetime.datetime(2013, 1, 4, 15, 45) + assert p == datetime.datetime(2013, 1, 4, 15, 45), p def test_normal(self): self._run_test('* * * * *', 60) @@ -66,6 +66,11 @@ class TestCrontab(unittest.TestCase): self._run_test('0-6,50-59/2 * * * *', 60, datetime.datetime(2011, 1, 1, 1, 55)) self._run_test('0-6,50/2 * * * *', 60, datetime.datetime(2011, 1, 1, 1, 55)) + self._run_test('10,20 15 * * *', 9*60, datetime.datetime(2011, 1, 1, 15, 1), min_delay=9*60) + self._run_test('10,20 15 * * *', 5*60, datetime.datetime(2011, 1, 1, 15, 15), min_delay=5*60) + self._run_test('10,20 15 * * *', 86400 - 600, datetime.datetime(2011, 1, 1, 15, 20), min_delay=86400 - 600) + self._run_test('* 2-5 * * *', 12525, datetime.datetime(2013, 6, 19, 22, 31, 15), min_delay=12525) + def test_alternate(self): self._run_test('0 0 1 jan-dec *', 32 * 86400) self._run_test('0 0 ? * sun-sat', 86400)