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