简体   繁体   中英

Turn a php array of dates into simple human-readable string

I have an array of performance dates for an event such as:

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

Is there a straightforward way in php to turn this into a human-readable string such as;

20th-23rd Jan 8pm, 22nd Jan 2pm, 25th-26th Jan 8pm, 31st Jan - 1st Feb 8pm

Note the array will always be in datetime order, although not necessarily contiguous. Matinee performances throw in an extra complication as they need to be dealt with separately.

We'll convert the strings to timestamps with array_map and strtotime and then group together the consecutive elements in the array which are in an Arithmetic Progression with common difference = 86400 (because 86400 seconds = 1 day):

$arr = 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"
);
$arr = array_map("strtotime", $arr);
$_arr = array();
$size = sizeof($arr);
$val = $arr[0];
for($i = 0; $i < $size; $i++){
    if(!array_key_exists($i+1, $arr) || $arr[$i+1]-$arr[$i] != 86400){
    $d1 = date("jS", $val);
    $d2 = date("jS", $arr[$i]);
    $m1 = date("M", $val);
    $m2 = date("M", $arr[$i]);
    $t = date("ga", $val);
        if($m1 == $m2){
            if($d1 == $d2){
            $_arr[] = $d1." ".$m1." ".$t;
            }
            else{
            $_arr[] = $d1."-".$d2." ".$m1." ".$t;
            }
        }
        else{
        $_arr[] = $d1." ".$m1."-".$d2." ".$m2." ".$t;
        }
        if(array_key_exists($i+1, $arr)){
        $val = $arr[$i+1];
        }
    }
}
print(implode(", ", $_arr));

The output will be:

20th-21st Jan 8pm, 22nd Jan 2pm, 22nd-23rd Jan 8pm, 25th-26th Jan 8pm, 31st Jan-1st Feb 8pm

See the code at work here .

You can loop through each line and create a new array of human-readable timestamps like so:

$new_array[] = date("D d M", strtotime($current_line));

Obviously changing the outputted format to the out of your choice ( http://www.php.net/manual/en/function.date.php )

Your expected output:

20th-23rd Jan 8pm, 22nd Jan 2pm, 25th-26th Jan 8pm, 31st Jan - 1st Feb 8pm

I think it is not possible seems that there are no sequences or no pattern in output. you should have pattern for logic.

Live Demo : https://eval.in/91823
Try this:

$str = "2014-01-21 20:00:00";
echo date("dS M g:i a", strtotime($str));

OUTPUT:

21st Jan 8:00 pm

For array : Demo : https://eval.in/91834

$arr = 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");
foreach($arr as $a )
{

echo date("dS M ga", strtotime($a))."\n";
}

OUTPUT:

20th Jan 8pm
21st Jan 8pm
22nd Jan 2pm
22nd Jan 8pm
23rd Jan 8pm
25th Jan 8pm
26th Jan 8pm
31st Jan 8pm
01st Feb 8pm

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;
}

I wrote a generic function that takes an array of date strings and convert them into a human readable date ranges. Note that it makes use of the DateTime class, which is available by default on PHP => 5.2.0.

First, create an array of timestamps using array_map() with strtotime as the callback function. Now loop through the result array and determine the end of the range. To do that, we use a while loop.

while (isset($tsArr[$i+1]) && ($tsArr[$i+1]-$tsArr[$i]) / (60*60) <= 24) {
    $rend = $tsArr[++$i];
}

We check if the numbers of hours between the current timestamp and next timestamp is greater than 24, and if so, update the $rend variable. On the last iteration, $rend will contain the timestamp of the end of the range.

Here's the full function:

function getDateRanges($dates) {        
    $tsArr = array_map('strtotime', $dates);
    $ranges = array();

    for ($i=0, $len = count($tsArr); $i < $len; $i++) {     

        $rstart = $tsArr[$i];
        $rend = $rstart;

        // determine the end of the range
        while (isset($tsArr[$i+1]) && ($tsArr[$i+1]-$tsArr[$i]) / (60*60) <= 24) {
            $rend = $tsArr[++$i];
        }

        // create DateTime objects to work with dates
        $startDate = new DateTime('@'.$rstart);
        $endDate = new DateTime('@'.$rend);

        // get date start and last day of month
        $start = $startDate->format('j');
        $lastDay = $startDate->format('t');

        // if last day of month, append month name
        if ($start == $lastDay)
            $start = $startDate->format('jS M');
        else 
            $start = $startDate->format('jS');

        // end date
        $end = $endDate->format('jS M ga');

        $ranges[] = $start.'-'.$end;
    }
    return $ranges;
}

It can be used as below:

$arr = getDateRanges($dates);
echo implode(', ', $arr);

Output :

20th-23rd Jan 8pm, 25th-26th Jan 8pm, 31st Jan-1st Feb 8pm

The output doesn't include 22nd Jan 2pm , but it should be easy enough to modify the function to get the required output (hint: check if date('G') > 12 to know if the time of the day is afternoon).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM