Check whether a specified user is able to do a specified workshift.
    Parameters:
        workshift_profile is the workshift profile for a user
        shift is a weekly recurring workshift
    Returns:
        True if the user has enough free time between the shift's start time
            and end time to do the shift's required number of hours.
        False otherwise.

def is_available(workshift_profile, shift):
    """
    Check whether a specified user is able to do a specified workshift.
    Parameters:
        workshift_profile is the workshift profile for a user
        shift is a weekly recurring workshift
    Returns:
        True if the user has enough free time between the shift's start time
            and end time to do the shift's required number of hours.
        False otherwise.
    """
    if shift.week_long:
        return True

    start_time = (
        shift.start_time
        if shift.start_time is not None
        else time(hour=0)
    )
    end_time = (
        shift.end_time
        if shift.end_time is not None
        else time(hour=23, minute=59)
    )
    relevant_blocks = list()

    for block in workshift_profile.time_blocks.order_by('start_time'):
        if block.day == shift.day and block.preference == TimeBlock.BUSY \
           and block.start_time < end_time \
           and block.end_time > start_time:
            relevant_blocks.append(block)

    # Time blocks should be ordered; so go through and see if there is a wide
    # enough window for the workshifter to do the shift.  If there is,
    # return True.
    if not relevant_blocks:
        return True

    hours_delta = timedelta(hours=float(shift.hours))

    # Check the time between shift start and block start
    block = relevant_blocks.pop(0)
    start_delta = timedelta(
        hours=block.start_time.hour - start_time.hour,
        minutes=block.start_time.minute - start_time.minute,
    )

    if start_delta >= hours_delta:
        return True

    while len(relevant_blocks) > 0:
        block, prev_block = relevant_blocks.pop(0), block

        # Check the time between the last block and the next block
        # is larger than the length of the shift
        start_end_delta = timedelta(
            hours=block.start_time.hour - prev_block.end_time.hour,
            minutes=block.start_time.minute - prev_block.end_time.minute,
        )

        if start_end_delta >= hours_delta:
            return True

    # Check the time between the end of the time block to the end of the shift
    end_delta = timedelta(
        hours=end_time.hour - block.end_time.hour,
        minutes=end_time.minute - block.end_time.minute,
    )

    if end_delta >= hours_delta:
        return True

    return False