Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Doc/library/calendar.rst
Original file line number Diff line number Diff line change
Expand Up @@ -710,8 +710,7 @@ The following options are accepted:
.. option:: month

The month of the specified :option:`year` to print the calendar for.
Must be a number between 1 and 12,
and may only be used in text mode.
Must be a number between 1 and 12.
Defaults to printing a calendar for the full year.


Expand Down
40 changes: 35 additions & 5 deletions Lib/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,35 @@ def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None):
return ''.join(v).encode(encoding, "xmlcharrefreplace")


def formatmonthpage(self, theyear, themonth, width=3, css='calendar.css', encoding=None):
"""
Return a formatted month as a complete HTML page.
"""
if encoding is None:
encoding = 'utf-8'
v = []
a = v.append
a('<!DOCTYPE html>\n')
a('<html lang="en">\n')
a('<head>\n')
a(f'<meta charset="{encoding}">\n')
a('<meta name="viewport" content="width=device-width, initial-scale=1">\n')
a(f'<title>Calendar for {theyear}</title>\n')
a('<style>\n')
a(':root { color-scheme: light dark; }\n')
a('table.year { border: solid; }\n')
a('table.year > tbody > tr > td { border: solid; vertical-align: top; }\n')
a('</style>\n')
if css is not None:
a(f'<link rel="stylesheet" href="{css}">\n')
a('</head>\n')
a('<body>\n')
a(self.formatmonth(theyear, themonth, width))
a('</body>\n')
a('</html>\n')
return ''.join(v).encode(encoding, "xmlcharrefreplace")


class different_locale:
def __init__(self, locale):
self.locale = locale
Expand Down Expand Up @@ -886,7 +915,7 @@ def main(args=None):
parser.add_argument(
"month",
nargs='?', type=int,
help="month number (1-12, text only)"
help="month number (1-12)"
)

options = parser.parse_args(args)
Expand All @@ -899,9 +928,6 @@ def main(args=None):
today = datetime.date.today()

if options.type == "html":
if options.month:
parser.error("incorrect number of arguments")
sys.exit(1)
if options.locale:
cal = LocaleHTMLCalendar(locale=locale)
else:
Expand All @@ -912,10 +938,14 @@ def main(args=None):
encoding = 'utf-8'
optdict = dict(encoding=encoding, css=options.css)
write = sys.stdout.buffer.write

if options.year is None:
write(cal.formatyearpage(today.year, **optdict))
else:
write(cal.formatyearpage(options.year, **optdict))
if options.month:
write(cal.formatmonthpage(options.year, options.month, **optdict))
else:
write(cal.formatyearpage(options.year, **optdict))
else:
if options.locale:
cal = _CLIDemoLocaleCalendar(highlight_day=today, locale=locale)
Expand Down
36 changes: 35 additions & 1 deletion Lib/test/test_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,34 @@
</html>
"""

result_2009_6_html = """\
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Calendar for 2009</title>
<style>
:root { color-scheme: light dark; }
table.year { border: solid; }
table.year > tbody > tr > td { border: solid; vertical-align: top; }
</style>
<link rel="stylesheet" href="calendar.css">
</head>
<body>
<table class="month">
<tr><th colspan="7" class="month">June 2009</th></tr>
<tr><th class="mon">Mon</th><th class="tue">Tue</th><th class="wed">Wed</th><th class="thu">Thu</th><th class="fri">Fri</th><th class="sat">Sat</th><th class="sun">Sun</th></tr>
<tr><td class="mon">1</td><td class="tue">2</td><td class="wed">3</td><td class="thu">4</td><td class="fri">5</td><td class="sat">6</td><td class="sun">7</td></tr>
<tr><td class="mon">8</td><td class="tue">9</td><td class="wed">10</td><td class="thu">11</td><td class="fri">12</td><td class="sat">13</td><td class="sun">14</td></tr>
<tr><td class="mon">15</td><td class="tue">16</td><td class="wed">17</td><td class="thu">18</td><td class="fri">19</td><td class="sat">20</td><td class="sun">21</td></tr>
<tr><td class="mon">22</td><td class="tue">23</td><td class="wed">24</td><td class="thu">25</td><td class="fri">26</td><td class="sat">27</td><td class="sun">28</td></tr>
<tr><td class="mon">29</td><td class="tue">30</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td></tr>
</table>
</body>
</html>
"""

result_2004_days = [
[[[0, 0, 0, 1, 2, 3, 4],
[5, 6, 7, 8, 9, 10, 11],
Expand Down Expand Up @@ -506,6 +534,13 @@ def test_format(self):
calendar.format(["1", "2", "3"], colwidth=3, spacing=1)
self.assertEqual(out.getvalue().strip(), "1 2 3")

def test_format_html_year_with_month(self):
self.assertEqual(
calendar.HTMLCalendar().formatmonthpage(2009, 6).decode("ascii"),
result_2009_6_html
)


class CalendarTestCase(unittest.TestCase):

def test_deprecation_warning(self):
Expand Down Expand Up @@ -1102,7 +1137,6 @@ def test_illegal_arguments(self):
self.assertFailure('2004', '1', 'spam')
self.assertFailure('2004', '1', '1')
self.assertFailure('2004', '1', '1', 'spam')
self.assertFailure('-t', 'html', '2004', '1')

def test_output_current_year(self):
for run in self.runners:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Calendar's HTML formatting now accepts year and month as options.
Previously, running ``python -m calendar -t html 2025 10`` would result in an
error message. It now generates an HTML document displaying the calendar for
the specified month.
Contributed by Pål Grønås Drange.
Loading