1

I have the following array with dates in php:

Array ( 
  [0] => '2017-02-22'
  [1] => '2017-02-23'
  [2] => '2017-04-03'
  [3] => '2017-04-04'
  [4] => '2017-04-05'
  [5] => '2017-04-06'
)

Is there a way to find the continuous date periods included within the array?

For example for the above array the expected result would be:

2017-02-22 to 2017-02-23

and

2017-04-03 to 2017-04-06.

Thanks for your help!

Larzan
  • 9,389
  • 3
  • 42
  • 41
  • 1
    Loop over the entries, and check if the previous entry was one day before the current one. (You probably want to create DateTime objects out of those data strings, to be able to check that more easily.) If yes, then you still have a continuous period, if not you are at the start of a new one. – CBroe Feb 24 '17 at 11:22
  • Maybe interesting (duplicate)? :[Algorithm to combine / merge date ranges](http://stackoverflow.com/questions/4972552/algorithm-to-combine-merge-date-ranges) – Ryan Vincent Feb 24 '17 at 14:04

1 Answers1

4

There will be many ways to perform this task. Some that are harder to read than others. I'll list a few. I recommend using the first method for efficiency, and perhaps the third method for easy of output modification.

I have modified the sample data in the $dates array to show that it accommodates month changes, and stand-alone dates.

All of the following methods will assume that the dates are already sorted in ascending ordered. If not, then sort() will work on Y-m-d formatted dates; otherwise usort() will be neccessary.

My methods will take an array of dates like this:

$dates=[
  '2017-02-28',
  '2017-03-01',
  '2017-04-03',
  '2017-04-04',
  '2017-04-06',
  '2017-04-08',
  '2017-04-09',
  '2017-04-10'
];

And output this:

array (
  0 => '2017-02-28 to 2017-03-01',
  1 => '2017-04-03 to 2017-04-04',
  2 => '2017-04-06 to 2017-04-06',
  3 => '2017-04-08 to 2017-04-10',
)

Method #1: Single Foreach Loop (Demo)

foreach($dates as $date){
    if(!isset($start_date)){
        $start_date=$date;  // temporarily store new start date
    }elseif($date==date("Y-m-d",strtotime("$start_date +1 day")) || (isset($end_date) && $date==date("Y-m-d",strtotime("$end_date +1 day")))){
        $end_date=$date;    // temporarily store new or overwrite existing end date
    }else{
        $result[]="$start_date to ".(!isset($end_date)?$start_date:$end_date);  // current date is not in group, move temporary dates to result array
        $start_date=$date;  // overwrite previous start date
        unset($end_date);  // destroy previous end date
    }
}
$result[]="$start_date to ".(!isset($end_date)?$start_date:$end_date);  // move temporary dates to result array
var_export($result);

Method #2: Nested While Loops (Demo)

$copy=$dates;  // make a copy in case $dates is to be used again later
while($copy){  // iterate until the array is empty
    while(!isset($range) || current($copy)==date("Y-m-d",strtotime(substr($range,-10)." +1 day"))){  // iterate while date is new or consecutive
        $range=(!isset($range)?'':substr($range,0,10).' to ').array_shift($copy);  // temporarily store / overwrite the range data
    }
    $result[]=(strlen($range)==10?"$range to $range":$range);  // permanently store range data
    unset($range);  // destroy range string, for next iteration
}
var_export($result);

Method #3: Two Foreach Loops (Demo)

foreach($dates as $date){
    if(!isset($grouped)){  // first group
        $i=0;  // first group, index is zero
    }elseif($date!=date("Y-m-d",strtotime("$date_checker +1 day"))){  // if non-consecutive
        ++$i;  // next group, index is incremented
    }
    $grouped[$i][]=$date_checker=$date;  // store date as temporary date checker and into appropriate group
}

foreach($grouped as $group){
    $result[]=current($group)." to ".end($group);
}
var_export($result);

Or from the $grouped array in Method 3, you could simply alter the output structure to contain "range strings" as keys with subarrays holding the individual dates as elements using this:

foreach($grouped as $group){
    $result[current($group)." to ".end($group)]=$group;
}

Alternative Output:

array (
  '2017-02-28 to 2017-03-01' => 
  array (
    0 => '2017-02-28',
    1 => '2017-03-01',
  ),
  '2017-04-03 to 2017-04-04' => 
  array (
    0 => '2017-04-03',
    1 => '2017-04-04',
  ),
  '2017-04-06 to 2017-04-06' => 
  array (
    0 => '2017-04-06',
  ),
  '2017-04-08 to 2017-04-10' => 
  array (
    0 => '2017-04-08',
    1 => '2017-04-09',
    2 => '2017-04-10',
  ),
)
mickmackusa
  • 43,625
  • 12
  • 83
  • 136