diff options
Diffstat (limited to 'Lib/calendar.py')
-rw-r--r-- | Lib/calendar.py | 202 |
1 files changed, 81 insertions, 121 deletions
diff --git a/Lib/calendar.py b/Lib/calendar.py index 160b8ba..9ef9e3d 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -1,29 +1,40 @@ -# module calendar - ############################## # Calendar support functions # ############################## -# This is based on UNIX ctime() et al. (also Standard C and POSIX) -# Subtle but crucial differences: -# - the order of the elements of a 'struct tm' differs, to ease sorting -# - months numbers are 1-12, not 0-11; month arrays have a dummy element 0 -# - Monday is the first day of the week (numbered 0) -# - years are expressed in full, e.g. 1970, not 70. -# - timezone is currently hardcoded -# - doesn't know about daylight saving time +# Revision 2: uses funtions from built-in time module where possible. -# These are really parameters of the 'time' module: -epoch = 1970 # Time began on January 1 of this year (00:00:00 UTC) -day_0 = 3 # The epoch begins on a Thursday (Monday = 0) +# Import functions and variables from time module +from time import gmtime, localtime, mktime +from time import timezone, altzone, daylight, tzname -# Localization: Minutes West from Greenwich -timezone = -2*60 # Middle-European time with DST on -# timezone = 5*60 # EST (sigh -- THINK time() doesn't return UTC) +# Exception raised for bad input (with string parameter for details) +error = 'calendar.error' -# Return 1 for leap years, 0 for non-leap years -def isleap(year): - return year % 4 == 0 and (year % 100 <> 0 or year % 400 == 0) +# Abbreviated names of months (1-based arrays!!!) +month_abbr = [' ', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', \ + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + +# Turn calendar time as returned by localtime() into a string +def asctime(arg): + year, month, day, hours, mins, secs, wday, yday, isdst = arg + return '%s %s %02d %02d:%02d:%02d %04d' % ( + day_abbr[wday], month_abbr[month], day, + hours, mins, secs, year) + +# UNIX-style ctime (except it doesn't append '\n'!) +def ctime(secs): + return asctime(localtime(secs)) + +###################### +# Non-UNIX additions # +###################### + +# Calendar printing etc. + +# Note when comparing these calendars to the ones printed by cal(1): +# My calendars have Monday as the first day of the week, and Sunday as +# the last! (I believe this is the European convention.) # Constants for months referenced later January = 1 @@ -32,100 +43,34 @@ February = 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] -# Exception raised for bad input (with string parameter for details) -error = 'calendar error' - -# Turn seconds since epoch into calendar time -def gmtime(secs): - if secs < 0: raise error, 'negative input to gmtime()' - secs = int(secs) - mins, secs = divmod(secs, 60) - hours, mins = divmod(mins, 60) - days, hours = divmod(hours, 24) - wday = (days + day_0) % 7 - year = epoch - # XXX Most of the following loop can be replaced by one division - while 1: - yd = 365 + isleap(year) - if days < yd: break - days = days - yd - year = year + 1 - yday = days - month = January - while 1: - md = mdays[month] + (month == February and isleap(year)) - if days < md: break - days = days - md - month = month + 1 - return year, month, days + 1, hours, mins, secs, yday, wday - # XXX Week number also? - -# Return number of leap years in range [y1, y2) -# Assume y1 <= y2 and no funny (non-leap century) years -def leapdays(y1, y2): - return (y2+3)/4 - (y1+3)/4 - -# Inverse of gmtime(): -# Turn UTC calendar time (less yday, wday) into seconds since epoch -def mktime(year, month, day, hours, mins, secs): - days = day - 1 - for m in range(January, month): days = days + mdays[m] - if isleap(year) and month > February: days = days+1 - days = days + (year-epoch)*365 + leapdays(epoch, year) - return ((days*24 + hours)*60 + mins)*60 + secs - # Full and abbreviated names of weekdays day_name = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', \ 'Friday', 'Saturday', 'Sunday'] day_abbr = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] -# Full and abbreviated of months (1-based arrays!!!) +# Full names of months (1-based arrays!!!) month_name = ['', 'January', 'February', 'March', 'April', \ 'May', 'June', 'July', 'August', \ 'September', 'October', 'November', 'December'] -month_abbr = [' ', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', \ - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] - -# Zero-fill string to two positions (helper for asctime()) -def dd(s): - while len(s) < 2: s = '0' + s - return s - -# Blank-fill string to two positions (helper for asctime()) -def zd(s): - while len(s) < 2: s = ' ' + s - return s - -# Turn calendar time as returned by gmtime() into a string -# (the yday parameter is for compatibility with gmtime()) -def asctime(arg): - year, month, day, hours, mins, secs, yday, wday = arg - s = day_abbr[wday] + ' ' + month_abbr[month] + ' ' + zd(`day`) - s = s + ' ' + dd(`hours`) + ':' + dd(`mins`) + ':' + dd(`secs`) - return s + ' ' + `year` - -# Local time ignores DST issues for now -- adjust 'timezone' to fake it -def localtime(secs): - return gmtime(secs - timezone*60) - -# UNIX-style ctime (except it doesn't append '\n'!) -def ctime(secs): - return asctime(localtime(secs)) -###################### -# Non-UNIX additions # -###################### +# Return 1 for leap years, 0 for non-leap years +def isleap(year): + return year % 4 == 0 and (year % 100 <> 0 or year % 400 == 0) -# Calendar printing etc. +# Return number of leap years in range [y1, y2) +# Assume y1 <= y2 and no funny (non-leap century) years +def leapdays(y1, y2): + return (y2+3)/4 - (y1+3)/4 # Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12), day (1-31) def weekday(year, month, day): - secs = mktime(year, month, day, 0, 0, 0) - days = secs / (24*60*60) - return (days + day_0) % 7 + secs = mktime((year, month, day, 0, 0, 0, 0, 0, 0)) + tuple = localtime(secs) + return tuple[6] # Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for year, month def monthrange(year, month): + if not 1 <= month <= 12: raise ValueError, 'bad month number' day1 = weekday(year, month, 1) ndays = mdays[month] + (month == February and isleap(year)) return day1, ndays @@ -146,53 +91,68 @@ def _monthcalendar(year, month): return rows # Caching interface to _monthcalendar -mc_cache = {} +_mc_cache = {} def monthcalendar(year, month): - key = `year` + month_abbr[month] - try: - return mc_cache[key] - except KeyError: - mc_cache[key] = ret = _monthcalendar(year, month) + key = (year, month) + if _mc_cache.has_key(key): + return _mc_cache[key] + else: + _mc_cache[key] = ret = _monthcalendar(year, month) return ret # Center a string in a field -def center(str, width): +def _center(str, width): n = width - len(str) - if n < 0: return str - return ' '*(n/2) + str + ' '*(n-n/2) + if n <= 0: return str + return ' '*((n+1)/2) + str + ' '*((n)/2) # XXX The following code knows that print separates items with space! # Print a single week (no newline) def prweek(week, width): for day in week: - if day == 0: print ' '*width, - else: - if width > 2: print ' '*(width-3), - if day < 10: print '', - print day, + if day == 0: s = '' + else: s = `day` + print _center(s, width), # Return a header for a week def weekheader(width): str = '' + if width >= 9: names = day_name + else: names = day_abbr for i in range(7): if str: str = str + ' ' - str = str + day_abbr[i%7][:width] + str = str + _center(names[i%7][:width], width) return str # Print a month's calendar -def prmonth(year, month): - print weekheader(3) +def prmonth(year, month, *rest): + if rest[2:]: raise TypeError, 'too many args' + w = 0 + l = 0 + if rest[0:]: w = rest[0] + if rest[1:]: l = rest[1] + w = max(2, w) + l = max(1, l) + print _center(month_name[month] + ' ' + `year`, 7*(w+1) - 1), + print '\n'*l, + print weekheader(w), + print '\n'*l, for week in monthcalendar(year, month): - prweek(week, 3) - print + prweek(week, w) + print '\n'*l, -# Spacing between month columns -spacing = ' ' +# Spacing of month columns +_colwidth = 7*3 - 1 # Amount printed by prweek() +_spacing = ' '*4 # Spaces between columns # 3-column formatting for year calendars def format3c(a, b, c): - print center(a, 20), spacing, center(b, 20), spacing, center(c, 20) + print _center(a, _colwidth), + print _spacing, + print _center(b, _colwidth), + print _spacing, + print _center(c, _colwidth) # Print a year's calendar def prcal(year): @@ -211,8 +171,8 @@ def prcal(year): for i in range(height): for cal in data: if i >= len(cal): - print ' '*20, + print ' '*_colwidth, else: prweek(cal[i], 2) - print spacing, + print _spacing, print |