Splitting an array of dates into adjacent blocks

Possible duplicate:
Check consecutive dates in a set and return as a range

I have an array of dates that comes from mySQL query. I need to split an array into multiple arrays so that the dates in each array are contiguous.

So, if I start with

$datearray = array("2013-05-05", "2013-05-06", "2013-05-07", "2013-05-08", "2013-06-19", "2013-06-20", "2013-06-21"); 

I need to break it down into

 $firstdatearray = array("2013-05-05", "2013-05-06", "2013-05-07", "2013-05-08"); $seconddatearray = array("2013-06-29", "2013-06-30", "2013-07-01"); 

Finally, I can print

March 5 - 8, June 29 - July 1

How can i do this? I don’t know where to start.

+6
source share
7 answers

THIS IS A FULL WORKING ANSWER. (Enjoy it!)

You will need to iterate over each value in $ datearray

 <?php $datearray = array("2013-05-05", "2013-05-06", "2013-05-07", "2013-05-08", "2013-06-19", "2013-06-20", "2013-06-21"); asort($datearray); $resultArray = array(); $index = -1; $last = 0; $out = ""; foreach ($datearray as $date) { $ts = strtotime($date); if (false !== $ts) { $diff = $ts - $last; if ($diff > 86400) { $index = $index + 1; $resultArray[$index][] = $date; } elseif ($diff > 0) { $resultArray[$index][] = $date; } else { // Error! dates are not in order from small to large } $last = $ts; } } foreach ($resultArray as $a) { if (count($a) > 1) { $firstDate = $a[0]; $firstDateBits = explode('-',$firstDate); $lastDate = $a[count($a)-1]; $lastDateBits = explode('-',$lastDate); if ($firstDateBits[1] === $lastDateBits[1]) { $out .= intval($firstDateBits[2]) . '-' . intval($lastDateBits[2]) . ' ' . date("M",strtotime($firstDate)) . ', '; } else { $out .= date("M d",strtotime($firstDate)) . '-' . date("M d",strtotime($lastDate)) . ', '; } } } 

This is the conclusion:

 5-8 May, 19-21 Jun 
+4
source

I cannot help you with acutal code, but you can do something like this:

  • find the beginning and end points of ranges
  • count the steps between them (1 step = 1 day)
  • use mktime (or DateTime or strftime) to calculate the end date based on the start date + step)
  • print as you like.
0
source

All dates that you have are indicated in the same year. You can convert each date to the number of days this year.

Then you will have an array of numbers. For this array, you can simply do the following:

Another way is to calculate the next date based on the previous one, and then compare it with the next date in the array. If both are equal, increase the current time interval; if not, create a new time interval. Then reduce the array to time intervals:

 $consecutiveDates = function ($result, $date) { if ($count = count($result)) { $next = clone $result[$count - 1][1]; $next->add(new DateInterval('P1D')); } $date = new DateTime($date); if (!$count || $date != $next) { $result[$count++] = [$date]; } $result[$count - 1][1] = $date; return $result; }; $reduced = array_reduce($datearray, $consecutiveDates, []); 

This gives the following result (for your array):

 Array ( [0] => Array ( [0] => DateTime Object ( [date] => 2013-05-05 00:00:00 [timezone_type] => 3 [timezone] => Europe/London ) [1] => DateTime Object ( [date] => 2013-05-08 00:00:00 [timezone_type] => 3 [timezone] => Europe/London ) ) [1] => Array ( [0] => DateTime Object ( [date] => 2013-06-19 00:00:00 [timezone_type] => 3 [timezone] => Europe/London ) [1] => DateTime Object ( [date] => 2013-06-21 00:00:00 [timezone_type] => 3 [timezone] => Europe/London ) ) ) 

These two entries can now be easily displayed in your output style using the display function:

 $consecutiveDatesString = function ($pair) { list($start, $end) = $pair; return $start == $end ? $start->format('j M') : $start->format($start->format('M') != $end->format('M') ? 'j M' : 'j') . $end->format(' - j M'); }; $consecutiveDatesStrings = array_map($consecutiveDatesString, $reduced); 

This leads to a more compact result:

 Array ( [0] => 5 - 8 May [1] => 19 - 21 Jun ) 

And finally, print separated by commas:

 echo implode(', ', $consecutiveDatesStrings), "\n"; 

What gives, guess that:

 5 - 8 May, 19 - 21 Jun 
0
source

This will work:

 $datearray = array("2013-05-05", "2013-05-06", "2013-05-07", "2013-05-08", "2013-06-19", "2013-06-20", "2013-06-21"); $current_date_array_index = 0; $dates = array(); for($i=0,$c=count($datearray);$i<$c;$i++){ if(strtotime($dates[$current_date_array_index][count($dates[$current_date_array_index])-1]." +1 day") != strtotime($datearray[$i])){ $current_date_array_index++; } $dates[$current_date_array_index][] = $datearray[$i]; } foreach($dates as $date){ if(count($date) == 1){ $output[] = date('j M',strtotime($date[0])); }else{ $output[] = date('j',strtotime($date[0]))." - ".date('j M',strtotime($date[count($date)-1])); } } echo implode($output,", "); // output: 5 - 8 May, 19 - 21 Jun 

It is worth noting that if the dates pass a month, he will say something like 29 - 5 Mar - 29 from what is not defined, so I would make 5 Mar - 8 Mar if I were you.

0
source

Try something like this:

 $prev = null; $groups = array(); $idx = 0; foreach($datearray as $day) { if ($prev == null) { $prev = $day; } else { $currentDay = strtotime($day); $prevDay =strtotime($prev); if ($currentDay + 86400 > $prevDay) { // New Array $idx++; } $groups[$idx][] = $day; } } // TODO : Sort the array into date order 

Note. I have not tested the above code so it could contain typos.

I simply repeat every day in the array and determine if it is a continuous day from the previous day. If so, then it is added to the same group; if it is not, then it is added to another index of the array. You may need to sort the dates after that using some sorting function from php . Then iterate over the array groups to determine the date range.

0
source

Assuming PHP 5.3 or higher.

You can use DateTime, DateInterval and a little algorithmic work.

 // create the same array but with DateTime objects to represent the dates $dt_array = array_map(function ($e) { return new DateTime($e); }, $datearray); $intervals = array(); $len_dt_array_m1 = count($dt_array) - 1; if ($len_dt_array_m1 >= 0) { $current_interval = &$intervals[]; } // now we traverse the array left to right. // if the difference between the current date and the next is not +1 day, we assume a new interval has begun. for ($i = 0; $i < $len_dt_array_m1; ++$i) { $current_dt = $dt_array[$i]; $next_dt = $dt_array[$i+1]; $diff = $current_dt->diff($next_dt); $current_interval[] = $current_dt->format('Ym-d'); if ($diff->days != 1 || $diff->invert != 0) { $current_interval = &$intervals[]; } } // add last dt to the interval if ($len_dt_array_m1 >= 0) { $current_interval[] = $dt_array[$len_dt_array_m1]->format('Ym-d'); } print_r($intervals); 
0
source
 class Date_Array_Split { private $date_arrays = array(); public function __construct( Array $dates, $split_into = 2 ) { // Sort the array asort( $dates ); // Calculate the array size to pass to array_chunk $size = ceil( count( $dates ) / $split_into ); // Break up the array into pieces $dates = array_chunk( $dates, $size ); $this->date_arrays = $dates; } public function __toString() { $string = array(); // Oh, the irony! // Iterate through the chunks foreach( $this->date_arrays as $date_array ) { // Here the oldest date in the chunk $date_min = min( $date_array ); // Here the newest date in the chunk $date_max = max( $date_array ); // Default format for output $date_min_format = 'j M'; // Accomodate the slight formatting change if( date( 'my', strtotime( $date_min ) ) === date( 'my', strtotime( $date_max ) ) ) { // Moth and year are the same, omit the month $date_min_format = 'j'; } // String-i-fy the dates for output $date_min_string = date( $date_min_format, strtotime( $date_min ) ); $date_max_string = date( 'j M', strtotime( $date_max ) ); // Add them to the output array $string[] = sprintf( '%s - %s', $date_min_string, $date_max_string ); } // Return the output array separated by commas return implode( $string, ", " ); } } $dates_array = array( "2013-05-05", "2013-06-20", "2013-05-07", "2013-05-08", "2014-05-09", "2013-05-09", "2013-06-19", "2013-06-21" ); $dates = new Date_Array_Split( $dates_array ); echo( $dates ); 

Exit: 5 - 9 May, 19 Jun - 9 May

Please note that this handles years correctly. That is why the result looks a little strange. Perhaps this will be taken into account in the output.

0
source

All Articles