~ubuntu-branches/ubuntu/maverick/python3.1/maverick

« back to all changes in this revision

Viewing changes to Lib/calendar.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2009-03-23 00:01:27 UTC
  • Revision ID: james.westby@ubuntu.com-20090323000127-5fstfxju4ufrhthq
Tags: upstream-3.1~a1+20090322
ImportĀ upstreamĀ versionĀ 3.1~a1+20090322

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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), end=' ')
 
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), end=' ')
 
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">&nbsp;</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 different_locale:
 
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 different_locale(self.locale):
 
512
            if width >= 9:
 
513
                names = day_name
 
514
            else:
 
515
                names = day_abbr
 
516
            name = names[day]
 
517
            return name[:width].center(width)
 
518
 
 
519
    def formatmonthname(self, theyear, themonth, width, withyear=True):
 
520
        with different_locale(self.locale):
 
521
            s = month_name[themonth]
 
522
            if withyear:
 
523
                s = "%s %r" % (s, theyear)
 
524
            return s.center(width)
 
525
 
 
526
 
 
527
class LocaleHTMLCalendar(HTMLCalendar):
 
528
    """
 
529
    This class can be passed a locale name in the constructor and will return
 
530
    month and weekday names in the specified locale. If this locale includes
 
531
    an encoding all strings containing month and weekday names will be returned
 
532
    as unicode.
 
533
    """
 
534
    def __init__(self, firstweekday=0, locale=None):
 
535
        HTMLCalendar.__init__(self, firstweekday)
 
536
        if locale is None:
 
537
            locale = _locale.getdefaultlocale()
 
538
        self.locale = locale
 
539
 
 
540
    def formatweekday(self, day):
 
541
        with different_locale(self.locale):
 
542
            s = day_abbr[day]
 
543
            return '<th class="%s">%s</th>' % (self.cssclasses[day], s)
 
544
 
 
545
    def formatmonthname(self, theyear, themonth, withyear=True):
 
546
        with different_locale(self.locale):
 
547
            s = month_name[themonth]
 
548
            if withyear:
 
549
                s = '%s %s' % (s, theyear)
 
550
            return '<tr><th colspan="7" class="month">%s</th></tr>' % s
 
551
 
 
552
 
 
553
# Support for old module level interface
 
554
c = TextCalendar()
 
555
 
 
556
firstweekday = c.getfirstweekday
 
557
 
 
558
def setfirstweekday(firstweekday):
 
559
    if not MONDAY <= firstweekday <= SUNDAY:
 
560
        raise IllegalWeekdayError(firstweekday)
 
561
    c.firstweekday = firstweekday
 
562
 
 
563
monthcalendar = c.monthdayscalendar
 
564
prweek = c.prweek
 
565
week = c.formatweek
 
566
weekheader = c.formatweekheader
 
567
prmonth = c.prmonth
 
568
month = c.formatmonth
 
569
calendar = c.formatyear
 
570
prcal = c.pryear
 
571
 
 
572
 
 
573
# Spacing of month columns for multi-column year calendar
 
574
_colwidth = 7*3 - 1         # Amount printed by prweek()
 
575
_spacing = 6                # Number of spaces between columns
 
576
 
 
577
 
 
578
def format(cols, colwidth=_colwidth, spacing=_spacing):
 
579
    """Prints multi-column formatting for year calendars"""
 
580
    print(formatstring(cols, colwidth, spacing))
 
581
 
 
582
 
 
583
def formatstring(cols, colwidth=_colwidth, spacing=_spacing):
 
584
    """Returns a string formatted from n strings, centered within n columns."""
 
585
    spacing *= ' '
 
586
    return spacing.join(c.center(colwidth) for c in cols)
 
587
 
 
588
 
 
589
EPOCH = 1970
 
590
_EPOCH_ORD = datetime.date(EPOCH, 1, 1).toordinal()
 
591
 
 
592
 
 
593
def timegm(tuple):
 
594
    """Unrelated but handy function to calculate Unix timestamp from GMT."""
 
595
    year, month, day, hour, minute, second = tuple[:6]
 
596
    days = datetime.date(year, month, 1).toordinal() - _EPOCH_ORD + day - 1
 
597
    hours = days*24 + hour
 
598
    minutes = hours*60 + minute
 
599
    seconds = minutes*60 + second
 
600
    return seconds
 
601
 
 
602
 
 
603
def main(args):
 
604
    import optparse
 
605
    parser = optparse.OptionParser(usage="usage: %prog [options] [year [month]]")
 
606
    parser.add_option(
 
607
        "-w", "--width",
 
608
        dest="width", type="int", default=2,
 
609
        help="width of date column (default 2, text only)"
 
610
    )
 
611
    parser.add_option(
 
612
        "-l", "--lines",
 
613
        dest="lines", type="int", default=1,
 
614
        help="number of lines for each week (default 1, text only)"
 
615
    )
 
616
    parser.add_option(
 
617
        "-s", "--spacing",
 
618
        dest="spacing", type="int", default=6,
 
619
        help="spacing between months (default 6, text only)"
 
620
    )
 
621
    parser.add_option(
 
622
        "-m", "--months",
 
623
        dest="months", type="int", default=3,
 
624
        help="months per row (default 3, text only)"
 
625
    )
 
626
    parser.add_option(
 
627
        "-c", "--css",
 
628
        dest="css", default="calendar.css",
 
629
        help="CSS to use for page (html only)"
 
630
    )
 
631
    parser.add_option(
 
632
        "-L", "--locale",
 
633
        dest="locale", default=None,
 
634
        help="locale to be used from month and weekday names"
 
635
    )
 
636
    parser.add_option(
 
637
        "-e", "--encoding",
 
638
        dest="encoding", default=None,
 
639
        help="Encoding to use for output"
 
640
    )
 
641
    parser.add_option(
 
642
        "-t", "--type",
 
643
        dest="type", default="text",
 
644
        choices=("text", "html"),
 
645
        help="output type (text or html)"
 
646
    )
 
647
 
 
648
    (options, args) = parser.parse_args(args)
 
649
 
 
650
    if options.locale and not options.encoding:
 
651
        parser.error("if --locale is specified --encoding is required")
 
652
        sys.exit(1)
 
653
 
 
654
    locale = options.locale, options.encoding
 
655
 
 
656
    if options.type == "html":
 
657
        if options.locale:
 
658
            cal = LocaleHTMLCalendar(locale=locale)
 
659
        else:
 
660
            cal = HTMLCalendar()
 
661
        encoding = options.encoding
 
662
        if encoding is None:
 
663
            encoding = sys.getdefaultencoding()
 
664
        optdict = dict(encoding=encoding, css=options.css)
 
665
        if len(args) == 1:
 
666
            print(cal.formatyearpage(datetime.date.today().year, **optdict))
 
667
        elif len(args) == 2:
 
668
            print(cal.formatyearpage(int(args[1]), **optdict))
 
669
        else:
 
670
            parser.error("incorrect number of arguments")
 
671
            sys.exit(1)
 
672
    else:
 
673
        if options.locale:
 
674
            cal = LocaleTextCalendar(locale=locale)
 
675
        else:
 
676
            cal = TextCalendar()
 
677
        optdict = dict(w=options.width, l=options.lines)
 
678
        if len(args) != 3:
 
679
            optdict["c"] = options.spacing
 
680
            optdict["m"] = options.months
 
681
        if len(args) == 1:
 
682
            result = cal.formatyear(datetime.date.today().year, **optdict)
 
683
        elif len(args) == 2:
 
684
            result = cal.formatyear(int(args[1]), **optdict)
 
685
        elif len(args) == 3:
 
686
            result = cal.formatmonth(int(args[1]), int(args[2]), **optdict)
 
687
        else:
 
688
            parser.error("incorrect number of arguments")
 
689
            sys.exit(1)
 
690
        if options.encoding:
 
691
            result = result.encode(options.encoding)
 
692
        print(result)
 
693
 
 
694
 
 
695
if __name__ == "__main__":
 
696
    main(sys.argv)