183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh"""Calendar printing functions
283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehNote when comparing these calendars to the ones printed by cal(1): By
483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdefault, these calendars have Monday as the first day of the week, and
583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehSunday as the last (the European convention). Use setfirstweekday() to
683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehset the first day of the week (0=Monday, 6=Sunday)."""
783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport sys
983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport datetime
1083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport locale as _locale
1183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
1283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh__all__ = ["IllegalMonthError", "IllegalWeekdayError", "setfirstweekday",
1383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh           "firstweekday", "isleap", "leapdays", "weekday", "monthrange",
1483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh           "monthcalendar", "prmonth", "month", "prcal", "calendar",
1583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh           "timegm", "month_name", "month_abbr", "day_name", "day_abbr"]
1683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
1783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Exception raised for bad input (with string parameter for details)
1883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieherror = ValueError
1983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Exceptions raised for bad input
2183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass IllegalMonthError(ValueError):
2283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, month):
2383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.month = month
2483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __str__(self):
2583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return "bad month number %r; must be 1-12" % self.month
2683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass IllegalWeekdayError(ValueError):
2983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, weekday):
3083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.weekday = weekday
3183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __str__(self):
3283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return "bad weekday number %r; must be 0 (Monday) to 6 (Sunday)" % self.weekday
3383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
3483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
3583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Constants for months referenced later
3683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehJanuary = 1
3783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehFebruary = 2
3883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
3983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Number of days per month (except for February in leap years)
4083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehmdays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
4183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
4283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# This module used to have hard-coded lists of day and month names, as
4383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# English strings.  The classes following emulate a read-only version of
4483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# that, but supply localized names.  Note that the values are computed
4583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# fresh on each call, in case the user changes locale between calls.
4683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
4783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass _localized_month:
4883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
4983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    _months = [datetime.date(2001, i+1, 1).strftime for i in range(12)]
5083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    _months.insert(0, lambda x: "")
5183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
5283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, format):
5383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.format = format
5483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
5583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __getitem__(self, i):
5683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        funcs = self._months[i]
5783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if isinstance(i, slice):
5883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return [f(self.format) for f in funcs]
5983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
6083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return funcs(self.format)
6183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
6283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __len__(self):
6383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return 13
6483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
6583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
6683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass _localized_day:
6783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
6883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # January 1, 2001, was a Monday.
6983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    _days = [datetime.date(2001, 1, i+1).strftime for i in range(7)]
7083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
7183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, format):
7283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.format = format
7383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
7483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __getitem__(self, i):
7583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        funcs = self._days[i]
7683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if isinstance(i, slice):
7783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return [f(self.format) for f in funcs]
7883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
7983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return funcs(self.format)
8083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
8183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __len__(self):
8283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return 7
8383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
8483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
8583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Full and abbreviated names of weekdays
8683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehday_name = _localized_day('%A')
8783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehday_abbr = _localized_day('%a')
8883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
8983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Full and abbreviated names of months (1-based arrays!!!)
9083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehmonth_name = _localized_month('%B')
9183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehmonth_abbr = _localized_month('%b')
9283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
9383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Constants for weekdays
9483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)
9583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
9683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
9783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef isleap(year):
9883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Return True for leap years, False for non-leap years."""
9983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
10083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
10183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
10283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef leapdays(y1, y2):
10383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Return number of leap years in range [y1, y2).
10483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh       Assume y1 <= y2."""
10583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    y1 -= 1
10683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    y2 -= 1
10783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return (y2//4 - y1//4) - (y2//100 - y1//100) + (y2//400 - y1//400)
10883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
10983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef weekday(year, month, day):
11183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12),
11283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh       day (1-31)."""
11383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return datetime.date(year, month, day).weekday()
11483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef monthrange(year, month):
11783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for
11883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh       year, month."""
11983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if not 1 <= month <= 12:
12083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise IllegalMonthError(month)
12183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    day1 = weekday(year, month, 1)
12283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ndays = mdays[month] + (month == February and isleap(year))
12383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return day1, ndays
12483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
12583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
12683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass Calendar(object):
12783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
12883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Base calendar class. This class doesn't do any formatting. It simply
12983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    provides data to subclasses.
13083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
13183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
13283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, firstweekday=0):
13383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.firstweekday = firstweekday # 0 = Monday, 6 = Sunday
13483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
13583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getfirstweekday(self):
13683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._firstweekday % 7
13783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
13883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def setfirstweekday(self, firstweekday):
13983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._firstweekday = firstweekday
14083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
14183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    firstweekday = property(getfirstweekday, setfirstweekday)
14283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
14383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def iterweekdays(self):
14483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
14583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return a iterator for one week of weekday numbers starting with the
14683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        configured first one.
14783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
14883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for i in range(self.firstweekday, self.firstweekday + 7):
14983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            yield i%7
15083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
15183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def itermonthdates(self, year, month):
15283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
15383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return an iterator for one month. The iterator will yield datetime.date
15483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        values and will always iterate through complete weeks, so it will yield
15583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dates outside the specified month.
15683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
15783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        date = datetime.date(year, month, 1)
15883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # Go back to the beginning of the week
15983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        days = (date.weekday() - self.firstweekday) % 7
16083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        date -= datetime.timedelta(days=days)
16183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        oneday = datetime.timedelta(days=1)
16283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        while True:
16383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            yield date
16483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            try:
16583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                date += oneday
16683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            except OverflowError:
16783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                # Adding one day could fail after datetime.MAXYEAR
16883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                break
16983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if date.month != month and date.weekday() == self.firstweekday:
17083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                break
17183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
17283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def itermonthdays2(self, year, month):
17383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
17483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Like itermonthdates(), but will yield (day number, weekday number)
17583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        tuples. For days outside the specified month the day number is 0.
17683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
17783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for date in self.itermonthdates(year, month):
17883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if date.month != month:
17983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                yield (0, date.weekday())
18083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            else:
18183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                yield (date.day, date.weekday())
18283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
18383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def itermonthdays(self, year, month):
18483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
18583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Like itermonthdates(), but will yield day numbers. For days outside
18683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        the specified month the day number is 0.
18783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
18883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for date in self.itermonthdates(year, month):
18983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if date.month != month:
19083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                yield 0
19183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            else:
19283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                yield date.day
19383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
19483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def monthdatescalendar(self, year, month):
19583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
19683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return a matrix (list of lists) representing a month's calendar.
19783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Each row represents a week; week entries are datetime.date values.
19883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
19983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dates = list(self.itermonthdates(year, month))
20083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return [ dates[i:i+7] for i in range(0, len(dates), 7) ]
20183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
20283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def monthdays2calendar(self, year, month):
20383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
20483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return a matrix representing a month's calendar.
20583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Each row represents a week; week entries are
20683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        (day number, weekday number) tuples. Day numbers outside this month
20783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        are zero.
20883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
20983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        days = list(self.itermonthdays2(year, month))
21083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return [ days[i:i+7] for i in range(0, len(days), 7) ]
21183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
21283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def monthdayscalendar(self, year, month):
21383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
21483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return a matrix representing a month's calendar.
21583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Each row represents a week; days outside this month are zero.
21683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
21783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        days = list(self.itermonthdays(year, month))
21883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return [ days[i:i+7] for i in range(0, len(days), 7) ]
21983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
22083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def yeardatescalendar(self, year, width=3):
22183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
22283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return the data for the specified year ready for formatting. The return
22383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        value is a list of month rows. Each month row contains upto width months.
22483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Each month contains between 4 and 6 weeks and each week contains 1-7
22583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        days. Days are datetime.date objects.
22683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
22783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        months = [
22883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.monthdatescalendar(year, i)
22983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            for i in range(January, January+12)
23083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        ]
23183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return [months[i:i+width] for i in range(0, len(months), width) ]
23283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
23383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def yeardays2calendar(self, year, width=3):
23483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
23583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return the data for the specified year ready for formatting (similar to
23683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        yeardatescalendar()). Entries in the week lists are
23783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        (day number, weekday number) tuples. Day numbers outside this month are
23883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        zero.
23983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
24083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        months = [
24183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.monthdays2calendar(year, i)
24283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            for i in range(January, January+12)
24383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        ]
24483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return [months[i:i+width] for i in range(0, len(months), width) ]
24583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
24683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def yeardayscalendar(self, year, width=3):
24783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
24883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return the data for the specified year ready for formatting (similar to
24983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        yeardatescalendar()). Entries in the week lists are day numbers.
25083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Day numbers outside this month are zero.
25183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
25283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        months = [
25383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.monthdayscalendar(year, i)
25483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            for i in range(January, January+12)
25583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        ]
25683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return [months[i:i+width] for i in range(0, len(months), width) ]
25783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
25883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
25983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass TextCalendar(Calendar):
26083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
26183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Subclass of Calendar that outputs a calendar as a simple plain text
26283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    similar to the UNIX program cal.
26383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
26483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
26583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def prweek(self, theweek, width):
26683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
26783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Print a single week (no newline).
26883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
26983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        print self.formatweek(theweek, width),
27083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
27183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatday(self, day, weekday, width):
27283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
27383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Returns a formatted day.
27483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
27583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if day == 0:
27683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            s = ''
27783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
27883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            s = '%2i' % day             # right-align single-digit days
27983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return s.center(width)
28083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
28183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatweek(self, theweek, width):
28283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
28383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Returns a single week in a string (no newline).
28483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
28583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return ' '.join(self.formatday(d, wd, width) for (d, wd) in theweek)
28683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
28783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatweekday(self, day, width):
28883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
28983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Returns a formatted week day name.
29083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
29183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if width >= 9:
29283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            names = day_name
29383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
29483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            names = day_abbr
29583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return names[day][:width].center(width)
29683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
29783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatweekheader(self, width):
29883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
29983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return a header for a week.
30083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
30183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return ' '.join(self.formatweekday(i, width) for i in self.iterweekdays())
30283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
30383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatmonthname(self, theyear, themonth, width, withyear=True):
30483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
30583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return a formatted month name.
30683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
30783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        s = month_name[themonth]
30883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if withyear:
30983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            s = "%s %r" % (s, theyear)
31083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return s.center(width)
31183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
31283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def prmonth(self, theyear, themonth, w=0, l=0):
31383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
31483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Print a month's calendar.
31583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
31683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        print self.formatmonth(theyear, themonth, w, l),
31783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
31883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatmonth(self, theyear, themonth, w=0, l=0):
31983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
32083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return a month's calendar string (multi-line).
32183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
32283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        w = max(2, w)
32383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        l = max(1, l)
32483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1)
32583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        s = s.rstrip()
32683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        s += '\n' * l
32783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        s += self.formatweekheader(w).rstrip()
32883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        s += '\n' * l
32983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for week in self.monthdays2calendar(theyear, themonth):
33083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            s += self.formatweek(week, w).rstrip()
33183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            s += '\n' * l
33283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return s
33383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
33483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatyear(self, theyear, w=2, l=1, c=6, m=3):
33583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
33683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Returns a year's calendar as a multi-line string.
33783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
33883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        w = max(2, w)
33983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        l = max(1, l)
34083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        c = max(2, c)
34183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        colwidth = (w + 1) * 7 - 1
34283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        v = []
34383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a = v.append
34483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip())
34583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('\n'*l)
34683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        header = self.formatweekheader(w)
34783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for (i, row) in enumerate(self.yeardays2calendar(theyear, m)):
34883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # months in this row
34983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            months = range(m*i+1, min(m*(i+1)+1, 13))
35083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            a('\n'*l)
35183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            names = (self.formatmonthname(theyear, k, colwidth, False)
35283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                     for k in months)
35383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            a(formatstring(names, colwidth, c).rstrip())
35483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            a('\n'*l)
35583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            headers = (header for k in months)
35683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            a(formatstring(headers, colwidth, c).rstrip())
35783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            a('\n'*l)
35883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # max number of weeks for this row
35983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            height = max(len(cal) for cal in row)
36083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            for j in range(height):
36183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                weeks = []
36283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                for cal in row:
36383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    if j >= len(cal):
36483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                        weeks.append('')
36583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    else:
36683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                        weeks.append(self.formatweek(cal[j], w))
36783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                a(formatstring(weeks, colwidth, c).rstrip())
36883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                a('\n' * l)
36983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return ''.join(v)
37083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
37183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def pryear(self, theyear, w=0, l=0, c=6, m=3):
37283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """Print a year's calendar."""
37383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        print self.formatyear(theyear, w, l, c, m)
37483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
37583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
37683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass HTMLCalendar(Calendar):
37783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
37883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    This calendar returns complete HTML pages.
37983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
38083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
38183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # CSS classes for the day <td>s
38283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    cssclasses = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
38383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
38483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatday(self, day, weekday):
38583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
38683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return a day as a table cell.
38783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
38883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if day == 0:
38983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return '<td class="noday">&nbsp;</td>' # day outside month
39083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
39183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return '<td class="%s">%d</td>' % (self.cssclasses[weekday], day)
39283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
39383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatweek(self, theweek):
39483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
39583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return a complete week as a table row.
39683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
39783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        s = ''.join(self.formatday(d, wd) for (d, wd) in theweek)
39883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return '<tr>%s</tr>' % s
39983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
40083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatweekday(self, day):
40183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
40283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return a weekday name as a table header.
40383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
40483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return '<th class="%s">%s</th>' % (self.cssclasses[day], day_abbr[day])
40583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
40683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatweekheader(self):
40783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
40883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return a header for a week as a table row.
40983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
41083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        s = ''.join(self.formatweekday(i) for i in self.iterweekdays())
41183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return '<tr>%s</tr>' % s
41283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
41383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatmonthname(self, theyear, themonth, withyear=True):
41483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
41583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return a month name as a table row.
41683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
41783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if withyear:
41883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            s = '%s %s' % (month_name[themonth], theyear)
41983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
42083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            s = '%s' % month_name[themonth]
42183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return '<tr><th colspan="7" class="month">%s</th></tr>' % s
42283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
42383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatmonth(self, theyear, themonth, withyear=True):
42483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
42583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return a formatted month as a table.
42683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
42783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        v = []
42883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a = v.append
42983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('<table border="0" cellpadding="0" cellspacing="0" class="month">')
43083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('\n')
43183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a(self.formatmonthname(theyear, themonth, withyear=withyear))
43283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('\n')
43383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a(self.formatweekheader())
43483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('\n')
43583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for week in self.monthdays2calendar(theyear, themonth):
43683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            a(self.formatweek(week))
43783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            a('\n')
43883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('</table>')
43983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('\n')
44083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return ''.join(v)
44183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
44283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatyear(self, theyear, width=3):
44383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
44483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return a formatted year as a table of tables.
44583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
44683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        v = []
44783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a = v.append
44883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        width = max(width, 1)
44983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('<table border="0" cellpadding="0" cellspacing="0" class="year">')
45083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('\n')
45183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('<tr><th colspan="%d" class="year">%s</th></tr>' % (width, theyear))
45283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for i in range(January, January+12, width):
45383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # months in this row
45483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            months = range(i, min(i+width, 13))
45583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            a('<tr>')
45683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            for m in months:
45783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                a('<td>')
45883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                a(self.formatmonth(theyear, m, withyear=False))
45983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                a('</td>')
46083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            a('</tr>')
46183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('</table>')
46283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return ''.join(v)
46383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
46483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None):
46583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
46683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Return a formatted year as a complete HTML page.
46783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
46883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if encoding is None:
46983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            encoding = sys.getdefaultencoding()
47083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        v = []
47183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a = v.append
47283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('<?xml version="1.0" encoding="%s"?>\n' % encoding)
47383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n')
47483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('<html>\n')
47583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('<head>\n')
47683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('<meta http-equiv="Content-Type" content="text/html; charset=%s" />\n' % encoding)
47783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if css is not None:
47883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            a('<link rel="stylesheet" type="text/css" href="%s" />\n' % css)
47983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('<title>Calendar for %d</title>\n' % theyear)
48083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('</head>\n')
48183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('<body>\n')
48283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a(self.formatyear(theyear, width))
48383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('</body>\n')
48483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a('</html>\n')
48583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return ''.join(v).encode(encoding, "xmlcharrefreplace")
48683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
48783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
48883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass TimeEncoding:
48983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, locale):
49083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.locale = locale
49183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
49283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __enter__(self):
49383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.oldlocale = _locale.getlocale(_locale.LC_TIME)
49483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _locale.setlocale(_locale.LC_TIME, self.locale)
49583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return _locale.getlocale(_locale.LC_TIME)[1]
49683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
49783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __exit__(self, *args):
49883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _locale.setlocale(_locale.LC_TIME, self.oldlocale)
49983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
50083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
50183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass LocaleTextCalendar(TextCalendar):
50283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
50383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    This class can be passed a locale name in the constructor and will return
50483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    month and weekday names in the specified locale. If this locale includes
50583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    an encoding all strings containing month and weekday names will be returned
50683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    as unicode.
50783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
50883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
50983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, firstweekday=0, locale=None):
51083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        TextCalendar.__init__(self, firstweekday)
51183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if locale is None:
51283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            locale = _locale.getdefaultlocale()
51383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.locale = locale
51483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
51583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatweekday(self, day, width):
51683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        with TimeEncoding(self.locale) as encoding:
51783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if width >= 9:
51883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                names = day_name
51983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            else:
52083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                names = day_abbr
52183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            name = names[day]
52283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if encoding is not None:
52383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                name = name.decode(encoding)
52483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return name[:width].center(width)
52583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
52683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatmonthname(self, theyear, themonth, width, withyear=True):
52783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        with TimeEncoding(self.locale) as encoding:
52883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            s = month_name[themonth]
52983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if encoding is not None:
53083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                s = s.decode(encoding)
53183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if withyear:
53283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                s = "%s %r" % (s, theyear)
53383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return s.center(width)
53483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
53583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
53683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass LocaleHTMLCalendar(HTMLCalendar):
53783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
53883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    This class can be passed a locale name in the constructor and will return
53983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    month and weekday names in the specified locale. If this locale includes
54083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    an encoding all strings containing month and weekday names will be returned
54183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    as unicode.
54283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
54383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, firstweekday=0, locale=None):
54483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        HTMLCalendar.__init__(self, firstweekday)
54583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if locale is None:
54683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            locale = _locale.getdefaultlocale()
54783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.locale = locale
54883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
54983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatweekday(self, day):
55083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        with TimeEncoding(self.locale) as encoding:
55183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            s = day_abbr[day]
55283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if encoding is not None:
55383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                s = s.decode(encoding)
55483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return '<th class="%s">%s</th>' % (self.cssclasses[day], s)
55583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
55683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def formatmonthname(self, theyear, themonth, withyear=True):
55783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        with TimeEncoding(self.locale) as encoding:
55883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            s = month_name[themonth]
55983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if encoding is not None:
56083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                s = s.decode(encoding)
56183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if withyear:
56283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                s = '%s %s' % (s, theyear)
56383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return '<tr><th colspan="7" class="month">%s</th></tr>' % s
56483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
56583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
56683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Support for old module level interface
56783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehc = TextCalendar()
56883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
56983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehfirstweekday = c.getfirstweekday
57083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
57183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef setfirstweekday(firstweekday):
57283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    try:
57383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        firstweekday.__index__
57483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    except AttributeError:
57583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise IllegalWeekdayError(firstweekday)
57683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if not MONDAY <= firstweekday <= SUNDAY:
57783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise IllegalWeekdayError(firstweekday)
57883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    c.firstweekday = firstweekday
57983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
58083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehmonthcalendar = c.monthdayscalendar
58183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehprweek = c.prweek
58283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehweek = c.formatweek
58383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehweekheader = c.formatweekheader
58483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehprmonth = c.prmonth
58583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehmonth = c.formatmonth
58683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehcalendar = c.formatyear
58783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehprcal = c.pryear
58883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
58983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
59083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Spacing of month columns for multi-column year calendar
59183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh_colwidth = 7*3 - 1         # Amount printed by prweek()
59283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh_spacing = 6                # Number of spaces between columns
59383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
59483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
59583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef format(cols, colwidth=_colwidth, spacing=_spacing):
59683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Prints multi-column formatting for year calendars"""
59783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    print formatstring(cols, colwidth, spacing)
59883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
59983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
60083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef formatstring(cols, colwidth=_colwidth, spacing=_spacing):
60183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Returns a string formatted from n strings, centered within n columns."""
60283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    spacing *= ' '
60383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return spacing.join(c.center(colwidth) for c in cols)
60483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
60583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
60683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehEPOCH = 1970
60783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh_EPOCH_ORD = datetime.date(EPOCH, 1, 1).toordinal()
60883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
60983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
61083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef timegm(tuple):
61183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Unrelated but handy function to calculate Unix timestamp from GMT."""
61283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    year, month, day, hour, minute, second = tuple[:6]
61383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    days = datetime.date(year, month, 1).toordinal() - _EPOCH_ORD + day - 1
61483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    hours = days*24 + hour
61583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    minutes = hours*60 + minute
61683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    seconds = minutes*60 + second
61783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return seconds
61883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
61983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
62083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef main(args):
62183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    import optparse
62283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    parser = optparse.OptionParser(usage="usage: %prog [options] [year [month]]")
62383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    parser.add_option(
62483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        "-w", "--width",
62583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dest="width", type="int", default=2,
62683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        help="width of date column (default 2, text only)"
62783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    )
62883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    parser.add_option(
62983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        "-l", "--lines",
63083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dest="lines", type="int", default=1,
63183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        help="number of lines for each week (default 1, text only)"
63283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    )
63383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    parser.add_option(
63483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        "-s", "--spacing",
63583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dest="spacing", type="int", default=6,
63683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        help="spacing between months (default 6, text only)"
63783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    )
63883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    parser.add_option(
63983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        "-m", "--months",
64083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dest="months", type="int", default=3,
64183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        help="months per row (default 3, text only)"
64283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    )
64383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    parser.add_option(
64483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        "-c", "--css",
64583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dest="css", default="calendar.css",
64683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        help="CSS to use for page (html only)"
64783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    )
64883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    parser.add_option(
64983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        "-L", "--locale",
65083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dest="locale", default=None,
65183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        help="locale to be used from month and weekday names"
65283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    )
65383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    parser.add_option(
65483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        "-e", "--encoding",
65583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dest="encoding", default=None,
65683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        help="Encoding to use for output"
65783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    )
65883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    parser.add_option(
65983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        "-t", "--type",
66083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dest="type", default="text",
66183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        choices=("text", "html"),
66283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        help="output type (text or html)"
66383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    )
66483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
66583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    (options, args) = parser.parse_args(args)
66683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
66783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if options.locale and not options.encoding:
66883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        parser.error("if --locale is specified --encoding is required")
66983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        sys.exit(1)
67083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
67183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    locale = options.locale, options.encoding
67283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
67383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if options.type == "html":
67483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if options.locale:
67583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            cal = LocaleHTMLCalendar(locale=locale)
67683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
67783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            cal = HTMLCalendar()
67883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        encoding = options.encoding
67983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if encoding is None:
68083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            encoding = sys.getdefaultencoding()
68183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        optdict = dict(encoding=encoding, css=options.css)
68283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if len(args) == 1:
68383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            print cal.formatyearpage(datetime.date.today().year, **optdict)
68483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif len(args) == 2:
68583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            print cal.formatyearpage(int(args[1]), **optdict)
68683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
68783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            parser.error("incorrect number of arguments")
68883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            sys.exit(1)
68983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    else:
69083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if options.locale:
69183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            cal = LocaleTextCalendar(locale=locale)
69283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
69383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            cal = TextCalendar()
69483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        optdict = dict(w=options.width, l=options.lines)
69583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if len(args) != 3:
69683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            optdict["c"] = options.spacing
69783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            optdict["m"] = options.months
69883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if len(args) == 1:
69983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            result = cal.formatyear(datetime.date.today().year, **optdict)
70083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif len(args) == 2:
70183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            result = cal.formatyear(int(args[1]), **optdict)
70283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif len(args) == 3:
70383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            result = cal.formatmonth(int(args[1]), int(args[2]), **optdict)
70483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
70583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            parser.error("incorrect number of arguments")
70683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            sys.exit(1)
70783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if options.encoding:
70883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            result = result.encode(options.encoding)
70983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        print result
71083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
71183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
71283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehif __name__ == "__main__":
71383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    main(sys.argv)
714