blob: 4965773c73c19e53ba11da36f52c5037d2b6b603 [file] [log] [blame]
Skip Montanaroad3bc442000-08-30 14:01:28 +00001"""Calendar printing functions
2
3Note when comparing these calendars to the ones printed by cal(1): By
4default, these calendars have Monday as the first day of the week, and
5Sunday as the last (the European convention). Use setfirstweekday() to
6set the first day of the week (0=Monday, 6=Sunday)."""
Guido van Rossumc6360141990-10-13 19:23:40 +00007
Walter Dörwald48d5e502006-04-01 07:57:00 +00008import sys, datetime, locale
Guido van Rossumc6360141990-10-13 19:23:40 +00009
Walter Dörwald58917a62006-03-31 15:26:22 +000010__all__ = ["IllegalMonthError", "IllegalWeekdayError", "setfirstweekday",
11 "firstweekday", "isleap", "leapdays", "weekday", "monthrange",
12 "monthcalendar", "prmonth", "month", "prcal", "calendar",
13 "timegm", "month_name", "month_abbr", "day_name", "day_abbr"]
Skip Montanaroe99d5ea2001-01-20 19:54:20 +000014
Guido van Rossumc6360141990-10-13 19:23:40 +000015# Exception raised for bad input (with string parameter for details)
Guido van Rossum00245cf1999-05-03 18:07:40 +000016error = ValueError
Guido van Rossumc6360141990-10-13 19:23:40 +000017
Walter Dörwald58917a62006-03-31 15:26:22 +000018# Exceptions raised for bad input
19class IllegalMonthError(ValueError):
20 def __init__(self, month):
21 self.month = month
22 def __str__(self):
23 return "bad month number %r; must be 1-12" % self.month
24
25
26class IllegalWeekdayError(ValueError):
27 def __init__(self, weekday):
28 self.weekday = weekday
29 def __str__(self):
30 return "bad weekday number %r; must be 0 (Monday) to 6 (Sunday)" % self.weekday
31
32
Guido van Rossum9b3bc711993-06-20 21:02:22 +000033# Constants for months referenced later
34January = 1
35February = 2
36
37# Number of days per month (except for February in leap years)
38mdays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
39
Tim Peters0c2c8e72002-03-23 03:26:53 +000040# This module used to have hard-coded lists of day and month names, as
41# English strings. The classes following emulate a read-only version of
42# that, but supply localized names. Note that the values are computed
43# fresh on each call, in case the user changes locale between calls.
44
Raymond Hettinger9c051d72002-06-20 03:38:12 +000045class _localized_month:
Tim Petersbbc0d442004-11-13 16:18:32 +000046
Walter Dörwald58917a62006-03-31 15:26:22 +000047 _months = [datetime.date(2001, i+1, 1).strftime for i in xrange(12)]
Tim Petersbbc0d442004-11-13 16:18:32 +000048 _months.insert(0, lambda x: "")
49
Tim Peters0c2c8e72002-03-23 03:26:53 +000050 def __init__(self, format):
Barry Warsaw1d099102001-05-22 15:58:30 +000051 self.format = format
Tim Peters0c2c8e72002-03-23 03:26:53 +000052
53 def __getitem__(self, i):
Tim Petersbbc0d442004-11-13 16:18:32 +000054 funcs = self._months[i]
55 if isinstance(i, slice):
56 return [f(self.format) for f in funcs]
57 else:
58 return funcs(self.format)
Tim Peters0c2c8e72002-03-23 03:26:53 +000059
Skip Montanaro4c834952002-03-15 04:08:38 +000060 def __len__(self):
Tim Peters0c2c8e72002-03-23 03:26:53 +000061 return 13
62
Walter Dörwald58917a62006-03-31 15:26:22 +000063
Raymond Hettinger9c051d72002-06-20 03:38:12 +000064class _localized_day:
Tim Petersbbc0d442004-11-13 16:18:32 +000065
66 # January 1, 2001, was a Monday.
Walter Dörwald58917a62006-03-31 15:26:22 +000067 _days = [datetime.date(2001, 1, i+1).strftime for i in xrange(7)]
Tim Petersbbc0d442004-11-13 16:18:32 +000068
Tim Peters0c2c8e72002-03-23 03:26:53 +000069 def __init__(self, format):
70 self.format = format
71
72 def __getitem__(self, i):
Tim Petersbbc0d442004-11-13 16:18:32 +000073 funcs = self._days[i]
74 if isinstance(i, slice):
75 return [f(self.format) for f in funcs]
76 else:
77 return funcs(self.format)
Tim Peters0c2c8e72002-03-23 03:26:53 +000078
Neal Norwitz492faa52004-06-07 03:47:06 +000079 def __len__(self):
Tim Peters0c2c8e72002-03-23 03:26:53 +000080 return 7
Barry Warsaw1d099102001-05-22 15:58:30 +000081
Walter Dörwald58917a62006-03-31 15:26:22 +000082
Guido van Rossum9b3bc711993-06-20 21:02:22 +000083# Full and abbreviated names of weekdays
Tim Peters0c2c8e72002-03-23 03:26:53 +000084day_name = _localized_day('%A')
85day_abbr = _localized_day('%a')
Guido van Rossum9b3bc711993-06-20 21:02:22 +000086
Guido van Rossum5cfa5df1993-06-23 09:30:50 +000087# Full and abbreviated names of months (1-based arrays!!!)
Tim Peters0c2c8e72002-03-23 03:26:53 +000088month_name = _localized_month('%B')
89month_abbr = _localized_month('%b')
Guido van Rossum9b3bc711993-06-20 21:02:22 +000090
Skip Montanaroad3bc442000-08-30 14:01:28 +000091# Constants for weekdays
92(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)
93
Skip Montanaroad3bc442000-08-30 14:01:28 +000094
Guido van Rossum9b3bc711993-06-20 21:02:22 +000095def isleap(year):
Guido van Rossum4acc25b2000-02-02 15:10:15 +000096 """Return 1 for leap years, 0 for non-leap years."""
Fred Drake8152d322000-12-12 23:20:45 +000097 return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
Guido van Rossum9b3bc711993-06-20 21:02:22 +000098
Walter Dörwald58917a62006-03-31 15:26:22 +000099
Guido van Rossum9b3bc711993-06-20 21:02:22 +0000100def leapdays(y1, y2):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000101 """Return number of leap years in range [y1, y2).
Guido van Rossum46735ad2000-10-09 12:42:04 +0000102 Assume y1 <= y2."""
103 y1 -= 1
104 y2 -= 1
Raymond Hettingere11b5102002-12-25 16:37:19 +0000105 return (y2//4 - y1//4) - (y2//100 - y1//100) + (y2//400 - y1//400)
Guido van Rossum9b3bc711993-06-20 21:02:22 +0000106
Walter Dörwald58917a62006-03-31 15:26:22 +0000107
Guido van Rossumc6360141990-10-13 19:23:40 +0000108def weekday(year, month, day):
Skip Montanaroad3bc442000-08-30 14:01:28 +0000109 """Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12),
110 day (1-31)."""
Raymond Hettingere11b5102002-12-25 16:37:19 +0000111 return datetime.date(year, month, day).weekday()
Guido van Rossumc6360141990-10-13 19:23:40 +0000112
Walter Dörwald58917a62006-03-31 15:26:22 +0000113
Guido van Rossumc6360141990-10-13 19:23:40 +0000114def monthrange(year, month):
Skip Montanaroad3bc442000-08-30 14:01:28 +0000115 """Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for
116 year, month."""
117 if not 1 <= month <= 12:
Walter Dörwald58917a62006-03-31 15:26:22 +0000118 raise IllegalMonthError(month)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000119 day1 = weekday(year, month, 1)
120 ndays = mdays[month] + (month == February and isleap(year))
121 return day1, ndays
Guido van Rossumc6360141990-10-13 19:23:40 +0000122
Guido van Rossumc6360141990-10-13 19:23:40 +0000123
Walter Dörwald58917a62006-03-31 15:26:22 +0000124class Calendar(object):
125 """
126 Base calendar class. This class doesn't do any formatting. It simply
127 provides data to subclasses.
128 """
Skip Montanaroad3bc442000-08-30 14:01:28 +0000129
Walter Dörwald58917a62006-03-31 15:26:22 +0000130 def __init__(self, firstweekday=0):
Walter Dörwaldf878b812006-04-01 20:40:23 +0000131 self.firstweekday = firstweekday # 0 = Monday, 6 = Sunday
Walter Dörwald58917a62006-03-31 15:26:22 +0000132
Walter Dörwaldaba10cf2006-04-03 15:20:28 +0000133 def getfirstweekday(self):
Walter Dörwald72d84af2006-04-03 15:21:59 +0000134 return self._firstweekday % 7
Walter Dörwaldaba10cf2006-04-03 15:20:28 +0000135
136 def setfirstweekday(self, firstweekday):
Walter Dörwaldaba10cf2006-04-03 15:20:28 +0000137 self._firstweekday = firstweekday
138 firstweekday = property(getfirstweekday, setfirstweekday)
139
Walter Dörwald58917a62006-03-31 15:26:22 +0000140 def iterweekdays(self):
141 """
142 Return a iterator for one week of weekday numbers starting with the
143 configured first one.
144 """
Walter Dörwaldf878b812006-04-01 20:40:23 +0000145 for i in xrange(self.firstweekday, self.firstweekday + 7):
Walter Dörwald58917a62006-03-31 15:26:22 +0000146 yield i%7
147
148 def itermonthdates(self, year, month):
149 """
150 Return an iterator for one month. The iterator will yield datetime.date
151 values and will always iterate through complete weeks, so it will yield
152 dates outside the specified month.
153 """
154 date = datetime.date(year, month, 1)
155 # Go back to the beginning of the week
Walter Dörwaldf878b812006-04-01 20:40:23 +0000156 days = (date.weekday() - self.firstweekday) % 7
Walter Dörwald58917a62006-03-31 15:26:22 +0000157 date -= datetime.timedelta(days=days)
158 oneday = datetime.timedelta(days=1)
159 while True:
160 yield date
161 date += oneday
Walter Dörwald04ee8702006-04-02 22:11:10 +0000162 if date.month != month and date.weekday() == self.firstweekday%7:
Walter Dörwald58917a62006-03-31 15:26:22 +0000163 break
164
165 def itermonthdays2(self, year, month):
166 """
167 Like itermonthdates(), but will yield (day number, weekday number)
168 tuples. For days outside the specified month the day number is 0.
169 """
170 for date in self.itermonthdates(year, month):
171 if date.month != month:
172 yield (0, date.weekday())
173 else:
174 yield (date.day, date.weekday())
175
176 def itermonthdays(self, year, month):
177 """
178 Like itermonthdates(), but will yield day numbers tuples. For days
179 outside the specified month the day number is 0.
180 """
181 for date in self.itermonthdates(year, month):
182 if date.month != month:
183 yield 0
184 else:
185 yield date.day
186
187 def monthdatescalendar(self, year, month):
188 """
189 Return a matrix (list of lists) representing a month's calendar.
190 Each row represents a week; week entries are datetime.date values.
191 """
192 dates = list(self.itermonthdates(year, month))
193 return [ dates[i:i+7] for i in xrange(0, len(dates), 7) ]
194
195 def monthdays2calendar(self, year, month):
196 """
197 Return a matrix representing a month's calendar.
198 Each row represents a week; week entries are
199 (day number, weekday number) tuples. Day numbers outside this month
200 are zero.
201 """
202 days = list(self.itermonthdays2(year, month))
203 return [ days[i:i+7] for i in xrange(0, len(days), 7) ]
204
205 def monthdayscalendar(self, year, month):
206 """
207 Return a matrix representing a month's calendar.
208 Each row represents a week; days outside this month are zero.
209 """
210 days = list(self.itermonthdays(year, month))
211 return [ days[i:i+7] for i in xrange(0, len(days), 7) ]
212
213 def yeardatescalendar(self, year, width=3):
214 """
215 Return the data for the specified year ready for formatting. The return
216 value is a list of month rows. Each month row contains upto width months.
217 Each month contains between 4 and 6 weeks and each week contains 1-7
218 days. Days are datetime.date objects.
219 """
220 months = [
221 self.monthdatescalendar(year, i)
222 for i in xrange(January, January+12)
223 ]
224 return [months[i:i+width] for i in xrange(0, len(months), width) ]
225
226 def yeardays2calendar(self, year, width=3):
227 """
228 Return the data for the specified year ready for formatting (similar to
229 yeardatescalendar()). Entries in the week lists are
230 (day number, weekday number) tuples. Day numbers outside this month are
231 zero.
232 """
233 months = [
234 self.monthdays2calendar(year, i)
235 for i in xrange(January, January+12)
236 ]
237 return [months[i:i+width] for i in xrange(0, len(months), width) ]
238
239 def yeardayscalendar(self, year, width=3):
240 """
241 Return the data for the specified year ready for formatting (similar to
242 yeardatescalendar()). Entries in the week lists are day numbers.
243 Day numbers outside this month are zero.
244 """
245 months = [
246 self.monthdayscalendar(year, i)
247 for i in xrange(January, January+12)
248 ]
249 return [months[i:i+width] for i in xrange(0, len(months), width) ]
250
251
252class TextCalendar(Calendar):
253 """
254 Subclass of Calendar that outputs a calendar as a simple plain text
255 similar to the UNIX program cal.
256 """
257
258 def prweek(theweek, width):
259 """
260 Print a single week (no newline).
261 """
262 print self.week(theweek, width),
263
264 def formatday(self, day, weekday, width):
265 """
266 Returns a formatted day.
267 """
Skip Montanaroad3bc442000-08-30 14:01:28 +0000268 if day == 0:
269 s = ''
270 else:
271 s = '%2i' % day # right-align single-digit days
Walter Dörwald58917a62006-03-31 15:26:22 +0000272 return s.center(width)
Guido van Rossumc6360141990-10-13 19:23:40 +0000273
Walter Dörwald58917a62006-03-31 15:26:22 +0000274 def formatweek(self, theweek, width):
275 """
276 Returns a single week in a string (no newline).
277 """
278 return ' '.join(self.formatday(d, wd, width) for (d, wd) in theweek)
Guido van Rossumc6360141990-10-13 19:23:40 +0000279
Walter Dörwald58917a62006-03-31 15:26:22 +0000280 def formatweekday(self, day, width):
281 """
282 Returns a formatted week day name.
283 """
284 if width >= 9:
285 names = day_name
286 else:
287 names = day_abbr
288 return names[day][:width].center(width)
Skip Montanaroad3bc442000-08-30 14:01:28 +0000289
Walter Dörwald58917a62006-03-31 15:26:22 +0000290 def formatweekheader(self, width):
291 """
292 Return a header for a week.
293 """
294 return ' '.join(self.formatweekday(i, width) for i in self.iterweekdays())
Guido van Rossumc6360141990-10-13 19:23:40 +0000295
Walter Dörwald48d5e502006-04-01 07:57:00 +0000296 def formatmonthname(self, theyear, themonth, width, withyear=True):
Walter Dörwald58917a62006-03-31 15:26:22 +0000297 """
298 Return a formatted month name.
299 """
Walter Dörwald48d5e502006-04-01 07:57:00 +0000300 s = month_name[themonth]
301 if withyear:
302 s = "%s %r" % (s, theyear)
Walter Dörwald58917a62006-03-31 15:26:22 +0000303 return s.center(width)
304
305 def prmonth(self, theyear, themonth, w=0, l=0):
306 """
307 Print a month's calendar.
308 """
309 print self.formatmonth(theyear, themonth, w, l),
310
311 def formatmonth(self, theyear, themonth, w=0, l=0):
312 """
313 Return a month's calendar string (multi-line).
314 """
315 w = max(2, w)
316 l = max(1, l)
317 s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1)
318 s = s.rstrip()
319 s += '\n' * l
320 s += self.formatweekheader(w).rstrip()
321 s += '\n' * l
322 for week in self.monthdays2calendar(theyear, themonth):
323 s += self.formatweek(week, w).rstrip()
324 s += '\n' * l
325 return s
326
327 def formatyear(self, theyear, w=2, l=1, c=6, m=3):
328 """
329 Returns a year's calendar as a multi-line string.
330 """
331 w = max(2, w)
332 l = max(1, l)
333 c = max(2, c)
334 colwidth = (w + 1) * 7 - 1
335 v = []
336 a = v.append
337 a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip())
338 a('\n'*l)
339 header = self.formatweekheader(w)
340 for (i, row) in enumerate(self.yeardays2calendar(theyear, m)):
341 # months in this row
342 months = xrange(m*i+1, min(m*(i+1)+1, 13))
343 a('\n'*l)
Walter Dörwald48d5e502006-04-01 07:57:00 +0000344 names = (self.formatmonthname(theyear, k, colwidth, False)
345 for k in months)
346 a(formatstring(names, colwidth, c).rstrip())
Walter Dörwald58917a62006-03-31 15:26:22 +0000347 a('\n'*l)
Walter Dörwald48d5e502006-04-01 07:57:00 +0000348 headers = (header for k in months)
349 a(formatstring(headers, colwidth, c).rstrip())
Walter Dörwald58917a62006-03-31 15:26:22 +0000350 a('\n'*l)
351 # max number of weeks for this row
352 height = max(len(cal) for cal in row)
353 for j in xrange(height):
354 weeks = []
355 for cal in row:
356 if j >= len(cal):
357 weeks.append('')
358 else:
359 weeks.append(self.formatweek(cal[j], w))
360 a(formatstring(weeks, colwidth, c).rstrip())
361 a('\n' * l)
362 return ''.join(v)
363
364 def pryear(self, theyear, w=0, l=0, c=6, m=3):
365 """Print a year's calendar."""
366 print self.formatyear(theyear, w, l, c, m)
367
368
369class HTMLCalendar(Calendar):
370 """
371 This calendar returns complete HTML pages.
372 """
373
374 # CSS classes for the day <td>s
375 cssclasses = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
376
377 def formatday(self, day, weekday):
378 """
379 Return a day as a table cell.
380 """
381 if day == 0:
382 return '<td class="noday">&nbsp;</td>' # day outside month
383 else:
384 return '<td class="%s">%d</td>' % (self.cssclasses[weekday], day)
385
386 def formatweek(self, theweek):
387 """
388 Return a complete week as a table row.
389 """
390 s = ''.join(self.formatday(d, wd) for (d, wd) in theweek)
391 return '<tr>%s</tr>' % s
392
393 def formatweekday(self, day):
394 """
395 Return a weekday name as a table header.
396 """
397 return '<th class="%s">%s</th>' % (self.cssclasses[day], day_abbr[day])
398
399 def formatweekheader(self):
400 """
401 Return a header for a week as a table row.
402 """
403 s = ''.join(self.formatweekday(i) for i in self.iterweekdays())
404 return '<tr>%s</tr>' % s
405
406 def formatmonthname(self, theyear, themonth, withyear=True):
407 """
408 Return a month name as a table row.
409 """
410 if withyear:
411 s = '%s %s' % (month_name[themonth], theyear)
412 else:
413 s = '%s' % month_name[themonth]
414 return '<tr><th colspan="7" class="month">%s</th></tr>' % s
415
416 def formatmonth(self, theyear, themonth, withyear=True):
417 """
418 Return a formatted month as a table.
419 """
420 v = []
421 a = v.append
422 a('<table border="0" cellpadding="0" cellspacing="0" class="month">')
423 a('\n')
424 a(self.formatmonthname(theyear, themonth, withyear=withyear))
425 a('\n')
426 a(self.formatweekheader())
427 a('\n')
428 for week in self.monthdays2calendar(theyear, themonth):
429 a(self.formatweek(week))
430 a('\n')
431 a('</table>')
432 a('\n')
433 return ''.join(v)
434
435 def formatyear(self, theyear, width=3):
436 """
437 Return a formatted year as a table of tables.
438 """
439 v = []
440 a = v.append
441 width = max(width, 1)
442 a('<table border="0" cellpadding="0" cellspacing="0" class="year">')
443 a('\n')
444 a('<tr><th colspan="%d" class="year">%s</th></tr>' % (width, theyear))
445 for i in xrange(January, January+12, width):
446 # months in this row
447 months = xrange(i, min(i+width, 13))
448 a('<tr>')
449 for m in months:
450 a('<td>')
451 a(self.formatmonth(theyear, m, withyear=False))
452 a('</td>')
453 a('</tr>')
454 a('</table>')
455 return ''.join(v)
456
457 def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None):
458 """
459 Return a formatted year as a complete HTML page.
460 """
461 if encoding is None:
462 encoding = sys.getdefaultencoding()
463 v = []
464 a = v.append
465 a('<?xml version="1.0" encoding="%s"?>\n' % encoding)
466 a('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n')
467 a('<html>\n')
468 a('<head>\n')
469 a('<meta http-equiv="Content-Type" content="text/html; charset=%s" />\n' % encoding)
470 if css is not None:
471 a('<link rel="stylesheet" type="text/css" href="%s" />\n' % css)
472 a('<title>Calendar for %d</title\n' % theyear)
473 a('</head>\n')
474 a('<body>\n')
475 a(self.formatyear(theyear, width))
476 a('</body>\n')
477 a('</html>\n')
Walter Dörwald48d5e502006-04-01 07:57:00 +0000478 return ''.join(v).encode(encoding, "xmlcharrefreplace")
479
480
481class LocaleTextCalendar(TextCalendar):
482 """
483 This class can be passed a locale name in the constructor and will return
484 month and weekday names in the specified locale. If this locale includes
485 an encoding all strings containing month and weekday names will be returned
486 as unicode.
487 """
488
489 def __init__(self, firstweekday=0, locale=None):
490 TextCalendar.__init__(self, firstweekday)
491 if locale is None:
492 locale = locale.getdefaultlocale()
493 self.locale = locale
494
495 def formatweekday(self, day, width):
496 oldlocale = locale.setlocale(locale.LC_TIME, self.locale)
497 try:
498 encoding = locale.getlocale(locale.LC_TIME)[1]
499 if width >= 9:
500 names = day_name
501 else:
502 names = day_abbr
503 name = names[day]
504 if encoding is not None:
505 name = name.decode(encoding)
506 result = name[:width].center(width)
507 finally:
508 locale.setlocale(locale.LC_TIME, oldlocale)
509 return result
510
511 def formatmonthname(self, theyear, themonth, width, withyear=True):
512 oldlocale = locale.setlocale(locale.LC_TIME, self.locale)
513 try:
514 encoding = locale.getlocale(locale.LC_TIME)[1]
515 s = month_name[themonth]
516 if encoding is not None:
517 s = s.decode(encoding)
518 if withyear:
519 s = "%s %r" % (s, theyear)
520 result = s.center(width)
521 finally:
522 locale.setlocale(locale.LC_TIME, oldlocale)
523 return result
524
525
526class LocaleHTMLCalendar(HTMLCalendar):
527 """
528 This class can be passed a locale name in the constructor and will return
529 month and weekday names in the specified locale. If this locale includes
530 an encoding all strings containing month and weekday names will be returned
531 as unicode.
532 """
533 def __init__(self, firstweekday=0, locale=None):
534 HTMLCalendar.__init__(self, firstweekday)
535 if locale is None:
536 locale = locale.getdefaultlocale()
537 self.locale = locale
538
539 def formatweekday(self, day):
540 oldlocale = locale.setlocale(locale.LC_TIME, self.locale)
541 try:
542 encoding = locale.getlocale(locale.LC_TIME)[1]
543 s = day_abbr[day]
544 if encoding is not None:
545 s = s.decode(encoding)
546 result = '<th class="%s">%s</th>' % (self.cssclasses[day], s)
547 finally:
548 locale.setlocale(locale.LC_TIME, oldlocale)
549 return result
550
551 def formatmonthname(self, theyear, themonth, withyear=True):
552 oldlocale = locale.setlocale(locale.LC_TIME, self.locale)
553 try:
554 encoding = locale.getlocale(locale.LC_TIME)[1]
555 s = month_name[themonth]
556 if encoding is not None:
557 s = s.decode(encoding)
558 if withyear:
559 s = '%s %s' % (s, theyear)
560 result = '<tr><th colspan="7" class="month">%s</th></tr>' % s
561 finally:
562 locale.setlocale(locale.LC_TIME, oldlocale)
563 return result
Walter Dörwald58917a62006-03-31 15:26:22 +0000564
565
566# Support for old module level interface
567c = TextCalendar()
568
Walter Dörwaldaba10cf2006-04-03 15:20:28 +0000569firstweekday = c.getfirstweekday
570setfirstweekday = c.setfirstweekday
Walter Dörwald58917a62006-03-31 15:26:22 +0000571monthcalendar = c.monthdayscalendar
572prweek = c.prweek
573week = c.formatweek
574weekheader = c.formatweekheader
575prmonth = c.prmonth
576month = c.formatmonth
577calendar = c.formatyear
578prcal = c.pryear
579
580
581# Spacing of month columns for multi-column year calendar
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000582_colwidth = 7*3 - 1 # Amount printed by prweek()
Skip Montanaroad3bc442000-08-30 14:01:28 +0000583_spacing = 6 # Number of spaces between columns
Guido van Rossumc6360141990-10-13 19:23:40 +0000584
Guido van Rossumc6360141990-10-13 19:23:40 +0000585
Walter Dörwald58917a62006-03-31 15:26:22 +0000586def format(cols, colwidth=_colwidth, spacing=_spacing):
587 """Prints multi-column formatting for year calendars"""
588 print formatstring(cols, colwidth, spacing)
Skip Montanaroad3bc442000-08-30 14:01:28 +0000589
Skip Montanaroad3bc442000-08-30 14:01:28 +0000590
Walter Dörwald58917a62006-03-31 15:26:22 +0000591def formatstring(cols, colwidth=_colwidth, spacing=_spacing):
592 """Returns a string formatted from n strings, centered within n columns."""
593 spacing *= ' '
594 return spacing.join(c.center(colwidth) for c in cols)
595
Guido van Rossumb39aff81999-06-09 15:07:38 +0000596
Guido van Rossumb39aff81999-06-09 15:07:38 +0000597EPOCH = 1970
Raymond Hettingere11b5102002-12-25 16:37:19 +0000598_EPOCH_ORD = datetime.date(EPOCH, 1, 1).toordinal()
599
Walter Dörwald58917a62006-03-31 15:26:22 +0000600
Guido van Rossumb39aff81999-06-09 15:07:38 +0000601def timegm(tuple):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000602 """Unrelated but handy function to calculate Unix timestamp from GMT."""
603 year, month, day, hour, minute, second = tuple[:6]
Raymond Hettinger61436482003-02-13 22:58:02 +0000604 days = datetime.date(year, month, 1).toordinal() - _EPOCH_ORD + day - 1
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000605 hours = days*24 + hour
606 minutes = hours*60 + minute
607 seconds = minutes*60 + second
608 return seconds
Walter Dörwald58917a62006-03-31 15:26:22 +0000609
610
611def main(args):
612 import optparse
Walter Dörwald48d5e502006-04-01 07:57:00 +0000613 parser = optparse.OptionParser(usage="usage: %prog [options] [year [month]]")
614 parser.add_option(
615 "-w", "--width",
616 dest="width", type="int", default=2,
617 help="width of date column (default 2, text only)"
618 )
619 parser.add_option(
620 "-l", "--lines",
621 dest="lines", type="int", default=1,
622 help="number of lines for each week (default 1, text only)"
623 )
624 parser.add_option(
625 "-s", "--spacing",
626 dest="spacing", type="int", default=6,
627 help="spacing between months (default 6, text only)"
628 )
629 parser.add_option(
630 "-m", "--months",
631 dest="months", type="int", default=3,
632 help="months per row (default 3, text only)"
633 )
634 parser.add_option(
635 "-c", "--css",
636 dest="css", default="calendar.css",
637 help="CSS to use for page (html only)"
638 )
639 parser.add_option(
640 "-L", "--locale",
641 dest="locale", default=None,
642 help="locale to be used from month and weekday names"
643 )
644 parser.add_option(
645 "-e", "--encoding",
646 dest="encoding", default=None,
647 help="Encoding to use for output"
648 )
649 parser.add_option(
650 "-t", "--type",
651 dest="type", default="text",
652 choices=("text", "html"),
653 help="output type (text or html)"
654 )
Walter Dörwald58917a62006-03-31 15:26:22 +0000655
656 (options, args) = parser.parse_args(args)
657
Walter Dörwald48d5e502006-04-01 07:57:00 +0000658 if options.locale and not options.encoding:
659 parser.error("if --locale is specified --encoding is required")
660 sys.exit(1)
661
Walter Dörwald58917a62006-03-31 15:26:22 +0000662 if options.type == "html":
Walter Dörwald48d5e502006-04-01 07:57:00 +0000663 if options.locale:
664 cal = LocaleHTMLCalendar(locale=options.locale)
665 else:
666 cal = HTMLCalendar()
Walter Dörwald58917a62006-03-31 15:26:22 +0000667 encoding = options.encoding
668 if encoding is None:
669 encoding = sys.getdefaultencoding()
670 optdict = dict(encoding=encoding, css=options.css)
671 if len(args) == 1:
672 print cal.formatyearpage(datetime.date.today().year, **optdict)
673 elif len(args) == 2:
674 print cal.formatyearpage(int(args[1]), **optdict)
675 else:
676 parser.error("incorrect number of arguments")
677 sys.exit(1)
678 else:
Walter Dörwald48d5e502006-04-01 07:57:00 +0000679 if options.locale:
680 cal = LocaleTextCalendar(locale=options.locale)
681 else:
682 cal = TextCalendar()
Walter Dörwald58917a62006-03-31 15:26:22 +0000683 optdict = dict(w=options.width, l=options.lines)
684 if len(args) != 3:
685 optdict["c"] = options.spacing
686 optdict["m"] = options.months
687 if len(args) == 1:
Walter Dörwald48d5e502006-04-01 07:57:00 +0000688 result = cal.formatyear(datetime.date.today().year, **optdict)
Walter Dörwald58917a62006-03-31 15:26:22 +0000689 elif len(args) == 2:
Walter Dörwald48d5e502006-04-01 07:57:00 +0000690 result = cal.formatyear(int(args[1]), **optdict)
Walter Dörwald58917a62006-03-31 15:26:22 +0000691 elif len(args) == 3:
Walter Dörwald48d5e502006-04-01 07:57:00 +0000692 result = cal.formatmonth(int(args[1]), int(args[2]), **optdict)
Walter Dörwald58917a62006-03-31 15:26:22 +0000693 else:
694 parser.error("incorrect number of arguments")
695 sys.exit(1)
Walter Dörwald48d5e502006-04-01 07:57:00 +0000696 if options.encoding:
697 result = result.encode(options.encoding)
698 print result
Walter Dörwald58917a62006-03-31 15:26:22 +0000699
700
701if __name__ == "__main__":
702 main(sys.argv)