3

I fetch appointments via Exchange EWS API and create json arrays containing start/end busy (datetime) periods. I want to reverse this data to fetch a list of free time periods per day.

Office hours start is 8:00 a.m. Office hours end is 6:00 p.m.

Finally I found the solution for the arithmetic problem of how to calculate free time periods out of tuples of busy time periods. Below a simplified code how I did it.

from datetime import datetime, timedelta
import pprint
pp = pprint.PrettyPrinter()

tstart = datetime.strptime('08:00:00', '%H:%M:%S')
tstop = datetime.strptime('18:00:00', '%H:%M:%S')

appointments = ([
    {
        'start' : datetime.strptime('08:30:00', '%H:%M:%S'),
        'end' : datetime.strptime('10:00:00', '%H:%M:%S')
    },
    {
        'start' : datetime.strptime('09:30:00', '%H:%M:%S'),
        'end' : datetime.strptime('11:00:00', '%H:%M:%S')
    },
    {
        'start' : datetime.strptime('12:30:00', '%H:%M:%S'),
        'end' : datetime.strptime('14:00:00', '%H:%M:%S')
    },
    {
        'start' : datetime.strptime('15:30:00', '%H:%M:%S'),
        'end' : datetime.strptime('16:00:00', '%H:%M:%S')
    },
    {
        'start' : datetime.strptime('16:00:00', '%H:%M:%S'),
        'end' : datetime.strptime('17:00:00', '%H:%M:%S')
    },
    ])

"""
CORRECT FREE PERIODS ARE:
8:00 - 8:30
11:00 - 12:30
14:00 - 15:30
17:00 - 18:00
"""

tp = [(tstart , tstart)]
free_time = []
for t in appointments:
    tp.append( ( t['start'] , t['end'] ) )
tp.append( (tstop , tstop) )

for i,v in enumerate(tp):
    if i > 0:
        if (tp[i][0] - tp[i-1][1]) > timedelta(seconds=0):
            tf_start = tp[i-1][1]
            delta = tp[i][0] - tp[i-1][1]
            tf_end = tf_start + delta
            free_time.append( (tf_start ,tf_end ) )

for tp in free_time:
    print tp
NoKnow
  • 31
  • 4
  • "Following code fails on the if clauses" Can you provide us a [mcve]? – TemporalWolf Mar 03 '17 at 21:41
  • 1
    Beside the (very good) suggestion from @TemporalWolf to write a [MCVE], you also need to describe your errors in as much detail as you can - what happens, what you expect to happen and how they differ. – pvg Mar 03 '17 at 21:51
  • Thanks for your suggestions, now I posted a minimal, *in*complete but verifiable example. Unfortunately I cannot give a complete working example because that is where my question comes from. The error is clear when you run the code above - the if clauses are wrong and incomplete, because too complex. I search for a working approach for the described goal. – NoKnow Mar 04 '17 at 02:05
  • Found the arithmetic solution for my task, maybe someone else can make use of it too.... – NoKnow Mar 04 '17 at 09:24

1 Answers1

1

Your code helped us build a project! Thank you. However, I found an edge case where the above algorithm falsely registers free times when there are overlapping events:

Ex: An event from 7-8 and an event from 7:30-9 will break it.

I added a little to your code to fix this issue. Here's what I used:

a_busy = [
    {'start': datetime.datetime(2020, 1, 1, 6), 'end': datetime.datetime(2020, 1, 1, 6, 30)},
    {'start': datetime.datetime(2020, 1, 1, 7), 'end': datetime.datetime(2020, 1, 1, 8)},
    {'start': datetime.datetime(2020, 1, 1, 7, 30), 'end': datetime.datetime(2020, 1, 1, 9, 30)},
    {'start': datetime.datetime(2020, 1, 1, 11), 'end': datetime.datetime(2020, 1, 1, 12, 30)},
    {'start': datetime.datetime(2020, 1, 1, 15), 'end': datetime.datetime(2020, 1, 1, 15, 45)},
    {'start': datetime.datetime(2020, 1, 1, 16, 45), 'end': datetime.datetime(2020, 1, 1, 17, 30)}
]

b_busy = [
    {'start': datetime.datetime(2020, 1, 1, 5, 45), 'end': datetime.datetime(2020, 1, 1, 6, 30)},
    {'start': datetime.datetime(2020, 1, 1, 7, 30), 'end': datetime.datetime(2020, 1, 1, 8)},
    {'start': datetime.datetime(2020, 1, 1, 8), 'end': datetime.datetime(2020, 1, 1, 9)},
    {'start': datetime.datetime(2020, 1, 1, 10, 30), 'end': datetime.datetime(2020, 1, 1, 13)},
    {'start': datetime.datetime(2020, 1, 1, 14), 'end': datetime.datetime(2020, 1, 1, 15)},
    {'start': datetime.datetime(2020, 1, 1, 15, 30), 'end': datetime.datetime(2020, 1, 1, 16, 30)}
]
# free times: 6:30-7, 9:30-10:30, 13:00-14:00, 16:30-16:45
tstart = datetime.datetime(2020, 1, 1, 6)
tstop = datetime.datetime(2020, 1, 1, 17)

together = sorted(a_busy + b_busy, key=lambda k: k['start'])
tp = [(tstart, tstart)]
free_time = []
for t in together:
    tp.append((t['start'], t['end']))
tp.append((tstop, tstop))

# This section added to resolve the case mentioned above
i = 1
while i < len(tp):
    if tp[i][0] < tp[i - 1][1]:
        start_times = [tp[i - 1][0], tp[i][0]]
        end_times = [tp[i - 1][1], tp[i][1]]
        tp[i - 1] = (min(start_times), max(end_times))
        tp.pop(i)
    else:
        i += 1

for i, v in enumerate(tp):
    if i > 0:
        if (tp[i][0] - tp[i - 1][1]) > datetime.timedelta(seconds=0):
            tf_start = tp[i - 1][1]
            delta = tp[i][0] - tp[i - 1][1]
            tf_end = tf_start + delta
            free_time.append(tup)
choskie
  • 71
  • 1
  • 5