2
Utility functions for SchoolBell.
4
These include various date manipulation routines.
8
from datetime import datetime, date, timedelta, tzinfo
12
"""Calculate the first day of the previous month for a given date.
14
>>> prev_month(date(2004, 8, 1))
15
datetime.date(2004, 7, 1)
16
>>> prev_month(date(2004, 8, 31))
17
datetime.date(2004, 7, 1)
18
>>> prev_month(date(2004, 12, 15))
19
datetime.date(2004, 11, 1)
20
>>> prev_month(date(2005, 1, 28))
21
datetime.date(2004, 12, 1)
24
return (date.replace(day=1) - timedelta(1)).replace(day=1)
28
"""Calculate the first day of the next month for a given date.
30
>>> next_month(date(2004, 8, 1))
31
datetime.date(2004, 9, 1)
32
>>> next_month(date(2004, 8, 31))
33
datetime.date(2004, 9, 1)
34
>>> next_month(date(2004, 12, 15))
35
datetime.date(2005, 1, 1)
36
>>> next_month(date(2004, 2, 28))
37
datetime.date(2004, 3, 1)
38
>>> next_month(date(2004, 2, 29))
39
datetime.date(2004, 3, 1)
40
>>> next_month(date(2005, 2, 28))
41
datetime.date(2005, 3, 1)
44
return (date.replace(day=28) + timedelta(7)).replace(day=1)
47
def week_start(date, first_day_of_week=0):
48
"""Calculate the first day of the week for a given date.
50
Assuming that week starts on Mondays:
52
>>> week_start(date(2004, 8, 19))
53
datetime.date(2004, 8, 16)
54
>>> week_start(date(2004, 8, 15))
55
datetime.date(2004, 8, 9)
56
>>> week_start(date(2004, 8, 14))
57
datetime.date(2004, 8, 9)
58
>>> week_start(date(2004, 8, 21))
59
datetime.date(2004, 8, 16)
60
>>> week_start(date(2004, 8, 22))
61
datetime.date(2004, 8, 16)
62
>>> week_start(date(2004, 8, 23))
63
datetime.date(2004, 8, 23)
65
Assuming that week starts on Sundays:
68
>>> week_start(date(2004, 8, 19), calendar.SUNDAY)
69
datetime.date(2004, 8, 15)
70
>>> week_start(date(2004, 8, 15), calendar.SUNDAY)
71
datetime.date(2004, 8, 15)
72
>>> week_start(date(2004, 8, 14), calendar.SUNDAY)
73
datetime.date(2004, 8, 8)
74
>>> week_start(date(2004, 8, 21), calendar.SUNDAY)
75
datetime.date(2004, 8, 15)
76
>>> week_start(date(2004, 8, 22), calendar.SUNDAY)
77
datetime.date(2004, 8, 22)
78
>>> week_start(date(2004, 8, 23), calendar.SUNDAY)
79
datetime.date(2004, 8, 22)
82
assert 0 <= first_day_of_week < 7
83
delta = date.weekday() - first_day_of_week
86
return date - timedelta(delta)
89
def weeknum_bounds(year, weeknum):
90
"""Calculate the inclusive date bounds for a (year, weeknum) tuple.
92
Week numbers are as defined in ISO 8601 and returned by
93
datetime.date.isocalendar().
95
>>> weeknum_bounds(2003, 52)
96
(datetime.date(2003, 12, 22), datetime.date(2003, 12, 28))
97
>>> weeknum_bounds(2004, 1)
98
(datetime.date(2003, 12, 29), datetime.date(2004, 1, 4))
99
>>> weeknum_bounds(2004, 2)
100
(datetime.date(2004, 1, 5), datetime.date(2004, 1, 11))
103
# The first week of a year is at least 4 days long, so January 4th
104
# is in the first week.
105
firstweek = week_start(date(year, 1, 4), calendar.MONDAY)
106
# move forward to the right week number
107
weekstart = firstweek + timedelta(weeks=weeknum-1)
108
weekend = weekstart + timedelta(days=6)
109
return (weekstart, weekend)
112
def check_weeknum(year, weeknum):
113
"""Check to see whether a (year, weeknum) tuple refers to a real
116
>>> check_weeknum(2004, 1)
118
>>> check_weeknum(2004, 53)
120
>>> check_weeknum(2004, 0)
122
>>> check_weeknum(2004, 54)
124
>>> check_weeknum(2003, 52)
126
>>> check_weeknum(2003, 53)
130
weekstart, weekend = weeknum_bounds(year, weeknum)
131
isoyear, isoweek, isoday = weekstart.isocalendar()
132
return (year, weeknum) == (isoyear, isoweek)
135
"""A dict with automatic key selection.
137
The add method automatically selects the lowest unused numeric key
149
{0: 'first', 1: 'second'}
151
The keys can be reused:
156
{0: 'third', 1: 'second'}