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