将日期数组划分为连续的块

时间:2013-01-07 17:53:16

标签: php arrays

  

可能重复:
  Check for consecutive dates within a set and return as range

我有一个从mySQL查询中获取的日期数组。我需要将数组分成多个数组,以便每个数组中的日期是连续的。

所以,如果我从

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

我需要将其分成

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

最后我将能够打印

  

3月5日至8月3日,7月29日至7月1日

我该怎么做?我不知道从哪里开始。

7 个答案:

答案 0 :(得分:4)

这是完整的工作答案。 (享受!)

你必须遍历$ 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)) . ', ';  
        }
    }
}

这是输出:

5-8 May, 19-21 Jun

答案 1 :(得分:0)

我无法帮助您使用实际代码,但您可以执行以下操作:

  • 查找范围的起点和终点
  • 计算介于两者之间的步数(1步= 1天)
  • 使用mktime(或DateTime或strftime)根据开始日期+步骤天计算结束日期。
  • 随意打印出来。

答案 2 :(得分:0)

您拥有的所有日期都在同一年。您可以将每个日期转换为该年中的日期数。

然后你会得到一组数字。对于那个阵列,您可以像这里概述的那样:

另一种方法是根据前一个开启计算下一个日期,然后将其与数组中的下一个日期进行比较。如果两者相等,则延长当前时间跨度,如果不相等,则创建新的时间跨度。然后将数组减少到时间跨度:

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

这给出了以下结果(对于你的数组):

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
                )

        )

)

现在可以使用映射函数轻松地将这两个条目映射到您的输出样式:

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

然后导致更紧凑的结果:

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

最后打印出以逗号分隔的内容:

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

这给出了猜测:

5 - 8 May, 19 - 21 Jun

答案 3 :(得分:0)

这将有效:

$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

值得注意的是,如果日期超过了一个月,它会说29 - 5 Mar之类的东西 - 没有定义的第29个,所以如果我是你,我会5 Mar - 8 Mar

答案 4 :(得分:0)

尝试这样的事情:

$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

注意:我没有测试过上面的代码,因此它可能包含拼写错误。

我只是在数组中每天迭代,并确定它是否是前一天的连续日。如果是,则将其添加到同一组中,如果不是,则将其添加到不同的数组索引中。您可能需要使用某种sorting function from php对此后的日期进行排序。然后迭代数组以确定日期范围。

答案 5 :(得分:0)

假设PHP 5.3或更高版本。

您可以使用DateTime,DateInterval和一些算法工作。

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

答案 6 :(得分: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 );

输出:5 - 9 May, 19 Jun - 9 May

请注意,这是正确处理年份。这就是输出看起来有点奇怪的原因。可能想在输出中考虑到这一点。