@@ -111,8 +111,9 @@ def leapdays(y1, y2):
111
111
112
112
113
113
def weekday (year , month , day ):
114
- """Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12),
115
- day (1-31)."""
114
+ """Return weekday (0-6 ~ Mon-Sun) for year, month (1-12), day (1-31)."""
115
+ if not datetime .MINYEAR <= year <= datetime .MAXYEAR :
116
+ year = 2000 + year % 400
116
117
return datetime .date (year , month , day ).weekday ()
117
118
118
119
@@ -126,6 +127,24 @@ def monthrange(year, month):
126
127
return day1 , ndays
127
128
128
129
130
+ def _monthlen (year , month ):
131
+ return mdays [month ] + (month == February and isleap (year ))
132
+
133
+
134
+ def _prevmonth (year , month ):
135
+ if month == 1 :
136
+ return year - 1 , 12
137
+ else :
138
+ return year , month - 1
139
+
140
+
141
+ def _nextmonth (year , month ):
142
+ if month == 12 :
143
+ return year + 1 , 1
144
+ else :
145
+ return year , month + 1
146
+
147
+
129
148
class Calendar (object ):
130
149
"""
131
150
Base calendar class. This class doesn't do any formatting. It simply
@@ -157,20 +176,20 @@ def itermonthdates(self, year, month):
157
176
values and will always iterate through complete weeks, so it will yield
158
177
dates outside the specified month.
159
178
"""
160
- date = datetime . date (year , month , 1 )
161
- # Go back to the beginning of the week
162
- days = ( date . weekday () - self . firstweekday ) % 7
163
- date -= datetime . timedelta ( days = days )
164
- oneday = datetime . timedelta ( days = 1 )
165
- while True :
166
- yield date
167
- try :
168
- date += oneday
169
- except OverflowError :
170
- # Adding one day could fail after datetime.MAXYEAR
171
- break
172
- if date . month != month and date . weekday () == self . firstweekday :
173
- break
179
+ for y , m , d in self . itermonthdays3 (year , month ):
180
+ yield datetime . date ( y , m , d )
181
+
182
+ def itermonthdays ( self , year , month ):
183
+ """
184
+ Like itermonthdates(), but will yield day numbers. For days outside
185
+ the specified month the day number is 0.
186
+ """
187
+ day1 , ndays = monthrange ( year , month )
188
+ days_before = ( day1 - self . firstweekday ) % 7
189
+ yield from repeat ( 0 , days_before )
190
+ yield from range ( 1 , ndays + 1 )
191
+ days_after = ( self . firstweekday - day1 - ndays ) % 7
192
+ yield from repeat ( 0 , days_after )
174
193
175
194
def itermonthdays2 (self , year , month ):
176
195
"""
@@ -180,17 +199,31 @@ def itermonthdays2(self, year, month):
180
199
for i , d in enumerate (self .itermonthdays (year , month ), self .firstweekday ):
181
200
yield d , i % 7
182
201
183
- def itermonthdays (self , year , month ):
202
+ def itermonthdays3 (self , year , month ):
184
203
"""
185
- Like itermonthdates(), but will yield day numbers. For days outside
186
- the specified month the day number is 0 .
204
+ Like itermonthdates(), but will yield (year, month, day) tuples. Can be
205
+ used for dates outside of datetime.date range .
187
206
"""
188
207
day1 , ndays = monthrange (year , month )
189
208
days_before = (day1 - self .firstweekday ) % 7
190
- yield from repeat (0 , days_before )
191
- yield from range (1 , ndays + 1 )
192
209
days_after = (self .firstweekday - day1 - ndays ) % 7
193
- yield from repeat (0 , days_after )
210
+ y , m = _prevmonth (year , month )
211
+ end = _monthlen (y , m ) + 1
212
+ for d in range (end - days_before , end ):
213
+ yield y , m , d
214
+ for d in range (1 , ndays + 1 ):
215
+ yield year , month , d
216
+ y , m = _nextmonth (year , month )
217
+ for d in range (1 , days_after + 1 ):
218
+ yield y , m , d
219
+
220
+ def itermonthdays4 (self , year , month ):
221
+ """
222
+ Like itermonthdates(), but will yield (year, month, day, day_of_week) tuples.
223
+ Can be used for dates outside of datetime.date range.
224
+ """
225
+ for i , (y , m , d ) in enumerate (self .itermonthdays3 (year , month )):
226
+ yield y , m , d , (self .firstweekday + i ) % 7
194
227
195
228
def monthdatescalendar (self , year , month ):
196
229
"""
@@ -267,7 +300,7 @@ def prweek(self, theweek, width):
267
300
"""
268
301
Print a single week (no newline).
269
302
"""
270
- print (self .formatweek (theweek , width ), end = ' ' )
303
+ print (self .formatweek (theweek , width ), end = '' )
271
304
272
305
def formatday (self , day , weekday , width ):
273
306
"""
@@ -371,7 +404,7 @@ def formatyear(self, theyear, w=2, l=1, c=6, m=3):
371
404
372
405
def pryear (self , theyear , w = 0 , l = 0 , c = 6 , m = 3 ):
373
406
"""Print a year's calendar."""
374
- print (self .formatyear (theyear , w , l , c , m ))
407
+ print (self .formatyear (theyear , w , l , c , m ), end = '' )
375
408
376
409
377
410
class HTMLCalendar (Calendar ):
@@ -382,12 +415,31 @@ class HTMLCalendar(Calendar):
382
415
# CSS classes for the day <td>s
383
416
cssclasses = ["mon" , "tue" , "wed" , "thu" , "fri" , "sat" , "sun" ]
384
417
418
+ # CSS classes for the day <th>s
419
+ cssclasses_weekday_head = cssclasses
420
+
421
+ # CSS class for the days before and after current month
422
+ cssclass_noday = "noday"
423
+
424
+ # CSS class for the month's head
425
+ cssclass_month_head = "month"
426
+
427
+ # CSS class for the month
428
+ cssclass_month = "month"
429
+
430
+ # CSS class for the year's table head
431
+ cssclass_year_head = "year"
432
+
433
+ # CSS class for the whole year table
434
+ cssclass_year = "year"
435
+
385
436
def formatday (self , day , weekday ):
386
437
"""
387
438
Return a day as a table cell.
388
439
"""
389
440
if day == 0 :
390
- return '<td class="noday"> </td>' # day outside month
441
+ # day outside month
442
+ return '<td class="%s"> </td>' % self .cssclass_noday
391
443
else :
392
444
return '<td class="%s">%d</td>' % (self .cssclasses [weekday ], day )
393
445
@@ -402,7 +454,8 @@ def formatweekday(self, day):
402
454
"""
403
455
Return a weekday name as a table header.
404
456
"""
405
- return '<th class="%s">%s</th>' % (self .cssclasses [day ], day_abbr [day ])
457
+ return '<th class="%s">%s</th>' % (
458
+ self .cssclasses_weekday_head [day ], day_abbr [day ])
406
459
407
460
def formatweekheader (self ):
408
461
"""
@@ -419,15 +472,17 @@ def formatmonthname(self, theyear, themonth, withyear=True):
419
472
s = '%s %s' % (month_name [themonth ], theyear )
420
473
else :
421
474
s = '%s' % month_name [themonth ]
422
- return '<tr><th colspan="7" class="month">%s</th></tr>' % s
475
+ return '<tr><th colspan="7" class="%s">%s</th></tr>' % (
476
+ self .cssclass_month_head , s )
423
477
424
478
def formatmonth (self , theyear , themonth , withyear = True ):
425
479
"""
426
480
Return a formatted month as a table.
427
481
"""
428
482
v = []
429
483
a = v .append
430
- a ('<table border="0" cellpadding="0" cellspacing="0" class="month">' )
484
+ a ('<table border="0" cellpadding="0" cellspacing="0" class="%s">' % (
485
+ self .cssclass_month ))
431
486
a ('\n ' )
432
487
a (self .formatmonthname (theyear , themonth , withyear = withyear ))
433
488
a ('\n ' )
@@ -447,9 +502,11 @@ def formatyear(self, theyear, width=3):
447
502
v = []
448
503
a = v .append
449
504
width = max (width , 1 )
450
- a ('<table border="0" cellpadding="0" cellspacing="0" class="year">' )
505
+ a ('<table border="0" cellpadding="0" cellspacing="0" class="%s">' %
506
+ self .cssclass_year )
451
507
a ('\n ' )
452
- a ('<tr><th colspan="%d" class="year">%s</th></tr>' % (width , theyear ))
508
+ a ('<tr><th colspan="%d" class="%s">%s</th></tr>' % (
509
+ width , self .cssclass_year_head , theyear ))
453
510
for i in range (January , January + 12 , width ):
454
511
# months in this row
455
512
months = range (i , min (i + width , 13 ))
0 commit comments