|
1 """Calendar printing functions |
|
2 |
|
3 Note when comparing these calendars to the ones printed by cal(1): By |
|
4 default, these calendars have Monday as the first day of the week, and |
|
5 Sunday as the last (the European convention). Use setfirstweekday() to |
|
6 set the first day of the week (0=Monday, 6=Sunday).""" |
|
7 |
|
8 import sys |
|
9 import datetime |
|
10 import locale as _locale |
|
11 |
|
12 __all__ = ["IllegalMonthError", "IllegalWeekdayError", "setfirstweekday", |
|
13 "firstweekday", "isleap", "leapdays", "weekday", "monthrange", |
|
14 "monthcalendar", "prmonth", "month", "prcal", "calendar", |
|
15 "timegm", "month_name", "month_abbr", "day_name", "day_abbr"] |
|
16 |
|
17 # Exception raised for bad input (with string parameter for details) |
|
18 error = ValueError |
|
19 |
|
20 # Exceptions raised for bad input |
|
21 class IllegalMonthError(ValueError): |
|
22 def __init__(self, month): |
|
23 self.month = month |
|
24 def __str__(self): |
|
25 return "bad month number %r; must be 1-12" % self.month |
|
26 |
|
27 |
|
28 class IllegalWeekdayError(ValueError): |
|
29 def __init__(self, weekday): |
|
30 self.weekday = weekday |
|
31 def __str__(self): |
|
32 return "bad weekday number %r; must be 0 (Monday) to 6 (Sunday)" % self.weekday |
|
33 |
|
34 |
|
35 # Constants for months referenced later |
|
36 January = 1 |
|
37 February = 2 |
|
38 |
|
39 # Number of days per month (except for February in leap years) |
|
40 mdays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] |
|
41 |
|
42 # This module used to have hard-coded lists of day and month names, as |
|
43 # English strings. The classes following emulate a read-only version of |
|
44 # that, but supply localized names. Note that the values are computed |
|
45 # fresh on each call, in case the user changes locale between calls. |
|
46 |
|
47 class _localized_month: |
|
48 |
|
49 _months = [datetime.date(2001, i+1, 1).strftime for i in range(12)] |
|
50 _months.insert(0, lambda x: "") |
|
51 |
|
52 def __init__(self, format): |
|
53 self.format = format |
|
54 |
|
55 def __getitem__(self, i): |
|
56 funcs = self._months[i] |
|
57 if isinstance(i, slice): |
|
58 return [f(self.format) for f in funcs] |
|
59 else: |
|
60 return funcs(self.format) |
|
61 |
|
62 def __len__(self): |
|
63 return 13 |
|
64 |
|
65 |
|
66 class _localized_day: |
|
67 |
|
68 # January 1, 2001, was a Monday. |
|
69 _days = [datetime.date(2001, 1, i+1).strftime for i in range(7)] |
|
70 |
|
71 def __init__(self, format): |
|
72 self.format = format |
|
73 |
|
74 def __getitem__(self, i): |
|
75 funcs = self._days[i] |
|
76 if isinstance(i, slice): |
|
77 return [f(self.format) for f in funcs] |
|
78 else: |
|
79 return funcs(self.format) |
|
80 |
|
81 def __len__(self): |
|
82 return 7 |
|
83 |
|
84 |
|
85 # Full and abbreviated names of weekdays |
|
86 day_name = _localized_day('%A') |
|
87 day_abbr = _localized_day('%a') |
|
88 |
|
89 # Full and abbreviated names of months (1-based arrays!!!) |
|
90 month_name = _localized_month('%B') |
|
91 month_abbr = _localized_month('%b') |
|
92 |
|
93 # Constants for weekdays |
|
94 (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7) |
|
95 |
|
96 |
|
97 def isleap(year): |
|
98 """Return 1 for leap years, 0 for non-leap years.""" |
|
99 return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) |
|
100 |
|
101 |
|
102 def leapdays(y1, y2): |
|
103 """Return number of leap years in range [y1, y2). |
|
104 Assume y1 <= y2.""" |
|
105 y1 -= 1 |
|
106 y2 -= 1 |
|
107 return (y2//4 - y1//4) - (y2//100 - y1//100) + (y2//400 - y1//400) |
|
108 |
|
109 |
|
110 def weekday(year, month, day): |
|
111 """Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12), |
|
112 day (1-31).""" |
|
113 return datetime.date(year, month, day).weekday() |
|
114 |
|
115 |
|
116 def monthrange(year, month): |
|
117 """Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for |
|
118 year, month.""" |
|
119 if not 1 <= month <= 12: |
|
120 raise IllegalMonthError(month) |
|
121 day1 = weekday(year, month, 1) |
|
122 ndays = mdays[month] + (month == February and isleap(year)) |
|
123 return day1, ndays |
|
124 |
|
125 |
|
126 class Calendar(object): |
|
127 """ |
|
128 Base calendar class. This class doesn't do any formatting. It simply |
|
129 provides data to subclasses. |
|
130 """ |
|
131 |
|
132 def __init__(self, firstweekday=0): |
|
133 self.firstweekday = firstweekday # 0 = Monday, 6 = Sunday |
|
134 |
|
135 def getfirstweekday(self): |
|
136 return self._firstweekday % 7 |
|
137 |
|
138 def setfirstweekday(self, firstweekday): |
|
139 self._firstweekday = firstweekday |
|
140 |
|
141 firstweekday = property(getfirstweekday, setfirstweekday) |
|
142 |
|
143 def iterweekdays(self): |
|
144 """ |
|
145 Return a iterator for one week of weekday numbers starting with the |
|
146 configured first one. |
|
147 """ |
|
148 for i in range(self.firstweekday, self.firstweekday + 7): |
|
149 yield i%7 |
|
150 |
|
151 def itermonthdates(self, year, month): |
|
152 """ |
|
153 Return an iterator for one month. The iterator will yield datetime.date |
|
154 values and will always iterate through complete weeks, so it will yield |
|
155 dates outside the specified month. |
|
156 """ |
|
157 date = datetime.date(year, month, 1) |
|
158 # Go back to the beginning of the week |
|
159 days = (date.weekday() - self.firstweekday) % 7 |
|
160 date -= datetime.timedelta(days=days) |
|
161 oneday = datetime.timedelta(days=1) |
|
162 while True: |
|
163 yield date |
|
164 date += oneday |
|
165 if date.month != month and date.weekday() == self.firstweekday: |
|
166 break |
|
167 |
|
168 def itermonthdays2(self, year, month): |
|
169 """ |
|
170 Like itermonthdates(), but will yield (day number, weekday number) |
|
171 tuples. For days outside the specified month the day number is 0. |
|
172 """ |
|
173 for date in self.itermonthdates(year, month): |
|
174 if date.month != month: |
|
175 yield (0, date.weekday()) |
|
176 else: |
|
177 yield (date.day, date.weekday()) |
|
178 |
|
179 def itermonthdays(self, year, month): |
|
180 """ |
|
181 Like itermonthdates(), but will yield day numbers. For days outside |
|
182 the specified month the day number is 0. |
|
183 """ |
|
184 for date in self.itermonthdates(year, month): |
|
185 if date.month != month: |
|
186 yield 0 |
|
187 else: |
|
188 yield date.day |
|
189 |
|
190 def monthdatescalendar(self, year, month): |
|
191 """ |
|
192 Return a matrix (list of lists) representing a month's calendar. |
|
193 Each row represents a week; week entries are datetime.date values. |
|
194 """ |
|
195 dates = list(self.itermonthdates(year, month)) |
|
196 return [ dates[i:i+7] for i in range(0, len(dates), 7) ] |
|
197 |
|
198 def monthdays2calendar(self, year, month): |
|
199 """ |
|
200 Return a matrix representing a month's calendar. |
|
201 Each row represents a week; week entries are |
|
202 (day number, weekday number) tuples. Day numbers outside this month |
|
203 are zero. |
|
204 """ |
|
205 days = list(self.itermonthdays2(year, month)) |
|
206 return [ days[i:i+7] for i in range(0, len(days), 7) ] |
|
207 |
|
208 def monthdayscalendar(self, year, month): |
|
209 """ |
|
210 Return a matrix representing a month's calendar. |
|
211 Each row represents a week; days outside this month are zero. |
|
212 """ |
|
213 days = list(self.itermonthdays(year, month)) |
|
214 return [ days[i:i+7] for i in range(0, len(days), 7) ] |
|
215 |
|
216 def yeardatescalendar(self, year, width=3): |
|
217 """ |
|
218 Return the data for the specified year ready for formatting. The return |
|
219 value is a list of month rows. Each month row contains upto width months. |
|
220 Each month contains between 4 and 6 weeks and each week contains 1-7 |
|
221 days. Days are datetime.date objects. |
|
222 """ |
|
223 months = [ |
|
224 self.monthdatescalendar(year, i) |
|
225 for i in range(January, January+12) |
|
226 ] |
|
227 return [months[i:i+width] for i in range(0, len(months), width) ] |
|
228 |
|
229 def yeardays2calendar(self, year, width=3): |
|
230 """ |
|
231 Return the data for the specified year ready for formatting (similar to |
|
232 yeardatescalendar()). Entries in the week lists are |
|
233 (day number, weekday number) tuples. Day numbers outside this month are |
|
234 zero. |
|
235 """ |
|
236 months = [ |
|
237 self.monthdays2calendar(year, i) |
|
238 for i in range(January, January+12) |
|
239 ] |
|
240 return [months[i:i+width] for i in range(0, len(months), width) ] |
|
241 |
|
242 def yeardayscalendar(self, year, width=3): |
|
243 """ |
|
244 Return the data for the specified year ready for formatting (similar to |
|
245 yeardatescalendar()). Entries in the week lists are day numbers. |
|
246 Day numbers outside this month are zero. |
|
247 """ |
|
248 months = [ |
|
249 self.monthdayscalendar(year, i) |
|
250 for i in range(January, January+12) |
|
251 ] |
|
252 return [months[i:i+width] for i in range(0, len(months), width) ] |
|
253 |
|
254 |
|
255 class TextCalendar(Calendar): |
|
256 """ |
|
257 Subclass of Calendar that outputs a calendar as a simple plain text |
|
258 similar to the UNIX program cal. |
|
259 """ |
|
260 |
|
261 def prweek(self, theweek, width): |
|
262 """ |
|
263 Print a single week (no newline). |
|
264 """ |
|
265 print self.formatweek(theweek, width), |
|
266 |
|
267 def formatday(self, day, weekday, width): |
|
268 """ |
|
269 Returns a formatted day. |
|
270 """ |
|
271 if day == 0: |
|
272 s = '' |
|
273 else: |
|
274 s = '%2i' % day # right-align single-digit days |
|
275 return s.center(width) |
|
276 |
|
277 def formatweek(self, theweek, width): |
|
278 """ |
|
279 Returns a single week in a string (no newline). |
|
280 """ |
|
281 return ' '.join(self.formatday(d, wd, width) for (d, wd) in theweek) |
|
282 |
|
283 def formatweekday(self, day, width): |
|
284 """ |
|
285 Returns a formatted week day name. |
|
286 """ |
|
287 if width >= 9: |
|
288 names = day_name |
|
289 else: |
|
290 names = day_abbr |
|
291 return names[day][:width].center(width) |
|
292 |
|
293 def formatweekheader(self, width): |
|
294 """ |
|
295 Return a header for a week. |
|
296 """ |
|
297 return ' '.join(self.formatweekday(i, width) for i in self.iterweekdays()) |
|
298 |
|
299 def formatmonthname(self, theyear, themonth, width, withyear=True): |
|
300 """ |
|
301 Return a formatted month name. |
|
302 """ |
|
303 s = month_name[themonth] |
|
304 if withyear: |
|
305 s = "%s %r" % (s, theyear) |
|
306 return s.center(width) |
|
307 |
|
308 def prmonth(self, theyear, themonth, w=0, l=0): |
|
309 """ |
|
310 Print a month's calendar. |
|
311 """ |
|
312 print self.formatmonth(theyear, themonth, w, l), |
|
313 |
|
314 def formatmonth(self, theyear, themonth, w=0, l=0): |
|
315 """ |
|
316 Return a month's calendar string (multi-line). |
|
317 """ |
|
318 w = max(2, w) |
|
319 l = max(1, l) |
|
320 s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1) |
|
321 s = s.rstrip() |
|
322 s += '\n' * l |
|
323 s += self.formatweekheader(w).rstrip() |
|
324 s += '\n' * l |
|
325 for week in self.monthdays2calendar(theyear, themonth): |
|
326 s += self.formatweek(week, w).rstrip() |
|
327 s += '\n' * l |
|
328 return s |
|
329 |
|
330 def formatyear(self, theyear, w=2, l=1, c=6, m=3): |
|
331 """ |
|
332 Returns a year's calendar as a multi-line string. |
|
333 """ |
|
334 w = max(2, w) |
|
335 l = max(1, l) |
|
336 c = max(2, c) |
|
337 colwidth = (w + 1) * 7 - 1 |
|
338 v = [] |
|
339 a = v.append |
|
340 a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip()) |
|
341 a('\n'*l) |
|
342 header = self.formatweekheader(w) |
|
343 for (i, row) in enumerate(self.yeardays2calendar(theyear, m)): |
|
344 # months in this row |
|
345 months = range(m*i+1, min(m*(i+1)+1, 13)) |
|
346 a('\n'*l) |
|
347 names = (self.formatmonthname(theyear, k, colwidth, False) |
|
348 for k in months) |
|
349 a(formatstring(names, colwidth, c).rstrip()) |
|
350 a('\n'*l) |
|
351 headers = (header for k in months) |
|
352 a(formatstring(headers, colwidth, c).rstrip()) |
|
353 a('\n'*l) |
|
354 # max number of weeks for this row |
|
355 height = max(len(cal) for cal in row) |
|
356 for j in range(height): |
|
357 weeks = [] |
|
358 for cal in row: |
|
359 if j >= len(cal): |
|
360 weeks.append('') |
|
361 else: |
|
362 weeks.append(self.formatweek(cal[j], w)) |
|
363 a(formatstring(weeks, colwidth, c).rstrip()) |
|
364 a('\n' * l) |
|
365 return ''.join(v) |
|
366 |
|
367 def pryear(self, theyear, w=0, l=0, c=6, m=3): |
|
368 """Print a year's calendar.""" |
|
369 print self.formatyear(theyear, w, l, c, m) |
|
370 |
|
371 |
|
372 class HTMLCalendar(Calendar): |
|
373 """ |
|
374 This calendar returns complete HTML pages. |
|
375 """ |
|
376 |
|
377 # CSS classes for the day <td>s |
|
378 cssclasses = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] |
|
379 |
|
380 def formatday(self, day, weekday): |
|
381 """ |
|
382 Return a day as a table cell. |
|
383 """ |
|
384 if day == 0: |
|
385 return '<td class="noday"> </td>' # day outside month |
|
386 else: |
|
387 return '<td class="%s">%d</td>' % (self.cssclasses[weekday], day) |
|
388 |
|
389 def formatweek(self, theweek): |
|
390 """ |
|
391 Return a complete week as a table row. |
|
392 """ |
|
393 s = ''.join(self.formatday(d, wd) for (d, wd) in theweek) |
|
394 return '<tr>%s</tr>' % s |
|
395 |
|
396 def formatweekday(self, day): |
|
397 """ |
|
398 Return a weekday name as a table header. |
|
399 """ |
|
400 return '<th class="%s">%s</th>' % (self.cssclasses[day], day_abbr[day]) |
|
401 |
|
402 def formatweekheader(self): |
|
403 """ |
|
404 Return a header for a week as a table row. |
|
405 """ |
|
406 s = ''.join(self.formatweekday(i) for i in self.iterweekdays()) |
|
407 return '<tr>%s</tr>' % s |
|
408 |
|
409 def formatmonthname(self, theyear, themonth, withyear=True): |
|
410 """ |
|
411 Return a month name as a table row. |
|
412 """ |
|
413 if withyear: |
|
414 s = '%s %s' % (month_name[themonth], theyear) |
|
415 else: |
|
416 s = '%s' % month_name[themonth] |
|
417 return '<tr><th colspan="7" class="month">%s</th></tr>' % s |
|
418 |
|
419 def formatmonth(self, theyear, themonth, withyear=True): |
|
420 """ |
|
421 Return a formatted month as a table. |
|
422 """ |
|
423 v = [] |
|
424 a = v.append |
|
425 a('<table border="0" cellpadding="0" cellspacing="0" class="month">') |
|
426 a('\n') |
|
427 a(self.formatmonthname(theyear, themonth, withyear=withyear)) |
|
428 a('\n') |
|
429 a(self.formatweekheader()) |
|
430 a('\n') |
|
431 for week in self.monthdays2calendar(theyear, themonth): |
|
432 a(self.formatweek(week)) |
|
433 a('\n') |
|
434 a('</table>') |
|
435 a('\n') |
|
436 return ''.join(v) |
|
437 |
|
438 def formatyear(self, theyear, width=3): |
|
439 """ |
|
440 Return a formatted year as a table of tables. |
|
441 """ |
|
442 v = [] |
|
443 a = v.append |
|
444 width = max(width, 1) |
|
445 a('<table border="0" cellpadding="0" cellspacing="0" class="year">') |
|
446 a('\n') |
|
447 a('<tr><th colspan="%d" class="year">%s</th></tr>' % (width, theyear)) |
|
448 for i in range(January, January+12, width): |
|
449 # months in this row |
|
450 months = range(i, min(i+width, 13)) |
|
451 a('<tr>') |
|
452 for m in months: |
|
453 a('<td>') |
|
454 a(self.formatmonth(theyear, m, withyear=False)) |
|
455 a('</td>') |
|
456 a('</tr>') |
|
457 a('</table>') |
|
458 return ''.join(v) |
|
459 |
|
460 def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): |
|
461 """ |
|
462 Return a formatted year as a complete HTML page. |
|
463 """ |
|
464 if encoding is None: |
|
465 encoding = sys.getdefaultencoding() |
|
466 v = [] |
|
467 a = v.append |
|
468 a('<?xml version="1.0" encoding="%s"?>\n' % encoding) |
|
469 a('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n') |
|
470 a('<html>\n') |
|
471 a('<head>\n') |
|
472 a('<meta http-equiv="Content-Type" content="text/html; charset=%s" />\n' % encoding) |
|
473 if css is not None: |
|
474 a('<link rel="stylesheet" type="text/css" href="%s" />\n' % css) |
|
475 a('<title>Calendar for %d</title>\n' % theyear) |
|
476 a('</head>\n') |
|
477 a('<body>\n') |
|
478 a(self.formatyear(theyear, width)) |
|
479 a('</body>\n') |
|
480 a('</html>\n') |
|
481 return ''.join(v).encode(encoding, "xmlcharrefreplace") |
|
482 |
|
483 |
|
484 class TimeEncoding: |
|
485 def __init__(self, locale): |
|
486 self.locale = locale |
|
487 |
|
488 def __enter__(self): |
|
489 self.oldlocale = _locale.setlocale(_locale.LC_TIME, self.locale) |
|
490 return _locale.getlocale(_locale.LC_TIME)[1] |
|
491 |
|
492 def __exit__(self, *args): |
|
493 _locale.setlocale(_locale.LC_TIME, self.oldlocale) |
|
494 |
|
495 |
|
496 class LocaleTextCalendar(TextCalendar): |
|
497 """ |
|
498 This class can be passed a locale name in the constructor and will return |
|
499 month and weekday names in the specified locale. If this locale includes |
|
500 an encoding all strings containing month and weekday names will be returned |
|
501 as unicode. |
|
502 """ |
|
503 |
|
504 def __init__(self, firstweekday=0, locale=None): |
|
505 TextCalendar.__init__(self, firstweekday) |
|
506 if locale is None: |
|
507 locale = _locale.getdefaultlocale() |
|
508 self.locale = locale |
|
509 |
|
510 def formatweekday(self, day, width): |
|
511 with TimeEncoding(self.locale) as encoding: |
|
512 if width >= 9: |
|
513 names = day_name |
|
514 else: |
|
515 names = day_abbr |
|
516 name = names[day] |
|
517 if encoding is not None: |
|
518 name = name.decode(encoding) |
|
519 return name[:width].center(width) |
|
520 |
|
521 def formatmonthname(self, theyear, themonth, width, withyear=True): |
|
522 with TimeEncoding(self.locale) as encoding: |
|
523 s = month_name[themonth] |
|
524 if encoding is not None: |
|
525 s = s.decode(encoding) |
|
526 if withyear: |
|
527 s = "%s %r" % (s, theyear) |
|
528 return s.center(width) |
|
529 |
|
530 |
|
531 class LocaleHTMLCalendar(HTMLCalendar): |
|
532 """ |
|
533 This class can be passed a locale name in the constructor and will return |
|
534 month and weekday names in the specified locale. If this locale includes |
|
535 an encoding all strings containing month and weekday names will be returned |
|
536 as unicode. |
|
537 """ |
|
538 def __init__(self, firstweekday=0, locale=None): |
|
539 HTMLCalendar.__init__(self, firstweekday) |
|
540 if locale is None: |
|
541 locale = _locale.getdefaultlocale() |
|
542 self.locale = locale |
|
543 |
|
544 def formatweekday(self, day): |
|
545 with TimeEncoding(self.locale) as encoding: |
|
546 s = day_abbr[day] |
|
547 if encoding is not None: |
|
548 s = s.decode(encoding) |
|
549 return '<th class="%s">%s</th>' % (self.cssclasses[day], s) |
|
550 |
|
551 def formatmonthname(self, theyear, themonth, withyear=True): |
|
552 with TimeEncoding(self.locale) as encoding: |
|
553 s = month_name[themonth] |
|
554 if encoding is not None: |
|
555 s = s.decode(encoding) |
|
556 if withyear: |
|
557 s = '%s %s' % (s, theyear) |
|
558 return '<tr><th colspan="7" class="month">%s</th></tr>' % s |
|
559 |
|
560 |
|
561 # Support for old module level interface |
|
562 c = TextCalendar() |
|
563 |
|
564 firstweekday = c.getfirstweekday |
|
565 |
|
566 def setfirstweekday(firstweekday): |
|
567 if not MONDAY <= firstweekday <= SUNDAY: |
|
568 raise IllegalWeekdayError(firstweekday) |
|
569 c.firstweekday = firstweekday |
|
570 |
|
571 monthcalendar = c.monthdayscalendar |
|
572 prweek = c.prweek |
|
573 week = c.formatweek |
|
574 weekheader = c.formatweekheader |
|
575 prmonth = c.prmonth |
|
576 month = c.formatmonth |
|
577 calendar = c.formatyear |
|
578 prcal = c.pryear |
|
579 |
|
580 |
|
581 # Spacing of month columns for multi-column year calendar |
|
582 _colwidth = 7*3 - 1 # Amount printed by prweek() |
|
583 _spacing = 6 # Number of spaces between columns |
|
584 |
|
585 |
|
586 def format(cols, colwidth=_colwidth, spacing=_spacing): |
|
587 """Prints multi-column formatting for year calendars""" |
|
588 print formatstring(cols, colwidth, spacing) |
|
589 |
|
590 |
|
591 def 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 |
|
596 |
|
597 EPOCH = 1970 |
|
598 _EPOCH_ORD = datetime.date(EPOCH, 1, 1).toordinal() |
|
599 |
|
600 |
|
601 def timegm(tuple): |
|
602 """Unrelated but handy function to calculate Unix timestamp from GMT.""" |
|
603 year, month, day, hour, minute, second = tuple[:6] |
|
604 days = datetime.date(year, month, 1).toordinal() - _EPOCH_ORD + day - 1 |
|
605 hours = days*24 + hour |
|
606 minutes = hours*60 + minute |
|
607 seconds = minutes*60 + second |
|
608 return seconds |
|
609 |
|
610 |
|
611 def main(args): |
|
612 import optparse |
|
613 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 ) |
|
655 |
|
656 (options, args) = parser.parse_args(args) |
|
657 |
|
658 if options.locale and not options.encoding: |
|
659 parser.error("if --locale is specified --encoding is required") |
|
660 sys.exit(1) |
|
661 |
|
662 locale = options.locale, options.encoding |
|
663 |
|
664 if options.type == "html": |
|
665 if options.locale: |
|
666 cal = LocaleHTMLCalendar(locale=locale) |
|
667 else: |
|
668 cal = HTMLCalendar() |
|
669 encoding = options.encoding |
|
670 if encoding is None: |
|
671 encoding = sys.getdefaultencoding() |
|
672 optdict = dict(encoding=encoding, css=options.css) |
|
673 if len(args) == 1: |
|
674 print cal.formatyearpage(datetime.date.today().year, **optdict) |
|
675 elif len(args) == 2: |
|
676 print cal.formatyearpage(int(args[1]), **optdict) |
|
677 else: |
|
678 parser.error("incorrect number of arguments") |
|
679 sys.exit(1) |
|
680 else: |
|
681 if options.locale: |
|
682 cal = LocaleTextCalendar(locale=locale) |
|
683 else: |
|
684 cal = TextCalendar() |
|
685 optdict = dict(w=options.width, l=options.lines) |
|
686 if len(args) != 3: |
|
687 optdict["c"] = options.spacing |
|
688 optdict["m"] = options.months |
|
689 if len(args) == 1: |
|
690 result = cal.formatyear(datetime.date.today().year, **optdict) |
|
691 elif len(args) == 2: |
|
692 result = cal.formatyear(int(args[1]), **optdict) |
|
693 elif len(args) == 3: |
|
694 result = cal.formatmonth(int(args[1]), int(args[2]), **optdict) |
|
695 else: |
|
696 parser.error("incorrect number of arguments") |
|
697 sys.exit(1) |
|
698 if options.encoding: |
|
699 result = result.encode(options.encoding) |
|
700 print result |
|
701 |
|
702 |
|
703 if __name__ == "__main__": |
|
704 main(sys.argv) |