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.
This commit is contained in:
Alexander Belopolsky 2016-09-27 20:27:55 -04:00
commit e3fd248c77
2 changed files with 30 additions and 10 deletions

View File

@ -8,6 +8,7 @@ set the first day of the week (0=Monday, 6=Sunday)."""
import sys import sys
import datetime import datetime
import locale as _locale import locale as _locale
from itertools import repeat
__all__ = ["IllegalMonthError", "IllegalWeekdayError", "setfirstweekday", __all__ = ["IllegalMonthError", "IllegalWeekdayError", "setfirstweekday",
"firstweekday", "isleap", "leapdays", "weekday", "monthrange", "firstweekday", "isleap", "leapdays", "weekday", "monthrange",
@ -176,22 +177,20 @@ class Calendar(object):
Like itermonthdates(), but will yield (day number, weekday number) Like itermonthdates(), but will yield (day number, weekday number)
tuples. For days outside the specified month the day number is 0. tuples. For days outside the specified month the day number is 0.
""" """
for date in self.itermonthdates(year, month): for i, d in enumerate(self.itermonthdays(year, month), self.firstweekday):
if date.month != month: yield d, i % 7
yield (0, date.weekday())
else:
yield (date.day, date.weekday())
def itermonthdays(self, year, month): def itermonthdays(self, year, month):
""" """
Like itermonthdates(), but will yield day numbers. For days outside Like itermonthdates(), but will yield day numbers. For days outside
the specified month the day number is 0. the specified month the day number is 0.
""" """
for date in self.itermonthdates(year, month): day1, ndays = monthrange(year, month)
if date.month != month: days_before = (day1 - self.firstweekday) % 7
yield 0 yield from repeat(0, days_before)
else: yield from range(1, ndays + 1)
yield date.day days_after = (self.firstweekday - day1 - ndays) % 7
yield from repeat(0, days_after)
def monthdatescalendar(self, year, month): def monthdatescalendar(self, year, month):
""" """

View File

@ -502,6 +502,27 @@ class CalendarTestCase(unittest.TestCase):
# see #15421 # see #15421
list(calendar.Calendar().itermonthdates(datetime.MAXYEAR, 12)) 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): class MonthCalendarTestCase(unittest.TestCase):
def setUp(self): def setUp(self):