请注意,如果期间重叠,某些方法将不起作用。如果周期重叠,您需要更复杂的算法。
首先,几个定义:
>>> from datetime import datetime, timedelta
>>> dates_by_num = {'0': {'enddate': '2015/08/31', 'startdate':'2015/01/01'}, '1': {'enddate': '2018/10/31', 'startdate': '2017/01/01'}, '2': {'enddate': '2019/03/29', 'startdate': '2019/01/01'}}
>>> def to_date(s): return datetime.strptime(s, r'%Y/%m/%d')
>>> def to_string(d): return datetime.strftime(d, r'%Y/%m/%d')
关键思想是对所有日期进行排序并保留一个值来指示它是开始 ( ) 日期1还是结束 ( -1) 日期。
>>> dates = sorted(d_s for _, v in dates_by_num.items() for d_s in ((to_date(v['startdate']), 1), (to_date(v['enddate']), -1)))
>>> dates
[(datetime.datetime(2015, 1, 1, 0, 0), 1), (datetime.datetime(2015, 8, 31, 0, 0), -1), (datetime.datetime(2017, 1, 1, 0, 0), 1), (datetime.datetime(2018, 10, 31, 0, 0), -1), (datetime.datetime(2019, 1, 1, 0, 0), 1), (datetime.datetime(2019, 3, 29, 0, 0), -1)]
现在,我们遍历日期并c记录我们所处的间隔数:每个开始日期c增加,每个结束日期c减少,因此c是开始日期的数量减去结束日期的数量。当(且仅当)时,我们不在所有区间内c == 0。
>>> gaps = []
>>> last_c, last_d = 1, 0
>>> c = 0
>>> for d, s in dates:
... c += s
... assert c >= 0 # c is the number of intervals we are in
... if last_c == 0 and d - last_d > timedelta(days=1): # we were out of all the intervals
... gaps.append({'startdate': to_string(last_d+timedelta(days=1)), 'endate': to_string(d+timedelta(days=-1))})
... last_c, last_d = c, d
...
我添加了一个测试来消除空隙(d - last_d > timedelta(days=1))。要获得您想要的演示文稿:
>>> {str(i): d for i, d in enumerate(gaps)}
{'0': {'startdate': '2015/09/01', 'endate': '2016/12/31'}, '1': {'startdate': '2018/11/01', 'endate': '2018/12/31'}}
代码:
from datetime import datetime, timedelta
def to_date(s): return datetime.strptime(s, r'%Y/%m/%d')
def to_string(d): return datetime.strftime(d, r'%Y/%m/%d')
def find_gaps(dates_by_num):
dates = sorted(d_s for _, v in dates_by_num.items() for d_s in ((to_date(v['startdate']), 1), (to_date(v['enddate']), -1)))
gaps = []
last_c, last_d = 1, 0
c = 0
for d, s in dates:
c += s
assert c >= 0 # c is the number of interval we are in
if last_c == 0 and d - last_d > timedelta(days=1): # we were not in any interval
gaps.append({'startdate': to_string(last_d+timedelta(days=1)), 'endate': to_string(d+timedelta(days=-1))})
last_c, last_d = c, d
return {str(i): d for i, d in enumerate(gaps)}
例子:
>>> find_gaps({'0': {'enddate': '2018/08/31', 'startdate':'2015/01/01'}, '1': {'enddate': '2018/10/31', 'startdate': '2017/01/01'}, '2': {'enddate': '2019/03/29', 'startdate': '2019/01/01'}})
{'0': {'startdate': '2018/11/01', 'endate': '2018/12/31'}}
>>> find_gaps({'0': {'enddate': '2016/12/31', 'startdate':'2015/01/01'}, '1': {'enddate': '2018/10/31', 'startdate': '2017/01/01'}, '2': {'enddate': '2019/03/29', 'startdate': '2019/01/01'}})
{'0': {'startdate': '2018/11/01', 'endate': '2018/12/31'}}
>>> find_gaps({'0': {'enddate': '2016/08/31', 'startdate':'2015/01/01'}, '1': {'enddate': '2019/10/31', 'startdate': '2017/01/01'}, '2': {'enddate': '2019/03/29', 'startdate': '2019/01/01'}})
{'0': {'startdate': '2016/09/01', 'endate': '2016/12/31'}}