查找数据数组中的下一个可用时间段

I have an array with an 'events' array inside of it that contains an array of events.

$data = array(
'events'=> array(
    array(
    'title' => 't1',
    'start_time' => '12:00AM',
    'end_time' => '1:00AM',
    ),
        array(
    'title' => 't1',
    'start_time' => '1:00PM',
    'end_time' => '1:30PM',
    ),
        array(
    'title' => 't1',
    'start_time' => '1:30PM',
    'end_time' => '2:00PM',
    ),
        array(
    'title' => 't1',
    'start_time' => '3:15PM',
    'end_time' => '3:45PM',
    ),
        array(
    'title' => 't1',
    'start_time' => '3:45PM',
    'end_time' => '4:45PM',
    )
    )
);

Based on the current time, i'm trying to find the next available time slot of at least 15 minutes. So say its currently 1:15PM, the next available time slot would be 2:00PM (based on the supplied array of data). This seems easy enough but i'm stumped.

The code below is where i'm at. It will return the title if there is an event taking place at the current time but i'm not really sure how to get the next time slot that is not being taken.

$time = time();
$timePlus15 = time() + 60*15;

foreach ($events->events as $key => $event) {
    $start_time = strtotime($event->start_time);
    $end_time = strtotime($event->end_time);

        if (($time > $start_time && $timePlus15 < $end_time) || $time > $start_time && $time < $end_time || ($time < $start_time && $timePlus15 > $start_time && $timePlus15 < $end_time)) {
            echo $event->title;
    }
}

Basically, I want to loop through the array. If at the current time there is an event, increment the time by 15 minutes and check again. If there is an event, increment 15 minutes and check again, and repeat until there is no event for the time, then return that time.

the code is in php but i'll accept a javascript/jquery answer as well

** THE SOLUTION TO MY PROBLEM IS BELOW **

this.setNextAvailableTime = function() {
    var requiredGap = 15 * 60 * 1000;
    var events = app.rooms.events[app.rooms.current_room].events;
    var prev = events[0];
    var firstGap = null;
    if (events.length === 1) {
        firstGap = prev.end_24time;
    } else {
        for (var i = 1; i < events.length; i += 1) {

            var current = events[i];

            var current_start = new Date();
            var prev_end = new Date();
            current_start.setHours(events[i].start_24time.substring(0,2),events[i].start_24time.substring(3,5), 0);
            prev_end.setHours(prev.end_24time.substring(0,2),prev.end_24time.substring(3,5), 0);

            var diff = current_start.getTime() - prev_end.getTime();

            if( diff >= requiredGap) {
                firstGap = prev.end_24time;
                break;
            }

            prev = current;
        }           
    }
    //do something with the firstGap time
    $('#start_time').val(firstGap);
};

It's probably easier to see what the difference between the end of the first event, and the start of the next event is. Here's an example in javascript:

(note that the string to date conversion isn't very solid, it's just some test data)

var events = [{
  start: "2016-01-01T12:00",
  end: "2016-01-01T13:00"
}, {
  start: "2016-01-01T13:00",
  end: "2016-01-01T13:30"
}, {
  start: "2016-01-01T13:30",
  end: "2016-01-01T14:00"
}, {
  start: "2016-01-01T15:15",
  end: "2016-01-01T15:45"
}, {
  start: "2016-01-01T15:45",
  end: "2016-01-01T16:45"
}];

var dateEvents = events.map(function(event) {
  return {
    start: new Date(event.start),
    end: new Date(event.end)
  };
});

var requiredGap = 15 * 60 * 1000;
var prev = dateEvents[0];
var firstGap = null;

for (var i = 1; i < dateEvents.length; i += 1) {
  var current = dateEvents[i];
  var diff = current.start - prev.end;
  
  if (diff >= requiredGap) {
    firstGap = {
      start: prev.end,
      end: current.start
    };
    break;
  }
  
  prev = current;
}

if (firstGap != null) {
  console.log("First gap starts at: " + firstGap.start); 
} else {
  console.log("No gaps available");
}

</div>

Javascript solution. Run in es6fiddle: http://www.es6fiddle.net/irz39uss/

Converting from strings to minutes and vice versa is most of the actual work. After that, it is simply creating an array that, instead of times taken with events, represents times available between events. I'm using a slightly cheating version of map for this (cheating because I forward reference an element, which is not a real functional approach to this, but hey, it's JS). Next, find the first one where the start time is equal to or after the current time, and where there are at least 15 minutes available.

Simple conceptually, just cumbersome to do the string to time conversions. This works midnight to midnight, not across days. I used your hard-to-work-with time strings instead of the other solution, which is not what you posted, so consider an "accepted" for this since it works with your actual input ;)

let eventsArr = [{title: 't1', start_time: '1:00PM', end_time: '1:30PM'},
                 {title: 't1', start_time: '1:30PM', end_time: '2:00PM'},
                 {title: 't1', start_time: '3:15PM', end_time: '3:45PM'}]

function strTimeToMins(c) {
    let rex = /([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])([P,p,A,a])/
    let mat = c.match(rex)
    return 60*mat[1] + 1*mat[2] + ((mat[3] === 'P') ? 720 : 0)
}

function minsToStrTime(m) {
    let hr = Math.floor(m/60)
    let min = m%60
    let hours = (hr > 12 ? hr-12 : hr)
    return hours.toString() + ':' + ("00" + min).slice(-2) + (hr > 12 ? 'PM' : 'AM')
}

function objTimeToMins(o) {
    return { title: o.title,
             start_time: strTimeToMins(o.start_time),
             end_time: strTimeToMins(o.end_time) }
    }

let eventsArrMins = eventsArr.map(objTimeToMins)

let openTimes = eventsArrMins.map( (c,i,a) => ( (i+1 != a.length) ?
    { start: c.end_time, end: a[i+1].start_time } : {} ))

//let currTime = strTimeToMins ('1:15PM')
let currTime = Math.floor((new Date() - new Date().setHours(0,0,0,0))/1000)

let nextAvailable = openTimes.find(c => currTime <= c.start && c.end - c.start >= 15)

if (nextAvailable)
    console.log('Next available time is: ' + minsToStrTime(nextAvailable.start))
else
    console.log('There are no available times.')

I transformed the time from AM/PM to 24 hours and the software finds the nearest 15 minute available timeslot.

// LIST
var t1 = [{
    start_time:"00:00", 
    end_time:"01:00" 
    },{ 
    start_time:"13:00", 
    end_time:"13:30" 
    },{
    start_time:"13:30", 
    end_time:"14:00" 
    },{
    start_time:"15:15", 
    end_time:"15:45" 
    },{ 
    start_time:"15:45", 
    end_time:"16:45" 
    },{ 
    start_time:"17:45", 
    end_time:"18:00" 
    },{ 
    start_time:"20:00", 
    end_time:"20:15" 
    },{ 
    start_time:"21:20", 
    end_time:"21:30" 
    }];

// VARIABLES
var gap = 15;
var currentTime = new Date();
var currentHour = currentTime.getHours();
var currentMinute = currentTime.getMinutes();
while (currentMinute % 5 !== 0)
{
    AddMinutes(1);
}
var lowestCounter = 0;
var tempStart =t1[lowestCounter].start_time;
var tempEnd = t1[lowestCounter].end_time;
var tempSlotStart = FormattedTime(currentHour) + ":" + FormattedTime(currentMinute);
var tempSlotEnd = TimeInMinutes(currentHour, currentMinute) + gap;
tempSlotEnd = MinutesInTime(tempSlotEnd);


// 1 - START - Skip the past Events
while (tempStart <= tempSlotStart && tempEnd <= tempSlotStart && lowestCounter < t1.length){
    lowestCounter++;    
    if (lowestCounter < t1.length){
        tempStart =t1[lowestCounter].start_time;
        tempEnd = t1[lowestCounter].end_time;
    }
}
// 1 - END


// 2 - START - Check for the first available gap
if (lowestCounter < t1.length){
    tempStart =t1[lowestCounter].start_time;
    tempEnd = t1[lowestCounter].end_time;
    lowestCounter--;
}

while ((tempStart < tempSlotStart && tempEnd > tempSlotStart) || (tempStart < tempSlotEnd && tempEnd > tempSlotEnd) && lowestCounter < t1.length){
    lowestCounter++;
    if (lowestCounter < t1.length){
        tempStart =t1[lowestCounter].start_time;
        tempEnd = t1[lowestCounter].end_time;
    tempSlotStart = tempEnd;
    tempSlotEnd = TimeInMinutes(Number(tempSlotStart.split(":")[0]),Number(tempSlotStart.split(":")[1]));
    tempSlotEnd += gap;
    tempSlotEnd = MinutesInTime(tempSlotEnd);
  }
}
// 2 - END


// 3 - START - Print
console.log("First Available Schedule: " + tempSlotStart + " -> " + tempSlotEnd);
// 3 - END


// ASSETS - START
function AddMinutes(x){
  currentMinute += x;
  if (currentMinute >= 60){
    currentMinute %= 60;
    currentHour++;
  }
  if (currentHour >= 24){
    currentHour %= 24;
  }
}

function TimeInMinutes(hours,minutes){
    return hours * 60 + minutes;
}

function MinutesInTime(minutes){
    return FormattedTime(Math.floor(minutes/60) % 24) + ":" + FormattedTime(minutes%60);
}

function FormattedTime(x){
    if (x < 10){
    return "0" + x;
  }
  return x;
}
// ASSETS - END

</div>