• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import calendar
2import unittest
3
4from test import support
5from test.support.script_helper import assert_python_ok, assert_python_failure
6import contextlib
7import datetime
8import io
9import locale
10import os
11import sys
12import time
13
14# From https://en.wikipedia.org/wiki/Leap_year_starting_on_Saturday
15result_0_02_text = """\
16     February 0
17Mo Tu We Th Fr Sa Su
18    1  2  3  4  5  6
19 7  8  9 10 11 12 13
2014 15 16 17 18 19 20
2121 22 23 24 25 26 27
2228 29
23"""
24
25result_0_text = """\
26                                   0
27
28      January                   February                   March
29Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
30                1  2          1  2  3  4  5  6             1  2  3  4  5
31 3  4  5  6  7  8  9       7  8  9 10 11 12 13       6  7  8  9 10 11 12
3210 11 12 13 14 15 16      14 15 16 17 18 19 20      13 14 15 16 17 18 19
3317 18 19 20 21 22 23      21 22 23 24 25 26 27      20 21 22 23 24 25 26
3424 25 26 27 28 29 30      28 29                     27 28 29 30 31
3531
36
37       April                      May                       June
38Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
39                1  2       1  2  3  4  5  6  7                1  2  3  4
40 3  4  5  6  7  8  9       8  9 10 11 12 13 14       5  6  7  8  9 10 11
4110 11 12 13 14 15 16      15 16 17 18 19 20 21      12 13 14 15 16 17 18
4217 18 19 20 21 22 23      22 23 24 25 26 27 28      19 20 21 22 23 24 25
4324 25 26 27 28 29 30      29 30 31                  26 27 28 29 30
44
45        July                     August                  September
46Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
47                1  2          1  2  3  4  5  6                   1  2  3
48 3  4  5  6  7  8  9       7  8  9 10 11 12 13       4  5  6  7  8  9 10
4910 11 12 13 14 15 16      14 15 16 17 18 19 20      11 12 13 14 15 16 17
5017 18 19 20 21 22 23      21 22 23 24 25 26 27      18 19 20 21 22 23 24
5124 25 26 27 28 29 30      28 29 30 31               25 26 27 28 29 30
5231
53
54      October                   November                  December
55Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
56                   1             1  2  3  4  5                   1  2  3
57 2  3  4  5  6  7  8       6  7  8  9 10 11 12       4  5  6  7  8  9 10
58 9 10 11 12 13 14 15      13 14 15 16 17 18 19      11 12 13 14 15 16 17
5916 17 18 19 20 21 22      20 21 22 23 24 25 26      18 19 20 21 22 23 24
6023 24 25 26 27 28 29      27 28 29 30               25 26 27 28 29 30 31
6130 31
62"""
63
64result_2004_01_text = """\
65    January 2004
66Mo Tu We Th Fr Sa Su
67          1  2  3  4
68 5  6  7  8  9 10 11
6912 13 14 15 16 17 18
7019 20 21 22 23 24 25
7126 27 28 29 30 31
72"""
73
74result_2004_text = """\
75                                  2004
76
77      January                   February                   March
78Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
79          1  2  3  4                         1       1  2  3  4  5  6  7
80 5  6  7  8  9 10 11       2  3  4  5  6  7  8       8  9 10 11 12 13 14
8112 13 14 15 16 17 18       9 10 11 12 13 14 15      15 16 17 18 19 20 21
8219 20 21 22 23 24 25      16 17 18 19 20 21 22      22 23 24 25 26 27 28
8326 27 28 29 30 31         23 24 25 26 27 28 29      29 30 31
84
85       April                      May                       June
86Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
87          1  2  3  4                      1  2          1  2  3  4  5  6
88 5  6  7  8  9 10 11       3  4  5  6  7  8  9       7  8  9 10 11 12 13
8912 13 14 15 16 17 18      10 11 12 13 14 15 16      14 15 16 17 18 19 20
9019 20 21 22 23 24 25      17 18 19 20 21 22 23      21 22 23 24 25 26 27
9126 27 28 29 30            24 25 26 27 28 29 30      28 29 30
92                          31
93
94        July                     August                  September
95Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
96          1  2  3  4                         1             1  2  3  4  5
97 5  6  7  8  9 10 11       2  3  4  5  6  7  8       6  7  8  9 10 11 12
9812 13 14 15 16 17 18       9 10 11 12 13 14 15      13 14 15 16 17 18 19
9919 20 21 22 23 24 25      16 17 18 19 20 21 22      20 21 22 23 24 25 26
10026 27 28 29 30 31         23 24 25 26 27 28 29      27 28 29 30
101                          30 31
102
103      October                   November                  December
104Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
105             1  2  3       1  2  3  4  5  6  7             1  2  3  4  5
106 4  5  6  7  8  9 10       8  9 10 11 12 13 14       6  7  8  9 10 11 12
10711 12 13 14 15 16 17      15 16 17 18 19 20 21      13 14 15 16 17 18 19
10818 19 20 21 22 23 24      22 23 24 25 26 27 28      20 21 22 23 24 25 26
10925 26 27 28 29 30 31      29 30                     27 28 29 30 31
110"""
111
112
113default_format = dict(year="year", month="month", encoding="ascii")
114
115result_2004_html = """\
116<?xml version="1.0" encoding="{encoding}"?>
117<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
118<html>
119<head>
120<meta http-equiv="Content-Type" content="text/html; charset={encoding}" />
121<link rel="stylesheet" type="text/css" href="calendar.css" />
122<title>Calendar for 2004</title>
123</head>
124<body>
125<table border="0" cellpadding="0" cellspacing="0" class="{year}">
126<tr><th colspan="3" class="{year}">2004</th></tr><tr><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
127<tr><th colspan="7" class="{month}">January</th></tr>
128<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>
129<tr><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="thu">1</td><td class="fri">2</td><td class="sat">3</td><td class="sun">4</td></tr>
130<tr><td class="mon">5</td><td class="tue">6</td><td class="wed">7</td><td class="thu">8</td><td class="fri">9</td><td class="sat">10</td><td class="sun">11</td></tr>
131<tr><td class="mon">12</td><td class="tue">13</td><td class="wed">14</td><td class="thu">15</td><td class="fri">16</td><td class="sat">17</td><td class="sun">18</td></tr>
132<tr><td class="mon">19</td><td class="tue">20</td><td class="wed">21</td><td class="thu">22</td><td class="fri">23</td><td class="sat">24</td><td class="sun">25</td></tr>
133<tr><td class="mon">26</td><td class="tue">27</td><td class="wed">28</td><td class="thu">29</td><td class="fri">30</td><td class="sat">31</td><td class="noday">&nbsp;</td></tr>
134</table>
135</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
136<tr><th colspan="7" class="{month}">February</th></tr>
137<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>
138<tr><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><td class="noday">&nbsp;</td><td class="sun">1</td></tr>
139<tr><td class="mon">2</td><td class="tue">3</td><td class="wed">4</td><td class="thu">5</td><td class="fri">6</td><td class="sat">7</td><td class="sun">8</td></tr>
140<tr><td class="mon">9</td><td class="tue">10</td><td class="wed">11</td><td class="thu">12</td><td class="fri">13</td><td class="sat">14</td><td class="sun">15</td></tr>
141<tr><td class="mon">16</td><td class="tue">17</td><td class="wed">18</td><td class="thu">19</td><td class="fri">20</td><td class="sat">21</td><td class="sun">22</td></tr>
142<tr><td class="mon">23</td><td class="tue">24</td><td class="wed">25</td><td class="thu">26</td><td class="fri">27</td><td class="sat">28</td><td class="sun">29</td></tr>
143</table>
144</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
145<tr><th colspan="7" class="{month}">March</th></tr>
146<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>
147<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>
148<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>
149<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>
150<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>
151<tr><td class="mon">29</td><td class="tue">30</td><td class="wed">31</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td></tr>
152</table>
153</td></tr><tr><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
154<tr><th colspan="7" class="{month}">April</th></tr>
155<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>
156<tr><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="thu">1</td><td class="fri">2</td><td class="sat">3</td><td class="sun">4</td></tr>
157<tr><td class="mon">5</td><td class="tue">6</td><td class="wed">7</td><td class="thu">8</td><td class="fri">9</td><td class="sat">10</td><td class="sun">11</td></tr>
158<tr><td class="mon">12</td><td class="tue">13</td><td class="wed">14</td><td class="thu">15</td><td class="fri">16</td><td class="sat">17</td><td class="sun">18</td></tr>
159<tr><td class="mon">19</td><td class="tue">20</td><td class="wed">21</td><td class="thu">22</td><td class="fri">23</td><td class="sat">24</td><td class="sun">25</td></tr>
160<tr><td class="mon">26</td><td class="tue">27</td><td class="wed">28</td><td class="thu">29</td><td class="fri">30</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td></tr>
161</table>
162</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
163<tr><th colspan="7" class="{month}">May</th></tr>
164<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>
165<tr><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><td class="sat">1</td><td class="sun">2</td></tr>
166<tr><td class="mon">3</td><td class="tue">4</td><td class="wed">5</td><td class="thu">6</td><td class="fri">7</td><td class="sat">8</td><td class="sun">9</td></tr>
167<tr><td class="mon">10</td><td class="tue">11</td><td class="wed">12</td><td class="thu">13</td><td class="fri">14</td><td class="sat">15</td><td class="sun">16</td></tr>
168<tr><td class="mon">17</td><td class="tue">18</td><td class="wed">19</td><td class="thu">20</td><td class="fri">21</td><td class="sat">22</td><td class="sun">23</td></tr>
169<tr><td class="mon">24</td><td class="tue">25</td><td class="wed">26</td><td class="thu">27</td><td class="fri">28</td><td class="sat">29</td><td class="sun">30</td></tr>
170<tr><td class="mon">31</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><td class="noday">&nbsp;</td></tr>
171</table>
172</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
173<tr><th colspan="7" class="{month}">June</th></tr>
174<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>
175<tr><td class="noday">&nbsp;</td><td class="tue">1</td><td class="wed">2</td><td class="thu">3</td><td class="fri">4</td><td class="sat">5</td><td class="sun">6</td></tr>
176<tr><td class="mon">7</td><td class="tue">8</td><td class="wed">9</td><td class="thu">10</td><td class="fri">11</td><td class="sat">12</td><td class="sun">13</td></tr>
177<tr><td class="mon">14</td><td class="tue">15</td><td class="wed">16</td><td class="thu">17</td><td class="fri">18</td><td class="sat">19</td><td class="sun">20</td></tr>
178<tr><td class="mon">21</td><td class="tue">22</td><td class="wed">23</td><td class="thu">24</td><td class="fri">25</td><td class="sat">26</td><td class="sun">27</td></tr>
179<tr><td class="mon">28</td><td class="tue">29</td><td class="wed">30</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td></tr>
180</table>
181</td></tr><tr><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
182<tr><th colspan="7" class="{month}">July</th></tr>
183<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>
184<tr><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="thu">1</td><td class="fri">2</td><td class="sat">3</td><td class="sun">4</td></tr>
185<tr><td class="mon">5</td><td class="tue">6</td><td class="wed">7</td><td class="thu">8</td><td class="fri">9</td><td class="sat">10</td><td class="sun">11</td></tr>
186<tr><td class="mon">12</td><td class="tue">13</td><td class="wed">14</td><td class="thu">15</td><td class="fri">16</td><td class="sat">17</td><td class="sun">18</td></tr>
187<tr><td class="mon">19</td><td class="tue">20</td><td class="wed">21</td><td class="thu">22</td><td class="fri">23</td><td class="sat">24</td><td class="sun">25</td></tr>
188<tr><td class="mon">26</td><td class="tue">27</td><td class="wed">28</td><td class="thu">29</td><td class="fri">30</td><td class="sat">31</td><td class="noday">&nbsp;</td></tr>
189</table>
190</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
191<tr><th colspan="7" class="{month}">August</th></tr>
192<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>
193<tr><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><td class="noday">&nbsp;</td><td class="sun">1</td></tr>
194<tr><td class="mon">2</td><td class="tue">3</td><td class="wed">4</td><td class="thu">5</td><td class="fri">6</td><td class="sat">7</td><td class="sun">8</td></tr>
195<tr><td class="mon">9</td><td class="tue">10</td><td class="wed">11</td><td class="thu">12</td><td class="fri">13</td><td class="sat">14</td><td class="sun">15</td></tr>
196<tr><td class="mon">16</td><td class="tue">17</td><td class="wed">18</td><td class="thu">19</td><td class="fri">20</td><td class="sat">21</td><td class="sun">22</td></tr>
197<tr><td class="mon">23</td><td class="tue">24</td><td class="wed">25</td><td class="thu">26</td><td class="fri">27</td><td class="sat">28</td><td class="sun">29</td></tr>
198<tr><td class="mon">30</td><td class="tue">31</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>
199</table>
200</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
201<tr><th colspan="7" class="{month}">September</th></tr>
202<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>
203<tr><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="wed">1</td><td class="thu">2</td><td class="fri">3</td><td class="sat">4</td><td class="sun">5</td></tr>
204<tr><td class="mon">6</td><td class="tue">7</td><td class="wed">8</td><td class="thu">9</td><td class="fri">10</td><td class="sat">11</td><td class="sun">12</td></tr>
205<tr><td class="mon">13</td><td class="tue">14</td><td class="wed">15</td><td class="thu">16</td><td class="fri">17</td><td class="sat">18</td><td class="sun">19</td></tr>
206<tr><td class="mon">20</td><td class="tue">21</td><td class="wed">22</td><td class="thu">23</td><td class="fri">24</td><td class="sat">25</td><td class="sun">26</td></tr>
207<tr><td class="mon">27</td><td class="tue">28</td><td class="wed">29</td><td class="thu">30</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td></tr>
208</table>
209</td></tr><tr><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
210<tr><th colspan="7" class="{month}">October</th></tr>
211<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>
212<tr><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="fri">1</td><td class="sat">2</td><td class="sun">3</td></tr>
213<tr><td class="mon">4</td><td class="tue">5</td><td class="wed">6</td><td class="thu">7</td><td class="fri">8</td><td class="sat">9</td><td class="sun">10</td></tr>
214<tr><td class="mon">11</td><td class="tue">12</td><td class="wed">13</td><td class="thu">14</td><td class="fri">15</td><td class="sat">16</td><td class="sun">17</td></tr>
215<tr><td class="mon">18</td><td class="tue">19</td><td class="wed">20</td><td class="thu">21</td><td class="fri">22</td><td class="sat">23</td><td class="sun">24</td></tr>
216<tr><td class="mon">25</td><td class="tue">26</td><td class="wed">27</td><td class="thu">28</td><td class="fri">29</td><td class="sat">30</td><td class="sun">31</td></tr>
217</table>
218</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
219<tr><th colspan="7" class="{month}">November</th></tr>
220<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>
221<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>
222<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>
223<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>
224<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>
225<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>
226</table>
227</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
228<tr><th colspan="7" class="{month}">December</th></tr>
229<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>
230<tr><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td><td class="wed">1</td><td class="thu">2</td><td class="fri">3</td><td class="sat">4</td><td class="sun">5</td></tr>
231<tr><td class="mon">6</td><td class="tue">7</td><td class="wed">8</td><td class="thu">9</td><td class="fri">10</td><td class="sat">11</td><td class="sun">12</td></tr>
232<tr><td class="mon">13</td><td class="tue">14</td><td class="wed">15</td><td class="thu">16</td><td class="fri">17</td><td class="sat">18</td><td class="sun">19</td></tr>
233<tr><td class="mon">20</td><td class="tue">21</td><td class="wed">22</td><td class="thu">23</td><td class="fri">24</td><td class="sat">25</td><td class="sun">26</td></tr>
234<tr><td class="mon">27</td><td class="tue">28</td><td class="wed">29</td><td class="thu">30</td><td class="fri">31</td><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td></tr>
235</table>
236</td></tr></table></body>
237</html>
238"""
239
240result_2004_days = [
241    [[[0, 0, 0, 1, 2, 3, 4],
242      [5, 6, 7, 8, 9, 10, 11],
243      [12, 13, 14, 15, 16, 17, 18],
244      [19, 20, 21, 22, 23, 24, 25],
245      [26, 27, 28, 29, 30, 31, 0]],
246     [[0, 0, 0, 0, 0, 0, 1],
247      [2, 3, 4, 5, 6, 7, 8],
248      [9, 10, 11, 12, 13, 14, 15],
249      [16, 17, 18, 19, 20, 21, 22],
250      [23, 24, 25, 26, 27, 28, 29]],
251     [[1, 2, 3, 4, 5, 6, 7],
252      [8, 9, 10, 11, 12, 13, 14],
253      [15, 16, 17, 18, 19, 20, 21],
254      [22, 23, 24, 25, 26, 27, 28],
255      [29, 30, 31, 0, 0, 0, 0]]],
256    [[[0, 0, 0, 1, 2, 3, 4],
257      [5, 6, 7, 8, 9, 10, 11],
258      [12, 13, 14, 15, 16, 17, 18],
259      [19, 20, 21, 22, 23, 24, 25],
260      [26, 27, 28, 29, 30, 0, 0]],
261     [[0, 0, 0, 0, 0, 1, 2],
262      [3, 4, 5, 6, 7, 8, 9],
263      [10, 11, 12, 13, 14, 15, 16],
264      [17, 18, 19, 20, 21, 22, 23],
265      [24, 25, 26, 27, 28, 29, 30],
266      [31, 0, 0, 0, 0, 0, 0]],
267     [[0, 1, 2, 3, 4, 5, 6],
268      [7, 8, 9, 10, 11, 12, 13],
269      [14, 15, 16, 17, 18, 19, 20],
270      [21, 22, 23, 24, 25, 26, 27],
271      [28, 29, 30, 0, 0, 0, 0]]],
272    [[[0, 0, 0, 1, 2, 3, 4],
273      [5, 6, 7, 8, 9, 10, 11],
274      [12, 13, 14, 15, 16, 17, 18],
275      [19, 20, 21, 22, 23, 24, 25],
276      [26, 27, 28, 29, 30, 31, 0]],
277     [[0, 0, 0, 0, 0, 0, 1],
278      [2, 3, 4, 5, 6, 7, 8],
279      [9, 10, 11, 12, 13, 14, 15],
280      [16, 17, 18, 19, 20, 21, 22],
281      [23, 24, 25, 26, 27, 28, 29],
282      [30, 31, 0, 0, 0, 0, 0]],
283     [[0, 0, 1, 2, 3, 4, 5],
284      [6, 7, 8, 9, 10, 11, 12],
285      [13, 14, 15, 16, 17, 18, 19],
286      [20, 21, 22, 23, 24, 25, 26],
287      [27, 28, 29, 30, 0, 0, 0]]],
288    [[[0, 0, 0, 0, 1, 2, 3],
289      [4, 5, 6, 7, 8, 9, 10],
290      [11, 12, 13, 14, 15, 16, 17],
291      [18, 19, 20, 21, 22, 23, 24],
292      [25, 26, 27, 28, 29, 30, 31]],
293     [[1, 2, 3, 4, 5, 6, 7],
294      [8, 9, 10, 11, 12, 13, 14],
295      [15, 16, 17, 18, 19, 20, 21],
296      [22, 23, 24, 25, 26, 27, 28],
297      [29, 30, 0, 0, 0, 0, 0]],
298     [[0, 0, 1, 2, 3, 4, 5],
299      [6, 7, 8, 9, 10, 11, 12],
300      [13, 14, 15, 16, 17, 18, 19],
301      [20, 21, 22, 23, 24, 25, 26],
302      [27, 28, 29, 30, 31, 0, 0]]]
303]
304
305result_2004_dates = \
306    [[['12/29/03 12/30/03 12/31/03 01/01/04 01/02/04 01/03/04 01/04/04',
307       '01/05/04 01/06/04 01/07/04 01/08/04 01/09/04 01/10/04 01/11/04',
308       '01/12/04 01/13/04 01/14/04 01/15/04 01/16/04 01/17/04 01/18/04',
309       '01/19/04 01/20/04 01/21/04 01/22/04 01/23/04 01/24/04 01/25/04',
310       '01/26/04 01/27/04 01/28/04 01/29/04 01/30/04 01/31/04 02/01/04'],
311      ['01/26/04 01/27/04 01/28/04 01/29/04 01/30/04 01/31/04 02/01/04',
312       '02/02/04 02/03/04 02/04/04 02/05/04 02/06/04 02/07/04 02/08/04',
313       '02/09/04 02/10/04 02/11/04 02/12/04 02/13/04 02/14/04 02/15/04',
314       '02/16/04 02/17/04 02/18/04 02/19/04 02/20/04 02/21/04 02/22/04',
315       '02/23/04 02/24/04 02/25/04 02/26/04 02/27/04 02/28/04 02/29/04'],
316      ['03/01/04 03/02/04 03/03/04 03/04/04 03/05/04 03/06/04 03/07/04',
317       '03/08/04 03/09/04 03/10/04 03/11/04 03/12/04 03/13/04 03/14/04',
318       '03/15/04 03/16/04 03/17/04 03/18/04 03/19/04 03/20/04 03/21/04',
319       '03/22/04 03/23/04 03/24/04 03/25/04 03/26/04 03/27/04 03/28/04',
320       '03/29/04 03/30/04 03/31/04 04/01/04 04/02/04 04/03/04 04/04/04']],
321     [['03/29/04 03/30/04 03/31/04 04/01/04 04/02/04 04/03/04 04/04/04',
322       '04/05/04 04/06/04 04/07/04 04/08/04 04/09/04 04/10/04 04/11/04',
323       '04/12/04 04/13/04 04/14/04 04/15/04 04/16/04 04/17/04 04/18/04',
324       '04/19/04 04/20/04 04/21/04 04/22/04 04/23/04 04/24/04 04/25/04',
325       '04/26/04 04/27/04 04/28/04 04/29/04 04/30/04 05/01/04 05/02/04'],
326      ['04/26/04 04/27/04 04/28/04 04/29/04 04/30/04 05/01/04 05/02/04',
327       '05/03/04 05/04/04 05/05/04 05/06/04 05/07/04 05/08/04 05/09/04',
328       '05/10/04 05/11/04 05/12/04 05/13/04 05/14/04 05/15/04 05/16/04',
329       '05/17/04 05/18/04 05/19/04 05/20/04 05/21/04 05/22/04 05/23/04',
330       '05/24/04 05/25/04 05/26/04 05/27/04 05/28/04 05/29/04 05/30/04',
331       '05/31/04 06/01/04 06/02/04 06/03/04 06/04/04 06/05/04 06/06/04'],
332      ['05/31/04 06/01/04 06/02/04 06/03/04 06/04/04 06/05/04 06/06/04',
333       '06/07/04 06/08/04 06/09/04 06/10/04 06/11/04 06/12/04 06/13/04',
334       '06/14/04 06/15/04 06/16/04 06/17/04 06/18/04 06/19/04 06/20/04',
335       '06/21/04 06/22/04 06/23/04 06/24/04 06/25/04 06/26/04 06/27/04',
336       '06/28/04 06/29/04 06/30/04 07/01/04 07/02/04 07/03/04 07/04/04']],
337     [['06/28/04 06/29/04 06/30/04 07/01/04 07/02/04 07/03/04 07/04/04',
338       '07/05/04 07/06/04 07/07/04 07/08/04 07/09/04 07/10/04 07/11/04',
339       '07/12/04 07/13/04 07/14/04 07/15/04 07/16/04 07/17/04 07/18/04',
340       '07/19/04 07/20/04 07/21/04 07/22/04 07/23/04 07/24/04 07/25/04',
341       '07/26/04 07/27/04 07/28/04 07/29/04 07/30/04 07/31/04 08/01/04'],
342      ['07/26/04 07/27/04 07/28/04 07/29/04 07/30/04 07/31/04 08/01/04',
343       '08/02/04 08/03/04 08/04/04 08/05/04 08/06/04 08/07/04 08/08/04',
344       '08/09/04 08/10/04 08/11/04 08/12/04 08/13/04 08/14/04 08/15/04',
345       '08/16/04 08/17/04 08/18/04 08/19/04 08/20/04 08/21/04 08/22/04',
346       '08/23/04 08/24/04 08/25/04 08/26/04 08/27/04 08/28/04 08/29/04',
347       '08/30/04 08/31/04 09/01/04 09/02/04 09/03/04 09/04/04 09/05/04'],
348      ['08/30/04 08/31/04 09/01/04 09/02/04 09/03/04 09/04/04 09/05/04',
349       '09/06/04 09/07/04 09/08/04 09/09/04 09/10/04 09/11/04 09/12/04',
350       '09/13/04 09/14/04 09/15/04 09/16/04 09/17/04 09/18/04 09/19/04',
351       '09/20/04 09/21/04 09/22/04 09/23/04 09/24/04 09/25/04 09/26/04',
352       '09/27/04 09/28/04 09/29/04 09/30/04 10/01/04 10/02/04 10/03/04']],
353     [['09/27/04 09/28/04 09/29/04 09/30/04 10/01/04 10/02/04 10/03/04',
354       '10/04/04 10/05/04 10/06/04 10/07/04 10/08/04 10/09/04 10/10/04',
355       '10/11/04 10/12/04 10/13/04 10/14/04 10/15/04 10/16/04 10/17/04',
356       '10/18/04 10/19/04 10/20/04 10/21/04 10/22/04 10/23/04 10/24/04',
357       '10/25/04 10/26/04 10/27/04 10/28/04 10/29/04 10/30/04 10/31/04'],
358      ['11/01/04 11/02/04 11/03/04 11/04/04 11/05/04 11/06/04 11/07/04',
359       '11/08/04 11/09/04 11/10/04 11/11/04 11/12/04 11/13/04 11/14/04',
360       '11/15/04 11/16/04 11/17/04 11/18/04 11/19/04 11/20/04 11/21/04',
361       '11/22/04 11/23/04 11/24/04 11/25/04 11/26/04 11/27/04 11/28/04',
362       '11/29/04 11/30/04 12/01/04 12/02/04 12/03/04 12/04/04 12/05/04'],
363      ['11/29/04 11/30/04 12/01/04 12/02/04 12/03/04 12/04/04 12/05/04',
364       '12/06/04 12/07/04 12/08/04 12/09/04 12/10/04 12/11/04 12/12/04',
365       '12/13/04 12/14/04 12/15/04 12/16/04 12/17/04 12/18/04 12/19/04',
366       '12/20/04 12/21/04 12/22/04 12/23/04 12/24/04 12/25/04 12/26/04',
367       '12/27/04 12/28/04 12/29/04 12/30/04 12/31/04 01/01/05 01/02/05']]]
368
369
370class OutputTestCase(unittest.TestCase):
371    def normalize_calendar(self, s):
372        # Filters out locale dependent strings
373        def neitherspacenordigit(c):
374            return not c.isspace() and not c.isdigit()
375
376        lines = []
377        for line in s.splitlines(keepends=False):
378            # Drop texts, as they are locale dependent
379            if line and not filter(neitherspacenordigit, line):
380                lines.append(line)
381        return lines
382
383    def check_htmlcalendar_encoding(self, req, res):
384        cal = calendar.HTMLCalendar()
385        format_ = default_format.copy()
386        format_["encoding"] = req or 'utf-8'
387        output = cal.formatyearpage(2004, encoding=req)
388        self.assertEqual(
389            output,
390            result_2004_html.format(**format_).encode(res)
391        )
392
393    def test_output(self):
394        self.assertEqual(
395            self.normalize_calendar(calendar.calendar(2004)),
396            self.normalize_calendar(result_2004_text)
397        )
398        self.assertEqual(
399            self.normalize_calendar(calendar.calendar(0)),
400            self.normalize_calendar(result_0_text)
401        )
402
403    def test_output_textcalendar(self):
404        self.assertEqual(
405            calendar.TextCalendar().formatyear(2004),
406            result_2004_text
407        )
408        self.assertEqual(
409            calendar.TextCalendar().formatyear(0),
410            result_0_text
411        )
412
413    def test_output_htmlcalendar_encoding_ascii(self):
414        self.check_htmlcalendar_encoding('ascii', 'ascii')
415
416    def test_output_htmlcalendar_encoding_utf8(self):
417        self.check_htmlcalendar_encoding('utf-8', 'utf-8')
418
419    def test_output_htmlcalendar_encoding_default(self):
420        self.check_htmlcalendar_encoding(None, sys.getdefaultencoding())
421
422    def test_yeardatescalendar(self):
423        def shrink(cal):
424            return [[[' '.join('{:02d}/{:02d}/{}'.format(
425                                d.month, d.day, str(d.year)[-2:]) for d in z)
426                            for z in y] for y in x] for x in cal]
427        self.assertEqual(
428            shrink(calendar.Calendar().yeardatescalendar(2004)),
429            result_2004_dates
430        )
431
432    def test_yeardayscalendar(self):
433        self.assertEqual(
434            calendar.Calendar().yeardayscalendar(2004),
435            result_2004_days
436        )
437
438    def test_formatweekheader_short(self):
439        self.assertEqual(
440            calendar.TextCalendar().formatweekheader(2),
441            'Mo Tu We Th Fr Sa Su'
442        )
443
444    def test_formatweekheader_long(self):
445        self.assertEqual(
446            calendar.TextCalendar().formatweekheader(9),
447            '  Monday   Tuesday  Wednesday  Thursday '
448            '  Friday   Saturday   Sunday '
449        )
450
451    def test_formatmonth(self):
452        self.assertEqual(
453            calendar.TextCalendar().formatmonth(2004, 1),
454            result_2004_01_text
455        )
456        self.assertEqual(
457            calendar.TextCalendar().formatmonth(0, 2),
458            result_0_02_text
459        )
460    def test_formatmonth_with_invalid_month(self):
461        with self.assertRaises(calendar.IllegalMonthError):
462            calendar.TextCalendar().formatmonth(2017, 13)
463        with self.assertRaises(calendar.IllegalMonthError):
464            calendar.TextCalendar().formatmonth(2017, -1)
465
466    def test_formatmonthname_with_year(self):
467        self.assertEqual(
468            calendar.HTMLCalendar().formatmonthname(2004, 1, withyear=True),
469            '<tr><th colspan="7" class="month">January 2004</th></tr>'
470        )
471
472    def test_formatmonthname_without_year(self):
473        self.assertEqual(
474            calendar.HTMLCalendar().formatmonthname(2004, 1, withyear=False),
475            '<tr><th colspan="7" class="month">January</th></tr>'
476        )
477
478    def test_prweek(self):
479        with support.captured_stdout() as out:
480            week = [(1,0), (2,1), (3,2), (4,3), (5,4), (6,5), (7,6)]
481            calendar.TextCalendar().prweek(week, 1)
482            self.assertEqual(out.getvalue(), " 1  2  3  4  5  6  7")
483
484    def test_prmonth(self):
485        with support.captured_stdout() as out:
486            calendar.TextCalendar().prmonth(2004, 1)
487            self.assertEqual(out.getvalue(), result_2004_01_text)
488
489    def test_pryear(self):
490        with support.captured_stdout() as out:
491            calendar.TextCalendar().pryear(2004)
492            self.assertEqual(out.getvalue(), result_2004_text)
493
494    def test_format(self):
495        with support.captured_stdout() as out:
496            calendar.format(["1", "2", "3"], colwidth=3, spacing=1)
497            self.assertEqual(out.getvalue().strip(), "1   2   3")
498
499class CalendarTestCase(unittest.TestCase):
500
501    def test_deprecation_warning(self):
502        with self.assertWarnsRegex(
503            DeprecationWarning,
504            "The 'January' attribute is deprecated, use 'JANUARY' instead"
505        ):
506            calendar.January
507
508    def test_isleap(self):
509        # Make sure that the return is right for a few years, and
510        # ensure that the return values are 1 or 0, not just true or
511        # false (see SF bug #485794).  Specific additional tests may
512        # be appropriate; this tests a single "cycle".
513        self.assertEqual(calendar.isleap(2000), 1)
514        self.assertEqual(calendar.isleap(2001), 0)
515        self.assertEqual(calendar.isleap(2002), 0)
516        self.assertEqual(calendar.isleap(2003), 0)
517
518    def test_setfirstweekday(self):
519        self.assertRaises(TypeError, calendar.setfirstweekday, 'flabber')
520        self.assertRaises(ValueError, calendar.setfirstweekday, -1)
521        self.assertRaises(ValueError, calendar.setfirstweekday, 200)
522        orig = calendar.firstweekday()
523        calendar.setfirstweekday(calendar.SUNDAY)
524        self.assertEqual(calendar.firstweekday(), calendar.SUNDAY)
525        calendar.setfirstweekday(calendar.MONDAY)
526        self.assertEqual(calendar.firstweekday(), calendar.MONDAY)
527        calendar.setfirstweekday(orig)
528
529    def test_illegal_weekday_reported(self):
530        with self.assertRaisesRegex(calendar.IllegalWeekdayError, '123'):
531            calendar.setfirstweekday(123)
532
533    def test_enumerate_weekdays(self):
534        self.assertRaises(IndexError, calendar.day_abbr.__getitem__, -10)
535        self.assertRaises(IndexError, calendar.day_name.__getitem__, 10)
536        self.assertEqual(len([d for d in calendar.day_abbr]), 7)
537
538    def test_days(self):
539        for attr in "day_name", "day_abbr":
540            value = getattr(calendar, attr)
541            self.assertEqual(len(value), 7)
542            self.assertEqual(len(value[:]), 7)
543            # ensure they're all unique
544            self.assertEqual(len(set(value)), 7)
545            # verify it "acts like a sequence" in two forms of iteration
546            self.assertEqual(value[::-1], list(reversed(value)))
547
548    def test_months(self):
549        for attr in "month_name", "month_abbr":
550            value = getattr(calendar, attr)
551            self.assertEqual(len(value), 13)
552            self.assertEqual(len(value[:]), 13)
553            self.assertEqual(value[0], "")
554            # ensure they're all unique
555            self.assertEqual(len(set(value)), 13)
556            # verify it "acts like a sequence" in two forms of iteration
557            self.assertEqual(value[::-1], list(reversed(value)))
558
559    def test_locale_text_calendar(self):
560        try:
561            cal = calendar.LocaleTextCalendar(locale='')
562            local_weekday = cal.formatweekday(1, 10)
563            local_weekday_abbr = cal.formatweekday(1, 3)
564            local_month = cal.formatmonthname(2010, 10, 10)
565        except locale.Error:
566            # cannot set the system default locale -- skip rest of test
567            raise unittest.SkipTest('cannot set the system default locale')
568        self.assertIsInstance(local_weekday, str)
569        self.assertIsInstance(local_weekday_abbr, str)
570        self.assertIsInstance(local_month, str)
571        self.assertEqual(len(local_weekday), 10)
572        self.assertEqual(len(local_weekday_abbr), 3)
573        self.assertGreaterEqual(len(local_month), 10)
574
575        cal = calendar.LocaleTextCalendar(locale=None)
576        local_weekday = cal.formatweekday(1, 10)
577        local_weekday_abbr = cal.formatweekday(1, 3)
578        local_month = cal.formatmonthname(2010, 10, 10)
579        self.assertIsInstance(local_weekday, str)
580        self.assertIsInstance(local_weekday_abbr, str)
581        self.assertIsInstance(local_month, str)
582        self.assertEqual(len(local_weekday), 10)
583        self.assertEqual(len(local_weekday_abbr), 3)
584        self.assertGreaterEqual(len(local_month), 10)
585
586        cal = calendar.LocaleTextCalendar(locale='C')
587        local_weekday = cal.formatweekday(1, 10)
588        local_weekday_abbr = cal.formatweekday(1, 3)
589        local_month = cal.formatmonthname(2010, 10, 10)
590        self.assertIsInstance(local_weekday, str)
591        self.assertIsInstance(local_weekday_abbr, str)
592        self.assertIsInstance(local_month, str)
593        self.assertEqual(len(local_weekday), 10)
594        self.assertEqual(len(local_weekday_abbr), 3)
595        self.assertGreaterEqual(len(local_month), 10)
596
597    def test_locale_html_calendar(self):
598        try:
599            cal = calendar.LocaleHTMLCalendar(locale='')
600            local_weekday = cal.formatweekday(1)
601            local_month = cal.formatmonthname(2010, 10)
602        except locale.Error:
603            # cannot set the system default locale -- skip rest of test
604            raise unittest.SkipTest('cannot set the system default locale')
605        self.assertIsInstance(local_weekday, str)
606        self.assertIsInstance(local_month, str)
607
608        cal = calendar.LocaleHTMLCalendar(locale=None)
609        local_weekday = cal.formatweekday(1)
610        local_month = cal.formatmonthname(2010, 10)
611        self.assertIsInstance(local_weekday, str)
612        self.assertIsInstance(local_month, str)
613
614        cal = calendar.LocaleHTMLCalendar(locale='C')
615        local_weekday = cal.formatweekday(1)
616        local_month = cal.formatmonthname(2010, 10)
617        self.assertIsInstance(local_weekday, str)
618        self.assertIsInstance(local_month, str)
619
620    def test_locale_calendars_reset_locale_properly(self):
621        # ensure that Locale{Text,HTML}Calendar resets the locale properly
622        # (it is still not thread-safe though)
623        old_october = calendar.TextCalendar().formatmonthname(2010, 10, 10)
624        try:
625            cal = calendar.LocaleTextCalendar(locale='')
626            local_weekday = cal.formatweekday(1, 10)
627            local_weekday_abbr = cal.formatweekday(1, 3)
628            local_month = cal.formatmonthname(2010, 10, 10)
629        except locale.Error:
630            # cannot set the system default locale -- skip rest of test
631            raise unittest.SkipTest('cannot set the system default locale')
632        self.assertIsInstance(local_weekday, str)
633        self.assertIsInstance(local_weekday_abbr, str)
634        self.assertIsInstance(local_month, str)
635        self.assertEqual(len(local_weekday), 10)
636        self.assertEqual(len(local_weekday_abbr), 3)
637        self.assertGreaterEqual(len(local_month), 10)
638
639        cal = calendar.LocaleHTMLCalendar(locale='')
640        local_weekday = cal.formatweekday(1)
641        local_month = cal.formatmonthname(2010, 10)
642        self.assertIsInstance(local_weekday, str)
643        self.assertIsInstance(local_month, str)
644
645        new_october = calendar.TextCalendar().formatmonthname(2010, 10, 10)
646        self.assertEqual(old_october, new_october)
647
648    def test_locale_calendar_formatweekday(self):
649        try:
650            # formatweekday uses different day names based on the available width.
651            cal = calendar.LocaleTextCalendar(locale='en_US')
652            # For really short widths, the abbreviated name is truncated.
653            self.assertEqual(cal.formatweekday(0, 1), "M")
654            self.assertEqual(cal.formatweekday(0, 2), "Mo")
655            # For short widths, a centered, abbreviated name is used.
656            self.assertEqual(cal.formatweekday(0, 3), "Mon")
657            self.assertEqual(cal.formatweekday(0, 5), " Mon ")
658            self.assertEqual(cal.formatweekday(0, 8), "  Mon   ")
659            # For long widths, the full day name is used.
660            self.assertEqual(cal.formatweekday(0, 9), "  Monday ")
661            self.assertEqual(cal.formatweekday(0, 10), "  Monday  ")
662        except locale.Error:
663            raise unittest.SkipTest('cannot set the en_US locale')
664
665    def test_locale_calendar_formatmonthname(self):
666        try:
667            # formatmonthname uses the same month names regardless of the width argument.
668            cal = calendar.LocaleTextCalendar(locale='en_US')
669            # For too short widths, a full name (with year) is used.
670            self.assertEqual(cal.formatmonthname(2022, 6, 2, withyear=False), "June")
671            self.assertEqual(cal.formatmonthname(2022, 6, 2, withyear=True), "June 2022")
672            self.assertEqual(cal.formatmonthname(2022, 6, 3, withyear=False), "June")
673            self.assertEqual(cal.formatmonthname(2022, 6, 3, withyear=True), "June 2022")
674            # For long widths, a centered name is used.
675            self.assertEqual(cal.formatmonthname(2022, 6, 10, withyear=False), "   June   ")
676            self.assertEqual(cal.formatmonthname(2022, 6, 15, withyear=True), "   June 2022   ")
677        except locale.Error:
678            raise unittest.SkipTest('cannot set the en_US locale')
679
680    def test_locale_html_calendar_custom_css_class_month_name(self):
681        try:
682            cal = calendar.LocaleHTMLCalendar(locale='')
683            local_month = cal.formatmonthname(2010, 10, 10)
684        except locale.Error:
685            # cannot set the system default locale -- skip rest of test
686            raise unittest.SkipTest('cannot set the system default locale')
687        self.assertIn('class="month"', local_month)
688        cal.cssclass_month_head = "text-center month"
689        local_month = cal.formatmonthname(2010, 10, 10)
690        self.assertIn('class="text-center month"', local_month)
691
692    def test_locale_html_calendar_custom_css_class_weekday(self):
693        try:
694            cal = calendar.LocaleHTMLCalendar(locale='')
695            local_weekday = cal.formatweekday(6)
696        except locale.Error:
697            # cannot set the system default locale -- skip rest of test
698            raise unittest.SkipTest('cannot set the system default locale')
699        self.assertIn('class="sun"', local_weekday)
700        cal.cssclasses_weekday_head = ["mon2", "tue2", "wed2", "thu2", "fri2", "sat2", "sun2"]
701        local_weekday = cal.formatweekday(6)
702        self.assertIn('class="sun2"', local_weekday)
703
704    def test_itermonthdays3(self):
705        # ensure itermonthdays3 doesn't overflow after datetime.MAXYEAR
706        list(calendar.Calendar().itermonthdays3(datetime.MAXYEAR, 12))
707
708    def test_itermonthdays4(self):
709        cal = calendar.Calendar(firstweekday=3)
710        days = list(cal.itermonthdays4(2001, 2))
711        self.assertEqual(days[0], (2001, 2, 1, 3))
712        self.assertEqual(days[-1], (2001, 2, 28, 2))
713
714    def test_itermonthdays(self):
715        for firstweekday in range(7):
716            cal = calendar.Calendar(firstweekday)
717            # Test the extremes, see #28253 and #26650
718            for y, m in [(1, 1), (9999, 12)]:
719                days = list(cal.itermonthdays(y, m))
720                self.assertIn(len(days), (35, 42))
721        # Test a short month
722        cal = calendar.Calendar(firstweekday=3)
723        days = list(cal.itermonthdays(2001, 2))
724        self.assertEqual(days, list(range(1, 29)))
725
726    def test_itermonthdays2(self):
727        for firstweekday in range(7):
728            cal = calendar.Calendar(firstweekday)
729            # Test the extremes, see #28253 and #26650
730            for y, m in [(1, 1), (9999, 12)]:
731                days = list(cal.itermonthdays2(y, m))
732                self.assertEqual(days[0][1], firstweekday)
733                self.assertEqual(days[-1][1], (firstweekday - 1) % 7)
734
735    def test_iterweekdays(self):
736        week0 = list(range(7))
737        for firstweekday in range(7):
738            cal = calendar.Calendar(firstweekday)
739            week = list(cal.iterweekdays())
740            expected = week0[firstweekday:] + week0[:firstweekday]
741            self.assertEqual(week, expected)
742
743
744class MonthCalendarTestCase(unittest.TestCase):
745    def setUp(self):
746        self.oldfirstweekday = calendar.firstweekday()
747        calendar.setfirstweekday(self.firstweekday)
748
749    def tearDown(self):
750        calendar.setfirstweekday(self.oldfirstweekday)
751
752    def check_weeks(self, year, month, weeks):
753        cal = calendar.monthcalendar(year, month)
754        self.assertEqual(len(cal), len(weeks))
755        for i in range(len(weeks)):
756            self.assertEqual(weeks[i], sum(day != 0 for day in cal[i]))
757
758
759class MondayTestCase(MonthCalendarTestCase):
760    firstweekday = calendar.MONDAY
761
762    def test_february(self):
763        # A 28-day february starting on monday (7+7+7+7 days)
764        self.check_weeks(1999, 2, (7, 7, 7, 7))
765
766        # A 28-day february starting on tuesday (6+7+7+7+1 days)
767        self.check_weeks(2005, 2, (6, 7, 7, 7, 1))
768
769        # A 28-day february starting on sunday (1+7+7+7+6 days)
770        self.check_weeks(1987, 2, (1, 7, 7, 7, 6))
771
772        # A 29-day february starting on monday (7+7+7+7+1 days)
773        self.check_weeks(1988, 2, (7, 7, 7, 7, 1))
774
775        # A 29-day february starting on tuesday (6+7+7+7+2 days)
776        self.check_weeks(1972, 2, (6, 7, 7, 7, 2))
777
778        # A 29-day february starting on sunday (1+7+7+7+7 days)
779        self.check_weeks(2004, 2, (1, 7, 7, 7, 7))
780
781    def test_april(self):
782        # A 30-day april starting on monday (7+7+7+7+2 days)
783        self.check_weeks(1935, 4, (7, 7, 7, 7, 2))
784
785        # A 30-day april starting on tuesday (6+7+7+7+3 days)
786        self.check_weeks(1975, 4, (6, 7, 7, 7, 3))
787
788        # A 30-day april starting on sunday (1+7+7+7+7+1 days)
789        self.check_weeks(1945, 4, (1, 7, 7, 7, 7, 1))
790
791        # A 30-day april starting on saturday (2+7+7+7+7 days)
792        self.check_weeks(1995, 4, (2, 7, 7, 7, 7))
793
794        # A 30-day april starting on friday (3+7+7+7+6 days)
795        self.check_weeks(1994, 4, (3, 7, 7, 7, 6))
796
797    def test_december(self):
798        # A 31-day december starting on monday (7+7+7+7+3 days)
799        self.check_weeks(1980, 12, (7, 7, 7, 7, 3))
800
801        # A 31-day december starting on tuesday (6+7+7+7+4 days)
802        self.check_weeks(1987, 12, (6, 7, 7, 7, 4))
803
804        # A 31-day december starting on sunday (1+7+7+7+7+2 days)
805        self.check_weeks(1968, 12, (1, 7, 7, 7, 7, 2))
806
807        # A 31-day december starting on thursday (4+7+7+7+6 days)
808        self.check_weeks(1988, 12, (4, 7, 7, 7, 6))
809
810        # A 31-day december starting on friday (3+7+7+7+7 days)
811        self.check_weeks(2017, 12, (3, 7, 7, 7, 7))
812
813        # A 31-day december starting on saturday (2+7+7+7+7+1 days)
814        self.check_weeks(2068, 12, (2, 7, 7, 7, 7, 1))
815
816
817class SundayTestCase(MonthCalendarTestCase):
818    firstweekday = calendar.SUNDAY
819
820    def test_february(self):
821        # A 28-day february starting on sunday (7+7+7+7 days)
822        self.check_weeks(2009, 2, (7, 7, 7, 7))
823
824        # A 28-day february starting on monday (6+7+7+7+1 days)
825        self.check_weeks(1999, 2, (6, 7, 7, 7, 1))
826
827        # A 28-day february starting on saturday (1+7+7+7+6 days)
828        self.check_weeks(1997, 2, (1, 7, 7, 7, 6))
829
830        # A 29-day february starting on sunday (7+7+7+7+1 days)
831        self.check_weeks(2004, 2, (7, 7, 7, 7, 1))
832
833        # A 29-day february starting on monday (6+7+7+7+2 days)
834        self.check_weeks(1960, 2, (6, 7, 7, 7, 2))
835
836        # A 29-day february starting on saturday (1+7+7+7+7 days)
837        self.check_weeks(1964, 2, (1, 7, 7, 7, 7))
838
839    def test_april(self):
840        # A 30-day april starting on sunday (7+7+7+7+2 days)
841        self.check_weeks(1923, 4, (7, 7, 7, 7, 2))
842
843        # A 30-day april starting on monday (6+7+7+7+3 days)
844        self.check_weeks(1918, 4, (6, 7, 7, 7, 3))
845
846        # A 30-day april starting on saturday (1+7+7+7+7+1 days)
847        self.check_weeks(1950, 4, (1, 7, 7, 7, 7, 1))
848
849        # A 30-day april starting on friday (2+7+7+7+7 days)
850        self.check_weeks(1960, 4, (2, 7, 7, 7, 7))
851
852        # A 30-day april starting on thursday (3+7+7+7+6 days)
853        self.check_weeks(1909, 4, (3, 7, 7, 7, 6))
854
855    def test_december(self):
856        # A 31-day december starting on sunday (7+7+7+7+3 days)
857        self.check_weeks(2080, 12, (7, 7, 7, 7, 3))
858
859        # A 31-day december starting on monday (6+7+7+7+4 days)
860        self.check_weeks(1941, 12, (6, 7, 7, 7, 4))
861
862        # A 31-day december starting on saturday (1+7+7+7+7+2 days)
863        self.check_weeks(1923, 12, (1, 7, 7, 7, 7, 2))
864
865        # A 31-day december starting on wednesday (4+7+7+7+6 days)
866        self.check_weeks(1948, 12, (4, 7, 7, 7, 6))
867
868        # A 31-day december starting on thursday (3+7+7+7+7 days)
869        self.check_weeks(1927, 12, (3, 7, 7, 7, 7))
870
871        # A 31-day december starting on friday (2+7+7+7+7+1 days)
872        self.check_weeks(1995, 12, (2, 7, 7, 7, 7, 1))
873
874class TimegmTestCase(unittest.TestCase):
875    TIMESTAMPS = [0, 10, 100, 1000, 10000, 100000, 1000000,
876                  1234567890, 1262304000, 1275785153,]
877    def test_timegm(self):
878        for secs in self.TIMESTAMPS:
879            tuple = time.gmtime(secs)
880            self.assertEqual(secs, calendar.timegm(tuple))
881
882class MonthRangeTestCase(unittest.TestCase):
883    def test_january(self):
884        # Tests valid lower boundary case.
885        self.assertEqual(calendar.monthrange(2004,1), (3,31))
886
887    def test_february_leap(self):
888        # Tests February during leap year.
889        self.assertEqual(calendar.monthrange(2004,2), (6,29))
890
891    def test_february_nonleap(self):
892        # Tests February in non-leap year.
893        self.assertEqual(calendar.monthrange(2010,2), (0,28))
894
895    def test_december(self):
896        # Tests valid upper boundary case.
897        self.assertEqual(calendar.monthrange(2004,12), (2,31))
898
899    def test_zeroth_month(self):
900        # Tests low invalid boundary case.
901        with self.assertRaises(calendar.IllegalMonthError):
902            calendar.monthrange(2004, 0)
903
904    def test_thirteenth_month(self):
905        # Tests high invalid boundary case.
906        with self.assertRaises(calendar.IllegalMonthError):
907            calendar.monthrange(2004, 13)
908
909    def test_illegal_month_reported(self):
910        with self.assertRaisesRegex(calendar.IllegalMonthError, '65'):
911            calendar.monthrange(2004, 65)
912
913class LeapdaysTestCase(unittest.TestCase):
914    def test_no_range(self):
915        # test when no range i.e. two identical years as args
916        self.assertEqual(calendar.leapdays(2010,2010), 0)
917
918    def test_no_leapdays(self):
919        # test when no leap years in range
920        self.assertEqual(calendar.leapdays(2010,2011), 0)
921
922    def test_no_leapdays_upper_boundary(self):
923        # test no leap years in range, when upper boundary is a leap year
924        self.assertEqual(calendar.leapdays(2010,2012), 0)
925
926    def test_one_leapday_lower_boundary(self):
927        # test when one leap year in range, lower boundary is leap year
928        self.assertEqual(calendar.leapdays(2012,2013), 1)
929
930    def test_several_leapyears_in_range(self):
931        self.assertEqual(calendar.leapdays(1997,2020), 5)
932
933
934def conv(s):
935    return s.replace('\n', os.linesep).encode()
936
937class CommandLineTestCase(unittest.TestCase):
938    def setUp(self):
939        self.runners = [self.run_cli_ok, self.run_cmd_ok]
940
941    @contextlib.contextmanager
942    def captured_stdout_with_buffer(self):
943        orig_stdout = sys.stdout
944        buffer = io.BytesIO()
945        sys.stdout = io.TextIOWrapper(buffer)
946        try:
947            yield sys.stdout
948        finally:
949            sys.stdout.flush()
950            sys.stdout.buffer.seek(0)
951            sys.stdout = orig_stdout
952
953    @contextlib.contextmanager
954    def captured_stderr_with_buffer(self):
955        orig_stderr = sys.stderr
956        buffer = io.BytesIO()
957        sys.stderr = io.TextIOWrapper(buffer)
958        try:
959            yield sys.stderr
960        finally:
961            sys.stderr.flush()
962            sys.stderr.buffer.seek(0)
963            sys.stderr = orig_stderr
964
965    def run_cli_ok(self, *args):
966        with self.captured_stdout_with_buffer() as stdout:
967            calendar.main(args)
968        return stdout.buffer.read()
969
970    def run_cmd_ok(self, *args):
971        return assert_python_ok('-m', 'calendar', *args)[1]
972
973    def assertCLIFails(self, *args):
974        with self.captured_stderr_with_buffer() as stderr:
975            self.assertRaises(SystemExit, calendar.main, args)
976        stderr = stderr.buffer.read()
977        self.assertIn(b'usage:', stderr)
978        return stderr
979
980    def assertCmdFails(self, *args):
981        rc, stdout, stderr = assert_python_failure('-m', 'calendar', *args)
982        self.assertIn(b'usage:', stderr)
983        self.assertEqual(rc, 2)
984        return rc, stdout, stderr
985
986    def assertFailure(self, *args):
987        self.assertCLIFails(*args)
988        self.assertCmdFails(*args)
989
990    def test_help(self):
991        stdout = self.run_cmd_ok('-h')
992        self.assertIn(b'usage:', stdout)
993        self.assertIn(b'calendar.py', stdout)
994        self.assertIn(b'--help', stdout)
995
996        # special case: stdout but sys.exit()
997        with self.captured_stdout_with_buffer() as output:
998            self.assertRaises(SystemExit, calendar.main, ['-h'])
999        output = output.buffer.read()
1000        self.assertIn(b'usage:', output)
1001        self.assertIn(b'--help', output)
1002
1003    def test_illegal_arguments(self):
1004        self.assertFailure('-z')
1005        self.assertFailure('spam')
1006        self.assertFailure('2004', 'spam')
1007        self.assertFailure('2004', '1', 'spam')
1008        self.assertFailure('2004', '1', '1')
1009        self.assertFailure('2004', '1', '1', 'spam')
1010        self.assertFailure('-t', 'html', '2004', '1')
1011
1012    def test_output_current_year(self):
1013        for run in self.runners:
1014            output = run()
1015            year = datetime.datetime.now().year
1016            self.assertIn(conv(' %s' % year), output)
1017            self.assertIn(b'January', output)
1018            self.assertIn(b'Mo Tu We Th Fr Sa Su', output)
1019
1020    def test_output_year(self):
1021        for run in self.runners:
1022            output = run('2004')
1023            self.assertEqual(output, conv(result_2004_text))
1024
1025    def test_output_month(self):
1026        for run in self.runners:
1027            output = run('2004', '1')
1028            self.assertEqual(output, conv(result_2004_01_text))
1029
1030    def test_option_encoding(self):
1031        self.assertFailure('-e')
1032        self.assertFailure('--encoding')
1033        for run in self.runners:
1034            output = run('--encoding', 'utf-16-le', '2004')
1035            self.assertEqual(output, result_2004_text.encode('utf-16-le'))
1036
1037    def test_option_locale(self):
1038        self.assertFailure('-L')
1039        self.assertFailure('--locale')
1040        self.assertFailure('-L', 'en')
1041
1042        lang, enc = locale.getlocale()
1043        lang = lang or 'C'
1044        enc = enc or 'UTF-8'
1045        try:
1046            oldlocale = locale.getlocale(locale.LC_TIME)
1047            try:
1048                locale.setlocale(locale.LC_TIME, (lang, enc))
1049            finally:
1050                locale.setlocale(locale.LC_TIME, oldlocale)
1051        except (locale.Error, ValueError):
1052            self.skipTest('cannot set the system default locale')
1053        for run in self.runners:
1054            for type in ('text', 'html'):
1055                output = run(
1056                    '--type', type, '--locale', lang, '--encoding', enc, '2004'
1057                )
1058                self.assertIn('2004'.encode(enc), output)
1059
1060    def test_option_width(self):
1061        self.assertFailure('-w')
1062        self.assertFailure('--width')
1063        self.assertFailure('-w', 'spam')
1064        for run in self.runners:
1065            output = run('--width', '3', '2004')
1066            self.assertIn(b'Mon Tue Wed Thu Fri Sat Sun', output)
1067
1068    def test_option_lines(self):
1069        self.assertFailure('-l')
1070        self.assertFailure('--lines')
1071        self.assertFailure('-l', 'spam')
1072        for run in self.runners:
1073            output = run('--lines', '2', '2004')
1074            self.assertIn(conv('December\n\nMo Tu We'), output)
1075
1076    def test_option_spacing(self):
1077        self.assertFailure('-s')
1078        self.assertFailure('--spacing')
1079        self.assertFailure('-s', 'spam')
1080        for run in self.runners:
1081            output = run('--spacing', '8', '2004')
1082            self.assertIn(b'Su        Mo', output)
1083
1084    def test_option_months(self):
1085        self.assertFailure('-m')
1086        self.assertFailure('--month')
1087        self.assertFailure('-m', 'spam')
1088        for run in self.runners:
1089            output = run('--months', '1', '2004')
1090            self.assertIn(conv('\nMo Tu We Th Fr Sa Su\n'), output)
1091
1092    def test_option_type(self):
1093        self.assertFailure('-t')
1094        self.assertFailure('--type')
1095        self.assertFailure('-t', 'spam')
1096        for run in self.runners:
1097            output = run('--type', 'text', '2004')
1098            self.assertEqual(output, conv(result_2004_text))
1099            output = run('--type', 'html', '2004')
1100            self.assertEqual(output[:6], b'<?xml ')
1101            self.assertIn(b'<title>Calendar for 2004</title>', output)
1102
1103    def test_html_output_current_year(self):
1104        for run in self.runners:
1105            output = run('--type', 'html')
1106            year = datetime.datetime.now().year
1107            self.assertIn(('<title>Calendar for %s</title>' % year).encode(), output)
1108            self.assertIn(b'<tr><th colspan="7" class="month">January</th></tr>', output)
1109
1110    def test_html_output_year_encoding(self):
1111        for run in self.runners:
1112            output = run('-t', 'html', '--encoding', 'ascii', '2004')
1113            self.assertEqual(output, result_2004_html.format(**default_format).encode('ascii'))
1114
1115    def test_html_output_year_css(self):
1116        self.assertFailure('-t', 'html', '-c')
1117        self.assertFailure('-t', 'html', '--css')
1118        for run in self.runners:
1119            output = run('-t', 'html', '--css', 'custom.css', '2004')
1120            self.assertIn(b'<link rel="stylesheet" type="text/css" '
1121                          b'href="custom.css" />', output)
1122
1123
1124class MiscTestCase(unittest.TestCase):
1125    def test__all__(self):
1126        not_exported = {
1127            'mdays', 'January', 'February', 'EPOCH',
1128            'different_locale', 'c', 'prweek', 'week', 'format',
1129            'formatstring', 'main', 'monthlen', 'prevmonth', 'nextmonth', ""}
1130        support.check__all__(self, calendar, not_exported=not_exported)
1131
1132
1133class TestSubClassingCase(unittest.TestCase):
1134
1135    def setUp(self):
1136
1137        class CustomHTMLCal(calendar.HTMLCalendar):
1138            cssclasses = [style + " text-nowrap" for style in
1139                          calendar.HTMLCalendar.cssclasses]
1140            cssclasses_weekday_head = ["red", "blue", "green", "lilac",
1141                                       "yellow", "orange", "pink"]
1142            cssclass_month_head = "text-center month-head"
1143            cssclass_month = "text-center month"
1144            cssclass_year = "text-italic "
1145            cssclass_year_head = "lead "
1146
1147        self.cal = CustomHTMLCal()
1148
1149    def test_formatmonthname(self):
1150        self.assertIn('class="text-center month-head"',
1151                      self.cal.formatmonthname(2017, 5))
1152
1153    def test_formatmonth(self):
1154        self.assertIn('class="text-center month"',
1155                      self.cal.formatmonth(2017, 5))
1156
1157    def test_formatmonth_with_invalid_month(self):
1158        with self.assertRaises(calendar.IllegalMonthError):
1159            self.cal.formatmonth(2017, 13)
1160        with self.assertRaises(calendar.IllegalMonthError):
1161            self.cal.formatmonth(2017, -1)
1162
1163
1164    def test_formatweek(self):
1165        weeks = self.cal.monthdays2calendar(2017, 5)
1166        self.assertIn('class="wed text-nowrap"', self.cal.formatweek(weeks[0]))
1167
1168    def test_formatweek_head(self):
1169        header = self.cal.formatweekheader()
1170        for color in self.cal.cssclasses_weekday_head:
1171            self.assertIn('<th class="%s">' % color, header)
1172
1173    def test_format_year(self):
1174        self.assertIn(
1175            ('<table border="0" cellpadding="0" cellspacing="0" class="%s">' %
1176             self.cal.cssclass_year), self.cal.formatyear(2017))
1177
1178    def test_format_year_head(self):
1179        self.assertIn('<tr><th colspan="%d" class="%s">%s</th></tr>' % (
1180            3, self.cal.cssclass_year_head, 2017), self.cal.formatyear(2017))
1181
1182if __name__ == "__main__":
1183    unittest.main()
1184