-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdate_utils.py
189 lines (147 loc) · 4.74 KB
/
date_utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
"""
Date functions that can be handy.
Some code below is copied from "http://pydoc.net/FinDates/0.2/findates.dateutils/".
"""
import datetime as dt
DAYS_IN_WEEK = 7
MONTHS_IN_YEAR = 12
DAYS_IN_NON_LEAP_YEAR = 365
DAYS_IN_LEAP_YEAR = 366
_days_in_month = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
_days_in_month_so_far = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
def as_datetime(d1: dt.datetime) -> dt.datetime:
"""
Get datetime from several possible representations.
:param d1: Date.
:return: Date.
"""
if isinstance(d1, dt.date):
return dt.datetime(d1.year, d1.month, d1.day)
elif isinstance(d1, str):
return dt.datetime.strptime(d1, '%Y-%m-%d')
else:
raise ValueError("Cannot extract date from: %s" % repr(d1))
def as_year(d1):
"""
Get year value from integer, date string, datetime.date or datetime.datetime class.
:param d1: Date in different formats.
:return: Year.
"""
if isinstance(d1, int):
return d1
elif isinstance(d1, dt.date) or isinstance(d1, dt.datetime):
return d1.year
elif isinstance(d1, str):
d = as_datetime(d1)
return d.year
else:
raise ValueError('Cannot extract year value from %s', repr(d1))
def leap_year(d1: dt.datetime) -> bool:
"""
Check if year is a leap year.
:param d1: Year.
:return: True or False.
"""
yr = as_year(d1)
if yr <= 1752:
return yr % 4 == 0
else:
return (yr % 4 == 0) and (yr % 100 != 0 or yr % 400 == 0)
def year_days(d1: dt.datetime) -> int:
"""
Get number of days in year.
:param d1: Date.
:return: Number of days.
"""
if leap_year(d1):
return DAYS_IN_LEAP_YEAR
else:
return DAYS_IN_NON_LEAP_YEAR
def start_of_year_to_date(d1: dt.datetime) -> dt.timedelta:
"""
Get the number of days in year to date.
:param d1: Date.
:return: Number of days.
"""
soy = dt.datetime(d1.year, 1, 1)
return d1 - soy
def date_to_end_of_year(d1: dt.datetime) -> dt.timedelta:
"""
Get the number of days to end of year.
:param d1: Date.
:return: Number of days.
"""
eoy = dt.datetime(d1.year + 1, 1, 1)
return eoy - d1
def eom(year, month: int) -> dt.datetime:
"""
Get date of last day in month.
:param year: Year.
:param month: Month number.
:return: Date.
"""
if month == 2 and leap_year(year):
d = 29
else:
d = _days_in_month[month]
return dt.datetime(year, month, d)
def is_eom(d1: dt.datetime) -> bool:
"""
Check if date is end of the month.
:param d1: Date.
:return: True or False.
"""
d1 = as_datetime(d1)
return eom(d1.year, d1.month) == d1
def l_weekday(year: int, month: int, weekday: int) -> dt.datetime:
"""
Date of the last occurrence of weekday in month of a given year.
:param year: Year.
:param month: Month number.
:param weekday: weekday number (0 - Monday, 6 - Sunday).
:return: Date.
"""
last_day = eom(year, month)
last_weekday = last_day.weekday()
return last_day - dt.timedelta(days=(last_weekday-weekday) % DAYS_IN_WEEK)
def n_weekday(year: int, month: int, nth: int, weekday: int) -> dt.datetime:
"""
Date of the n-th occurrence of weekday in month.
:param year: Year.
:param month: Month number.
:param nth: Occurrence.
:param weekday: weekday number (0 - Monday, 6 - Sunday).
:return: Date.
"""
first_weekday_of_month = dt.datetime(year, month, 1).weekday()
last_weekday_of_month = eom(year, month)
day = 1+(weekday - first_weekday_of_month) % DAYS_IN_WEEK + (nth-1)*DAYS_IN_WEEK
if day > last_weekday_of_month.day:
raise ValueError("No such n-th weekday in this month")
return dt.datetime(year, month, day)
def weekday_on_or_before(d1: dt.datetime, weekday: int) -> dt.datetime:
"""
Get weekday happening on or before a given date.
:param d1: Date.
:param weekday: weekday number (0 - Monday, 6 - Sunday).
:return: Date.
"""
end_wd = d1.weekday()
delta = end_wd - weekday
if delta < 0:
delta += 7
res = d1 - dt.timedelta(days=delta)
return res
def weekday_on_or_after(d1: dt.datetime, weekday: int) -> dt.datetime:
"""
Get weekday happening on or after a given date.
:param d1: Date.
:param weekday: weekday number (0 - Monday, 6 - Sunday).
:return: Date.
"""
start_wd = d1.weekday()
delta = weekday - start_wd
if delta < 0:
delta += 7
res = d1 + dt.timedelta(days=delta)
return res