0

I'm trying to see if a datetime is within a range of another datetime.

Essentially, I have two lists. One containing times of potential events, and one containing times when people are unavailable. I want to cross reference them to check that the event(s) will not run when people are unavailable.

I have this function at the minute, but having some trouble actually the configuration of the if statements working, as I'm getting them wrong and having dates wrongly removed.

def check_times(event_times, unavailable_times):

    for event_time in event_times:
        for unavailable_time in unavailable_times:
            if (event_time[0] >= unavailable_time[1]) and (event_time[1] >= unavailable_time[2])
                event_times.remove(event_time)
                break

With regard to below, unavailable_times[1] refers to the beginning of the time where the person is unavailable and unavailable_times[2] is when that time ends. event_times[0] is the beginning of the event and event_times[1] is the end time of the event.

Essentially, say I want a meeting to run from 13:30 - 15:00, but person X is busy from 14:15 - 14:40. I then would want to disregard the 13:30 - 15:00 period. I'm just having a bit of a hard time configuring the if statements to reflect this.

101892781
  • 79
  • 8
  • 1
    you can convert the `datetime's` to `timestamp` which would return a float then use a forloop to check which is within range – tushortz Oct 19 '20 at 20:35
  • Thanks, I'll have a look into this now :) – 101892781 Oct 19 '20 at 20:36
  • You can keep the times as `datetime` objects, but your algorithm needs to be changed. I'll put an answer together. – Ken Oct 19 '20 at 20:37
  • also this [link](https://stackoverflow.com/questions/9637838/convert-string-date-to-timestamp-in-python) might be helpful – tushortz Oct 19 '20 at 20:46

2 Answers2

3

First, I noticed a couple typos in your code. You are referring to event_times and unavailable_times in your if conditional statement when you should be referring to event_time and unavailable_time (no trailing s).

Second, the algorithm doesn't capture all of the cases in which the event_time overlaps the given unavailable_time. They are best visualized as follows, where E--E represents the event interval and U--U represents the unavailable interval.

# Case 1:
   E---------E
        U---------U
# Case 2:
               E---------E
        U---------U
# Case 3:
   E---------------------E
        U---------U
# Case 4:
           E---E
        U---------U

These cases are fully captured by the following conditional statements:

  1. (event_time[0] < unavailable_time[1]) and (event_time[1] > unavailable_time[1])
  2. (event_time[0] > unavailable_time[1]) and (event_time[0] < unavailable_time[2])

Edit: So your final function could be:

def check_times(event_times, unavailable_times):

    for event_time in event_times:
        for unavailable_time in unavailable_times:
            if ((event_time[0] < unavailable_time[1]) and (event_time[1] > unavailable_time[1])) or\
               ((event_time[0] > unavailable_time[1]) and (event_time[0] < unavailable_time[2])):
                event_times.remove(event_time)
                break
Ken
  • 443
  • 4
  • 8
  • Thanks Ken, the variables were just a typo from typing it up, as I edited some variable names to be more legible. I appreciate all the cases. :) – 101892781 Oct 19 '20 at 20:48
  • Hi Ken, just had a few follow up questions. Using your latest version I still have some issues. Datetimes where the event starts before the person is unavailable and ends after they are available again still slips through, as well as where the event starts at the same time they are unavailable. For example, if an event was to run from 11:30 - 13:30 but the person is unavailable from 12:30 - 13:00. – 101892781 Oct 20 '20 at 10:31
  • @101892781 The case you mention (event time of 11:30-13:30 and unavailable time of 12:30-13:00) is handled by the first conditional statement in my solution function. I tested it using the following definitions for the data, and the event was removed: `event_times = [(datetime(2020,10,20,11,30), datetime(2020,10,20,13,30))]` and `unavailable_times = [("First", datetime(2020,10,20,12,30), datetime(2020,10,20,13,0))]` – Ken Oct 20 '20 at 12:54
  • Hmm I'll have another look and see why it's slipping through, thanks. – 101892781 Oct 20 '20 at 13:12
  • Hi again. Weirdly, sometimes it gets removed and others it doesn't. For example, one sample output `Removing the event of 2020-10-13 11:00:00 to 2020-10-13 13:00:00 because someone is unavailable between 2020-10-13 12:30:00 and 2020-10-13 13:00:00`, whilst a time beginning at :30:00 seems to slip through. For example, an event beginning at 11:30:00 and ending on 13:30:00 would get through; same if it began at 12:30:00 and ended at 14:30:00. My function is exactly as you have it above. – 101892781 Oct 20 '20 at 15:23
  • Was too late to add this onto my previous message to say using the example you gave above alone it will remove that from the list, but it does not when it is in a larger dataset. Could be an issue with how it the data structure is made in the code, but from initial inspections it seems fine. – 101892781 Oct 20 '20 at 15:33
  • This can happen if there are duplicate values in the `event_times` list, since `.remove()` only removes the **first** element in the list that matches the argument. – Ken Oct 20 '20 at 15:37
  • Thank you. I think the issue also potentially stemmed from the fact I was attempting to iterate over the initial list without making a copy. I believe it is working properly now. I really appreciate your help! – 101892781 Oct 20 '20 at 15:47
0

I think the easiest way to go about it is to assume that for the event to overlap with an unavailable time, then the end of the event must be after the start of the unavailable time. then you only have to check that the start of the event isn't after the unavailable time. Something like this:

def check_times(event_times, unavailable_times):

    for event_time in event_times:
        for unavailable_time in unavailable_times:
            if (event_time[1] >= unavailable_time[1]) and !(event_time[0] >= unavailable_time[2])
                event_times.remove(event_time)
                break
Doragon
  • 199
  • 1
  • 10