0001 """Calendar printing functions 0002 0003 Note when comparing these calendars to the ones printed by cal(1): By 0004 default, these calendars have Monday as the first day of the week, and 0005 Sunday as the last (the European convention). Use setfirstweekday() to 0006 set the first day of the week (0=Monday, 6=Sunday).""" 0007 0008 import datetime 0009 0010 __all__ = ["error","setfirstweekday","firstweekday","isleap", 0011 "leapdays","weekday","monthrange","monthcalendar", 0012 "prmonth","month","prcal","calendar","timegm", 0013 "month_name", "month_abbr", "day_name", "day_abbr"] 0014 0015 # Exception raised for bad input (with string parameter for details) 0016 error = ValueError 0017 0018 # Constants for months referenced later 0019 January = 1 0020 February = 2 0021 0022 # Number of days per month (except for February in leap years) 0023 mdays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 0024 0025 # This module used to have hard-coded lists of day and month names, as 0026 # English strings. The classes following emulate a read-only version of 0027 # that, but supply localized names. Note that the values are computed 0028 # fresh on each call, in case the user changes locale between calls. 0029 0030 class _localized_month: 0031 def __init__(self, format): 0032 self.format = format 0033 0034 def __getitem__(self, i): 0035 data = [datetime.date(2001, j, 1).strftime(self.format) 0036 for j in range(1, 13)] 0037 data.insert(0, "") 0038 return data[i] 0039 0040 def __len__(self): 0041 return 13 0042 0043 class _localized_day: 0044 def __init__(self, format): 0045 self.format = format 0046 0047 def __getitem__(self, i): 0048 # January 1, 2001, was a Monday. 0049 data = [datetime.date(2001, 1, j+1).strftime(self.format) 0050 for j in range(7)] 0051 return data[i] 0052 0053 def __len__(self): 0054 return 7 0055 0056 # Full and abbreviated names of weekdays 0057 day_name = _localized_day('%A') 0058 day_abbr = _localized_day('%a') 0059 0060 # Full and abbreviated names of months (1-based arrays!!!) 0061 month_name = _localized_month('%B') 0062 month_abbr = _localized_month('%b') 0063 0064 # Constants for weekdays 0065 (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7) 0066 0067 _firstweekday = 0 # 0 = Monday, 6 = Sunday 0068 0069 def firstweekday(): 0070 return _firstweekday 0071 0072 def setfirstweekday(weekday): 0073 """Set weekday (Monday=0, Sunday=6) to start each week.""" 0074 global _firstweekday 0075 if not MONDAY <= weekday <= SUNDAY: 0076 raise ValueError, \ 0077 'bad weekday number; must be 0 (Monday) to 6 (Sunday)' 0078 _firstweekday = weekday 0079 0080 def isleap(year): 0081 """Return 1 for leap years, 0 for non-leap years.""" 0082 return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) 0083 0084 def leapdays(y1, y2): 0085 """Return number of leap years in range [y1, y2). 0086 Assume y1 <= y2.""" 0087 y1 -= 1 0088 y2 -= 1 0089 return (y2//4 - y1//4) - (y2//100 - y1//100) + (y2//400 - y1//400) 0090 0091 def weekday(year, month, day): 0092 """Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12), 0093 day (1-31).""" 0094 return datetime.date(year, month, day).weekday() 0095 0096 def monthrange(year, month): 0097 """Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for 0098 year, month.""" 0099 if not 1 <= month <= 12: 0100 raise ValueError, 'bad month number' 0101 day1 = weekday(year, month, 1) 0102 ndays = mdays[month] + (month == February and isleap(year)) 0103 return day1, ndays 0104 0105 def monthcalendar(year, month): 0106 """Return a matrix representing a month's calendar. 0107 Each row represents a week; days outside this month are zero.""" 0108 day1, ndays = monthrange(year, month) 0109 rows = [] 0110 r7 = range(7) 0111 day = (_firstweekday - day1 + 6) % 7 - 5 # for leading 0's in first week 0112 while day <= ndays: 0113 row = [0, 0, 0, 0, 0, 0, 0] 0114 for i in r7: 0115 if 1 <= day <= ndays: row[i] = day 0116 day = day + 1 0117 rows.append(row) 0118 return rows 0119 0120 def prweek(theweek, width): 0121 """Print a single week (no newline).""" 0122 print week(theweek, width), 0123 0124 def week(theweek, width): 0125 """Returns a single week in a string (no newline).""" 0126 days = [] 0127 for day in theweek: 0128 if day == 0: 0129 s = '' 0130 else: 0131 s = '%2i' % day # right-align single-digit days 0132 days.append(s.center(width)) 0133 return ' '.join(days) 0134 0135 def weekheader(width): 0136 """Return a header for a week.""" 0137 if width >= 9: 0138 names = day_name 0139 else: 0140 names = day_abbr 0141 days = [] 0142 for i in range(_firstweekday, _firstweekday + 7): 0143 days.append(names[i%7][:width].center(width)) 0144 return ' '.join(days) 0145 0146 def prmonth(theyear, themonth, w=0, l=0): 0147 """Print a month's calendar.""" 0148 print month(theyear, themonth, w, l), 0149 0150 def month(theyear, themonth, w=0, l=0): 0151 """Return a month's calendar string (multi-line).""" 0152 w = max(2, w) 0153 l = max(1, l) 0154 s = ("%s %r" % (month_name[themonth], theyear)).center( 0155 7 * (w + 1) - 1).rstrip() + \ 0156 '\n' * l + weekheader(w).rstrip() + '\n' * l 0157 for aweek in monthcalendar(theyear, themonth): 0158 s = s + week(aweek, w).rstrip() + '\n' * l 0159 return s[:-l] + '\n' 0160 0161 # Spacing of month columns for 3-column year calendar 0162 _colwidth = 7*3 - 1 # Amount printed by prweek() 0163 _spacing = 6 # Number of spaces between columns 0164 0165 def format3c(a, b, c, colwidth=_colwidth, spacing=_spacing): 0166 """Prints 3-column formatting for year calendars""" 0167 print format3cstring(a, b, c, colwidth, spacing) 0168 0169 def format3cstring(a, b, c, colwidth=_colwidth, spacing=_spacing): 0170 """Returns a string formatted from 3 strings, centered within 3 columns.""" 0171 return (a.center(colwidth) + ' ' * spacing + b.center(colwidth) + 0172 ' ' * spacing + c.center(colwidth)) 0173 0174 def prcal(year, w=0, l=0, c=_spacing): 0175 """Print a year's calendar.""" 0176 print calendar(year, w, l, c), 0177 0178 def calendar(year, w=0, l=0, c=_spacing): 0179 """Returns a year's calendar as a multi-line string.""" 0180 w = max(2, w) 0181 l = max(1, l) 0182 c = max(2, c) 0183 colwidth = (w + 1) * 7 - 1 0184 s = repr(year).center(colwidth * 3 + c * 2).rstrip() + '\n' * l 0185 header = weekheader(w) 0186 header = format3cstring(header, header, header, colwidth, c).rstrip() 0187 for q in range(January, January+12, 3): 0188 s = (s + '\n' * l + 0189 format3cstring(month_name[q], month_name[q+1], month_name[q+2], 0190 colwidth, c).rstrip() + 0191 '\n' * l + header + '\n' * l) 0192 data = [] 0193 height = 0 0194 for amonth in range(q, q + 3): 0195 cal = monthcalendar(year, amonth) 0196 if len(cal) > height: 0197 height = len(cal) 0198 data.append(cal) 0199 for i in range(height): 0200 weeks = [] 0201 for cal in data: 0202 if i >= len(cal): 0203 weeks.append('') 0204 else: 0205 weeks.append(week(cal[i], w)) 0206 s = s + format3cstring(weeks[0], weeks[1], weeks[2], 0207 colwidth, c).rstrip() + '\n' * l 0208 return s[:-l] + '\n' 0209 0210 EPOCH = 1970 0211 _EPOCH_ORD = datetime.date(EPOCH, 1, 1).toordinal() 0212 0213 def timegm(tuple): 0214 """Unrelated but handy function to calculate Unix timestamp from GMT.""" 0215 year, month, day, hour, minute, second = tuple[:6] 0216 days = datetime.date(year, month, 1).toordinal() - _EPOCH_ORD + day - 1 0217 hours = days*24 + hour 0218 minutes = hours*60 + minute 0219 seconds = minutes*60 + second 0220 return seconds 0221
Generated by PyXR 0.9.4