Handling date rollover in same function I have a program that specifies when an alarm should be active (say, 10AM to 8PM). Part of my program then checks if the alarm is active right now. The "is it active" checker needs to deal with alarm ranges that cross the midnight boundary into tomorrow (say, 8PM to 10AM alarm).

I am currently handling this with redundant functions that are identical, except for AND vs OR comparison. What is the better way, please? (Of course, any other review comments are also most welcome.)

import datetime as dt

def check_alarm_bounds_on_same_date(time_now, time_of_day_bounds):
    time_start_of_current_day = dt.datetime.combine(time_now.date(), dt.datetime.min.time())
    if ( time_now &gt;= (time_start_of_current_day + dt.timedelta(hours=time_of_day_bounds[0])) and #&lt;&lt; AND
         time_now &lt; (time_start_of_current_day + dt.timedelta(hours=time_of_day_bounds[1])) ):
        return True
    return False

def check_alarm_bounds_on_different_dates(time_now, time_of_day_bounds):
    time_start_of_current_day = dt.datetime.combine(time_now.date(), dt.datetime.min.time())
    if ( time_now &gt;= (time_start_of_current_day + dt.timedelta(hours=time_of_day_bounds[0])) or #&lt;&lt; OR
         time_now &lt; (time_start_of_current_day + dt.timedelta(hours=time_of_day_bounds[1])) ):
        return True
    return False

def is_alarm_live(time_current, time_of_day_bounds):
    alarm_start_time = time_of_day_bounds[0]
    alarm_end_time = time_of_day_bounds[1]
    if alarm_end_time &lt;= alarm_start_time:
        return check_alarm_bounds_on_different_dates(time_current, time_of_day_bounds)
    else:
        return check_alarm_bounds_on_same_date(time_current, time_of_day_bounds)

# specify two alarm periods (#1 @ 10AM-8PM; #2 @ 8PM-10AM next day)
alarms = [(10,20),(20,10)]
for hour in range(0,24):
    # test all hours in a sample day, and output which alarm is active
    print 'hour %i: ' % hour,
    time_test = dt.datetime(2017,01,22,hour,10,30)
    for alarm in alarms:
        print is_alarm_live(time_test, alarm),
    print ''
Very quick review.


No docstrings.
The code uses datetime.datetime.combine to make datetime.datetime objects representing the bounds, and then compares these with time_current. But it would be simpler to make datetime.time objects to represent the bounds, and then compare those with time_current.time.
Having done that, it would be simpler still if the function took a datetime.time object as its first argument, leaving it up to the caller to pass time_current.time if that's what's needed.
There's nothing about this code that is specific to alarms (it could be used for appointments, for example), so it would be easier to understand if the functionality were expressed abstractly.


Revised code:

import datetime

def time_in_range(time, start_hour, end_hour):
    """Return True if time (a datetime.time object) lies between
    start_hour and end_hour (inclusive).

    &gt;&gt;&gt; time_in_range(datetime.time(hour=12), 10, 14)
    True

    The range is allowed to span midnight:

    &gt;&gt;&gt; time_in_range(datetime.time(hour=0), 22, 2)
    True

    """
    start = datetime.time(hour=start_hour)
    end = datetime.time(hour=end_hour)
    if end &lt;= start: # Range spans midnight.
        return start &lt;= time or time &lt;= end
    else:
        return start &lt;= time &lt;= end