diff --git a/Lib/calendar.py b/Lib/calendar.py index 07594f3a8..7550d52c0 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -111,8 +111,9 @@ def leapdays(y1, y2): def weekday(year, month, day): - """Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12), - day (1-31).""" + """Return weekday (0-6 ~ Mon-Sun) for year, month (1-12), day (1-31).""" + if not datetime.MINYEAR <= year <= datetime.MAXYEAR: + year = 2000 + year % 400 return datetime.date(year, month, day).weekday() @@ -126,6 +127,24 @@ def monthrange(year, month): return day1, ndays +def _monthlen(year, month): + return mdays[month] + (month == February and isleap(year)) + + +def _prevmonth(year, month): + if month == 1: + return year-1, 12 + else: + return year, month-1 + + +def _nextmonth(year, month): + if month == 12: + return year+1, 1 + else: + return year, month+1 + + class Calendar(object): """ Base calendar class. This class doesn't do any formatting. It simply @@ -157,28 +176,8 @@ class Calendar(object): values and will always iterate through complete weeks, so it will yield dates outside the specified month. """ - date = datetime.date(year, month, 1) - # Go back to the beginning of the week - days = (date.weekday() - self.firstweekday) % 7 - date -= datetime.timedelta(days=days) - oneday = datetime.timedelta(days=1) - while True: - yield date - try: - date += oneday - except OverflowError: - # Adding one day could fail after datetime.MAXYEAR - break - if date.month != month and date.weekday() == self.firstweekday: - break - - def itermonthdays2(self, year, month): - """ - Like itermonthdates(), but will yield (day number, weekday number) - tuples. For days outside the specified month the day number is 0. - """ - for i, d in enumerate(self.itermonthdays(year, month), self.firstweekday): - yield d, i % 7 + for y, m, d in self.itermonthdays3(year, month): + yield datetime.date(y, m, d) def itermonthdays(self, year, month): """ @@ -192,6 +191,40 @@ class Calendar(object): days_after = (self.firstweekday - day1 - ndays) % 7 yield from repeat(0, days_after) + def itermonthdays2(self, year, month): + """ + Like itermonthdates(), but will yield (day number, weekday number) + tuples. For days outside the specified month the day number is 0. + """ + for i, d in enumerate(self.itermonthdays(year, month), self.firstweekday): + yield d, i % 7 + + def itermonthdays3(self, year, month): + """ + Like itermonthdates(), but will yield (year, month, day) tuples. Can be + used for dates outside of datetime.date range. + """ + day1, ndays = monthrange(year, month) + days_before = (day1 - self.firstweekday) % 7 + days_after = (self.firstweekday - day1 - ndays) % 7 + y, m = _prevmonth(year, month) + end = _monthlen(y, m) + 1 + for d in range(end-days_before, end): + yield y, m, d + for d in range(1, ndays + 1): + yield year, month, d + y, m = _nextmonth(year, month) + for d in range(1, days_after + 1): + yield y, m, d + + def itermonthdays4(self, year, month): + """ + Like itermonthdates(), but will yield (year, month, day, day_of_week) tuples. + Can be used for dates outside of datetime.date range. + """ + for i, (y, m, d) in enumerate(self.itermonthdays3(year, month)): + yield y, m, d, (self.firstweekday + i) % 7 + def monthdatescalendar(self, year, month): """ Return a matrix (list of lists) representing a month's calendar. @@ -267,7 +300,7 @@ class TextCalendar(Calendar): """ Print a single week (no newline). """ - print(self.formatweek(theweek, width), end=' ') + print(self.formatweek(theweek, width), end='') def formatday(self, day, weekday, width): """ @@ -371,7 +404,7 @@ class TextCalendar(Calendar): def pryear(self, theyear, w=0, l=0, c=6, m=3): """Print a year's calendar.""" - print(self.formatyear(theyear, w, l, c, m)) + print(self.formatyear(theyear, w, l, c, m), end='') class HTMLCalendar(Calendar): @@ -382,12 +415,31 @@ class HTMLCalendar(Calendar): # CSS classes for the day s cssclasses = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] + # CSS classes for the day s + cssclasses_weekday_head = cssclasses + + # CSS class for the days before and after current month + cssclass_noday = "noday" + + # CSS class for the month's head + cssclass_month_head = "month" + + # CSS class for the month + cssclass_month = "month" + + # CSS class for the year's table head + cssclass_year_head = "year" + + # CSS class for the whole year table + cssclass_year = "year" + def formatday(self, day, weekday): """ Return a day as a table cell. """ if day == 0: - return ' ' # day outside month + # day outside month + return ' ' % self.cssclass_noday else: return '%d' % (self.cssclasses[weekday], day) @@ -402,7 +454,8 @@ class HTMLCalendar(Calendar): """ Return a weekday name as a table header. """ - return '%s' % (self.cssclasses[day], day_abbr[day]) + return '%s' % ( + self.cssclasses_weekday_head[day], day_abbr[day]) def formatweekheader(self): """ @@ -419,7 +472,8 @@ class HTMLCalendar(Calendar): s = '%s %s' % (month_name[themonth], theyear) else: s = '%s' % month_name[themonth] - return '%s' % s + return '%s' % ( + self.cssclass_month_head, s) def formatmonth(self, theyear, themonth, withyear=True): """ @@ -427,7 +481,8 @@ class HTMLCalendar(Calendar): """ v = [] a = v.append - a('') + a('
' % ( + self.cssclass_month)) a('\n') a(self.formatmonthname(theyear, themonth, withyear=withyear)) a('\n') @@ -447,9 +502,11 @@ class HTMLCalendar(Calendar): v = [] a = v.append width = max(width, 1) - a('
') + a('
' % + self.cssclass_year) a('\n') - a('' % (width, theyear)) + a('' % ( + width, self.cssclass_year_head, theyear)) for i in range(January, January+12, width): # months in this row months = range(i, min(i+width, 13))
%s
%s