|  | @@ -558,6 +558,21 @@ def weekly():
 | 
	
		
			
				|  |  |      pass
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +@task.periodic_task(run_every=crontab(hour=7, minute=30,
 | 
	
		
			
				|  |  | +                                      day_of_week="thursday",
 | 
	
		
			
				|  |  | +                                      day_of_month="8-14"))
 | 
	
		
			
				|  |  | +def monthly():
 | 
	
		
			
				|  |  | +    pass
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +@task.periodic_task(run_every=crontab(hour=7, minute=30,
 | 
	
		
			
				|  |  | +                                      day_of_week="thursday",
 | 
	
		
			
				|  |  | +                                      day_of_month="8-14",
 | 
	
		
			
				|  |  | +                                      month_of_year=3))
 | 
	
		
			
				|  |  | +def yearly():
 | 
	
		
			
				|  |  | +    pass
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  def patch_crontab_nowfun(cls, retval):
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def create_patcher(fun):
 | 
	
	
		
			
				|  | @@ -589,6 +604,10 @@ class test_crontab_parser(Case):
 | 
	
		
			
				|  |  |          self.assertEqual(crontab_parser(24).parse('*'), set(range(24)))
 | 
	
		
			
				|  |  |          self.assertEqual(crontab_parser(60).parse('*'), set(range(60)))
 | 
	
		
			
				|  |  |          self.assertEqual(crontab_parser(7).parse('*'), set(range(7)))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab_parser(31, 1).parse('*'),
 | 
	
		
			
				|  |  | +                          set(range(1, 31 + 1)))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab_parser(12, 1).parse('*'),
 | 
	
		
			
				|  |  | +                          set(range(1, 12 + 1)))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def test_parse_range(self):
 | 
	
		
			
				|  |  |          self.assertEqual(crontab_parser(60).parse('1-10'),
 | 
	
	
		
			
				|  | @@ -597,12 +616,16 @@ class test_crontab_parser(Case):
 | 
	
		
			
				|  |  |                            set(range(0, 20 + 1)))
 | 
	
		
			
				|  |  |          self.assertEqual(crontab_parser().parse('2-10'),
 | 
	
		
			
				|  |  |                            set(range(2, 10 + 1)))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab_parser(60, 1).parse('1-10'),
 | 
	
		
			
				|  |  | +                          set(range(1, 10 + 1)))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def test_parse_groups(self):
 | 
	
		
			
				|  |  |          self.assertEqual(crontab_parser().parse('1,2,3,4'),
 | 
	
		
			
				|  |  |                            set([1, 2, 3, 4]))
 | 
	
		
			
				|  |  |          self.assertEqual(crontab_parser().parse('0,15,30,45'),
 | 
	
		
			
				|  |  |                            set([0, 15, 30, 45]))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab_parser(min_=1).parse('1,2,3,4'),
 | 
	
		
			
				|  |  | +                          set([1, 2, 3, 4]))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def test_parse_steps(self):
 | 
	
		
			
				|  |  |          self.assertEqual(crontab_parser(8).parse('*/2'),
 | 
	
	
		
			
				|  | @@ -611,6 +634,12 @@ class test_crontab_parser(Case):
 | 
	
		
			
				|  |  |                            set(i * 2 for i in xrange(30)))
 | 
	
		
			
				|  |  |          self.assertEqual(crontab_parser().parse('*/3'),
 | 
	
		
			
				|  |  |                            set(i * 3 for i in xrange(20)))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab_parser(8, 1).parse('*/2'),
 | 
	
		
			
				|  |  | +                          set([1, 3, 5, 7]))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab_parser(min_=1).parse('*/2'),
 | 
	
		
			
				|  |  | +                          set(i * 2 + 1 for i in xrange(30)))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab_parser(min_=1).parse('*/3'),
 | 
	
		
			
				|  |  | +                          set(i * 3 + 1 for i in xrange(20)))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def test_parse_composite(self):
 | 
	
		
			
				|  |  |          self.assertEqual(crontab_parser(8).parse('*/2'), set([0, 2, 4, 6]))
 | 
	
	
		
			
				|  | @@ -622,6 +651,16 @@ class test_crontab_parser(Case):
 | 
	
		
			
				|  |  |                       20, 25, 30, 35, 40, 45, 50, 55]))
 | 
	
		
			
				|  |  |          self.assertEqual(crontab_parser().parse('1-9/2'),
 | 
	
		
			
				|  |  |                  set([1, 3, 5, 7, 9]))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab_parser(8, 1).parse('*/2'), set([1, 3, 5, 7]))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab_parser(min_=1).parse('2-9/5'), set([2, 7]))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab_parser(min_=1).parse('2-10/5'), set([2, 7]))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab_parser(min_=1).parse('2-11/5,3'),
 | 
	
		
			
				|  |  | +                set([2, 3, 7]))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab_parser(min_=1).parse('2-4/3,*/5,1-21/4'),
 | 
	
		
			
				|  |  | +                set([1, 2, 5, 6, 9, 11, 13, 16, 17,
 | 
	
		
			
				|  |  | +                     21, 26, 31, 36, 41, 46, 51, 56]))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab_parser(min_=1).parse('1-9/2'),
 | 
	
		
			
				|  |  | +                set([1, 3, 5, 7, 9]))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def test_parse_errors_on_empty_string(self):
 | 
	
		
			
				|  |  |          with self.assertRaises(ParseException):
 | 
	
	
		
			
				|  | @@ -642,6 +681,8 @@ class test_crontab_parser(Case):
 | 
	
		
			
				|  |  |      def test_expand_cronspec_eats_iterables(self):
 | 
	
		
			
				|  |  |          self.assertEqual(crontab._expand_cronspec(iter([1, 2, 3]), 100),
 | 
	
		
			
				|  |  |                           set([1, 2, 3]))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab._expand_cronspec(iter([1, 2, 3]), 100, 1),
 | 
	
		
			
				|  |  | +                         set([1, 2, 3]))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def test_expand_cronspec_invalid_type(self):
 | 
	
		
			
				|  |  |          with self.assertRaises(TypeError):
 | 
	
	
		
			
				|  | @@ -653,9 +694,15 @@ class test_crontab_parser(Case):
 | 
	
		
			
				|  |  |      def test_eq(self):
 | 
	
		
			
				|  |  |          self.assertEqual(crontab(day_of_week="1, 2"),
 | 
	
		
			
				|  |  |                           crontab(day_of_week="1-2"))
 | 
	
		
			
				|  |  | -        self.assertEqual(crontab(minute="1", hour="2", day_of_week="5"),
 | 
	
		
			
				|  |  | -                         crontab(minute="1", hour="2", day_of_week="5"))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab(day_of_month="1, 16, 31"),
 | 
	
		
			
				|  |  | +                         crontab(day_of_month="*/15"))
 | 
	
		
			
				|  |  | +        self.assertEqual(crontab(minute="1", hour="2", day_of_week="5",
 | 
	
		
			
				|  |  | +                                 day_of_month="10", month_of_year="5"),
 | 
	
		
			
				|  |  | +                         crontab(minute="1", hour="2", day_of_week="5",
 | 
	
		
			
				|  |  | +                                 day_of_month="10", month_of_year="5"))
 | 
	
		
			
				|  |  |          self.assertNotEqual(crontab(minute="1"), crontab(minute="2"))
 | 
	
		
			
				|  |  | +        self.assertNotEqual(crontab(month_of_year="1"),
 | 
	
		
			
				|  |  | +                            crontab(month_of_year="2"))
 | 
	
		
			
				|  |  |          self.assertFalse(object() == crontab(minute="1"))
 | 
	
		
			
				|  |  |          self.assertFalse(crontab(minute="1") == object())
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -709,6 +756,128 @@ class test_crontab_remaining_estimate(Case):
 | 
	
		
			
				|  |  |                                     datetime(2010, 9, 11, 14, 30, 15))
 | 
	
		
			
				|  |  |          self.assertEqual(next, datetime(2010, 9, 13, 0, 5))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    def test_monthday(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=30,
 | 
	
		
			
				|  |  | +                                           hour=14,
 | 
	
		
			
				|  |  | +                                           day_of_month=18),
 | 
	
		
			
				|  |  | +                                   datetime(2010, 9, 11, 14, 30, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2010, 9, 18, 14, 30))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_not_monthday(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=[5, 42],
 | 
	
		
			
				|  |  | +                                           day_of_month=29),
 | 
	
		
			
				|  |  | +                                   datetime(2010, 1, 22, 14, 30, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2010, 1, 29, 0, 5))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_weekday_monthday(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=30,
 | 
	
		
			
				|  |  | +                                           hour=14,
 | 
	
		
			
				|  |  | +                                           day_of_week="mon",
 | 
	
		
			
				|  |  | +                                           day_of_month=18),
 | 
	
		
			
				|  |  | +                                   datetime(2010, 1, 18, 14, 30, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2010, 10, 18, 14, 30))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_monthday_not_weekday(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=[5, 42],
 | 
	
		
			
				|  |  | +                                           day_of_week="sat",
 | 
	
		
			
				|  |  | +                                           day_of_month=29),
 | 
	
		
			
				|  |  | +                                   datetime(2010, 1, 29, 0, 5, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2010, 5, 29, 0, 5))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_weekday_not_monthday(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=[5, 42],
 | 
	
		
			
				|  |  | +                                           day_of_week="mon",
 | 
	
		
			
				|  |  | +                                           day_of_month=18),
 | 
	
		
			
				|  |  | +                                   datetime(2010, 1, 11, 0, 5, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2010, 1, 18, 0, 5))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_not_weekday_not_monthday(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=[5, 42],
 | 
	
		
			
				|  |  | +                                           day_of_week="mon",
 | 
	
		
			
				|  |  | +                                           day_of_month=18),
 | 
	
		
			
				|  |  | +                                   datetime(2010, 1, 10, 0, 5, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2010, 1, 18, 0, 5))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_leapday(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=30,
 | 
	
		
			
				|  |  | +                                           hour=14,
 | 
	
		
			
				|  |  | +                                           day_of_month=29),
 | 
	
		
			
				|  |  | +                                   datetime(2012, 1, 29, 14, 30, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2012, 2, 29, 14, 30))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_not_leapday(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=30,
 | 
	
		
			
				|  |  | +                                           hour=14,
 | 
	
		
			
				|  |  | +                                           day_of_month=29),
 | 
	
		
			
				|  |  | +                                   datetime(2010, 1, 29, 14, 30, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2010, 3, 29, 14, 30))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_weekmonthdayyear(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=30,
 | 
	
		
			
				|  |  | +                                           hour=14,
 | 
	
		
			
				|  |  | +                                           day_of_week="fri",
 | 
	
		
			
				|  |  | +                                           day_of_month=29,
 | 
	
		
			
				|  |  | +                                           month_of_year=1),
 | 
	
		
			
				|  |  | +                                   datetime(2010, 1, 22, 14, 30, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2010, 1, 29, 14, 30))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_monthdayyear_not_week(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=[5, 42],
 | 
	
		
			
				|  |  | +                                           day_of_week="wed,thu",
 | 
	
		
			
				|  |  | +                                           day_of_month=29,
 | 
	
		
			
				|  |  | +                                           month_of_year="1,4,7"),
 | 
	
		
			
				|  |  | +                                   datetime(2010, 1, 29, 14, 30, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2010, 4, 29, 0, 5))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_weekdaymonthyear_not_monthday(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=30,
 | 
	
		
			
				|  |  | +                                           hour=14,
 | 
	
		
			
				|  |  | +                                           day_of_week="fri",
 | 
	
		
			
				|  |  | +                                           day_of_month=29,
 | 
	
		
			
				|  |  | +                                           month_of_year="1-10"),
 | 
	
		
			
				|  |  | +                                   datetime(2010, 1, 29, 14, 30, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2010, 10, 29, 14, 30))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_weekmonthday_not_monthyear(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=[5, 42],
 | 
	
		
			
				|  |  | +                                           day_of_week="fri",
 | 
	
		
			
				|  |  | +                                           day_of_month=29,
 | 
	
		
			
				|  |  | +                                           month_of_year="2-10"),
 | 
	
		
			
				|  |  | +                                   datetime(2010, 1, 29, 14, 30, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2010, 10, 29, 0, 5))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_weekday_not_monthdayyear(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=[5, 42],
 | 
	
		
			
				|  |  | +                                           day_of_week="mon",
 | 
	
		
			
				|  |  | +                                           day_of_month=18,
 | 
	
		
			
				|  |  | +                                           month_of_year="2-10"),
 | 
	
		
			
				|  |  | +                                   datetime(2010, 1, 11, 0, 5, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2010, 10, 18, 0, 5))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_monthday_not_weekdaymonthyear(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=[5, 42],
 | 
	
		
			
				|  |  | +                                           day_of_week="mon",
 | 
	
		
			
				|  |  | +                                           day_of_month=29,
 | 
	
		
			
				|  |  | +                                           month_of_year="2-4"),
 | 
	
		
			
				|  |  | +                                   datetime(2010, 1, 29, 0, 5, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2010, 3, 29, 0, 5))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_monthyear_not_weekmonthday(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=[5, 42],
 | 
	
		
			
				|  |  | +                                           day_of_week="mon",
 | 
	
		
			
				|  |  | +                                           day_of_month=29,
 | 
	
		
			
				|  |  | +                                           month_of_year="2-4"),
 | 
	
		
			
				|  |  | +                                   datetime(2010, 2, 28, 0, 5, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2010, 3, 29, 0, 5))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_not_weekmonthdayyear(self):
 | 
	
		
			
				|  |  | +        next = self.next_ocurrance(crontab(minute=[5, 42],
 | 
	
		
			
				|  |  | +                                           day_of_week="fri,sat",
 | 
	
		
			
				|  |  | +                                           day_of_month=29,
 | 
	
		
			
				|  |  | +                                           month_of_year="2-10"),
 | 
	
		
			
				|  |  | +                                   datetime(2010, 1, 28, 14, 30, 15))
 | 
	
		
			
				|  |  | +        self.assertEqual(next, datetime(2010, 5, 29, 0, 5))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class test_crontab_is_due(Case):
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -721,12 +890,16 @@ class test_crontab_is_due(Case):
 | 
	
		
			
				|  |  |          self.assertEqual(c.minute, set(range(60)))
 | 
	
		
			
				|  |  |          self.assertEqual(c.hour, set(range(24)))
 | 
	
		
			
				|  |  |          self.assertEqual(c.day_of_week, set(range(7)))
 | 
	
		
			
				|  |  | +        self.assertEqual(c.day_of_month, set(range(1, 32)))
 | 
	
		
			
				|  |  | +        self.assertEqual(c.month_of_year, set(range(1, 13)))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def test_simple_crontab_spec(self):
 | 
	
		
			
				|  |  |          c = crontab(minute=30)
 | 
	
		
			
				|  |  |          self.assertEqual(c.minute, set([30]))
 | 
	
		
			
				|  |  |          self.assertEqual(c.hour, set(range(24)))
 | 
	
		
			
				|  |  |          self.assertEqual(c.day_of_week, set(range(7)))
 | 
	
		
			
				|  |  | +        self.assertEqual(c.day_of_month, set(range(1, 32)))
 | 
	
		
			
				|  |  | +        self.assertEqual(c.month_of_year, set(range(1, 13)))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def test_crontab_spec_minute_formats(self):
 | 
	
		
			
				|  |  |          c = crontab(minute=30)
 | 
	
	
		
			
				|  | @@ -772,16 +945,6 @@ class test_crontab_is_due(Case):
 | 
	
		
			
				|  |  |          c = crontab(day_of_week='*/2')
 | 
	
		
			
				|  |  |          self.assertEqual(c.day_of_week, set([0, 2, 4, 6]))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    def seconds_almost_equal(self, a, b, precision):
 | 
	
		
			
				|  |  | -        for index, skew in enumerate((+0.1, 0, -0.1)):
 | 
	
		
			
				|  |  | -            try:
 | 
	
		
			
				|  |  | -                self.assertAlmostEqual(a, b + skew, precision)
 | 
	
		
			
				|  |  | -            except AssertionError:
 | 
	
		
			
				|  |  | -                if index + 1 >= 3:
 | 
	
		
			
				|  |  | -                    raise
 | 
	
		
			
				|  |  | -            else:
 | 
	
		
			
				|  |  | -                break
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |      def test_crontab_spec_invalid_dow(self):
 | 
	
		
			
				|  |  |          with self.assertRaises(ValueError):
 | 
	
		
			
				|  |  |              crontab(day_of_week='fooday-barday')
 | 
	
	
		
			
				|  | @@ -792,6 +955,58 @@ class test_crontab_is_due(Case):
 | 
	
		
			
				|  |  |          with self.assertRaises(ValueError):
 | 
	
		
			
				|  |  |              crontab(day_of_week='12')
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    def test_crontab_spec_dom_formats(self):
 | 
	
		
			
				|  |  | +        c = crontab(day_of_month=5)
 | 
	
		
			
				|  |  | +        self.assertEqual(c.day_of_month, set([5]))
 | 
	
		
			
				|  |  | +        c = crontab(day_of_month='5')
 | 
	
		
			
				|  |  | +        self.assertEqual(c.day_of_month, set([5]))
 | 
	
		
			
				|  |  | +        c = crontab(day_of_month='2,4,6')
 | 
	
		
			
				|  |  | +        self.assertEqual(c.day_of_month, set([2, 4, 6]))
 | 
	
		
			
				|  |  | +        c = crontab(day_of_month='*/5')
 | 
	
		
			
				|  |  | +        self.assertEqual(c.day_of_month, set([1, 6, 11, 16, 21, 26, 31]))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_crontab_spec_invalid_dom(self):
 | 
	
		
			
				|  |  | +        with self.assertRaises(ValueError):
 | 
	
		
			
				|  |  | +            crontab(day_of_month=0)
 | 
	
		
			
				|  |  | +        with self.assertRaises(ValueError):
 | 
	
		
			
				|  |  | +            crontab(day_of_month='0-10')
 | 
	
		
			
				|  |  | +        with self.assertRaises(ValueError):
 | 
	
		
			
				|  |  | +            crontab(day_of_month=32)
 | 
	
		
			
				|  |  | +        with self.assertRaises(ValueError):
 | 
	
		
			
				|  |  | +            crontab(day_of_month='31,32')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_crontab_spec_moy_formats(self):
 | 
	
		
			
				|  |  | +        c = crontab(month_of_year=1)
 | 
	
		
			
				|  |  | +        self.assertEqual(c.month_of_year, set([1]))
 | 
	
		
			
				|  |  | +        c = crontab(month_of_year='1')
 | 
	
		
			
				|  |  | +        self.assertEqual(c.month_of_year, set([1]))
 | 
	
		
			
				|  |  | +        c = crontab(month_of_year='2,4,6')
 | 
	
		
			
				|  |  | +        self.assertEqual(c.month_of_year, set([2, 4, 6]))
 | 
	
		
			
				|  |  | +        c = crontab(month_of_year='*/2')
 | 
	
		
			
				|  |  | +        self.assertEqual(c.month_of_year, set([1, 3, 5, 7, 9, 11]))
 | 
	
		
			
				|  |  | +        c = crontab(month_of_year='2-12/2')
 | 
	
		
			
				|  |  | +        self.assertEqual(c.month_of_year, set([2, 4, 6, 8, 10, 12]))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def test_crontab_spec_invalid_moy(self):
 | 
	
		
			
				|  |  | +        with self.assertRaises(ValueError):
 | 
	
		
			
				|  |  | +            crontab(month_of_year=0)
 | 
	
		
			
				|  |  | +        with self.assertRaises(ValueError):
 | 
	
		
			
				|  |  | +            crontab(month_of_year='0-5')
 | 
	
		
			
				|  |  | +        with self.assertRaises(ValueError):
 | 
	
		
			
				|  |  | +            crontab(month_of_year=13)
 | 
	
		
			
				|  |  | +        with self.assertRaises(ValueError):
 | 
	
		
			
				|  |  | +            crontab(month_of_year='12,13')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def seconds_almost_equal(self, a, b, precision):
 | 
	
		
			
				|  |  | +        for index, skew in enumerate((+0.1, 0, -0.1)):
 | 
	
		
			
				|  |  | +            try:
 | 
	
		
			
				|  |  | +                self.assertAlmostEqual(a, b + skew, precision)
 | 
	
		
			
				|  |  | +            except AssertionError:
 | 
	
		
			
				|  |  | +                if index + 1 >= 3:
 | 
	
		
			
				|  |  | +                    raise
 | 
	
		
			
				|  |  | +            else:
 | 
	
		
			
				|  |  | +                break
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def test_every_minute_execution_is_due(self):
 | 
	
		
			
				|  |  |          last_ran = self.now - timedelta(seconds=61)
 | 
	
		
			
				|  |  |          due, remaining = every_minute.run_every.is_due(last_ran)
 | 
	
	
		
			
				|  | @@ -897,3 +1112,31 @@ class test_crontab_is_due(Case):
 | 
	
		
			
				|  |  |                  datetime(2010, 5, 6, 7, 30))
 | 
	
		
			
				|  |  |          self.assertFalse(due)
 | 
	
		
			
				|  |  |          self.assertEqual(remaining, 6 * 24 * 60 * 60 - 3 * 60 * 60)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @patch_crontab_nowfun(monthly, datetime(2010, 5, 13, 7, 30))
 | 
	
		
			
				|  |  | +    def test_monthly_execution_is_due(self):
 | 
	
		
			
				|  |  | +        due, remaining = monthly.run_every.is_due(
 | 
	
		
			
				|  |  | +                datetime(2010, 4, 8, 7, 30))
 | 
	
		
			
				|  |  | +        self.assertTrue(due)
 | 
	
		
			
				|  |  | +        self.assertEqual(remaining, 28 * 24 * 60 * 60)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @patch_crontab_nowfun(monthly, datetime(2010, 5, 9, 10, 30))
 | 
	
		
			
				|  |  | +    def test_monthly_execution_is_not_due(self):
 | 
	
		
			
				|  |  | +        due, remaining = monthly.run_every.is_due(
 | 
	
		
			
				|  |  | +                datetime(2010, 4, 8, 7, 30))
 | 
	
		
			
				|  |  | +        self.assertFalse(due)
 | 
	
		
			
				|  |  | +        self.assertEqual(remaining, 4 * 24 * 60 * 60 - 3 * 60 * 60)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @patch_crontab_nowfun(yearly, datetime(2010, 3, 11, 7, 30))
 | 
	
		
			
				|  |  | +    def test_yearly_execution_is_due(self):
 | 
	
		
			
				|  |  | +        due, remaining = yearly.run_every.is_due(
 | 
	
		
			
				|  |  | +                datetime(2009, 3, 12, 7, 30))
 | 
	
		
			
				|  |  | +        self.assertTrue(due)
 | 
	
		
			
				|  |  | +        self.assertEqual(remaining, 364 * 24 * 60 * 60)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @patch_crontab_nowfun(yearly, datetime(2010, 3, 7, 10, 30))
 | 
	
		
			
				|  |  | +    def test_yearly_execution_is_not_due(self):
 | 
	
		
			
				|  |  | +        due, remaining = yearly.run_every.is_due(
 | 
	
		
			
				|  |  | +                datetime(2009, 3, 12, 7, 30))
 | 
	
		
			
				|  |  | +        self.assertFalse(due)
 | 
	
		
			
				|  |  | +        self.assertEqual(remaining, 4 * 24 * 60 * 60 - 3 * 60 * 60)
 |