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_locale_calendar_formatweekday(self):
568        try:
569            # formatweekday uses different day names based on the available width.
570            cal = calendar.LocaleTextCalendar(locale='en_US')
571            # For really short widths, the abbreviated name is truncated.
572            self.assertEqual(cal.formatweekday(0, 1), "M")
573            self.assertEqual(cal.formatweekday(0, 2), "Mo")
574            # For short widths, a centered, abbreviated name is used.
575            self.assertEqual(cal.formatweekday(0, 3), "Mon")
576            self.assertEqual(cal.formatweekday(0, 5), " Mon ")
577            self.assertEqual(cal.formatweekday(0, 8), "  Mon   ")
578            # For long widths, the full day name is used.
579            self.assertEqual(cal.formatweekday(0, 9), "  Monday ")
580            self.assertEqual(cal.formatweekday(0, 10), "  Monday  ")
581        except locale.Error:
582            raise unittest.SkipTest('cannot set the en_US locale')
583
584    def test_locale_html_calendar_custom_css_class_month_name(self):
585        try:
586            cal = calendar.LocaleHTMLCalendar(locale='')
587            local_month = cal.formatmonthname(2010, 10, 10)
588        except locale.Error:
589            # cannot set the system default locale -- skip rest of test
590            raise unittest.SkipTest('cannot set the system default locale')
591        self.assertIn('class="month"', local_month)
592        cal.cssclass_month_head = "text-center month"
593        local_month = cal.formatmonthname(2010, 10, 10)
594        self.assertIn('class="text-center month"', local_month)
595
596    def test_locale_html_calendar_custom_css_class_weekday(self):
597        try:
598            cal = calendar.LocaleHTMLCalendar(locale='')
599            local_weekday = cal.formatweekday(6)
600        except locale.Error:
601            # cannot set the system default locale -- skip rest of test
602            raise unittest.SkipTest('cannot set the system default locale')
603        self.assertIn('class="sun"', local_weekday)
604        cal.cssclasses_weekday_head = ["mon2", "tue2", "wed2", "thu2", "fri2", "sat2", "sun2"]
605        local_weekday = cal.formatweekday(6)
606        self.assertIn('class="sun2"', local_weekday)
607
608    def test_itermonthdays3(self):
609        # ensure itermonthdays3 doesn't overflow after datetime.MAXYEAR
610        list(calendar.Calendar().itermonthdays3(datetime.MAXYEAR, 12))
611
612    def test_itermonthdays4(self):
613        cal = calendar.Calendar(firstweekday=3)
614        days = list(cal.itermonthdays4(2001, 2))
615        self.assertEqual(days[0], (2001, 2, 1, 3))
616        self.assertEqual(days[-1], (2001, 2, 28, 2))
617
618    def test_itermonthdays(self):
619        for firstweekday in range(7):
620            cal = calendar.Calendar(firstweekday)
621            # Test the extremes, see #28253 and #26650
622            for y, m in [(1, 1), (9999, 12)]:
623                days = list(cal.itermonthdays(y, m))
624                self.assertIn(len(days), (35, 42))
625        # Test a short month
626        cal = calendar.Calendar(firstweekday=3)
627        days = list(cal.itermonthdays(2001, 2))
628        self.assertEqual(days, list(range(1, 29)))
629
630    def test_itermonthdays2(self):
631        for firstweekday in range(7):
632            cal = calendar.Calendar(firstweekday)
633            # Test the extremes, see #28253 and #26650
634            for y, m in [(1, 1), (9999, 12)]:
635                days = list(cal.itermonthdays2(y, m))
636                self.assertEqual(days[0][1], firstweekday)
637                self.assertEqual(days[-1][1], (firstweekday - 1) % 7)
638
639    def test_iterweekdays(self):
640        week0 = list(range(7))
641        for firstweekday in range(7):
642            cal = calendar.Calendar(firstweekday)
643            week = list(cal.iterweekdays())
644            expected = week0[firstweekday:] + week0[:firstweekday]
645            self.assertEqual(week, expected)
646
647
648class MonthCalendarTestCase(unittest.TestCase):
649    def setUp(self):
650        self.oldfirstweekday = calendar.firstweekday()
651        calendar.setfirstweekday(self.firstweekday)
652
653    def tearDown(self):
654        calendar.setfirstweekday(self.oldfirstweekday)
655
656    def check_weeks(self, year, month, weeks):
657        cal = calendar.monthcalendar(year, month)
658        self.assertEqual(len(cal), len(weeks))
659        for i in range(len(weeks)):
660            self.assertEqual(weeks[i], sum(day != 0 for day in cal[i]))
661
662
663class MondayTestCase(MonthCalendarTestCase):
664    firstweekday = calendar.MONDAY
665
666    def test_february(self):
667        # A 28-day february starting on monday (7+7+7+7 days)
668        self.check_weeks(1999, 2, (7, 7, 7, 7))
669
670        # A 28-day february starting on tuesday (6+7+7+7+1 days)
671        self.check_weeks(2005, 2, (6, 7, 7, 7, 1))
672
673        # A 28-day february starting on sunday (1+7+7+7+6 days)
674        self.check_weeks(1987, 2, (1, 7, 7, 7, 6))
675
676        # A 29-day february starting on monday (7+7+7+7+1 days)
677        self.check_weeks(1988, 2, (7, 7, 7, 7, 1))
678
679        # A 29-day february starting on tuesday (6+7+7+7+2 days)
680        self.check_weeks(1972, 2, (6, 7, 7, 7, 2))
681
682        # A 29-day february starting on sunday (1+7+7+7+7 days)
683        self.check_weeks(2004, 2, (1, 7, 7, 7, 7))
684
685    def test_april(self):
686        # A 30-day april starting on monday (7+7+7+7+2 days)
687        self.check_weeks(1935, 4, (7, 7, 7, 7, 2))
688
689        # A 30-day april starting on tuesday (6+7+7+7+3 days)
690        self.check_weeks(1975, 4, (6, 7, 7, 7, 3))
691
692        # A 30-day april starting on sunday (1+7+7+7+7+1 days)
693        self.check_weeks(1945, 4, (1, 7, 7, 7, 7, 1))
694
695        # A 30-day april starting on saturday (2+7+7+7+7 days)
696        self.check_weeks(1995, 4, (2, 7, 7, 7, 7))
697
698        # A 30-day april starting on friday (3+7+7+7+6 days)
699        self.check_weeks(1994, 4, (3, 7, 7, 7, 6))
700
701    def test_december(self):
702        # A 31-day december starting on monday (7+7+7+7+3 days)
703        self.check_weeks(1980, 12, (7, 7, 7, 7, 3))
704
705        # A 31-day december starting on tuesday (6+7+7+7+4 days)
706        self.check_weeks(1987, 12, (6, 7, 7, 7, 4))
707
708        # A 31-day december starting on sunday (1+7+7+7+7+2 days)
709        self.check_weeks(1968, 12, (1, 7, 7, 7, 7, 2))
710
711        # A 31-day december starting on thursday (4+7+7+7+6 days)
712        self.check_weeks(1988, 12, (4, 7, 7, 7, 6))
713
714        # A 31-day december starting on friday (3+7+7+7+7 days)
715        self.check_weeks(2017, 12, (3, 7, 7, 7, 7))
716
717        # A 31-day december starting on saturday (2+7+7+7+7+1 days)
718        self.check_weeks(2068, 12, (2, 7, 7, 7, 7, 1))
719
720
721class SundayTestCase(MonthCalendarTestCase):
722    firstweekday = calendar.SUNDAY
723
724    def test_february(self):
725        # A 28-day february starting on sunday (7+7+7+7 days)
726        self.check_weeks(2009, 2, (7, 7, 7, 7))
727
728        # A 28-day february starting on monday (6+7+7+7+1 days)
729        self.check_weeks(1999, 2, (6, 7, 7, 7, 1))
730
731        # A 28-day february starting on saturday (1+7+7+7+6 days)
732        self.check_weeks(1997, 2, (1, 7, 7, 7, 6))
733
734        # A 29-day february starting on sunday (7+7+7+7+1 days)
735        self.check_weeks(2004, 2, (7, 7, 7, 7, 1))
736
737        # A 29-day february starting on monday (6+7+7+7+2 days)
738        self.check_weeks(1960, 2, (6, 7, 7, 7, 2))
739
740        # A 29-day february starting on saturday (1+7+7+7+7 days)
741        self.check_weeks(1964, 2, (1, 7, 7, 7, 7))
742
743    def test_april(self):
744        # A 30-day april starting on sunday (7+7+7+7+2 days)
745        self.check_weeks(1923, 4, (7, 7, 7, 7, 2))
746
747        # A 30-day april starting on monday (6+7+7+7+3 days)
748        self.check_weeks(1918, 4, (6, 7, 7, 7, 3))
749
750        # A 30-day april starting on saturday (1+7+7+7+7+1 days)
751        self.check_weeks(1950, 4, (1, 7, 7, 7, 7, 1))
752
753        # A 30-day april starting on friday (2+7+7+7+7 days)
754        self.check_weeks(1960, 4, (2, 7, 7, 7, 7))
755
756        # A 30-day april starting on thursday (3+7+7+7+6 days)
757        self.check_weeks(1909, 4, (3, 7, 7, 7, 6))
758
759    def test_december(self):
760        # A 31-day december starting on sunday (7+7+7+7+3 days)
761        self.check_weeks(2080, 12, (7, 7, 7, 7, 3))
762
763        # A 31-day december starting on monday (6+7+7+7+4 days)
764        self.check_weeks(1941, 12, (6, 7, 7, 7, 4))
765
766        # A 31-day december starting on saturday (1+7+7+7+7+2 days)
767        self.check_weeks(1923, 12, (1, 7, 7, 7, 7, 2))
768
769        # A 31-day december starting on wednesday (4+7+7+7+6 days)
770        self.check_weeks(1948, 12, (4, 7, 7, 7, 6))
771
772        # A 31-day december starting on thursday (3+7+7+7+7 days)
773        self.check_weeks(1927, 12, (3, 7, 7, 7, 7))
774
775        # A 31-day december starting on friday (2+7+7+7+7+1 days)
776        self.check_weeks(1995, 12, (2, 7, 7, 7, 7, 1))
777
778class TimegmTestCase(unittest.TestCase):
779    TIMESTAMPS = [0, 10, 100, 1000, 10000, 100000, 1000000,
780                  1234567890, 1262304000, 1275785153,]
781    def test_timegm(self):
782        for secs in self.TIMESTAMPS:
783            tuple = time.gmtime(secs)
784            self.assertEqual(secs, calendar.timegm(tuple))
785
786class MonthRangeTestCase(unittest.TestCase):
787    def test_january(self):
788        # Tests valid lower boundary case.
789        self.assertEqual(calendar.monthrange(2004,1), (3,31))
790
791    def test_february_leap(self):
792        # Tests February during leap year.
793        self.assertEqual(calendar.monthrange(2004,2), (6,29))
794
795    def test_february_nonleap(self):
796        # Tests February in non-leap year.
797        self.assertEqual(calendar.monthrange(2010,2), (0,28))
798
799    def test_december(self):
800        # Tests valid upper boundary case.
801        self.assertEqual(calendar.monthrange(2004,12), (2,31))
802
803    def test_zeroth_month(self):
804        # Tests low invalid boundary case.
805        with self.assertRaises(calendar.IllegalMonthError):
806            calendar.monthrange(2004, 0)
807
808    def test_thirteenth_month(self):
809        # Tests high invalid boundary case.
810        with self.assertRaises(calendar.IllegalMonthError):
811            calendar.monthrange(2004, 13)
812
813    def test_illegal_month_reported(self):
814        with self.assertRaisesRegex(calendar.IllegalMonthError, '65'):
815            calendar.monthrange(2004, 65)
816
817class LeapdaysTestCase(unittest.TestCase):
818    def test_no_range(self):
819        # test when no range i.e. two identical years as args
820        self.assertEqual(calendar.leapdays(2010,2010), 0)
821
822    def test_no_leapdays(self):
823        # test when no leap years in range
824        self.assertEqual(calendar.leapdays(2010,2011), 0)
825
826    def test_no_leapdays_upper_boundary(self):
827        # test no leap years in range, when upper boundary is a leap year
828        self.assertEqual(calendar.leapdays(2010,2012), 0)
829
830    def test_one_leapday_lower_boundary(self):
831        # test when one leap year in range, lower boundary is leap year
832        self.assertEqual(calendar.leapdays(2012,2013), 1)
833
834    def test_several_leapyears_in_range(self):
835        self.assertEqual(calendar.leapdays(1997,2020), 5)
836
837
838def conv(s):
839    return s.replace('\n', os.linesep).encode()
840
841class CommandLineTestCase(unittest.TestCase):
842    def run_ok(self, *args):
843        return assert_python_ok('-m', 'calendar', *args)[1]
844
845    def assertFailure(self, *args):
846        rc, stdout, stderr = assert_python_failure('-m', 'calendar', *args)
847        self.assertIn(b'usage:', stderr)
848        self.assertEqual(rc, 2)
849
850    def test_help(self):
851        stdout = self.run_ok('-h')
852        self.assertIn(b'usage:', stdout)
853        self.assertIn(b'calendar.py', stdout)
854        self.assertIn(b'--help', stdout)
855
856    def test_illegal_arguments(self):
857        self.assertFailure('-z')
858        self.assertFailure('spam')
859        self.assertFailure('2004', 'spam')
860        self.assertFailure('-t', 'html', '2004', '1')
861
862    def test_output_current_year(self):
863        stdout = self.run_ok()
864        year = datetime.datetime.now().year
865        self.assertIn((' %s' % year).encode(), stdout)
866        self.assertIn(b'January', stdout)
867        self.assertIn(b'Mo Tu We Th Fr Sa Su', stdout)
868
869    def test_output_year(self):
870        stdout = self.run_ok('2004')
871        self.assertEqual(stdout, conv(result_2004_text))
872
873    def test_output_month(self):
874        stdout = self.run_ok('2004', '1')
875        self.assertEqual(stdout, conv(result_2004_01_text))
876
877    def test_option_encoding(self):
878        self.assertFailure('-e')
879        self.assertFailure('--encoding')
880        stdout = self.run_ok('--encoding', 'utf-16-le', '2004')
881        self.assertEqual(stdout, result_2004_text.encode('utf-16-le'))
882
883    def test_option_locale(self):
884        self.assertFailure('-L')
885        self.assertFailure('--locale')
886        self.assertFailure('-L', 'en')
887
888        lang, enc = locale.getlocale()
889        lang = lang or 'C'
890        enc = enc or 'UTF-8'
891        try:
892            oldlocale = locale.getlocale(locale.LC_TIME)
893            try:
894                locale.setlocale(locale.LC_TIME, (lang, enc))
895            finally:
896                locale.setlocale(locale.LC_TIME, oldlocale)
897        except (locale.Error, ValueError):
898            self.skipTest('cannot set the system default locale')
899        stdout = self.run_ok('--locale', lang, '--encoding', enc, '2004')
900        self.assertIn('2004'.encode(enc), stdout)
901
902    def test_option_width(self):
903        self.assertFailure('-w')
904        self.assertFailure('--width')
905        self.assertFailure('-w', 'spam')
906        stdout = self.run_ok('--width', '3', '2004')
907        self.assertIn(b'Mon Tue Wed Thu Fri Sat Sun', stdout)
908
909    def test_option_lines(self):
910        self.assertFailure('-l')
911        self.assertFailure('--lines')
912        self.assertFailure('-l', 'spam')
913        stdout = self.run_ok('--lines', '2', '2004')
914        self.assertIn(conv('December\n\nMo Tu We'), stdout)
915
916    def test_option_spacing(self):
917        self.assertFailure('-s')
918        self.assertFailure('--spacing')
919        self.assertFailure('-s', 'spam')
920        stdout = self.run_ok('--spacing', '8', '2004')
921        self.assertIn(b'Su        Mo', stdout)
922
923    def test_option_months(self):
924        self.assertFailure('-m')
925        self.assertFailure('--month')
926        self.assertFailure('-m', 'spam')
927        stdout = self.run_ok('--months', '1', '2004')
928        self.assertIn(conv('\nMo Tu We Th Fr Sa Su\n'), stdout)
929
930    def test_option_type(self):
931        self.assertFailure('-t')
932        self.assertFailure('--type')
933        self.assertFailure('-t', 'spam')
934        stdout = self.run_ok('--type', 'text', '2004')
935        self.assertEqual(stdout, conv(result_2004_text))
936        stdout = self.run_ok('--type', 'html', '2004')
937        self.assertEqual(stdout[:6], b'<?xml ')
938        self.assertIn(b'<title>Calendar for 2004</title>', stdout)
939
940    def test_html_output_current_year(self):
941        stdout = self.run_ok('--type', 'html')
942        year = datetime.datetime.now().year
943        self.assertIn(('<title>Calendar for %s</title>' % year).encode(),
944                      stdout)
945        self.assertIn(b'<tr><th colspan="7" class="month">January</th></tr>',
946                      stdout)
947
948    def test_html_output_year_encoding(self):
949        stdout = self.run_ok('-t', 'html', '--encoding', 'ascii', '2004')
950        self.assertEqual(stdout,
951                         result_2004_html.format(**default_format).encode('ascii'))
952
953    def test_html_output_year_css(self):
954        self.assertFailure('-t', 'html', '-c')
955        self.assertFailure('-t', 'html', '--css')
956        stdout = self.run_ok('-t', 'html', '--css', 'custom.css', '2004')
957        self.assertIn(b'<link rel="stylesheet" type="text/css" '
958                      b'href="custom.css" />', stdout)
959
960
961class MiscTestCase(unittest.TestCase):
962    def test__all__(self):
963        not_exported = {
964            'mdays', 'January', 'February', 'EPOCH',
965            'different_locale', 'c', 'prweek', 'week', 'format',
966            'formatstring', 'main', 'monthlen', 'prevmonth', 'nextmonth'}
967        support.check__all__(self, calendar, not_exported=not_exported)
968
969
970class TestSubClassingCase(unittest.TestCase):
971
972    def setUp(self):
973
974        class CustomHTMLCal(calendar.HTMLCalendar):
975            cssclasses = [style + " text-nowrap" for style in
976                          calendar.HTMLCalendar.cssclasses]
977            cssclasses_weekday_head = ["red", "blue", "green", "lilac",
978                                       "yellow", "orange", "pink"]
979            cssclass_month_head = "text-center month-head"
980            cssclass_month = "text-center month"
981            cssclass_year = "text-italic "
982            cssclass_year_head = "lead "
983
984        self.cal = CustomHTMLCal()
985
986    def test_formatmonthname(self):
987        self.assertIn('class="text-center month-head"',
988                      self.cal.formatmonthname(2017, 5))
989
990    def test_formatmonth(self):
991        self.assertIn('class="text-center month"',
992                      self.cal.formatmonth(2017, 5))
993
994    def test_formatweek(self):
995        weeks = self.cal.monthdays2calendar(2017, 5)
996        self.assertIn('class="wed text-nowrap"', self.cal.formatweek(weeks[0]))
997
998    def test_formatweek_head(self):
999        header = self.cal.formatweekheader()
1000        for color in self.cal.cssclasses_weekday_head:
1001            self.assertIn('<th class="%s">' % color, header)
1002
1003    def test_format_year(self):
1004        self.assertIn(
1005            ('<table border="0" cellpadding="0" cellspacing="0" class="%s">' %
1006             self.cal.cssclass_year), self.cal.formatyear(2017))
1007
1008    def test_format_year_head(self):
1009        self.assertIn('<tr><th colspan="%d" class="%s">%s</th></tr>' % (
1010            3, self.cal.cssclass_year_head, 2017), self.cal.formatyear(2017))
1011
1012if __name__ == "__main__":
1013    unittest.main()
1014