changeset: 104121:f2aff898f7c8 branch: 2.7 parent: 104099:637ce96423ef user: Alexander Belopolsky date: Tue Sep 27 22:45:20 2016 -0400 files: Lib/calendar.py Lib/test/test_calendar.py Misc/NEWS description: Issue #28253: Fixed calendar functions for extreme months: 0001-01 and 9999-12. Methods itermonthdays() and itermonthdays2() are reimplemented so that they don't call itermonthdates() which can cause datetime.date under/overflow. diff -r 637ce96423ef -r f2aff898f7c8 Lib/calendar.py --- a/Lib/calendar.py Tue Sep 27 15:24:13 2016 +0300 +++ b/Lib/calendar.py Tue Sep 27 22:45:20 2016 -0400 @@ -174,22 +174,23 @@ Like itermonthdates(), but will yield (day number, weekday number) tuples. For days outside the specified month the day number is 0. """ - for date in self.itermonthdates(year, month): - if date.month != month: - yield (0, date.weekday()) - else: - yield (date.day, date.weekday()) + for i, d in enumerate(self.itermonthdays(year, month), self.firstweekday): + yield d, i % 7 def itermonthdays(self, year, month): """ Like itermonthdates(), but will yield day numbers. For days outside the specified month the day number is 0. """ - for date in self.itermonthdates(year, month): - if date.month != month: - yield 0 - else: - yield date.day + day1, ndays = monthrange(year, month) + days_before = (day1 - self.firstweekday) % 7 + for _ in range(days_before): + yield 0 + for d in range(1, ndays + 1): + yield d + days_after = (self.firstweekday - day1 - ndays) % 7 + for _ in range(days_after): + yield 0 def monthdatescalendar(self, year, month): """ diff -r 637ce96423ef -r f2aff898f7c8 Lib/test/test_calendar.py --- a/Lib/test/test_calendar.py Tue Sep 27 15:24:13 2016 +0300 +++ b/Lib/test/test_calendar.py Tue Sep 27 22:45:20 2016 -0400 @@ -291,6 +291,27 @@ # see #15421 list(calendar.Calendar().itermonthdates(datetime.MAXYEAR, 12)) + def test_itermonthdays(self): + for firstweekday in range(7): + cal = calendar.Calendar(firstweekday) + # Test the extremes, see #28253 and #26650 + for y, m in [(1, 1), (9999, 12)]: + days = list(cal.itermonthdays(y, m)) + self.assertIn(len(days), (35, 42)) + # Test a short month + cal = calendar.Calendar(firstweekday=3) + days = list(cal.itermonthdays(2001, 2)) + self.assertEqual(days, list(range(1, 29))) + + def test_itermonthdays2(self): + for firstweekday in range(7): + cal = calendar.Calendar(firstweekday) + # Test the extremes, see #28253 and #26650 + for y, m in [(1, 1), (9999, 12)]: + days = list(cal.itermonthdays2(y, m)) + self.assertEqual(days[0][1], firstweekday) + self.assertEqual(days[-1][1], (firstweekday - 1) % 7) + class MonthCalendarTestCase(unittest.TestCase): def setUp(self): diff -r 637ce96423ef -r f2aff898f7c8 Misc/NEWS --- a/Misc/NEWS Tue Sep 27 15:24:13 2016 +0300 +++ b/Misc/NEWS Tue Sep 27 22:45:20 2016 -0400 @@ -42,6 +42,13 @@ Library ------- +- Issue #28253: Fixed calendar functions for extreme months: 0001-01 + and 9999-12. + + Methods itermonthdays() and itermonthdays2() are reimplemented so + that they don't call itermonthdates() which can cause datetime.date + under/overflow. + - Issue #27963: Fixed possible null pointer dereference in ctypes.set_conversion_mode(). Patch by Xiang Zhang.