Kaydet (Commit) ad3bc44d authored tarafından Skip Montanaro's avatar Skip Montanaro

patches from David Goodger. Closes patch 101085.

* deletes cache
* adds firstweekday and setfirstweekday functions that allow user to control
  which day of the week is first when displaying calendars
* adds month, week, calendar functions that return their results instead of
  printing them
* adds symbolic constants MONDAY, ..., SUNDAY so users need not remember the
  ordinal values of the weekdays
üst 3cdb5761
"""Calendar printing functions""" """Calendar printing functions
Note when comparing these calendars to the ones printed by cal(1): 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 (0=Monday, 6=Sunday)."""
# Revision 2: uses functions from built-in time module # Revision 2: uses functions from built-in time module
...@@ -8,10 +13,6 @@ from time import localtime, mktime ...@@ -8,10 +13,6 @@ from time import localtime, mktime
# Exception raised for bad input (with string parameter for details) # Exception raised for bad input (with string parameter for details)
error = ValueError error = ValueError
# 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 # Constants for months referenced later
January = 1 January = 1
February = 2 February = 2
...@@ -31,35 +32,54 @@ month_name = ['', 'January', 'February', 'March', 'April', ...@@ -31,35 +32,54 @@ month_name = ['', 'January', 'February', 'March', 'April',
month_abbr = [' ', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', month_abbr = [' ', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
# Constants for weekdays
(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)
_firstweekday = 0 # 0 = Monday, 6 = Sunday
def firstweekday():
return _firstweekday
def setfirstweekday(weekday):
"""Set weekday (Monday=0, Sunday=6) to start each week."""
global _firstweekday
if not MONDAY <= weekday <= SUNDAY:
raise ValueError, \
'bad weekday number; must be 0 (Monday) to 6 (Sunday)'
_firstweekday = weekday
def isleap(year): def isleap(year):
"""Return 1 for leap years, 0 for non-leap years.""" """Return 1 for leap years, 0 for non-leap years."""
return year % 4 == 0 and (year % 100 <> 0 or year % 400 == 0) return year % 4 == 0 and (year % 100 <> 0 or year % 400 == 0)
def leapdays(y1, y2): def leapdays(y1, y2):
"""Return number of leap years in range [y1, y2). """Return number of leap years in range [y1, y2).
Assume y1 <= y2 and no funny (non-leap century) years.""" Assume y1 <= y2 and no funny (non-leap century) years."""
return (y2+3)/4 - (y1+3)/4 return (y2+3)/4 - (y1+3)/4
def weekday(year, month, day): def weekday(year, month, day):
"""Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12), day (1-31).""" """Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12),
day (1-31)."""
secs = mktime((year, month, day, 0, 0, 0, 0, 0, 0)) secs = mktime((year, month, day, 0, 0, 0, 0, 0, 0))
tuple = localtime(secs) tuple = localtime(secs)
return tuple[6] return tuple[6]
def monthrange(year, month): def monthrange(year, month):
"""Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for year, month.""" """Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for
if not 1 <= month <= 12: raise ValueError, 'bad month number' year, month."""
if not 1 <= month <= 12:
raise ValueError, 'bad month number'
day1 = weekday(year, month, 1) day1 = weekday(year, month, 1)
ndays = mdays[month] + (month == February and isleap(year)) ndays = mdays[month] + (month == February and isleap(year))
return day1, ndays return day1, ndays
def _monthcalendar(year, month): def monthcalendar(year, month):
"""Return a matrix representing a month's calendar. """Return a matrix representing a month's calendar.
Each row represents a week; days outside this month are zero.""" Each row represents a week; days outside this month are zero."""
day1, ndays = monthrange(year, month) day1, ndays = monthrange(year, month)
rows = [] rows = []
r7 = range(7) r7 = range(7)
day = 1 - day1 day = (_firstweekday - day1 + 6) % 7 - 5 # for leading 0's in first week
while day <= ndays: while day <= ndays:
row = [0, 0, 0, 0, 0, 0, 0] row = [0, 0, 0, 0, 0, 0, 0]
for i in r7: for i in r7:
...@@ -68,87 +88,102 @@ def _monthcalendar(year, month): ...@@ -68,87 +88,102 @@ def _monthcalendar(year, month):
rows.append(row) rows.append(row)
return rows return rows
_mc_cache = {}
def monthcalendar(year, month):
"""Caching interface to _monthcalendar."""
key = (year, month)
if _mc_cache.has_key(key):
return _mc_cache[key]
else:
_mc_cache[key] = ret = _monthcalendar(year, month)
return ret
def _center(str, width): def _center(str, width):
"""Center a string in a field.""" """Center a string in a field."""
n = width - len(str) n = width - len(str)
if n <= 0: return str if n <= 0:
return str
return ' '*((n+1)/2) + str + ' '*((n)/2) return ' '*((n+1)/2) + str + ' '*((n)/2)
# XXX The following code knows that print separates items with space! def prweek(theweek, width):
def prweek(week, width):
"""Print a single week (no newline).""" """Print a single week (no newline)."""
for day in week: print week(theweek, width),
if day == 0: s = ''
else: s = `day` def week(theweek, width):
print _center(s, width), """Returns a single week in a string (no newline)."""
days = []
for day in theweek:
if day == 0:
s = ''
else:
s = '%2i' % day # right-align single-digit days
days.append(_center(s, width))
return ' '.join(days)
def weekheader(width): def weekheader(width):
"""Return a header for a week.""" """Return a header for a week."""
str = '' if width >= 9:
if width >= 9: names = day_name names = day_name
else: names = day_abbr else:
for i in range(7): names = day_abbr
if str: str = str + ' ' days = []
str = str + _center(names[i%7][:width], width) for i in range(_firstweekday, _firstweekday + 7):
return str days.append(_center(names[i%7][:width], width))
return ' '.join(days)
def prmonth(year, month, w = 0, l = 0):
def prmonth(theyear, themonth, w=0, l=0):
"""Print a month's calendar.""" """Print a month's calendar."""
print month(theyear, themonth, w, l),
def month(theyear, themonth, w=0, l=0):
"""Return a month's calendar string (multi-line)."""
w = max(2, w) w = max(2, w)
l = max(1, l) l = max(1, l)
print _center(month_name[month] + ' ' + `year`, 7*(w+1) - 1), s = (_center(month_name[themonth] + ' ' + `theyear`,
print '\n'*l, 7 * (w + 1) - 1).rstrip() +
print weekheader(w), '\n' * l + weekheader(w).rstrip() + '\n' * l)
print '\n'*l, for aweek in monthcalendar(theyear, themonth):
for week in monthcalendar(year, month): s = s + week(aweek, w).rstrip() + '\n' * l
prweek(week, w) return s[:-l] + '\n'
print '\n'*l,
# Spacing of month columns for 3-column year calendar
# Spacing of month columns
_colwidth = 7*3 - 1 # Amount printed by prweek() _colwidth = 7*3 - 1 # Amount printed by prweek()
_spacing = ' '*4 # Spaces between columns _spacing = 6 # Number of spaces between columns
def format3c(a, b, c): def format3c(a, b, c, colwidth=_colwidth, spacing=_spacing):
"""3-column formatting for year calendars""" """Prints 3-column formatting for year calendars"""
print _center(a, _colwidth), print format3cstring(a, b, c, colwidth, spacing)
print _spacing,
print _center(b, _colwidth),
print _spacing,
print _center(c, _colwidth)
def prcal(year): def format3cstring(a, b, c, colwidth=_colwidth, spacing=_spacing):
"""Returns a string formatted from 3 strings, centered within 3 columns."""
return (_center(a, colwidth) + ' ' * spacing + _center(b, colwidth) +
' ' * spacing + _center(c, colwidth))
def prcal(year, w=0, l=0, c=_spacing):
"""Print a year's calendar.""" """Print a year's calendar."""
header = weekheader(2) print calendar(year, w, l, c),
format3c('', `year`, '')
def calendar(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 - 1
s = _center(`year`, colwidth * 3 + c * 2).rstrip() + '\n' * l
header = weekheader(w)
header = format3cstring(header, header, header, colwidth, c).rstrip()
for q in range(January, January+12, 3): for q in range(January, January+12, 3):
print s = (s + '\n' * l +
format3c(month_name[q], month_name[q+1], month_name[q+2]) format3cstring(month_name[q], month_name[q+1], month_name[q+2],
format3c(header, header, header) colwidth, c).rstrip() +
'\n' * l + header + '\n' * l)
data = [] data = []
height = 0 height = 0
for month in range(q, q+3): for amonth in range(q, q + 3):
cal = monthcalendar(year, month) cal = monthcalendar(year, amonth)
if len(cal) > height: height = len(cal) if len(cal) > height:
height = len(cal)
data.append(cal) data.append(cal)
for i in range(height): for i in range(height):
weeks = []
for cal in data: for cal in data:
if i >= len(cal): if i >= len(cal):
print ' '*_colwidth, weeks.append('')
else: else:
prweek(cal[i], 2) weeks.append(week(cal[i], w))
print _spacing, s = s + format3cstring(weeks[0], weeks[1], weeks[2],
print colwidth, c).rstrip() + '\n' * l
return s[:-l] + '\n'
EPOCH = 1970 EPOCH = 1970
def timegm(tuple): def timegm(tuple):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment