1- # module calendar
2-
31##############################
42# Calendar support functions #
53##############################
64
7- # This is based on UNIX ctime() et al. (also Standard C and POSIX)
8- # Subtle but crucial differences:
9- # - the order of the elements of a 'struct tm' differs, to ease sorting
10- # - months numbers are 1-12, not 0-11; month arrays have a dummy element 0
11- # - Monday is the first day of the week (numbered 0)
12- # - years are expressed in full, e.g. 1970, not 70.
13- # - timezone is currently hardcoded
14- # - doesn't know about daylight saving time
5+ # Revision 2: uses funtions from built-in time module where possible.
156
16- # These are really parameters of the ' time' module:
17- epoch = 1970 # Time began on January 1 of this year (00:00:00 UTC)
18- day_0 = 3 # The epoch begins on a Thursday (Monday = 0)
7+ # Import functions and variables from time module
8+ from time import gmtime , localtime , mktime
9+ from time import timezone , altzone , daylight , tzname
1910
20- # Localization: Minutes West from Greenwich
21- timezone = - 2 * 60 # Middle-European time with DST on
22- # timezone = 5*60 # EST (sigh -- THINK time() doesn't return UTC)
11+ # Exception raised for bad input (with string parameter for details)
12+ error = 'calendar.error'
2313
24- # Return 1 for leap years, 0 for non-leap years
25- def isleap (year ):
26- return year % 4 == 0 and (year % 100 <> 0 or year % 400 == 0 )
14+ # Abbreviated names of months (1-based arrays!!!)
15+ month_abbr = [' ' , 'Jan' , 'Feb' , 'Mar' , 'Apr' , 'May' , 'Jun' , \
16+ 'Jul' , 'Aug' , 'Sep' , 'Oct' , 'Nov' , 'Dec' ]
17+
18+ # Turn calendar time as returned by localtime() into a string
19+ def asctime (arg ):
20+ year , month , day , hours , mins , secs , wday , yday , isdst = arg
21+ return '%s %s %02d %02d:%02d:%02d %04d' % (
22+ day_abbr [wday ], month_abbr [month ], day ,
23+ hours , mins , secs , year )
24+
25+ # UNIX-style ctime (except it doesn't append '\n'!)
26+ def ctime (secs ):
27+ return asctime (localtime (secs ))
28+
29+ ######################
30+ # Non-UNIX additions #
31+ ######################
32+
33+ # Calendar printing etc.
34+
35+ # Note when comparing these calendars to the ones printed by cal(1):
36+ # My calendars have Monday as the first day of the week, and Sunday as
37+ # the last! (I believe this is the European convention.)
2738
2839# Constants for months referenced later
2940January = 1
@@ -32,100 +43,34 @@ def isleap(year):
3243# Number of days per month (except for February in leap years)
3344mdays = [0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 ]
3445
35- # Exception raised for bad input (with string parameter for details)
36- error = 'calendar error'
37-
38- # Turn seconds since epoch into calendar time
39- def gmtime (secs ):
40- if secs < 0 : raise error , 'negative input to gmtime()'
41- secs = int (secs )
42- mins , secs = divmod (secs , 60 )
43- hours , mins = divmod (mins , 60 )
44- days , hours = divmod (hours , 24 )
45- wday = (days + day_0 ) % 7
46- year = epoch
47- # XXX Most of the following loop can be replaced by one division
48- while 1 :
49- yd = 365 + isleap (year )
50- if days < yd : break
51- days = days - yd
52- year = year + 1
53- yday = days
54- month = January
55- while 1 :
56- md = mdays [month ] + (month == February and isleap (year ))
57- if days < md : break
58- days = days - md
59- month = month + 1
60- return year , month , days + 1 , hours , mins , secs , yday , wday
61- # XXX Week number also?
62-
63- # Return number of leap years in range [y1, y2)
64- # Assume y1 <= y2 and no funny (non-leap century) years
65- def leapdays (y1 , y2 ):
66- return (y2 + 3 )/ 4 - (y1 + 3 )/ 4
67-
68- # Inverse of gmtime():
69- # Turn UTC calendar time (less yday, wday) into seconds since epoch
70- def mktime (year , month , day , hours , mins , secs ):
71- days = day - 1
72- for m in range (January , month ): days = days + mdays [m ]
73- if isleap (year ) and month > February : days = days + 1
74- days = days + (year - epoch )* 365 + leapdays (epoch , year )
75- return ((days * 24 + hours )* 60 + mins )* 60 + secs
76-
7746# Full and abbreviated names of weekdays
7847day_name = ['Monday' , 'Tuesday' , 'Wednesday' , 'Thursday' , \
7948 'Friday' , 'Saturday' , 'Sunday' ]
8049day_abbr = ['Mon' , 'Tue' , 'Wed' , 'Thu' , 'Fri' , 'Sat' , 'Sun' ]
8150
82- # Full and abbreviated of months (1-based arrays!!!)
51+ # Full names of months (1-based arrays!!!)
8352month_name = ['' , 'January' , 'February' , 'March' , 'April' , \
8453 'May' , 'June' , 'July' , 'August' , \
8554 'September' , 'October' , 'November' , 'December' ]
86- month_abbr = [' ' , 'Jan' , 'Feb' , 'Mar' , 'Apr' , 'May' , 'Jun' , \
87- 'Jul' , 'Aug' , 'Sep' , 'Oct' , 'Nov' , 'Dec' ]
88-
89- # Zero-fill string to two positions (helper for asctime())
90- def dd (s ):
91- while len (s ) < 2 : s = '0' + s
92- return s
93-
94- # Blank-fill string to two positions (helper for asctime())
95- def zd (s ):
96- while len (s ) < 2 : s = ' ' + s
97- return s
98-
99- # Turn calendar time as returned by gmtime() into a string
100- # (the yday parameter is for compatibility with gmtime())
101- def asctime (arg ):
102- year , month , day , hours , mins , secs , yday , wday = arg
103- s = day_abbr [wday ] + ' ' + month_abbr [month ] + ' ' + zd (`day` )
104- s = s + ' ' + dd (`hours` ) + ':' + dd (`mins` ) + ':' + dd (`secs` )
105- return s + ' ' + `year`
106-
107- # Local time ignores DST issues for now -- adjust 'timezone' to fake it
108- def localtime (secs ):
109- return gmtime (secs - timezone * 60 )
110-
111- # UNIX-style ctime (except it doesn't append '\n'!)
112- def ctime (secs ):
113- return asctime (localtime (secs ))
11455
115- ######################
116- # Non-UNIX additions #
117- ######################
56+ # Return 1 for leap years, 0 for non-leap years
57+ def isleap ( year ):
58+ return year % 4 == 0 and ( year % 100 <> 0 or year % 400 == 0 )
11859
119- # Calendar printing etc.
60+ # Return number of leap years in range [y1, y2)
61+ # Assume y1 <= y2 and no funny (non-leap century) years
62+ def leapdays (y1 , y2 ):
63+ return (y2 + 3 )/ 4 - (y1 + 3 )/ 4
12064
12165# Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12), day (1-31)
12266def weekday (year , month , day ):
123- secs = mktime (year , month , day , 0 , 0 , 0 )
124- days = secs / ( 24 * 60 * 60 )
125- return ( days + day_0 ) % 7
67+ secs = mktime (( year , month , day , 0 , 0 , 0 , 0 , 0 , 0 ) )
68+ tuple = localtime ( secs )
69+ return tuple [ 6 ]
12670
12771# Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for year, month
12872def monthrange (year , month ):
73+ if not 1 <= month <= 12 : raise ValueError , 'bad month number'
12974 day1 = weekday (year , month , 1 )
13075 ndays = mdays [month ] + (month == February and isleap (year ))
13176 return day1 , ndays
@@ -146,53 +91,68 @@ def _monthcalendar(year, month):
14691 return rows
14792
14893# Caching interface to _monthcalendar
149- mc_cache = {}
94+ _mc_cache = {}
15095def monthcalendar (year , month ):
151- key = ` year` + month_abbr [ month ]
152- try :
153- return mc_cache [key ]
154- except KeyError :
155- mc_cache [key ] = ret = _monthcalendar (year , month )
96+ key = ( year , month )
97+ if _mc_cache . has_key ( key ) :
98+ return _mc_cache [key ]
99+ else :
100+ _mc_cache [key ] = ret = _monthcalendar (year , month )
156101 return ret
157102
158103# Center a string in a field
159- def center (str , width ):
104+ def _center (str , width ):
160105 n = width - len (str )
161- if n < 0 : return str
162- return ' ' * (n / 2 ) + str + ' ' * (n - n / 2 )
106+ if n <= 0 : return str
107+ return ' ' * (( n + 1 ) / 2 ) + str + ' ' * (( n ) / 2 )
163108
164109# XXX The following code knows that print separates items with space!
165110
166111# Print a single week (no newline)
167112def prweek (week , width ):
168113 for day in week :
169- if day == 0 : print ' ' * width ,
170- else :
171- if width > 2 : print ' ' * (width - 3 ),
172- if day < 10 : print '' ,
173- print day ,
114+ if day == 0 : s = ''
115+ else : s = `day`
116+ print _center (s , width ),
174117
175118# Return a header for a week
176119def weekheader (width ):
177120 str = ''
121+ if width >= 9 : names = day_name
122+ else : names = day_abbr
178123 for i in range (7 ):
179124 if str : str = str + ' '
180- str = str + day_abbr [i % 7 ][:width ]
125+ str = str + _center ( names [i % 7 ][:width ], width )
181126 return str
182127
183128# Print a month's calendar
184- def prmonth (year , month ):
185- print weekheader (3 )
129+ def prmonth (year , month , * rest ):
130+ if rest [2 :]: raise TypeError , 'too many args'
131+ w = 0
132+ l = 0
133+ if rest [0 :]: w = rest [0 ]
134+ if rest [1 :]: l = rest [1 ]
135+ w = max (2 , w )
136+ l = max (1 , l )
137+ print _center (month_name [month ] + ' ' + `year` , 7 * (w + 1 ) - 1 ),
138+ print '\n ' * l ,
139+ print weekheader (w ),
140+ print '\n ' * l ,
186141 for week in monthcalendar (year , month ):
187- prweek (week , 3 )
188- print
142+ prweek (week , w )
143+ print ' \n ' * l ,
189144
190- # Spacing between month columns
191- spacing = ' '
145+ # Spacing of month columns
146+ _colwidth = 7 * 3 - 1 # Amount printed by prweek()
147+ _spacing = ' ' * 4 # Spaces between columns
192148
193149# 3-column formatting for year calendars
194150def format3c (a , b , c ):
195- print center (a , 20 ), spacing , center (b , 20 ), spacing , center (c , 20 )
151+ print _center (a , _colwidth ),
152+ print _spacing ,
153+ print _center (b , _colwidth ),
154+ print _spacing ,
155+ print _center (c , _colwidth )
196156
197157# Print a year's calendar
198158def prcal (year ):
@@ -211,8 +171,8 @@ def prcal(year):
211171 for i in range (height ):
212172 for cal in data :
213173 if i >= len (cal ):
214- print ' ' * 20 ,
174+ print ' ' * _colwidth ,
215175 else :
216176 prweek (cal [i ], 2 )
217- print spacing ,
177+ print _spacing ,
218178 print
0 commit comments