"""Calendar printing functionsNote when comparing these calendars to the ones printed by cal(1): Bydefault, these calendars have Monday as the first day of the week, andSunday as the last (the European convention). Use setfirstweekday() toset the first day of the week (0=Monday, 6=Sunday)."""importdatetime__all__=["error","setfirstweekday","firstweekday","isleap","leapdays","weekday","monthrange","monthcalendar","prmonth","month","prcal","calendar","timegm","month_name","month_abbr","day_name","day_abbr"]# Exception raised for bad input (with string parameter for details)error=ValueError# Constants for months referenced laterJanuary=1February=2# 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 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.class_localized_month:def__init__(self,format):self.format=formatdef__getitem__(self,i):data=[datetime.date(2001,j,1).strftime(self.format)forjinrange(1,13)]data.insert(0,"")returndata[i]def__len__(self):return13class_localized_day:def__init__(self,format):self.format=formatdef__getitem__(self,i):# January 1, 2001, was a Monday.data=[datetime.date(2001,1,j+1).strftime(self.format)forjinrange(7)]returndata[i]def__len__(self):return7# Full and abbreviated names of weekdaysday_name=_localized_day('%A')day_abbr=_localized_day('%a')# Full and abbreviated names of months (1-based arrays!!!)month_name=_localized_month('%B')month_abbr=_localized_month('%b')# Constants for weekdays(MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY)=range(7)_firstweekday=0# 0 = Monday, 6 = Sundaydeffirstweekday():return_firstweekdaydefsetfirstweekday(weekday):"""Set weekday (Monday=0, Sunday=6) to start each week."""global_firstweekdayifnotMONDAY<=weekday<=SUNDAY:raiseValueError, \
'bad weekday number; must be 0 (Monday) to 6 (Sunday)'_firstweekday=weekdaydefisleap(year):"""Return 1 for leap years, 0 for non-leap years."""returnyear%4==0and(year%100!=0oryear%400==0)defleapdays(y1,y2):"""Return number of leap years in range [y1, y2). Assume y1 <= y2."""y1-=1y2-=1return(y2//4-y1//4)-(y2//100-y1//100)+(y2//400-y1//400)defweekday(year,month,day):"""Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12), day (1-31)."""returndatetime.date(year,month,day).weekday()defmonthrange(year,month):"""Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for year, month."""ifnot1<=month<=12:raiseValueError,'bad month number'day1=weekday(year,month,1)ndays=mdays[month]+(month==Februaryandisleap(year))returnday1,ndaysdefmonthcalendar(year,month):"""Return a matrix representing a month's calendar. Each row represents a week; days outside this month are zero."""day1,ndays=monthrange(year,month)rows=[]r7=range(7)day=(_firstweekday-day1+6)%7-5# for leading 0's in first weekwhileday<=ndays:row=[0,0,0,0,0,0,0]foriinr7:if1<=day<=ndays:row[i]=dayday=day+1rows.append(row)returnrowsdefprweek(theweek,width):"""Print a single week (no newline)."""printweek(theweek,width),defweek(theweek,width):"""Returns a single week in a string (no newline)."""days=[]fordayintheweek:ifday==0:s=''else:s='%2i'%day# right-align single-digit daysdays.append(s.center(width))return' '.join(days)defweekheader(width):"""Return a header for a week."""ifwidth>=9:names=day_nameelse:names=day_abbrdays=[]foriinrange(_firstweekday,_firstweekday+7):days.append(names[i%7][:width].center(width))return' '.join(days)defprmonth(theyear,themonth,w=0,l=0):"""Print a month's calendar."""printmonth(theyear,themonth,w,l),defmonth(theyear,themonth,w=0,l=0):"""Return a month's calendar string (multi-line)."""w=max(2,w)l=max(1,l)s=("%s%r"%(month_name[themonth],theyear)).center(7*(w+1)-1).rstrip()+ \
'\n'*l+weekheader(w).rstrip()+'\n'*lforaweekinmonthcalendar(theyear,themonth):s=s+week(aweek,w).rstrip()+'\n'*lreturns[:-l]+'\n'# Spacing of month columns for 3-column year calendar_colwidth=7*3-1# Amount printed by prweek()_spacing=6# Number of spaces between columnsdefformat3c(a,b,c,colwidth=_colwidth,spacing=_spacing):"""Prints 3-column formatting for year calendars"""printformat3cstring(a,b,c,colwidth,spacing)defformat3cstring(a,b,c,colwidth=_colwidth,spacing=_spacing):"""Returns a string formatted from 3 strings, centered within 3 columns."""return(a.center(colwidth)+' '*spacing+b.center(colwidth)+' '*spacing+c.center(colwidth))defprcal(year,w=0,l=0,c=_spacing):"""Print a year's calendar."""printcalendar(year,w,l,c),defcalendar(year,w=0,l=0,c=_spacing):"""Returns a year's calendar as a multi-line string."""w=max(2,w)l=max(1,l)c=max(2,c)colwidth=(w+1)*7-1s=repr(year).center(colwidth*3+c*2).rstrip()+'\n'*lheader=weekheader(w)header=format3cstring(header,header,header,colwidth,c).rstrip()forqinrange(January,January+12,3):s=(s+'\n'*l+format3cstring(month_name[q],month_name[q+1],month_name[q+2],colwidth,c).rstrip()+'\n'*l+header+'\n'*l)data=[]height=0foramonthinrange(q,q+3):cal=monthcalendar(year,amonth)iflen(cal)>height:height=len(cal)data.append(cal)foriinrange(height):weeks=[]forcalindata:ifi>=len(cal):weeks.append('')else:weeks.append(week(cal[i],w))s=s+format3cstring(weeks[0],weeks[1],weeks[2],colwidth,c).rstrip()+'\n'*lreturns[:-l]+'\n'EPOCH=1970_EPOCH_ORD=datetime.date(EPOCH,1,1).toordinal()deftimegm(tuple):"""Unrelated but handy function to calculate Unix timestamp from GMT."""year,month,day,hour,minute,second=tuple[:6]days=datetime.date(year,month,1).toordinal()-_EPOCH_ORD+day-1hours=days*24+hourminutes=hours*60+minuteseconds=minutes*60+secondreturnseconds