1- """Calendar printing functions"""
1+ """Calendar printing functions
2+
3+ Note when comparing these calendars to the ones printed by cal(1): By
4+ default, these calendars have Monday as the first day of the week, and
5+ Sunday as the last (the European convention). Use setfirstweekday() to
6+ set the first day of the week (0=Monday, 6=Sunday)."""
27
38# Revision 2: uses functions from built-in time module
49
813# Exception raised for bad input (with string parameter for details)
914error = ValueError
1015
11- # Note when comparing these calendars to the ones printed by cal(1):
12- # My calendars have Monday as the first day of the week, and Sunday as
13- # the last! (I believe this is the European convention.)
14-
1516# Constants for months referenced later
1617January = 1
1718February = 2
3132month_abbr = [' ' , 'Jan' , 'Feb' , 'Mar' , 'Apr' , 'May' , 'Jun' ,
3233 'Jul' , 'Aug' , 'Sep' , 'Oct' , 'Nov' , 'Dec' ]
3334
35+ # Constants for weekdays
36+ (MONDAY , TUESDAY , WEDNESDAY , THURSDAY , FRIDAY , SATURDAY , SUNDAY ) = range (7 )
37+
38+ _firstweekday = 0 # 0 = Monday, 6 = Sunday
39+
40+ def firstweekday ():
41+ return _firstweekday
42+
43+ def setfirstweekday (weekday ):
44+ """Set weekday (Monday=0, Sunday=6) to start each week."""
45+ global _firstweekday
46+ if not MONDAY <= weekday <= SUNDAY :
47+ raise ValueError , \
48+ 'bad weekday number; must be 0 (Monday) to 6 (Sunday)'
49+ _firstweekday = weekday
50+
3451def isleap (year ):
3552 """Return 1 for leap years, 0 for non-leap years."""
3653 return year % 4 == 0 and (year % 100 <> 0 or year % 400 == 0 )
3754
3855def leapdays (y1 , y2 ):
3956 """Return number of leap years in range [y1, y2).
40- Assume y1 <= y2 and no funny (non-leap century) years."""
57+ Assume y1 <= y2 and no funny (non-leap century) years."""
4158 return (y2 + 3 )/ 4 - (y1 + 3 )/ 4
4259
4360def weekday (year , month , day ):
44- """Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12), day (1-31)."""
61+ """Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12),
62+ day (1-31)."""
4563 secs = mktime ((year , month , day , 0 , 0 , 0 , 0 , 0 , 0 ))
4664 tuple = localtime (secs )
4765 return tuple [6 ]
4866
4967def monthrange (year , month ):
50- """Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for year, month."""
51- if not 1 <= month <= 12 : raise ValueError , 'bad month number'
68+ """Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for
69+ year, month."""
70+ if not 1 <= month <= 12 :
71+ raise ValueError , 'bad month number'
5272 day1 = weekday (year , month , 1 )
5373 ndays = mdays [month ] + (month == February and isleap (year ))
5474 return day1 , ndays
5575
56- def _monthcalendar (year , month ):
76+ def monthcalendar (year , month ):
5777 """Return a matrix representing a month's calendar.
58- Each row represents a week; days outside this month are zero."""
78+ Each row represents a week; days outside this month are zero."""
5979 day1 , ndays = monthrange (year , month )
6080 rows = []
6181 r7 = range (7 )
62- day = 1 - day1
82+ day = ( _firstweekday - day1 + 6 ) % 7 - 5 # for leading 0's in first week
6383 while day <= ndays :
6484 row = [0 , 0 , 0 , 0 , 0 , 0 , 0 ]
6585 for i in r7 :
@@ -68,87 +88,102 @@ def _monthcalendar(year, month):
6888 rows .append (row )
6989 return rows
7090
71- _mc_cache = {}
72- def monthcalendar (year , month ):
73- """Caching interface to _monthcalendar."""
74- key = (year , month )
75- if _mc_cache .has_key (key ):
76- return _mc_cache [key ]
77- else :
78- _mc_cache [key ] = ret = _monthcalendar (year , month )
79- return ret
80-
8191def _center (str , width ):
8292 """Center a string in a field."""
8393 n = width - len (str )
84- if n <= 0 : return str
94+ if n <= 0 :
95+ return str
8596 return ' ' * ((n + 1 )/ 2 ) + str + ' ' * ((n )/ 2 )
8697
87- # XXX The following code knows that print separates items with space!
88-
89- def prweek (week , width ):
98+ def prweek (theweek , width ):
9099 """Print a single week (no newline)."""
91- for day in week :
92- if day == 0 : s = ''
93- else : s = `day`
94- print _center (s , width ),
100+ print week (theweek , width ),
101+
102+ def week (theweek , width ):
103+ """Returns a single week in a string (no newline)."""
104+ days = []
105+ for day in theweek :
106+ if day == 0 :
107+ s = ''
108+ else :
109+ s = '%2i' % day # right-align single-digit days
110+ days .append (_center (s , width ))
111+ return ' ' .join (days )
95112
96113def weekheader (width ):
97114 """Return a header for a week."""
98- str = ''
99- if width >= 9 : names = day_name
100- else : names = day_abbr
101- for i in range (7 ):
102- if str : str = str + ' '
103- str = str + _center (names [i % 7 ][:width ], width )
104- return str
105-
106- def prmonth (year , month , w = 0 , l = 0 ):
115+ if width >= 9 :
116+ names = day_name
117+ else :
118+ names = day_abbr
119+ days = []
120+ for i in range (_firstweekday , _firstweekday + 7 ):
121+ days .append (_center (names [i % 7 ][:width ], width ))
122+ return ' ' .join (days )
123+
124+ def prmonth (theyear , themonth , w = 0 , l = 0 ):
107125 """Print a month's calendar."""
126+ print month (theyear , themonth , w , l ),
127+
128+ def month (theyear , themonth , w = 0 , l = 0 ):
129+ """Return a month's calendar string (multi-line)."""
108130 w = max (2 , w )
109131 l = max (1 , l )
110- print _center (month_name [month ] + ' ' + `year` , 7 * (w + 1 ) - 1 ),
111- print '\n ' * l ,
112- print weekheader (w ),
113- print '\n ' * l ,
114- for week in monthcalendar (year , month ):
115- prweek (week , w )
116- print '\n ' * l ,
117-
118- # Spacing of month columns
132+ s = (_center (month_name [themonth ] + ' ' + `theyear` ,
133+ 7 * (w + 1 ) - 1 ).rstrip () +
134+ '\n ' * l + weekheader (w ).rstrip () + '\n ' * l )
135+ for aweek in monthcalendar (theyear , themonth ):
136+ s = s + week (aweek , w ).rstrip () + '\n ' * l
137+ return s [:- l ] + '\n '
138+
139+ # Spacing of month columns for 3-column year calendar
119140_colwidth = 7 * 3 - 1 # Amount printed by prweek()
120- _spacing = ' ' * 4 # Spaces between columns
141+ _spacing = 6 # Number of spaces between columns
121142
122- def format3c (a , b , c ):
123- """3-column formatting for year calendars"""
124- print _center (a , _colwidth ),
125- print _spacing ,
126- print _center (b , _colwidth ),
127- print _spacing ,
128- print _center (c , _colwidth )
143+ def format3c (a , b , c , colwidth = _colwidth , spacing = _spacing ):
144+ """Prints 3-column formatting for year calendars"""
145+ print format3cstring (a , b , c , colwidth , spacing )
129146
130- def prcal (year ):
147+ def format3cstring (a , b , c , colwidth = _colwidth , spacing = _spacing ):
148+ """Returns a string formatted from 3 strings, centered within 3 columns."""
149+ return (_center (a , colwidth ) + ' ' * spacing + _center (b , colwidth ) +
150+ ' ' * spacing + _center (c , colwidth ))
151+
152+ def prcal (year , w = 0 , l = 0 , c = _spacing ):
131153 """Print a year's calendar."""
132- header = weekheader (2 )
133- format3c ('' , `year` , '' )
154+ print calendar (year , w , l , c ),
155+
156+ def calendar (year , w = 0 , l = 0 , c = _spacing ):
157+ """Returns a year's calendar as a multi-line string."""
158+ w = max (2 , w )
159+ l = max (1 , l )
160+ c = max (2 , c )
161+ colwidth = (w + 1 ) * 7 - 1
162+ s = _center (`year` , colwidth * 3 + c * 2 ).rstrip () + '\n ' * l
163+ header = weekheader (w )
164+ header = format3cstring (header , header , header , colwidth , c ).rstrip ()
134165 for q in range (January , January + 12 , 3 ):
135- print
136- format3c (month_name [q ], month_name [q + 1 ], month_name [q + 2 ])
137- format3c (header , header , header )
166+ s = (s + '\n ' * l +
167+ format3cstring (month_name [q ], month_name [q + 1 ], month_name [q + 2 ],
168+ colwidth , c ).rstrip () +
169+ '\n ' * l + header + '\n ' * l )
138170 data = []
139171 height = 0
140- for month in range (q , q + 3 ):
141- cal = monthcalendar (year , month )
142- if len (cal ) > height : height = len (cal )
172+ for amonth in range (q , q + 3 ):
173+ cal = monthcalendar (year , amonth )
174+ if len (cal ) > height :
175+ height = len (cal )
143176 data .append (cal )
144177 for i in range (height ):
178+ weeks = []
145179 for cal in data :
146180 if i >= len (cal ):
147- print ' ' * _colwidth ,
181+ weeks . append ( '' )
148182 else :
149- prweek (cal [i ], 2 )
150- print _spacing ,
151- print
183+ weeks .append (week (cal [i ], w ))
184+ s = s + format3cstring (weeks [0 ], weeks [1 ], weeks [2 ],
185+ colwidth , c ).rstrip () + '\n ' * l
186+ return s [:- l ] + '\n '
152187
153188EPOCH = 1970
154189def timegm (tuple ):
0 commit comments