2013-01-07 8 views
6

Eventuali duplicati:
Check for consecutive dates within a set and return as rangeDividendo un array di Date Into contigui blocchi

Ho una serie di date che si ottiene da una query mySQL. Devo dividere l'array in più array in modo che le date in ogni array siano contigue.

Quindi, se mi metto con

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

ho bisogno di essere in grado di dividere che in

$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"); 

Finalmente sarò in grado di stampare

5 - 8 Mar , 29 giu - 1 lug

Come posso farlo? Non ho idea di dove cominciare.

+3

Non hai idea di dove cominciare? Scommetto che hai almeno un'idea di cosa potresti fare. – hakre

+0

Quindi dovresti davvero provare a fare qualcosa e poi porre la domanda quando ti imbatti in problemi di implementazione specifici. –

+1

simile, adattabile: http: // stackoverflow.it/questions/8461543/check-for-consecutives-dates-within-a-set-and-return-as-range – goat

risposta

4

QUESTA È LA RISPOSTA DI LAVORO COMPLETA. (! Godetevi)

dovrete scorrere ogni valore 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)) . ', '; 
     } 
    } 
} 

Questa è l'uscita:

5-8 May, 19-21 Jun 
+0

Grazie a mrbinky. Sembra promettente. Sto lavorando per svilupparlo completamente nella mia applicazione ora. Tornerò quando avrò più notizie. – TrapezeArtist

+0

Era praticamente la "risposta completa di lavoro". Solo alcune piccole cose e vola: Non ho notato il $ out = "": linea all'inizio, quindi ho reinventato quella ruota da solo. data ("M d" ecc. Nell'ultima $ out. = La riga mi ha dato le date back-to-front e sempre a due caratteri. Modifica data ("j M" ecc. Risolto L'elenco di date è terminato con un virgola, quindi ho usato rtrim per riordinarlo – TrapezeArtist

+0

Grazie a @ mrbinky3000 per una soluzione davvero utile, è stato molto meno doloroso di quanto mi aspettassi, grazie anche a tutti quelli che si sono presi la briga di guardarlo. ci sono altre soluzioni di lavoro qui, ma questo è quello con cui sono stato eseguito. – TrapezeArtist

0

non posso fare con il codice acutal, ma si poteva fare qualcosa di simile:

  • trovare punti iniziali e finali delle gamme
  • fasi di conteggio in mezzo (1 punto = 1 giorno)
  • utilizzare mktime (o DateTime o strftime) per calcolare la data di fine in base alla data di inizio + giorni di passaggio)
  • stampare come desiderato.
0

Tutte le date che hai sono nello stesso anno. È possibile convertire ogni data nel numero del giorno in quell'anno.

Quindi avresti una serie di numeri. Per tale matrice si può solo fare come descritto qui:

Un altro modo sarebbe quello di calcolare la data successiva sulla base del precedente e poi confrontarlo con la data successiva nella matrice. Se entrambi sono uguali, estendere l'intervallo di tempo corrente, in caso contrario, creare un nuovo intervallo di tempo.Ridurre la matrice di time-campate poi:

$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, []); 

Questo dà il seguente risultato (per l'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 
       ) 

     ) 

) 

Queste due voci ora possono essere facilmente mappati nel vostro stile di output con una mappatura funzione:

$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); 

che poi porta ad un risultato più compatta:

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

E per la stampa, infine, che separati da virgole:

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

che dà, indovinate un po ':

5 - 8 May, 19 - 21 Jun 
0

questo funzionerà:

$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 

Vale la pena notare che se le date andare oltre il mese però, dirà qualcosa come 29 - 5 Mar - 29 di quello che non è definito, quindi vorrei fare 5 Mar - 8 Mar se fossi tu.

0

provare qualcosa di simile:

$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 

Nota: non ho ancora testato il codice di cui sopra in modo che può contenere errori di battitura.

Sto semplicemente iterando su ogni giorno nell'array e determinando se si tratta di un giorno continuo dal giorno precedente. Se lo è, viene aggiunto allo stesso gruppo, in caso contrario, viene aggiunto a un diverso indice di array. Potrebbe essere necessario ordinare le date successive utilizzando una sorta di sorting function from php. Quindi scorrere i gruppi di array per determinare l'intervallo di date.

0

Supponendo PHP 5.3 o superiore.

È possibile utilizzare DateTime, DateInterval e un po 'di lavoro algoritmico.

// 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('Y-m-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('Y-m-d'); 
} 

print_r($intervals); 
0
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's the oldest date in the chunk 
      $date_min = min($date_array); 

      // Here's 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); 

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

Nota che questa è la gestione degli anni in modo corretto. Ecco perché l'output sembra un po 'strano. Potrebbe voler tener conto di ciò nell'output.

Problemi correlati