1import calendar
2import unittest
3
4from test import support
5from test.support.script_helper import assert_python_ok, assert_python_failure
6import time
7import locale
8import sys
9import datetime
10import os
11
12# From https://en.wikipedia.org/wiki/Leap_year_starting_on_Saturday
13result_0_02_text = """\
14     February 0
15Mo Tu We Th Fr Sa Su
16    1  2  3  4  5  6
17 7  8  9 10 11 12 13
1814 15 16 17 18 19 20
1921 22 23 24 25 26 27
2028 29
21"""
22
23result_0_text = """\
24                                   0
25
26      January                   February                   March
27Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
28                1  2          1  2  3  4  5  6             1  2  3  4  5
29 3  4  5  6  7  8  9       7  8  9 10 11 12 13       6  7  8  9 10 11 12
3010 11 12 13 14 15 16      14 15 16 17 18 19 20      13 14 15 16 17 18 19
3117 18 19 20 21 22 23      21 22 23 24 25 26 27      20 21 22 23 24 25 26
3224 25 26 27 28 29 30      28 29                     27 28 29 30 31
3331
34
35       April                      May                       June
36Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
37                1  2       1  2  3  4  5  6  7                1  2  3  4
38 3  4  5  6  7  8  9       8  9 10 11 12 13 14       5  6  7  8  9 10 11
3910 11 12 13 14 15 16      15 16 17 18 19 20 21      12 13 14 15 16 17 18
4017 18 19 20 21 22 23      22 23 24 25 26 27 28      19 20 21 22 23 24 25
4124 25 26 27 28 29 30      29 30 31                  26 27 28 29 30
42
43        July                     August                  September
44Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
45                1  2          1  2  3  4  5  6                   1  2  3
46 3  4  5  6  7  8  9       7  8  9 10 11 12 13       4  5  6  7  8  9 10
4710 11 12 13 14 15 16      14 15 16 17 18 19 20      11 12 13 14 15 16 17
4817 18 19 20 21 22 23      21 22 23 24 25 26 27      18 19 20 21 22 23 24
4924 25 26 27 28 29 30      28 29 30 31               25 26 27 28 29 30
5031
51
52      October                   November                  December
53Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
54                   1             1  2  3  4  5                   1  2  3
55 2  3  4  5  6  7  8       6  7  8  9 10 11 12       4  5  6  7  8  9 10
56 9 10 11 12 13 14 15      13 14 15 16 17 18 19      11 12 13 14 15 16 17
5716 17 18 19 20 21 22      20 21 22 23 24 25 26      18 19 20 21 22 23 24
5823 24 25 26 27 28 29      27 28 29 30               25 26 27 28 29 30 31
5930 31
60"""
61
62result_2004_01_text = """\
63    January 2004
64Mo Tu We Th Fr Sa Su
65          1  2  3  4
66 5  6  7  8  9 10 11
6712 13 14 15 16 17 18
6819 20 21 22 23 24 25
6926 27 28 29 30 31
70"""
71
72result_2004_text = """\
73                                  2004
74
75      January                   February                   March
76Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
77          1  2  3  4                         1       1  2  3  4  5  6  7
78 5  6  7  8  9 10 11       2  3  4  5  6  7  8       8  9 10 11 12 13 14
7912 13 14 15 16 17 18       9 10 11 12 13 14 15      15 16 17 18 19 20 21
8019 20 21 22 23 24 25      16 17 18 19 20 21 22      22 23 24 25 26 27 28
8126 27 28 29 30 31         23 24 25 26 27 28 29      29 30 31
82
83       April                      May                       June
84Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
85          1  2  3  4                      1  2          1  2  3  4  5  6
86 5  6  7  8  9 10 11       3  4  5  6  7  8  9       7  8  9 10 11 12 13
8712 13 14 15 16 17 18      10 11 12 13 14 15 16      14 15 16 17 18 19 20
8819 20 21 22 23 24 25      17 18 19 20 21 22 23      21 22 23 24 25 26 27
8926 27 28 29 30            24 25 26 27 28 29 30      28 29 30
90                          31
91
92        July                     August                  September
93Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
94          1  2  3  4                         1             1  2  3  4  5
95 5  6  7  8  9 10 11       2  3  4  5  6  7  8       6  7  8  9 10 11 12
9612 13 14 15 16 17 18       9 10 11 12 13 14 15      13 14 15 16 17 18 19
9719 20 21 22 23 24 25      16 17 18 19 20 21 22      20 21 22 23 24 25 26
9826 27 28 29 30 31         23 24 25 26 27 28 29      27 28 29 30
99                          30 31
100
101      October                   November                  December
102Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
103             1  2  3       1  2  3  4  5  6  7             1  2  3  4  5
104 4  5  6  7  8  9 10       8  9 10 11 12 13 14       6  7  8  9 10 11 12
10511 12 13 14 15 16 17      15 16 17 18 19 20 21      13 14 15 16 17 18 19
10618 19 20 21 22 23 24      22 23 24 25 26 27 28      20 21 22 23 24 25 26
10725 26 27 28 29 30 31      29 30                     27 28 29 30 31
108"""
109
110
111default_format = dict(year="year", month="month", encoding="ascii")
112
113result_2004_html = """\
114<?xml version="1.0" encoding="{encoding}"?>
115<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
116<html>
117<head>
118<meta http-equiv="Content-Type" content="text/html; charset={encoding}" />
119<link rel="stylesheet" type="text/css" href="calendar.css" />
120<title>Calendar for 2004</title>
121</head>
122<body>
123<table border="0" cellpadding="0" cellspacing="0" class="{year}">
124<tr><th colspan="3" class="{year}">2004</th></tr><tr><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
125<tr><th colspan="7" class="{month}">January</th></tr>
126<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>
127<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>
128<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>
129<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>
130<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>
131<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>
132</table>
133</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
134<tr><th colspan="7" class="{month}">February</th></tr>
135<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>
136<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>
137<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>
138<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>
139<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>
140<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>
141</table>
142</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
143<tr><th colspan="7" class="{month}">March</th></tr>
144<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>
145<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>
146<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>
147<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>
148<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>
149<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>
150</table>
151</td></tr><tr><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
152<tr><th colspan="7" class="{month}">April</th></tr>
153<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>
154<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>
155<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>
156<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>
157<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>
158<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>
159</table>
160</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
161<tr><th colspan="7" class="{month}">May</th></tr>
162<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>
163<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>
164<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>
165<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>
166<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>
167<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>
168<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>
169</table>
170</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
171<tr><th colspan="7" class="{month}">June</th></tr>
172<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>
173<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>
174<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>
175<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>
176<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>
177<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>
178</table>
179</td></tr><tr><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
180<tr><th colspan="7" class="{month}">July</th></tr>
181<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>
182<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>
183<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>
184<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>
185<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>
186<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>
187</table>
188</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
189<tr><th colspan="7" class="{month}">August</th></tr>
190<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>
191<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>
192<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>
193<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>
194<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>
195<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>
196<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>
197</table>
198</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
199<tr><th colspan="7" class="{month}">September</th></tr>
200<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>
201<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>
202<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>
203<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>
204<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>
205<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>
206</table>
207</td></tr><tr><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
208<tr><th colspan="7" class="{month}">October</th></tr>
209<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>
210<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>
211<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>
212<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>
213<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>
214<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>
215</table>
216</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
217<tr><th colspan="7" class="{month}">November</th></tr>
218<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>
219<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>
220<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>
221<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>
222<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>
223<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>
224</table>
225</td><td><table border="0" cellpadding="0" cellspacing="0" class="{month}">
226<tr><th colspan="7" class="{month}">December</th></tr>
227<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>
228<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>
229<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>
230<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>
231<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>
232<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>
233</table>
234</td></tr></table></body>
235</html>
236"""
237
238result_2004_days = [
239    [[[0, 0, 0, 1, 2, 3, 4],
240      [5, 6, 7, 8, 9, 10, 11],
241      [12, 13, 14, 15, 16, 17, 18],
242      [19, 20, 21, 22, 23, 24, 25],
243      [26, 27, 28, 29, 30, 31, 0]],
244     [[0, 0, 0, 0, 0, 0, 1],
245      [2, 3, 4, 5, 6, 7, 8],
246      [9, 10, 11, 12, 13, 14, 15],
247      [16, 17, 18, 19, 20, 21, 22],
248      [23, 24, 25, 26, 27, 28, 29]],
249     [[1, 2, 3, 4, 5, 6, 7],
250      [8, 9, 10, 11, 12, 13, 14],
251      [15, 16, 17, 18, 19, 20, 21],
252      [22, 23, 24, 25, 26, 27, 28],
253      [29, 30, 31, 0, 0, 0, 0]]],
254    [[[0, 0, 0, 1, 2, 3, 4],
255      [5, 6, 7, 8, 9, 10, 11],
256      [12, 13, 14, 15, 16, 17, 18],
257      [19, 20, 21, 22, 23, 24, 25],
258      [26, 27, 28, 29, 30, 0, 0]],
259     [[0, 0, 0, 0, 0, 1, 2],
260      [3, 4, 5, 6, 7, 8, 9],
261      [10, 11, 12, 13, 14, 15, 16],
262      [17, 18, 19, 20, 21, 22, 23],
263      [24, 25, 26, 27, 28, 29, 30],
264      [31, 0, 0, 0, 0, 0, 0]],
265     [[0, 1, 2, 3, 4, 5, 6],
266      [7, 8, 9, 10, 11, 12, 13],
267      [14, 15, 16, 17, 18, 19, 20],
268      [21, 22, 23, 24, 25, 26, 27],
269      [28, 29, 30, 0, 0, 0, 0]]],
270    [[[0, 0, 0, 1, 2, 3, 4],
271      [5, 6, 7, 8, 9, 10, 11],
272      [12, 13, 14, 15, 16, 17, 18],
273      [19, 20, 21, 22, 23, 24, 25],
274      [26, 27, 28, 29, 30, 31, 0]],
275     [[0, 0, 0, 0, 0, 0, 1],
276      [2, 3, 4, 5, 6, 7, 8],
277      [9, 10, 11, 12, 13, 14, 15],
278      [16, 17, 18, 19, 20, 21, 22],
279      [23, 24, 25, 26, 27, 28, 29],
280      [30, 31, 0, 0, 0, 0, 0]],
281     [[0, 0, 1, 2, 3, 4, 5],
282      [6, 7, 8, 9, 10, 11, 12],
283      [13, 14, 15, 16, 17, 18, 19],
284      [20, 21, 22, 23, 24, 25, 26],
285      [27, 28, 29, 30, 0, 0, 0]]],
286    [[[0, 0, 0, 0, 1, 2, 3],
287      [4, 5, 6, 7, 8, 9, 10],
288      [11, 12, 13, 14, 15, 16, 17],
289      [18, 19, 20, 21, 22, 23, 24],
290      [25, 26, 27, 28, 29, 30, 31]],
291     [[1, 2, 3, 4, 5, 6, 7],
292      [8, 9, 10, 11, 12, 13, 14],
293      [15, 16, 17, 18, 19, 20, 21],
294      [22, 23, 24, 25, 26, 27, 28],
295      [29, 30, 0, 0, 0, 0, 0]],
296     [[0, 0, 1, 2, 3, 4, 5],
297      [6, 7, 8, 9, 10, 11, 12],
298      [13, 14, 15, 16, 17, 18, 19],
299      [20, 21, 22, 23, 24, 25, 26],
300      [27, 28, 29, 30, 31, 0, 0]]]
301]
302
303result_2004_dates = \
304    [[['12/29/03 12/30/03 12/31/03 01/01/04 01/02/04 01/03/04 01/04/04',
305       '01/05/04 01/06/04 01/07/04 01/08/04 01/09/04 01/10/04 01/11/04',
306       '01/12/04 01/13/04 01/14/04 01/15/04 01/16/04 01/17/04 01/18/04',
307       '01/19/04 01/20/04 01/21/04 01/22/04 01/23/04 01/24/04 01/25/04',
308       '01/26/04 01/27/04 01/28/04 01/29/04 01/30/04 01/31/04 02/01/04'],
309      ['01/26/04 01/27/04 01/28/04 01/29/04 01/30/04 01/31/04 02/01/04',
310       '02/02/04 02/03/04 02/04/04 02/05/04 02/06/04 02/07/04 02/08/04',
311       '02/09/04 02/10/04 02/11/04 02/12/04 02/13/04 02/14/04 02/15/04',
312       '02/16/04 02/17/04 02/18/04 02/19/04 02/20/04 02/21/04 02/22/04',
313       '02/23/04 02/24/04 02/25/04 02/26/04 02/27/04 02/28/04 02/29/04'],
314      ['03/01/04 03/02/04 03/03/04 03/04/04 03/05/04 03/06/04 03/07/04',
315       '03/08/04 03/09/04 03/10/04 03/11/04 03/12/04 03/13/04 03/14/04',
316       '03/15/04 03/16/04 03/17/04 03/18/04 03/19/04 03/20/04 03/21/04',
317       '03/22/04 03/23/04 03/24/04 03/25/04 03/26/04 03/27/04 03/28/04',
318       '03/29/04 03/30/04 03/31/04 04/01/04 04/02/04 04/03/04 04/04/04']],
319     [['03/29/04 03/30/04 03/31/04 04/01/04 04/02/04 04/03/04 04/04/04',
320       '04/05/04 04/06/04 04/07/04 04/08/04 04/09/04 04/10/04 04/11/04',
321       '04/12/04 04/13/04 04/14/04 04/15/04 04/16/04 04/17/04 04/18/04',
322       '04/19/04 04/20/04 04/21/04 04/22/04 04/23/04 04/24/04 04/25/04',
323       '04/26/04 04/27/04 04/28/04 04/29/04 04/30/04 05/01/04 05/02/04'],
324      ['04/26/04 04/27/04 04/28/04 04/29/04 04/30/04 05/01/04 05/02/04',
325       '05/03/04 05/04/04 05/05/04 05/06/04 05/07/04 05/08/04 05/09/04',
326       '05/10/04 05/11/04 05/12/04 05/13/04 05/14/04 05/15/04 05/16/04',
327       '05/17/04 05/18/04 05/19/04 05/20/04 05/21/04 05/22/04 05/23/04',
328       '05/24/04 05/25/04 05/26/04 05/27/04 05/28/04 05/29/04 05/30/04',
329       '05/31/04 06/01/04 06/02/04 06/03/04 06/04/04 06/05/04 06/06/04'],
330      ['05/31/04 06/01/04 06/02/04 06/03/04 06/04/04 06/05/04 06/06/04',
331       '06/07/04 06/08/04 06/09/04 06/10/04 06/11/04 06/12/04 06/13/04',
332       '06/14/04 06/15/04 06/16/04 06/17/04 06/18/04 06/19/04 06/20/04',
333       '06/21/04 06/22/04 06/23/04 06/24/04 06/25/04 06/26/04 06/27/04',
334       '06/28/04 06/29/04 06/30/04 07/01/04 07/02/04 07/03/04 07/04/04']],
335     [['06/28/04 06/29/04 06/30/04 07/01/04 07/02/04 07/03/04 07/04/04',
336       '07/05/04 07/06/04 07/07/04 07/08/04 07/09/04 07/10/04 07/11/04',
337       '07/12/04 07/13/04 07/14/04 07/15/04 07/16/04 07/17/04 07/18/04',
338       '07/19/04 07/20/04 07/21/04 07/22/04 07/23/04 07/24/04 07/25/04',
339       '07/26/04 07/27/04 07/28/04 07/29/04 07/30/04 07/31/04 08/01/04'],
340      ['07/26/04 07/27/04 07/28/04 07/29/04 07/30/04 07/31/04 08/01/04',
341       '08/02/04 08/03/04 08/04/04 08/05/04 08/06/04 08/07/04 08/08/04',
342       '08/09/04 08/10/04 08/11/04 08/12/04 08/13/04 08/14/04 08/15/04',
343       '08/16/04 08/17/04 08/18/04 08/19/04 08/20/04 08/21/04 08/22/04',
344       '08/23/04 08/24/04 08/25/04 08/26/04 08/27/04 08/28/04 08/29/04',
345       '08/30/04 08/31/04 09/01/04 09/02/04 09/03/04 09/04/04 09/05/04'],
346      ['08/30/04 08/31/04 09/01/04 09/02/04 09/03/04 09/04/04 09/05/04',
347       '09/06/04 09/07/04 09/08/04 09/09/04 09/10/04 09/11/04 09/12/04',
348       '09/13/04 09/14/04 09/15/04 09/16/04 09/17/04 09/18/04 09/19/04',
349       '09/20/04 09/21/04 09/22/04 09/23/04 09/24/04 09/25/04 09/26/04',
350       '09/27/04 09/28/04 09/29/04 09/30/04 10/01/04 10/02/04 10/03/04']],
351     [['09/27/04 09/28/04 09/29/04 09/30/04 10/01/04 10/02/04 10/03/04',
352       '10/04/04 10/05/04 10/06/04 10/07/04 10/08/04 10/09/04 10/10/04',
353       '10/11/04 10/12/04 10/13/04 10/14/04 10/15/04 10/16/04 10/17/04',
354       '10/18/04 10/19/04 10/20/04 10/21/04 10/22/04 10/23/04 10/24/04',
355       '10/25/04 10/26/04 10/27/04 10/28/04 10/29/04 10/30/04 10/31/04'],
356      ['11/01/04 11/02/04 11/03/04 11/04/04 11/05/04 11/06/04 11/07/04',
357       '11/08/04 11/09/04 11/10/04 11/11/04 11/12/04 11/13/04 11/14/04',
358       '11/15/04 11/16/04 11/17/04 11/18/04 11/19/04 11/20/04 11/21/04',
359       '11/22/04 11/23/04 11/24/04 11/25/04 11/26/04 11/27/04 11/28/04',
360       '11/29/04 11/30/04 12/01/04 12/02/04 12/03/04 12/04/04 12/05/04'],
361      ['11/29/04 11/30/04 12/01/04 12/02/04 12/03/04 12/04/04 12/05/04',
362       '12/06/04 12/07/04 12/08/04 12/09/04 12/10/04 12/11/04 12/12/04',
363       '12/13/04 12/14/04 12/15/04 12/16/04 12/17/04 12/18/04 12/19/04',
364       '12/20/04 12/21/04 12/22/04 12/23/04 12/24/04 12/25/04 12/26/04',
365       '12/27/04 12/28/04 12/29/04 12/30/04 12/31/04 01/01/05 01/02/05']]]
366
367
368class OutputTestCase(unittest.TestCase):
369    def normalize_calendar(self, s):
370        # Filters out locale dependent strings
371        def neitherspacenordigit(c):
372            return not c.isspace() and not c.isdigit()
373
374        lines = []
375        for line in s.splitlines(keepends=False):
376            # Drop texts, as they are locale dependent
377            if line and not filter(neitherspacenordigit, line):
378                lines.append(line)
379        return lines
380
381    def check_htmlcalendar_encoding(self, req, res):
382        cal = calendar.HTMLCalendar()
383        format_ = default_format.copy()
384        format_["encoding"] = req or 'utf-8'
385        output = cal.formatyearpage(2004, encoding=req)
386        self.assertEqual(
387            output,
388            result_2004_html.format(**format_).encode(res)
389        )
390
391    def test_output(self):
392        self.assertEqual(
393            self.normalize_calendar(calendar.calendar(2004)),
394            self.normalize_calendar(result_2004_text)
395        )
396        self.assertEqual(
397            self.normalize_calendar(calendar.calendar(0)),
398            self.normalize_calendar(result_0_text)
399        )
400
401    def test_output_textcalendar(self):
402        self.assertEqual(
403            calendar.TextCalendar().formatyear(2004),
404            result_2004_text
405        )
406        self.assertEqual(
407            calendar.TextCalendar().formatyear(0),
408            result_0_text
409        )
410
411    def test_output_htmlcalendar_encoding_ascii(self):
412        self.check_htmlcalendar_encoding('ascii', 'ascii')
413
414    def test_output_htmlcalendar_encoding_utf8(self):
415        self.check_htmlcalendar_encoding('utf-8', 'utf-8')
416
417    def test_output_htmlcalendar_encoding_default(self):
418        self.check_htmlcalendar_encoding(None, sys.getdefaultencoding())
419
420    def test_yeardatescalendar(self):
421        def shrink(cal):
422            return [[[' '.join('{:02d}/{:02d}/{}'.format(
423                                d.month, d.day, str(d.year)[-2:]) for d in z)
424                            for z in y] for y in x] for x in cal]
425        self.assertEqual(
426            shrink(calendar.Calendar().yeardatescalendar(2004)),
427            result_2004_dates
428        )
429
430    def test_yeardayscalendar(self):
431        self.assertEqual(
432            calendar.Calendar().yeardayscalendar(2004),
433            result_2004_days
434        )
435
436    def test_formatweekheader_short(self):
437        self.assertEqual(
438            calendar.TextCalendar().formatweekheader(2),
439            'Mo Tu We Th Fr Sa Su'
440        )
441
442    def test_formatweekheader_long(self):
443        self.assertEqual(
444            calendar.TextCalendar().formatweekheader(9),
445            '  Monday   Tuesday  Wednesday  Thursday '
446            '  Friday   Saturday   Sunday '
447        )
448
449    def test_formatmonth(self):
450        self.assertEqual(
451            calendar.TextCalendar().formatmonth(2004, 1),
452            result_2004_01_text
453        )
454        self.assertEqual(
455            calendar.TextCalendar().formatmonth(0, 2),
456            result_0_02_text
457        )
458
459    def test_formatmonthname_with_year(self):
460        self.assertEqual(
461            calendar.HTMLCalendar().formatmonthname(2004, 1, withyear=True),
462            '<tr><th colspan="7" class="month">January 2004</th></tr>'
463        )
464
465    def test_formatmonthname_without_year(self):
466        self.assertEqual(
467            calendar.HTMLCalendar().formatmonthname(2004, 1, withyear=False),
468            '<tr><th colspan="7" class="month">January</th></tr>'
469        )
470
471    def test_prweek(self):
472        with support.captured_stdout() as out:
473            week = [(1,0), (2,1), (3,2), (4,3), (5,4), (6,5), (7,6)]
474            calendar.TextCalendar().prweek(week, 1)
475            self.assertEqual(out.getvalue(), " 1  2  3  4  5  6  7")
476
477    def test_prmonth(self):
478        with support.captured_stdout() as out:
479            calendar.TextCalendar().prmonth(2004, 1)
480            self.assertEqual(out.getvalue(), result_2004_01_text)
481
482    def test_pryear(self):
483        with support.captured_stdout() as out:
484            calendar.TextCalendar().pryear(2004)
485            self.assertEqual(out.getvalue(), result_2004_text)
486
487    def test_format(self):
488        with support.captured_stdout() as out:
489            calendar.format(["1", "2", "3"], colwidth=3, spacing=1)
490            self.assertEqual(out.getvalue().strip(), "1   2   3")
491
492class CalendarTestCase(unittest.TestCase):
493    def test_isleap(self):
494        # Make sure that the return is right for a few years, and
495        # ensure that the return values are 1 or 0, not just true or
496        # false (see SF bug #485794).  Specific additional tests may
497        # be appropriate; this tests a single "cycle".
498        self.assertEqual(calendar.isleap(2000), 1)
499        self.assertEqual(calendar.isleap(2001), 0)
500        self.assertEqual(calendar.isleap(2002), 0)
501        self.assertEqual(calendar.isleap(2003), 0)
502
503    def test_setfirstweekday(self):
504        self.assertRaises(TypeError, calendar.setfirstweekday, 'flabber')
505        self.assertRaises(ValueError, calendar.setfirstweekday, -1)
506        self.assertRaises(ValueError, calendar.setfirstweekday, 200)
507        orig = calendar.firstweekday()
508        calendar.setfirstweekday(calendar.SUNDAY)
509        self.assertEqual(calendar.firstweekday(), calendar.SUNDAY)
510        calendar.setfirstweekday(calendar.MONDAY)
511        self.assertEqual(calendar.firstweekday(), calendar.MONDAY)
512        calendar.setfirstweekday(orig)
513
514    def test_illegal_weekday_reported(self):
515        with self.assertRaisesRegex(calendar.IllegalWeekdayError, '123'):
516            calendar.setfirstweekday(123)
517
518    def test_enumerate_weekdays(self):
519        self.assertRaises(IndexError, calendar.day_abbr.__getitem__, -10)
520        self.assertRaises(IndexError, calendar.day_name.__getitem__, 10)
521        self.assertEqual(len([d for d in calendar.day_abbr]), 7)
522
523    def test_days(self):
524        for attr in "day_name", "day_abbr":
525            value = getattr(calendar, attr)
526            self.assertEqual(len(value), 7)
527            self.assertEqual(len(value[:]), 7)
528            # ensure they're all unique
529            self.assertEqual(len(set(value)), 7)
530            # verify it "acts like a sequence" in two forms of iteration
531            self.assertEqual(value[::-1], list(reversed(value)))
532
533    def test_months(self):
534        for attr in "month_name", "month_abbr":
535            value = getattr(calendar, attr)
536            self.assertEqual(len(value), 13)
537            self.assertEqual(len(value[:]), 13)
538            self.assertEqual(value[0], "")
539            # ensure they're all unique
540            self.assertEqual(len(set(value)), 13)
541            # verify it "acts like a sequence" in two forms of iteration
542            self.assertEqual(value[::-1], list(reversed(value)))
543
544    def test_locale_calendars(self):
545        # ensure that Locale{Text,HTML}Calendar resets the locale properly
546        # (it is still not thread-safe though)
547        old_october = calendar.TextCalendar().formatmonthname(2010, 10, 10)
548        try:
549            cal = calendar.LocaleTextCalendar(locale='')
550            local_weekday = cal.formatweekday(1, 10)
551            local_month = cal.formatmonthname(2010, 10, 10)
552        except locale.Error:
553            # cannot set the system default locale -- skip rest of test
554            raise unittest.SkipTest('cannot set the system default locale')
555        self.assertIsInstance(local_weekday, str)
556        self.assertIsInstance(local_month, str)
557        self.assertEqual(len(local_weekday), 10)
558        self.assertGreaterEqual(len(local_month), 10)
559        cal = calendar.LocaleHTMLCalendar(locale='')
560        local_weekday = cal.formatweekday(1)
561        local_month = cal.formatmonthname(2010, 10)
562        self.assertIsInstance(local_weekday, str)
563        self.assertIsInstance(local_month, str)
564        new_october = calendar.TextCalendar().formatmonthname(2010, 10, 10)
565        self.assertEqual(old_october, new_october)
566
567    def test_itermonthdays3(self):
568        # ensure itermonthdays3 doesn't overflow after datetime.MAXYEAR
569        list(calendar.Calendar().itermonthdays3(datetime.MAXYEAR, 12))
570
571    def test_itermonthdays4(self):
572        cal = calendar.Calendar(firstweekday=3)
573        days = list(cal.itermonthdays4(2001, 2))
574        self.assertEqual(days[0], (2001, 2, 1, 3))
575        self.assertEqual(days[-1], (2001, 2, 28, 2))
576
577    def test_itermonthdays(self):
578        for firstweekday in range(7):
579            cal = calendar.Calendar(firstweekday)
580            # Test the extremes, see #28253 and #26650
581            for y, m in [(1, 1), (9999, 12)]:
582                days = list(cal.itermonthdays(y, m))
583                self.assertIn(len(days), (35, 42))
584        # Test a short month
585        cal = calendar.Calendar(firstweekday=3)
586        days = list(cal.itermonthdays(2001, 2))
587        self.assertEqual(days, list(range(1, 29)))
588
589    def test_itermonthdays2(self):
590        for firstweekday in range(7):
591            cal = calendar.Calendar(firstweekday)
592            # Test the extremes, see #28253 and #26650
593            for y, m in [(1, 1), (9999, 12)]:
594                days = list(cal.itermonthdays2(y, m))
595                self.assertEqual(days[0][1], firstweekday)
596                self.assertEqual(days[-1][1], (firstweekday - 1) % 7)
597
598
599class MonthCalendarTestCase(unittest.TestCase):
600    def setUp(self):
601        self.oldfirstweekday = calendar.firstweekday()
602        calendar.setfirstweekday(self.firstweekday)
603
604    def tearDown(self):
605        calendar.setfirstweekday(self.oldfirstweekday)
606
607    def check_weeks(self, year, month, weeks):
608        cal = calendar.monthcalendar(year, month)
609        self.assertEqual(len(cal), len(weeks))
610        for i in range(len(weeks)):
611            self.assertEqual(weeks[i], sum(day != 0 for day in cal[i]))
612
613
614class MondayTestCase(MonthCalendarTestCase):
615    firstweekday = calendar.MONDAY
616
617    def test_february(self):
618        # A 28-day february starting on monday (7+7+7+7 days)
619        self.check_weeks(1999, 2, (7, 7, 7, 7))
620
621        # A 28-day february starting on tuesday (6+7+7+7+1 days)
622        self.check_weeks(2005, 2, (6, 7, 7, 7, 1))
623
624        # A 28-day february starting on sunday (1+7+7+7+6 days)
625        self.check_weeks(1987, 2, (1, 7, 7, 7, 6))
626
627        # A 29-day february starting on monday (7+7+7+7+1 days)
628        self.check_weeks(1988, 2, (7, 7, 7, 7, 1))
629
630        # A 29-day february starting on tuesday (6+7+7+7+2 days)
631        self.check_weeks(1972, 2, (6, 7, 7, 7, 2))
632
633        # A 29-day february starting on sunday (1+7+7+7+7 days)
634        self.check_weeks(2004, 2, (1, 7, 7, 7, 7))
635
636    def test_april(self):
637        # A 30-day april starting on monday (7+7+7+7+2 days)
638        self.check_weeks(1935, 4, (7, 7, 7, 7, 2))
639
640        # A 30-day april starting on tuesday (6+7+7+7+3 days)
641        self.check_weeks(1975, 4, (6, 7, 7, 7, 3))
642
643        # A 30-day april starting on sunday (1+7+7+7+7+1 days)
644        self.check_weeks(1945, 4, (1, 7, 7, 7, 7, 1))
645
646        # A 30-day april starting on saturday (2+7+7+7+7 days)
647        self.check_weeks(1995, 4, (2, 7, 7, 7, 7))
648
649        # A 30-day april starting on friday (3+7+7+7+6 days)
650        self.check_weeks(1994, 4, (3, 7, 7, 7, 6))
651
652    def test_december(self):
653        # A 31-day december starting on monday (7+7+7+7+3 days)
654        self.check_weeks(1980, 12, (7, 7, 7, 7, 3))
655
656        # A 31-day december starting on tuesday (6+7+7+7+4 days)
657        self.check_weeks(1987, 12, (6, 7, 7, 7, 4))
658
659        # A 31-day december starting on sunday (1+7+7+7+7+2 days)
660        self.check_weeks(1968, 12, (1, 7, 7, 7, 7, 2))
661
662        # A 31-day december starting on thursday (4+7+7+7+6 days)
663        self.check_weeks(1988, 12, (4, 7, 7, 7, 6))
664
665        # A 31-day december starting on friday (3+7+7+7+7 days)
666        self.check_weeks(2017, 12, (3, 7, 7, 7, 7))
667
668        # A 31-day december starting on saturday (2+7+7+7+7+1 days)
669        self.check_weeks(2068, 12, (2, 7, 7, 7, 7, 1))
670
671
672class SundayTestCase(MonthCalendarTestCase):
673    firstweekday = calendar.SUNDAY
674
675    def test_february(self):
676        # A 28-day february starting on sunday (7+7+7+7 days)
677        self.check_weeks(2009, 2, (7, 7, 7, 7))
678
679        # A 28-day february starting on monday (6+7+7+7+1 days)
680        self.check_weeks(1999, 2, (6, 7, 7, 7, 1))
681
682        # A 28-day february starting on saturday (1+7+7+7+6 days)
683        self.check_weeks(1997, 2, (1, 7, 7, 7, 6))
684
685        # A 29-day february starting on sunday (7+7+7+7+1 days)
686        self.check_weeks(2004, 2, (7, 7, 7, 7, 1))
687
688        # A 29-day february starting on monday (6+7+7+7+2 days)
689        self.check_weeks(1960, 2, (6, 7, 7, 7, 2))
690
691        # A 29-day february starting on saturday (1+7+7+7+7 days)
692        self.check_weeks(1964, 2, (1, 7, 7, 7, 7))
693
694    def test_april(self):
695        # A 30-day april starting on sunday (7+7+7+7+2 days)
696        self.check_weeks(1923, 4, (7, 7, 7, 7, 2))
697
698        # A 30-day april starting on monday (6+7+7+7+3 days)
699        self.check_weeks(1918, 4, (6, 7, 7, 7, 3))
700
701        # A 30-day april starting on saturday (1+7+7+7+7+1 days)
702        self.check_weeks(1950, 4, (1, 7, 7, 7, 7, 1))
703
704        # A 30-day april starting on friday (2+7+7+7+7 days)
705        self.check_weeks(1960, 4, (2, 7, 7, 7, 7))
706
707        # A 30-day april starting on thursday (3+7+7+7+6 days)
708        self.check_weeks(1909, 4, (3, 7, 7, 7, 6))
709
710    def test_december(self):
711        # A 31-day december starting on sunday (7+7+7+7+3 days)
712        self.check_weeks(2080, 12, (7, 7, 7, 7, 3))
713
714        # A 31-day december starting on monday (6+7+7+7+4 days)
715        self.check_weeks(1941, 12, (6, 7, 7, 7, 4))
716
717        # A 31-day december starting on saturday (1+7+7+7+7+2 days)
718        self.check_weeks(1923, 12, (1, 7, 7, 7, 7, 2))
719
720        # A 31-day december starting on wednesday (4+7+7+7+6 days)
721        self.check_weeks(1948, 12, (4, 7, 7, 7, 6))
722
723        # A 31-day december starting on thursday (3+7+7+7+7 days)
724        self.check_weeks(1927, 12, (3, 7, 7, 7, 7))
725
726        # A 31-day december starting on friday (2+7+7+7+7+1 days)
727        self.check_weeks(1995, 12, (2, 7, 7, 7, 7, 1))
728
729class TimegmTestCase(unittest.TestCase):
730    TIMESTAMPS = [0, 10, 100, 1000, 10000, 100000, 1000000,
731                  1234567890, 1262304000, 1275785153,]
732    def test_timegm(self):
733        for secs in self.TIMESTAMPS:
734            tuple = time.gmtime(secs)
735            self.assertEqual(secs, calendar.timegm(tuple))
736
737class MonthRangeTestCase(unittest.TestCase):
738    def test_january(self):
739        # Tests valid lower boundary case.
740        self.assertEqual(calendar.monthrange(2004,1), (3,31))
741
742    def test_february_leap(self):
743        # Tests February during leap year.
744        self.assertEqual(calendar.monthrange(2004,2), (6,29))
745
746    def test_february_nonleap(self):
747        # Tests February in non-leap year.
748        self.assertEqual(calendar.monthrange(2010,2), (0,28))
749
750    def test_december(self):
751        # Tests valid upper boundary case.
752        self.assertEqual(calendar.monthrange(2004,12), (2,31))
753
754    def test_zeroth_month(self):
755        # Tests low invalid boundary case.
756        with self.assertRaises(calendar.IllegalMonthError):
757            calendar.monthrange(2004, 0)
758
759    def test_thirteenth_month(self):
760        # Tests high invalid boundary case.
761        with self.assertRaises(calendar.IllegalMonthError):
762            calendar.monthrange(2004, 13)
763
764    def test_illegal_month_reported(self):
765        with self.assertRaisesRegex(calendar.IllegalMonthError, '65'):
766            calendar.monthrange(2004, 65)
767
768class LeapdaysTestCase(unittest.TestCase):
769    def test_no_range(self):
770        # test when no range i.e. two identical years as args
771        self.assertEqual(calendar.leapdays(2010,2010), 0)
772
773    def test_no_leapdays(self):
774        # test when no leap years in range
775        self.assertEqual(calendar.leapdays(2010,2011), 0)
776
777    def test_no_leapdays_upper_boundary(self):
778        # test no leap years in range, when upper boundary is a leap year
779        self.assertEqual(calendar.leapdays(2010,2012), 0)
780
781    def test_one_leapday_lower_boundary(self):
782        # test when one leap year in range, lower boundary is leap year
783        self.assertEqual(calendar.leapdays(2012,2013), 1)
784
785    def test_several_leapyears_in_range(self):
786        self.assertEqual(calendar.leapdays(1997,2020), 5)
787
788
789def conv(s):
790    return s.replace('\n', os.linesep).encode()
791
792class CommandLineTestCase(unittest.TestCase):
793    def run_ok(self, *args):
794        return assert_python_ok('-m', 'calendar', *args)[1]
795
796    def assertFailure(self, *args):
797        rc, stdout, stderr = assert_python_failure('-m', 'calendar', *args)
798        self.assertIn(b'usage:', stderr)
799        self.assertEqual(rc, 2)
800
801    def test_help(self):
802        stdout = self.run_ok('-h')
803        self.assertIn(b'usage:', stdout)
804        self.assertIn(b'calendar.py', stdout)
805        self.assertIn(b'--help', stdout)
806
807    def test_illegal_arguments(self):
808        self.assertFailure('-z')
809        self.assertFailure('spam')
810        self.assertFailure('2004', 'spam')
811        self.assertFailure('-t', 'html', '2004', '1')
812
813    def test_output_current_year(self):
814        stdout = self.run_ok()
815        year = datetime.datetime.now().year
816        self.assertIn((' %s' % year).encode(), stdout)
817        self.assertIn(b'January', stdout)
818        self.assertIn(b'Mo Tu We Th Fr Sa Su', stdout)
819
820    def test_output_year(self):
821        stdout = self.run_ok('2004')
822        self.assertEqual(stdout, conv(result_2004_text))
823
824    def test_output_month(self):
825        stdout = self.run_ok('2004', '1')
826        self.assertEqual(stdout, conv(result_2004_01_text))
827
828    def test_option_encoding(self):
829        self.assertFailure('-e')
830        self.assertFailure('--encoding')
831        stdout = self.run_ok('--encoding', 'utf-16-le', '2004')
832        self.assertEqual(stdout, result_2004_text.encode('utf-16-le'))
833
834    def test_option_locale(self):
835        self.assertFailure('-L')
836        self.assertFailure('--locale')
837        self.assertFailure('-L', 'en')
838        lang, enc = locale.getdefaultlocale()
839        lang = lang or 'C'
840        enc = enc or 'UTF-8'
841        try:
842            oldlocale = locale.getlocale(locale.LC_TIME)
843            try:
844                locale.setlocale(locale.LC_TIME, (lang, enc))
845            finally:
846                locale.setlocale(locale.LC_TIME, oldlocale)
847        except (locale.Error, ValueError):
848            self.skipTest('cannot set the system default locale')
849        stdout = self.run_ok('--locale', lang, '--encoding', enc, '2004')
850        self.assertIn('2004'.encode(enc), stdout)
851
852    def test_option_width(self):
853        self.assertFailure('-w')
854        self.assertFailure('--width')
855        self.assertFailure('-w', 'spam')
856        stdout = self.run_ok('--width', '3', '2004')
857        self.assertIn(b'Mon Tue Wed Thu Fri Sat Sun', stdout)
858
859    def test_option_lines(self):
860        self.assertFailure('-l')
861        self.assertFailure('--lines')
862        self.assertFailure('-l', 'spam')
863        stdout = self.run_ok('--lines', '2', '2004')
864        self.assertIn(conv('December\n\nMo Tu We'), stdout)
865
866    def test_option_spacing(self):
867        self.assertFailure('-s')
868        self.assertFailure('--spacing')
869        self.assertFailure('-s', 'spam')
870        stdout = self.run_ok('--spacing', '8', '2004')
871        self.assertIn(b'Su        Mo', stdout)
872
873    def test_option_months(self):
874        self.assertFailure('-m')
875        self.assertFailure('--month')
876        self.assertFailure('-m', 'spam')
877        stdout = self.run_ok('--months', '1', '2004')
878        self.assertIn(conv('\nMo Tu We Th Fr Sa Su\n'), stdout)
879
880    def test_option_type(self):
881        self.assertFailure('-t')
882        self.assertFailure('--type')
883        self.assertFailure('-t', 'spam')
884        stdout = self.run_ok('--type', 'text', '2004')
885        self.assertEqual(stdout, conv(result_2004_text))
886        stdout = self.run_ok('--type', 'html', '2004')
887        self.assertEqual(stdout[:6], b'<?xml ')
888        self.assertIn(b'<title>Calendar for 2004</title>', stdout)
889
890    def test_html_output_current_year(self):
891        stdout = self.run_ok('--type', 'html')
892        year = datetime.datetime.now().year
893        self.assertIn(('<title>Calendar for %s</title>' % year).encode(),
894                      stdout)
895        self.assertIn(b'<tr><th colspan="7" class="month">January</th></tr>',
896                      stdout)
897
898    def test_html_output_year_encoding(self):
899        stdout = self.run_ok('-t', 'html', '--encoding', 'ascii', '2004')
900        self.assertEqual(stdout,
901                         result_2004_html.format(**default_format).encode('ascii'))
902
903    def test_html_output_year_css(self):
904        self.assertFailure('-t', 'html', '-c')
905        self.assertFailure('-t', 'html', '--css')
906        stdout = self.run_ok('-t', 'html', '--css', 'custom.css', '2004')
907        self.assertIn(b'<link rel="stylesheet" type="text/css" '
908                      b'href="custom.css" />', stdout)
909
910
911class MiscTestCase(unittest.TestCase):
912    def test__all__(self):
913        blacklist = {'mdays', 'January', 'February', 'EPOCH',
914                     'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY',
915                     'SATURDAY', 'SUNDAY', 'different_locale', 'c',
916                     'prweek', 'week', 'format', 'formatstring', 'main',
917                     'monthlen', 'prevmonth', 'nextmonth'}
918        support.check__all__(self, calendar, blacklist=blacklist)
919
920
921class TestSubClassingCase(unittest.TestCase):
922
923    def setUp(self):
924
925        class CustomHTMLCal(calendar.HTMLCalendar):
926            cssclasses = [style + " text-nowrap" for style in
927                          calendar.HTMLCalendar.cssclasses]
928            cssclasses_weekday_head = ["red", "blue", "green", "lilac",
929                                       "yellow", "orange", "pink"]
930            cssclass_month_head = "text-center month-head"
931            cssclass_month = "text-center month"
932            cssclass_year = "text-italic "
933            cssclass_year_head = "lead "
934
935        self.cal = CustomHTMLCal()
936
937    def test_formatmonthname(self):
938        self.assertIn('class="text-center month-head"',
939                      self.cal.formatmonthname(2017, 5))
940
941    def test_formatmonth(self):
942        self.assertIn('class="text-center month"',
943                      self.cal.formatmonth(2017, 5))
944
945    def test_formatweek(self):
946        weeks = self.cal.monthdays2calendar(2017, 5)
947        self.assertIn('class="wed text-nowrap"', self.cal.formatweek(weeks[0]))
948
949    def test_formatweek_head(self):
950        header = self.cal.formatweekheader()
951        for color in self.cal.cssclasses_weekday_head:
952            self.assertIn('<th class="%s">' % color, header)
953
954    def test_format_year(self):
955        self.assertIn(
956            ('<table border="0" cellpadding="0" cellspacing="0" class="%s">' %
957             self.cal.cssclass_year), self.cal.formatyear(2017))
958
959    def test_format_year_head(self):
960        self.assertIn('<tr><th colspan="%d" class="%s">%s</th></tr>' % (
961            3, self.cal.cssclass_year_head, 2017), self.cal.formatyear(2017))
962
963if __name__ == "__main__":
964    unittest.main()
965