如何有效地检查两个时间范围是否重叠 (Python)

How to check if two time ranges are overlapping in an efficient manner (Python)

我有一个包含一组特定时间范围的列表:

['ARTS  111  A', 'M', '09:00 - 12:00', 'W', '09:00 - 12:00', 'F', '02:00 - 12:00', 'COMP 111  A', 'M', '09:00 - 12:00', 'W', '09:00 - 12:00']

现在,我希望将这些范围与另一组日期和日期范围进行比较:

['COMP 200 A', 'M', '09:30 - 11:30', 'W', '09:00 - 12:00']

如果有帮助,这里是字典的原始版本:

dict = {
    'ARTS  111  A': {'course_code': 'ARTS  111  A', 'course_final': 'Fundam. of Drawing I', 'course_credits': '3 ',
                      'course_type': 'SBLOCK', 'course_room': 'S008', 'start_date': 'start:03/07/2022', 'days': 'M W',
                      'time': '09:00 - 12:00', 'instructor': 'John', 'capacity': '20', 'enrolled': '15', 'days_2': 'F',
                      'time_2': '02:00 - 12:00'
                     },
    'COMP  111  A': {'course_code': 'COMP 111  A', 'course_final': 'Fundam. of Drawing I', 'course_credits': '3 ',
                      'course_type': 'SBLOCK', 'course_room': 'S008', 'start_date': 'start:03/07/2022', 'days': 'M W',
                      'time': '09:00 - 12:00', 'instructor': 'John', 'capacity': '20', 'enrolled': '15'}
}

clash = {

'SCI 100  A': {'course_code': 'SCI  100  A', 'course_final': 'Fundam. of Drawing I', 'course_credits': '3 ',
                      'course_type': 'SBLOCK', 'course_room': 'S008', 'start_date': 'start:03/07/2022', 'days': 'M W',
                      'time': '09:00 - 12:00', 'instructor': 'John', 'capacity': '20', 'enrolled': '15'}
}

我构建了以下函数,以便 'clean' 产生上述输出:

def date_time(dict):
    lst = []
    for x in dict:
        lst.append(dict[x]['course_code'])
        days = dict[x]['days']
        days = days.split()
        for y in days:
            lst.append(y)
            lst.append(dict[x]['time'])
        return lst

显然,仅仅将这两者等同起来是行不通的。我正在创建一个时间表,用于检查预先存在的 class 之间的冲突,并将它们与用户希望 select 的新 class 进行比较。 希望对我需要做的事情提供一些帮助!

所以在工作和睡眠之后 我已经设法为您提供了解决方案 有一个函数可以将给定的列表分解为舒适的字典 然后是一个检查它们是否重叠的函数

exisitng_classes=['ARTS  111  A', 'M', '09:00 - 12:00', 'W', '09:00 - 12:00', 'F',
                  '02:00 - 12:00', 'COMP 111  A', 'M', '09:00 - 12:00', 'W', '09:00 - 12:00']

proposed_class=['COMP 200 A', 'M', '09:30 - 11:30', 'W', '09:00 - 12:00']

def check_time_overlap(time1,time2):
    """
    returns boolean whether these times overlap or not, True if overlap
    """
    split=time1.split("-")
    min1=int(split[0].replace(":",""))
    max1=int(split[-1].replace(":",""))
    split=time2.split("-")
    min2=int(split[0].replace(":",""))
    max2=int(split[-1].replace(":",""))

    return not ((max2 - min1) * (min2 - max1) >= 0)
      
    
def where_overlapping(existing,proposed):
    """
    This function checks whether or not
    and where the classes are overlapping

    gets 1 list of all lessons and their hours
    and 1 list of new single proposed lesson and its hours

    returns a list of all overlaps, if empty = no overlaps
    """
    name_of_class=""
    sliced=existing[0].split(" ")[1:]
    for item in sliced[:-1]:
        name_of_class+=item+" "
    name_of_class+=sliced[-1]
    
    indexes=[]
    counter=0
    for each in existing:
        
        if name_of_class in each:
            indexes.append((counter,each))

        counter+=1


        
    class_dictionary={}
    index=0
    while index<len(indexes)-1:
        class_dictionary[indexes[index][1]]=existing[indexes[index][0]+1:indexes[index+1][0]]
        index+=1
   
    class_dictionary[indexes[-1][1]]=existing[indexes[-1][0]+1:]

    overlaps=[]

    

    for classroom in class_dictionary:
        prop_counter=0
        while prop_counter<len(proposed[1:]):
            counter=0
            while counter<len(class_dictionary[classroom]):
                if class_dictionary[classroom][counter]==proposed[1:][prop_counter]:

                    if check_time_overlap(class_dictionary[classroom][counter+1],proposed[1:][prop_counter+1]):

                        
                        overlaps.append(classroom+" of "+ class_dictionary[classroom][counter+1]
                                        +" with "+proposed[0]+" at "+proposed[1:][prop_counter+1]+" at day "
                                        +proposed[1:][prop_counter])
                    
                counter+=2
            prop_counter+=2
            
    return overlaps
            
            
if __name__ == "__main__":

    for x in where_overlapping(exisitng_classes,proposed_class):
        print(x)
    print(check_time_overlap("12:00 - 14:00","13:00-17:00"))
    print(check_time_overlap("12:00 - 14:00","15:00-17:00"))
    print(check_time_overlap("12:00 - 14:00","12:00-14:00"))
    print(check_time_overlap("12:00 - 14:00","11:00-15:00"))
    print(check_time_overlap("12:00 - 14:00","10:00-16:00"))
            
import datetime
import re
from dataclasses import dataclass, field

raw_data = ['ARTS  111  A', 'M', '09:00 - 12:00', 'W', '09:00 - 12:00', 'F', '02:00 - 12:00',
            'COMP 111  A', 'M', '09:00 - 12:00', 'W', '09:00 - 12:00',
            'COMP 200 A', 'M', '09:30 - 11:30', 'W', '09:00 - 12:00']

# the data is not structured, so let's parse it !

days_letters = ('M', 'T', 'W', 'H', 'F')  # 'H' used for tHursday
timerange_pattern = re.compile(r"(\d\d):(\d\d) - (\d\d):(\d\d)")
#                        group(1) ^^^^  2^^^^    3^^^^  4^^^^
coursename_pattern = re.compile(r"(\w+\s+\d+\s+\w)")
#                         group(1) ^^^^^^^^^^^^^^


@dataclass
class Course:
    name: str
    slots: list = field(default_factory=list)


@dataclass
class CourseSlot:
    day: str
    time_start: datetime.time
    time_end: datetime.time


tokens = list(raw_data)
courses = []
while tokens:
    token = tokens.pop(0)  # get the first string

    course_name_match = coursename_pattern.fullmatch(token)
    assert course_name_match is not None
    course_name = course_name_match.group(1)

    course = Course(name=course_name)

    # then read the days and hours
    while tokens:
        if tokens[0] in days_letters:
            day = tokens.pop(0)
            timerange_match = timerange_pattern.fullmatch(tokens.pop(0))
            assert timerange_match is not None

            start_hour = int(timerange_match.group(1))
            start_minute = int(timerange_match.group(2))
            end_hour = int(timerange_match.group(3))
            end_minute = int(timerange_match.group(4))

            course.slots.append(CourseSlot(
                day=day,
                time_start=datetime.time(hour=start_hour, minute=start_minute),
                time_end=datetime.time(hour=end_hour, minute=end_minute),
            ))

        else:
            break
    courses.append(course)

print(courses)
# [Course(name='ARTS  111  A', slots=[CourseSlot(day='M', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
#                                     CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
#                                     CourseSlot(day='F', time_start=datetime.time(2, 0), time_end=datetime.time(12, 0))]),
#  Course(name='COMP 111  A', slots=[CourseSlot(day='M', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
#                                    CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0))]),
#  Course(name='COMP 200 A', slots=[CourseSlot(day='M', time_start=datetime.time(9, 30), time_end=datetime.time(11, 30)),
#                                   CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0))])]

@dataclass
class Clash:
    course1_name: str
    course2_name: str
    course1_slot: CourseSlot
    course2_slot: CourseSlot

# now search for overlap :
clashes = []
for i, first_course in enumerate(courses[:-1], start=1):  # from first to last-1
    for second_course in courses[i:]:  # from after the first to the last
        for first_slot in first_course.slots:
            for second_slot in second_course.slots:
                if first_slot.day == second_slot.day:
                    if first_slot.time_start <= second_slot.time_end and \
                            second_slot.time_start <= first_slot.time_end:  # see 
                        # we have a match !
                        clashes.append(Clash(course1_name=first_course.name,
                                             course2_name=second_course.name,
                                             course1_slot=first_slot,
                                             course2_slot=second_slot))

print(clashes)
# [
#   Clash(course1_name='ARTS  111  A',
#         course2_name='COMP 111  A',
#         course1_slot=CourseSlot(day='M', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
#         course2_slot=CourseSlot(day='M', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0))
#   ),
#   Clash(course1_name='ARTS  111  A',
#         course2_name='COMP 111  A',
#         course1_slot=CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
#         course2_slot=CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0))
#   ),
#   Clash(course1_name='ARTS  111  A',
#         course2_name='COMP 200 A',
#         course1_slot=CourseSlot(day='M', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
#         course2_slot=CourseSlot(day='M', time_start=datetime.time(9, 30), time_end=datetime.time(11, 30))
#   ),
#   Clash(course1_name='ARTS  111  A',
#         course2_name='COMP 200 A',
#         course1_slot=CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
#         course2_slot=CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0))
#   ),
#   Clash(course1_name='COMP 111  A',
#         course2_name='COMP 200 A',
#         course1_slot=CourseSlot(day='M', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
#         course2_slot=CourseSlot(day='M', time_start=datetime.time(9, 30), time_end=datetime.time(11, 30))
#   ),
#   Clash(course1_name='COMP 111  A',
#         course2_name='COMP 200 A',
#         course1_slot=CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0)),
#         course2_slot=CourseSlot(day='W', time_start=datetime.time(9, 0), time_end=datetime.time(12, 0))
#   )
# ]