我需要一个函数来计算除周末日之外的两个日期之间的时间(以秒为单位),这将是这样的:
# friday 9 PM
start_date = datetime.datetime(2015, 9, 18, 21, 0, 0)
# monday 3 AM
end_date = datetime.datetime(2015, 9, 21, 3, 0, 0)
# should return 6 hours
time = time_between_two_dates_except_weekends(start_date, end_date)
我实现了自己的功能,这是有效的,但似乎不必要地庞大而复杂。我认为它可以更简单。
import datetime
from dateutil.relativedelta import relativedelta
from dateutil.rrule import DAILY, rrule
def time_between_two_dates_except_weekends(start_date, end_date):
WEEKEND_DAYS = [5, 6]
result = datetime.timedelta()
if all([start_date.year == end_date.year, start_date.month == end_date.month, start_date.day == end_date.day]):
result += datetime.timedelta(seconds = (end_date-start_date).seconds )
return result
day_after_start_date = start_date + relativedelta(days=1)
day_after_start_date = day_after_start_date.replace(hour=0, minute=0, second=0)
day_before_end_date = end_date - relativedelta(days=1)
if start_date.weekday() not in WEEKEND_DAYS:
result += datetime.timedelta(seconds = (day_after_start_date - start_date).total_seconds())
dates_range = rrule(
DAILY,
byhour=0,
byminute=0,
bysecond=0,
dtstart=day_after_start_date,
until=day_before_end_date
)
for date in dates_range:
if date.weekday() not in WEEKEND_DAYS:
result += datetime.timedelta(seconds=24 * 60 * 60)
if end_date.weekday() not in WEEKEND_DAYS:
end_date_beginning = end_date.replace(hour=0, minute=0, second=0)
result += datetime.timedelta(seconds = (end_date - end_date_beginning).total_seconds())
return result
有没有办法改善这个?
UPD。结果表明,不仅我的代码很复杂,而且在某些极端情况下返回错误的结果(例如,当开始或结束日期的周末日期过去时)。我建议只使用下面正确答案的代码
答案 0 :(得分:4)
from datetime import timedelta
def diff(s, e):
_diff = (end_date - start_date)
while s < e:
if s.weekday() in {5, 6}:
_diff -= timedelta(days=1)
s += timedelta(days=1)
return timedelta(seconds=_diff.total_seconds())
如果您的日期可以在周末结束或开始,我们需要将它们移动到下一个星期一,我们可以使用帮助函数来执行:
from datetime import timedelta
def helper(d):
if d.weekday() == 5:
d += timedelta(days=1)
return d.replace(hour=0, minute=0, second=0, microsecond=0)
def diff(s, e):
if e.weekday() in {5, 6}:
e = helper(e)
if s.weekday() in {5, 6}:
s = helper(s)
_diff = (e - s)
while s < e:
if s.weekday() in {5, 6}:
_diff -= timedelta(days=1)
elif s.weekday() == 0:
s += timedelta(days=4)
s += timedelta(days=1)
return timedelta(seconds=_diff.total_seconds())
仍然跑得快一点:
In [57]: timeit time_between_two_dates_except_weekends(start_date,end_date)
10 loops, best of 3: 95.5 ms per loop
In [58]: timeit diff(start_date,end_date)
100 loops, best of 3: 12.4 ms per loop
In [59]: diff(start_date,end_date)
Out[59]: datetime.timedelta(7699, 9300)
In [60]: time_between_two_dates_except_weekends(start_date,end_date)
Out[60]: datetime.timedelta(7699, 9300)
只是做数学:
from datetime import timedelta, datetime
def helper(d):
if d.weekday() == 5:
d += timedelta(days=1)
return d.replace(hour=0, minute=0, second=0, microsecond=0)
def diff(s, e):
weekend = {5, 6}
both = e.weekday() in weekend and s.weekday() in weekend
is_weekend = e.weekday() in {5, 6} or s.weekday() in {5, 6}
if e.weekday() in weekend:
e = helper(e)
if s.weekday() in weekend:
s = helper(s)
_diff = (e - s)
wek = _diff.days / 7 * 2 + is_weekend - both
if s.weekday() > e.weekday() and not is_weekend:
wek += 2
return timedelta(seconds=_diff.total_seconds()) - timedelta(wek)
运行速度要快得多:
In [2]: start_date = datetime(2016, 02, 29, 21, 25, 0)
In [3]: end_date = datetime(2045, 9, 02, 03, 56, 0)
In [4]: timeit diff(start_date,end_date)
100000 loops, best of 3: 6.8 µs per loop
In [5]: diff(start_date,end_date)
Out[5]: datetime.timedelta(7699, 9300)