I think Sharanya's answer is probably the most elegant solution, although I'll share what I managed to come up with myself.
The basic logic was to create a temporary working array to hold the ranges of dates with a start and end date plus the time of the performance. I then looped through the array of dates and added to the array when the end date wasn't 1 day less than the current date or the time didn't match.
$dates = array("2014-01-20 20:00:00","2014-01-21 20:00:00","2014-01-22 14:00:00","2014-01-22 20:00:00","2014-01-23 20:00:00","2014-01-25 20:00:00","2014-01-26 20:00:00","2014-01-31 20:00:00","2014-02-01 20:00:00");
$workingarray = array('start' => array(), 'end' => array(), 'time' => array());
// Loop through the results
foreach($dates as $date) {
// Check to see if there's a match at this time already
$key = reverse_search(date("ga", strtotime($date)), $workingarray['time']);
if($key===false) {
$workingarray['start'][] = $date;
$workingarray['end'][] = $date;
$workingarray['time'][] = date("ga", strtotime($date));
} else {
// Check to see if the end date of this performance is exactly one day less
if($workingarray['end'][$key]==date("Y-m-d H:i:s", strtotime('-1 day', strtotime($date)))) {
$workingarray['end'][$key] = $date;
} else {
$workingarray['start'][] = $date;
$workingarray['end'][] = $date;
$workingarray['time'][] = date("ga", strtotime($date));
}
}
}
// All the ranges are now in the $workingarray array, so just need to assemble them
$datesarray = array();
for($n = 0; $n <= count($workingarray); $n++) {
// Handle situations where range spans different months
if(date("m", strtotime($workingarray['start'][$n]))==date("m", strtotime($workingarray['end'][$n]))) {
$startstr = date("jS - ", strtotime($workingarray['start'][$n]));
} else {
$startstr = date("jS M - ", strtotime($workingarray['start'][$n]));
}
// Handle situations where performance is only on one day
if($workingarray['start'][$n]==$workingarray['end'][$n]) {
$startstr = "";
}
$endstr = date("jS M ", strtotime($workingarray['end'][$n]));
$timestr = $workingarray['time'][$n];
$datesarray[] = $startstr.$endstr.$timestr;
}
// Glue the array together as one string
$datestring = implode(", ", $datesarray);
echo $datestring;
// Function to find last matching key as array_search will only find the first one
function reverse_search($value, $array) {
$found = FALSE;
foreach($array as $key => $item) {
if($item==$value) {
$found = $key;
}
}
return $found;
}