calendar printing functions note when comparing these calendars to the ones printed by cal1 by default these calendars have monday as the first day of the week and sunday as the last the european convention use setfirstweekday to set the first day of the week 0monday 6sunday import sys import datetime from enum import intenum globalenum import locale as locale from itertools import repeat all illegalmontherror illegalweekdayerror setfirstweekday firstweekday isleap leapdays weekday monthrange monthcalendar prmonth month prcal calendar timegm monthname monthabbr dayname dayabbr calendar textcalendar htmlcalendar localetextcalendar localehtmlcalendar weekheader day month january february march april may june july august september october november december monday tuesday wednesday thursday friday saturday sunday exception raised for bad input with string parameter for details error valueerror exceptions raised for bad input class illegalmontherrorvalueerror def initself month self month month def strself return bad month number r must be 112 self month class illegalweekdayerrorvalueerror def initself weekday self weekday weekday def strself return bad weekday number r must be 0 monday to 6 sunday self weekday def getattrname if name in january february import warnings warnings warnfthe name attribute is deprecated use name upper instead deprecationwarning stacklevel2 if name january return 1 else return 2 raise attributeerrorfmodule name has no attribute name constants for months globalenum class monthintenum january 1 february 2 march 3 april 4 may 5 june 6 july 7 august 8 september 9 october 10 november 11 december 12 constants for days globalenum class dayintenum monday 0 tuesday 1 wednesday 2 thursday 3 friday 4 saturday 5 sunday 6 number of days per month except for february in leap years mdays 0 31 28 31 30 31 30 31 31 30 31 30 31 this module used to have hardcoded lists of day and month names as english strings the classes following emulate a readonly version of that but supply localized names note that the values are computed fresh on each call in case the user changes locale between calls class localizedmonth months datetime date2001 i1 1 strftime for i in range12 months insert0 lambda x def initself format self format format def getitemself i funcs self monthsi if isinstancei slice return fself format for f in funcs else return funcsself format def lenself return 13 class localizedday january 1 2001 was a monday days datetime date2001 1 i1 strftime for i in range7 def initself format self format format def getitemself i funcs self daysi if isinstancei slice return fself format for f in funcs else return funcsself format def lenself return 7 full and abbreviated names of weekdays dayname localizedday a dayabbr localizedday a full and abbreviated names of months 1based arrays monthname localizedmonth b monthabbr localizedmonth b def isleapyear return number of leap years in range y1 y2 assume y1 y2 y1 1 y2 1 return y24 y14 y2100 y1100 y2400 y1400 def weekdayyear month day return weekday 06 monsun and number of days 2831 for year month if not 1 month 12 raise illegalmontherrormonth day1 weekdayyear month 1 ndays mdaysmonth month february and isleapyear return day1 ndays def monthlenyear month return mdaysmonth month february and isleapyear def prevmonthyear month if month 1 return year1 12 else return year month1 def nextmonthyear month if month 12 return year1 1 else return year month1 class calendarobject def initself firstweekday0 self firstweekday firstweekday 0 monday 6 sunday def getfirstweekdayself return self firstweekday 7 def setfirstweekdayself firstweekday self firstweekday firstweekday firstweekday propertygetfirstweekday setfirstweekday def iterweekdaysself for i in rangeself firstweekday self firstweekday 7 yield i7 def itermonthdatesself year month for y m d in self itermonthdays3year month yield datetime datey m d def itermonthdaysself year month day1 ndays monthrangeyear month daysbefore day1 self firstweekday 7 yield from repeat0 daysbefore yield from range1 ndays 1 daysafter self firstweekday day1 ndays 7 yield from repeat0 daysafter def itermonthdays2self year month for i d in enumerateself itermonthdaysyear month self firstweekday yield d i 7 def itermonthdays3self year month day1 ndays monthrangeyear month daysbefore day1 self firstweekday 7 daysafter self firstweekday day1 ndays 7 y m prevmonthyear month end monthleny m 1 for d in rangeenddaysbefore end yield y m d for d in range1 ndays 1 yield year month d y m nextmonthyear month for d in range1 daysafter 1 yield y m d def itermonthdays4self year month for i y m d in enumerateself itermonthdays3year month yield y m d self firstweekday i 7 def monthdatescalendarself year month dates listself itermonthdatesyear month return datesi i7 for i in range0 lendates 7 def monthdays2calendarself year month days listself itermonthdays2year month return daysi i7 for i in range0 lendays 7 def monthdayscalendarself year month days listself itermonthdaysyear month return daysi i7 for i in range0 lendays 7 def yeardatescalendarself year width3 months self monthdatescalendaryear m for m in month return monthsi iwidth for i in range0 lenmonths width def yeardays2calendarself year width3 months self monthdays2calendaryear m for m in month return monthsi iwidth for i in range0 lenmonths width def yeardayscalendarself year width3 months self monthdayscalendaryear m for m in month return monthsi iwidth for i in range0 lenmonths width class textcalendarcalendar def prweekself theweek width printself formatweektheweek width end def formatdayself day weekday width if day 0 s else s 2i day rightalign singledigit days return s centerwidth def formatweekself theweek width return joinself formatdayd wd width for d wd in theweek def formatweekdayself day width if width 9 names dayname else names dayabbr return namesday width centerwidth def formatweekheaderself width return joinself formatweekdayi width for i in self iterweekdays def formatmonthnameself theyear themonth width withyeartrue s monthnamethemonth if withyear s s r s theyear return s centerwidth def prmonthself theyear themonth w0 l0 printself formatmonththeyear themonth w l end def formatmonthself theyear themonth w0 l0 w max2 w l max1 l s self formatmonthnametheyear themonth 7 w 1 1 s s rstrip s n l s self formatweekheaderw rstrip s n l for week in self monthdays2calendartheyear themonth s self formatweekweek w rstrip s n l return s def formatyearself theyear w2 l1 c6 m3 w max2 w l max1 l c max2 c colwidth w 1 7 1 v a v append areprtheyear centercolwidthmcm1 rstrip a n l header self formatweekheaderw for i row in enumerateself yeardays2calendartheyear m months in this row months rangemi1 minmi11 13 a n l names self formatmonthnametheyear k colwidth false for k in months aformatstringnames colwidth c rstrip a n l headers header for k in months aformatstringheaders colwidth c rstrip a n l max number of weeks for this row height maxlencal for cal in row for j in rangeheight weeks for cal in row if j lencal weeks append else weeks appendself formatweekcalj w aformatstringweeks colwidth c rstrip a n l return joinv def pryearself theyear w0 l0 c6 m3 this calendar returns complete html pages css classes for the day tds css classes for the day ths css class for the days before and after current month css class for the month s head css class for the month css class for the year s table head css class for the whole year table return a day as a table cell day outside month return a complete week as a table row return a weekday name as a table header return a header for a week as a table row return a month name as a table row return a formatted month as a table return a formatted year as a table of tables months in this row return a formatted year as a complete html page the lctime locale does not seem to be configured get the user preferred locale this class can be passed a locale name in the constructor and will return month and weekday names in the specified locale this class can be passed a locale name in the constructor and will return month and weekday names in the specified locale support for old module level interface spacing of month columns for multicolumn year calendar prints multicolumn formatting for year calendars printformatstringcols colwidth spacing def formatstringcols colwidthcolwidth spacingspacing unrelated but handy function to calculate unix timestamp from gmt year month day hour minute second tuple 6 days datetime dateyear month 1 toordinal epochord day 1 hours days24 hour minutes hours60 minute seconds minutes60 second return seconds def mainargsnone import argparse parser argparse argumentparser textgroup parser addargumentgroup text only arguments htmlgroup parser addargumentgroup html only arguments textgroup addargument w width typeint default2 helpwidth of date column default 2 textgroup addargument l lines typeint default1 helpnumber of lines for each week default 1 textgroup addargument s spacing typeint default6 helpspacing between months default 6 textgroup addargument m months typeint default3 helpmonths per row default 3 htmlgroup addargument c css defaultcalendar css helpcss to use for page parser addargument l locale defaultnone helplocale to use for month and weekday names parser addargument e encoding defaultnone helpencoding to use for output parser addargument t type defaulttext choicestext html helpoutput type text or html parser addargument year nargs typeint helpyear number 19999 parser addargument month nargs typeint helpmonth number 112 text only options parser parseargsargs if options locale and not options encoding parser errorif locale is specified encoding is required sys exit1 locale options locale options encoding if options type html if options month parser errorincorrect number of arguments sys exit1 if options locale cal localehtmlcalendarlocalelocale else cal htmlcalendar encoding options encoding if encoding is none encoding sys getdefaultencoding optdict dictencodingencoding cssoptions css write sys stdout buffer write if options year is none writecal formatyearpagedatetime date today year optdict else writecal formatyearpageoptions year optdict else if options locale cal localetextcalendarlocalelocale else cal textcalendar optdict dictwoptions width loptions lines if options month is none optdictc options spacing optdictm options months if options year is none result cal formatyeardatetime date today year optdict elif options month is none result cal formatyearoptions year optdict else result cal formatmonthoptions year options month optdict write sys stdout write if options encoding result result encodeoptions encoding write sys stdout buffer write writeresult if name main main exception raised for bad input with string parameter for details exceptions raised for bad input constants for months constants for days number of days per month except for february in leap years this module used to have hard coded lists of day and month names as english strings the classes following emulate a read only version of that but supply localized names note that the values are computed fresh on each call in case the user changes locale between calls january 1 2001 was a monday full and abbreviated names of weekdays full and abbreviated names of months 1 based arrays return true for leap years false for non leap years return number of leap years in range y1 y2 assume y1 y2 return weekday 0 6 mon sun for year month 1 12 day 1 31 return weekday 0 6 mon sun and number of days 28 31 for year month base calendar class this class doesn t do any formatting it simply provides data to subclasses 0 monday 6 sunday return an iterator for one week of weekday numbers starting with the configured first one return an iterator for one month the iterator will yield datetime date values and will always iterate through complete weeks so it will yield dates outside the specified month like itermonthdates but will yield day numbers for days outside the specified month the day number is 0 like itermonthdates but will yield day number weekday number tuples for days outside the specified month the day number is 0 like itermonthdates but will yield year month day tuples can be used for dates outside of datetime date range like itermonthdates but will yield year month day day_of_week tuples can be used for dates outside of datetime date range return a matrix list of lists representing a month s calendar each row represents a week week entries are datetime date values return a matrix representing a month s calendar each row represents a week week entries are day number weekday number tuples day numbers outside this month are zero return a matrix representing a month s calendar each row represents a week days outside this month are zero return the data for the specified year ready for formatting the return value is a list of month rows each month row contains up to width months each month contains between 4 and 6 weeks and each week contains 1 7 days days are datetime date objects return the data for the specified year ready for formatting similar to yeardatescalendar entries in the week lists are day number weekday number tuples day numbers outside this month are zero return the data for the specified year ready for formatting similar to yeardatescalendar entries in the week lists are day numbers day numbers outside this month are zero subclass of calendar that outputs a calendar as a simple plain text similar to the unix program cal print a single week no newline returns a formatted day right align single digit days returns a single week in a string no newline returns a formatted week day name return a header for a week return a formatted month name print a month s calendar return a month s calendar string multi line returns a year s calendar as a multi line string months in this row max number of weeks for this row print a year s calendar this calendar returns complete html pages css classes for the day td s css classes for the day th s css class for the days before and after current month css class for the month s head css class for the month css class for the year s table head css class for the whole year table return a day as a table cell day outside month return a complete week as a table row return a weekday name as a table header return a header for a week as a table row return a month name as a table row return a formatted month as a table return a formatted year as a table of tables months in this row return a formatted year as a complete html page the lc_time locale does not seem to be configured get the user preferred locale this class can be passed a locale name in the constructor and will return month and weekday names in the specified locale this class can be passed a locale name in the constructor and will return month and weekday names in the specified locale support for old module level interface spacing of month columns for multi column year calendar amount printed by prweek number of spaces between columns prints multi column formatting for year calendars returns a string formatted from n strings centered within n columns unrelated but handy function to calculate unix timestamp from gmt
import sys
import datetime
from enum import IntEnum, global_enum
import locale as _locale
from itertools import repeat

__all__ = ["IllegalMonthError", "IllegalWeekdayError", "setfirstweekday",
           "firstweekday", "isleap", "leapdays", "weekday", "monthrange",
           "monthcalendar", "prmonth", "month", "prcal", "calendar",
           "timegm", "month_name", "month_abbr", "day_name", "day_abbr",
           "Calendar", "TextCalendar", "HTMLCalendar", "LocaleTextCalendar",
           "LocaleHTMLCalendar", "weekheader",
           "Day", "Month", "JANUARY", "FEBRUARY", "MARCH",
           "APRIL", "MAY", "JUNE", "JULY",
           "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER",
           "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY",
           "SATURDAY", "SUNDAY"]


error = ValueError


class IllegalMonthError(ValueError):
    def __init__(self, month):
        self.month = month
    def __str__(self):
        return "bad month number %r; must be 1-12" % self.month


class IllegalWeekdayError(ValueError):
    def __init__(self, weekday):
        self.weekday = weekday
    def __str__(self):
        return "bad weekday number %r; must be 0 (Monday) to 6 (Sunday)" % self.weekday


def __getattr__(name):
    if name in ('January', 'February'):
        import warnings
        warnings.warn(f"The '{name}' attribute is deprecated, use '{name.upper()}' instead",
                      DeprecationWarning, stacklevel=2)
        if name == 'January':
            return 1
        else:
            return 2

    raise AttributeError(f"module '{__name__}' has no attribute '{name}'")



@global_enum
class Month(IntEnum):
    JANUARY = 1
    FEBRUARY = 2
    MARCH = 3
    APRIL = 4
    MAY = 5
    JUNE = 6
    JULY = 7
    AUGUST = 8
    SEPTEMBER = 9
    OCTOBER = 10
    NOVEMBER = 11
    DECEMBER = 12



@global_enum
class Day(IntEnum):
    MONDAY = 0
    TUESDAY = 1
    WEDNESDAY = 2
    THURSDAY = 3
    FRIDAY = 4
    SATURDAY = 5
    SUNDAY = 6



mdays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]






class _localized_month:

    _months = [datetime.date(2001, i+1, 1).strftime for i in range(12)]
    _months.insert(0, lambda x: "")

    def __init__(self, format):
        self.format = format

    def __getitem__(self, i):
        funcs = self._months[i]
        if isinstance(i, slice):
            return [f(self.format) for f in funcs]
        else:
            return funcs(self.format)

    def __len__(self):
        return 13


class _localized_day:

    
    _days = [datetime.date(2001, 1, i+1).strftime for i in range(7)]

    def __init__(self, format):
        self.format = format

    def __getitem__(self, i):
        funcs = self._days[i]
        if isinstance(i, slice):
            return [f(self.format) for f in funcs]
        else:
            return funcs(self.format)

    def __len__(self):
        return 7



day_name = _localized_day('%A')
day_abbr = _localized_day('%a')


month_name = _localized_month('%B')
month_abbr = _localized_month('%b')


def isleap(year):
    
    return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)


def leapdays(y1, y2):
    
    y1 -= 1
    y2 -= 1
    return (y2//4 - y1//4) - (y2//100 - y1//100) + (y2//400 - y1//400)


def weekday(year, month, day):
    
    if not datetime.MINYEAR <= year <= datetime.MAXYEAR:
        year = 2000 + year % 400
    return Day(datetime.date(year, month, day).weekday())


def monthrange(year, month):
    
    if not 1 <= month <= 12:
        raise IllegalMonthError(month)
    day1 = weekday(year, month, 1)
    ndays = mdays[month] + (month == FEBRUARY and isleap(year))
    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):
    

    def __init__(self, firstweekday=0):
        self.firstweekday = firstweekday 

    def getfirstweekday(self):
        return self._firstweekday % 7

    def setfirstweekday(self, firstweekday):
        self._firstweekday = firstweekday

    firstweekday = property(getfirstweekday, setfirstweekday)

    def iterweekdays(self):
        
        for i in range(self.firstweekday, self.firstweekday + 7):
            yield i%7

    def itermonthdates(self, year, month):
        
        for y, m, d in self.itermonthdays3(year, month):
            yield datetime.date(y, m, d)

    def itermonthdays(self, year, month):
        
        day1, ndays = monthrange(year, month)
        days_before = (day1 - self.firstweekday) % 7
        yield from repeat(0, days_before)
        yield from range(1, ndays + 1)
        days_after = (self.firstweekday - day1 - ndays) % 7
        yield from repeat(0, days_after)

    def itermonthdays2(self, year, month):
        
        for i, d in enumerate(self.itermonthdays(year, month), self.firstweekday):
            yield d, i % 7

    def itermonthdays3(self, year, month):
        
        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):
        
        for i, (y, m, d) in enumerate(self.itermonthdays3(year, month)):
            yield y, m, d, (self.firstweekday + i) % 7

    def monthdatescalendar(self, year, month):
        
        dates = list(self.itermonthdates(year, month))
        return [ dates[i:i+7] for i in range(0, len(dates), 7) ]

    def monthdays2calendar(self, year, month):
        
        days = list(self.itermonthdays2(year, month))
        return [ days[i:i+7] for i in range(0, len(days), 7) ]

    def monthdayscalendar(self, year, month):
        
        days = list(self.itermonthdays(year, month))
        return [ days[i:i+7] for i in range(0, len(days), 7) ]

    def yeardatescalendar(self, year, width=3):
        
        months = [self.monthdatescalendar(year, m) for m in Month]
        return [months[i:i+width] for i in range(0, len(months), width) ]

    def yeardays2calendar(self, year, width=3):
        
        months = [self.monthdays2calendar(year, m) for m in Month]
        return [months[i:i+width] for i in range(0, len(months), width) ]

    def yeardayscalendar(self, year, width=3):
        
        months = [self.monthdayscalendar(year, m) for m in Month]
        return [months[i:i+width] for i in range(0, len(months), width) ]


class TextCalendar(Calendar):
    

    def prweek(self, theweek, width):
        
        print(self.formatweek(theweek, width), end='')

    def formatday(self, day, weekday, width):
        
        if day == 0:
            s = ''
        else:
            s = '%2i' % day             
        return s.center(width)

    def formatweek(self, theweek, width):
        
        return ' '.join(self.formatday(d, wd, width) for (d, wd) in theweek)

    def formatweekday(self, day, width):
        
        if width >= 9:
            names = day_name
        else:
            names = day_abbr
        return names[day][:width].center(width)

    def formatweekheader(self, width):
        
        return ' '.join(self.formatweekday(i, width) for i in self.iterweekdays())

    def formatmonthname(self, theyear, themonth, width, withyear=True):
        
        s = month_name[themonth]
        if withyear:
            s = "%s %r" % (s, theyear)
        return s.center(width)

    def prmonth(self, theyear, themonth, w=0, l=0):
        
        print(self.formatmonth(theyear, themonth, w, l), end='')

    def formatmonth(self, theyear, themonth, w=0, l=0):
        
        w = max(2, w)
        l = max(1, l)
        s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1)
        s = s.rstrip()
        s += '\n' * l
        s += self.formatweekheader(w).rstrip()
        s += '\n' * l
        for week in self.monthdays2calendar(theyear, themonth):
            s += self.formatweek(week, w).rstrip()
            s += '\n' * l
        return s

    def formatyear(self, theyear, w=2, l=1, c=6, m=3):
        
        w = max(2, w)
        l = max(1, l)
        c = max(2, c)
        colwidth = (w + 1) * 7 - 1
        v = []
        a = v.append
        a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip())
        a('\n'*l)
        header = self.formatweekheader(w)
        for (i, row) in enumerate(self.yeardays2calendar(theyear, m)):
            
            months = range(m*i+1, min(m*(i+1)+1, 13))
            a('\n'*l)
            names = (self.formatmonthname(theyear, k, colwidth, False)
                     for k in months)
            a(formatstring(names, colwidth, c).rstrip())
            a('\n'*l)
            headers = (header for k in months)
            a(formatstring(headers, colwidth, c).rstrip())
            a('\n'*l)
            
            height = max(len(cal) for cal in row)
            for j in range(height):
                weeks = []
                for cal in row:
                    if j >= len(cal):
                        weeks.append('')
                    else:
                        weeks.append(self.formatweek(cal[j], w))
                a(formatstring(weeks, colwidth, c).rstrip())
                a('\n' * l)
        return ''.join(v)

    def pryear(self, theyear, w=0, l=0, c=6, m=3):
        
        print(self.formatyear(theyear, w, l, c, m), end='')


class HTMLCalendar(Calendar):
    

    
    cssclasses = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]

    
    cssclasses_weekday_head = cssclasses

    
    cssclass_noday = "noday"

    
    cssclass_month_head = "month"

    
    cssclass_month = "month"

    
    cssclass_year_head = "year"

    
    cssclass_year = "year"

    def formatday(self, day, weekday):
        
        if day == 0:
            
            return '<td class="%s">&nbsp;</td>' % self.cssclass_noday
        else:
            return '<td class="%s">%d</td>' % (self.cssclasses[weekday], day)

    def formatweek(self, theweek):
        
        s = ''.join(self.formatday(d, wd) for (d, wd) in theweek)
        return '<tr>%s</tr>' % s

    def formatweekday(self, day):
        
        return '<th class="%s">%s</th>' % (
            self.cssclasses_weekday_head[day], day_abbr[day])

    def formatweekheader(self):
        
        s = ''.join(self.formatweekday(i) for i in self.iterweekdays())
        return '<tr>%s</tr>' % s

    def formatmonthname(self, theyear, themonth, withyear=True):
        
        if withyear:
            s = '%s %s' % (month_name[themonth], theyear)
        else:
            s = '%s' % month_name[themonth]
        return '<tr><th colspan="7" class="%s">%s</th></tr>' % (
            self.cssclass_month_head, s)

    def formatmonth(self, theyear, themonth, withyear=True):
        
        v = []
        a = v.append
        a('<table border="0" cellpadding="0" cellspacing="0" class="%s">' % (
            self.cssclass_month))
        a('\n')
        a(self.formatmonthname(theyear, themonth, withyear=withyear))
        a('\n')
        a(self.formatweekheader())
        a('\n')
        for week in self.monthdays2calendar(theyear, themonth):
            a(self.formatweek(week))
            a('\n')
        a('</table>')
        a('\n')
        return ''.join(v)

    def formatyear(self, theyear, width=3):
        
        v = []
        a = v.append
        width = max(width, 1)
        a('<table border="0" cellpadding="0" cellspacing="0" class="%s">' %
          self.cssclass_year)
        a('\n')
        a('<tr><th colspan="%d" class="%s">%s</th></tr>' % (
            width, self.cssclass_year_head, theyear))
        for i in range(JANUARY, JANUARY+12, width):
            
            months = range(i, min(i+width, 13))
            a('<tr>')
            for m in months:
                a('<td>')
                a(self.formatmonth(theyear, m, withyear=False))
                a('</td>')
            a('</tr>')
        a('</table>')
        return ''.join(v)

    def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None):
        
        if encoding is None:
            encoding = sys.getdefaultencoding()
        v = []
        a = v.append
        a('<?xml version="1.0" encoding="%s"?>\n' % encoding)
        a('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n')
        a('<html>\n')
        a('<head>\n')
        a('<meta http-equiv="Content-Type" content="text/html; charset=%s" />\n' % encoding)
        if css is not None:
            a('<link rel="stylesheet" type="text/css" href="%s" />\n' % css)
        a('<title>Calendar for %d</title>\n' % theyear)
        a('</head>\n')
        a('<body>\n')
        a(self.formatyear(theyear, width))
        a('</body>\n')
        a('</html>\n')
        return ''.join(v).encode(encoding, "xmlcharrefreplace")


class different_locale:
    def __init__(self, locale):
        self.locale = locale
        self.oldlocale = None

    def __enter__(self):
        self.oldlocale = _locale.setlocale(_locale.LC_TIME, None)
        _locale.setlocale(_locale.LC_TIME, self.locale)

    def __exit__(self, *args):
        _locale.setlocale(_locale.LC_TIME, self.oldlocale)


def _get_default_locale():
    locale = _locale.setlocale(_locale.LC_TIME, None)
    if locale == "C":
        with different_locale(""):
            
            
            locale = _locale.setlocale(_locale.LC_TIME, None)
    return locale


class LocaleTextCalendar(TextCalendar):
    

    def __init__(self, firstweekday=0, locale=None):
        TextCalendar.__init__(self, firstweekday)
        if locale is None:
            locale = _get_default_locale()
        self.locale = locale

    def formatweekday(self, day, width):
        with different_locale(self.locale):
            return super().formatweekday(day, width)

    def formatmonthname(self, theyear, themonth, width, withyear=True):
        with different_locale(self.locale):
            return super().formatmonthname(theyear, themonth, width, withyear)


class LocaleHTMLCalendar(HTMLCalendar):
    
    def __init__(self, firstweekday=0, locale=None):
        HTMLCalendar.__init__(self, firstweekday)
        if locale is None:
            locale = _get_default_locale()
        self.locale = locale

    def formatweekday(self, day):
        with different_locale(self.locale):
            return super().formatweekday(day)

    def formatmonthname(self, theyear, themonth, withyear=True):
        with different_locale(self.locale):
            return super().formatmonthname(theyear, themonth, withyear)


c = TextCalendar()

firstweekday = c.getfirstweekday

def setfirstweekday(firstweekday):
    if not MONDAY <= firstweekday <= SUNDAY:
        raise IllegalWeekdayError(firstweekday)
    c.firstweekday = firstweekday

monthcalendar = c.monthdayscalendar
prweek = c.prweek
week = c.formatweek
weekheader = c.formatweekheader
prmonth = c.prmonth
month = c.formatmonth
calendar = c.formatyear
prcal = c.pryear



_colwidth = 7*3 - 1         
_spacing = 6                


def format(cols, colwidth=_colwidth, spacing=_spacing):
    
    print(formatstring(cols, colwidth, spacing))


def formatstring(cols, colwidth=_colwidth, spacing=_spacing):
    
    spacing *= ' '
    return spacing.join(c.center(colwidth) for c in cols)


EPOCH = 1970
_EPOCH_ORD = datetime.date(EPOCH, 1, 1).toordinal()


def timegm(tuple):
    
    year, month, day, hour, minute, second = tuple[:6]
    days = datetime.date(year, month, 1).toordinal() - _EPOCH_ORD + day - 1
    hours = days*24 + hour
    minutes = hours*60 + minute
    seconds = minutes*60 + second
    return seconds


def main(args=None):
    import argparse
    parser = argparse.ArgumentParser()
    textgroup = parser.add_argument_group('text only arguments')
    htmlgroup = parser.add_argument_group('html only arguments')
    textgroup.add_argument(
        "-w", "--width",
        type=int, default=2,
        help="width of date column (default 2)"
    )
    textgroup.add_argument(
        "-l", "--lines",
        type=int, default=1,
        help="number of lines for each week (default 1)"
    )
    textgroup.add_argument(
        "-s", "--spacing",
        type=int, default=6,
        help="spacing between months (default 6)"
    )
    textgroup.add_argument(
        "-m", "--months",
        type=int, default=3,
        help="months per row (default 3)"
    )
    htmlgroup.add_argument(
        "-c", "--css",
        default="calendar.css",
        help="CSS to use for page"
    )
    parser.add_argument(
        "-L", "--locale",
        default=None,
        help="locale to use for month and weekday names"
    )
    parser.add_argument(
        "-e", "--encoding",
        default=None,
        help="encoding to use for output"
    )
    parser.add_argument(
        "-t", "--type",
        default="text",
        choices=("text", "html"),
        help="output type (text or html)"
    )
    parser.add_argument(
        "year",
        nargs='?', type=int,
        help="year number (1-9999)"
    )
    parser.add_argument(
        "month",
        nargs='?', type=int,
        help="month number (1-12, text only)"
    )

    options = parser.parse_args(args)

    if options.locale and not options.encoding:
        parser.error("if --locale is specified --encoding is required")
        sys.exit(1)

    locale = options.locale, options.encoding

    if options.type == "html":
        if options.month:
            parser.error("incorrect number of arguments")
            sys.exit(1)
        if options.locale:
            cal = LocaleHTMLCalendar(locale=locale)
        else:
            cal = HTMLCalendar()
        encoding = options.encoding
        if encoding is None:
            encoding = sys.getdefaultencoding()
        optdict = dict(encoding=encoding, css=options.css)
        write = sys.stdout.buffer.write
        if options.year is None:
            write(cal.formatyearpage(datetime.date.today().year, **optdict))
        else:
            write(cal.formatyearpage(options.year, **optdict))
    else:
        if options.locale:
            cal = LocaleTextCalendar(locale=locale)
        else:
            cal = TextCalendar()
        optdict = dict(w=options.width, l=options.lines)
        if options.month is None:
            optdict["c"] = options.spacing
            optdict["m"] = options.months
        if options.year is None:
            result = cal.formatyear(datetime.date.today().year, **optdict)
        elif options.month is None:
            result = cal.formatyear(options.year, **optdict)
        else:
            result = cal.formatmonth(options.year, options.month, **optdict)
        write = sys.stdout.write
        if options.encoding:
            result = result.encode(options.encoding)
            write = sys.stdout.buffer.write
        write(result)


if __name__ == "__main__":
    main()