This script checks if a 60 min slot is available within a time range taking into account already booked slots. It works fine for the first already booked slot but does not take into account the second. Also if I change the first booked slot to 12:10 it will not detect it.
Basically, the idea is if there is a 60 min slot between the range and the already booked slots to show it.
For example,, if we have a range 9:00 to 15:00 and we have two booked slots from 11:10 and 14:00, to return available slots from 9:00, 10:00 and 12:10
$start_time = '2022-10-21 09:00:00';  //start time as string
$end_time = '2022-10-21 15:00:00';  //end time as string
$booked = ['2022-10-21 12:00:00','2022-10-21 13:00:00']; //booked slots as arrays
$start = DateTime::createFromFormat('Y-m-d H:i:s',$start_time); //create date time objects
$end = DateTime::createFromFormat('Y-m-d H:i:s',$end_time);  //create date time objects
$time1 = $start;
$count = 0;  //number of slots
$out = array();   //array of slots 
for($i = $start; $i<$end;)  //for loop 
{
    $avoid = false; 
    $t1 = date_timestamp_get($i);
    $t2 = $t1+(60*60);
    for($k=0;$k<sizeof($booked);$k+=2)  //if booked hour
    {
        $st = DateTime::createFromFormat('Y-m-d H:i:s',$booked[$k]);
        $en = DateTime::createFromFormat('Y-m-d H:i:s',$booked[$k+1]);
          
        if( $t1 >= date_timestamp_get($st) && $t2 <= date_timestamp_get($en)  )
        $avoid = true;   //yes. booked
    }
    $slots =[ $i->format('H:i'),$i->modify("+60 minutes")->format('H:i')];
    if(!$avoid && $i<$end)  //if not booked and less than end time
    {
        $count++;  
        array_push($out,$slots);  //add slot to array
    }
}
var_dump($out);   //array out
Here is link to sandbox https://onlinephp.io/c/b0b77
Additional information
Let's say that we have a free time frame from 10:00 until 13:00. What the script should return is 10:00, 11:00, and 12:00 as possibilities if the time slot is 60 min.
If the timeslot is 45 min. for example, it should return 10:00, 10:45, 11:30, and 12:15.
So, the possible variants should start from the first possible option increasing with the time slot (45, 60, or whatever time slot is set)
The slot cannot start from 10:12, 10:44, or another custom time
The Solutions
Both solutions below by Salman A and Jim are working as expected. Just use a little bit of a different approach.
Overlapping bookings - Update provided
If you have overlapping bookings as suggested by Salman A we have to first merge them and then send unique intervals only.
I did it with that function
function mergeBookings($ranges)
{
    $retVal = [];
    //sort date ranges by begin time
    usort($ranges, function ($a, $b) {
        return strcmp($a['start'], $b['start']);
    });
    $currentRange = [];
    foreach ($ranges as $range) {
        // bypass invalid value
        if ($range['start'] >= $range['end']) {
            continue;
        }
        //fill in the first element
        if (empty($currentRange)) {
            $currentRange = $range;
            continue;
        }
        if ($currentRange['end'] < $range['start']) {
            $retVal[] = $currentRange;
            $currentRange = $range;
        } elseif ($currentRange['end'] < $range['end']) {
            $currentRange ['end'] = $range['end'];
        }
    }
    if ($currentRange) {
        $retVal[] = $currentRange;
    }
    return $retVal;
}
Then just use the function before sending the $booked array to the script. Like this
$booked = mergeBookings($booked);