日期范围的组合,涵盖输入日期范围的所有日期

时间:2013-07-05 10:58:50

标签: php algorithm

我想找到与输入日期范围重叠的所有可能的日期范围组合。例如,如果用户输入2013/01/10到2013/01/25并且我有以下日期范围:

2013/01/08 to 2013/01/10
2013/01/09 to 2013/01/15
2013/01/10 to 2013/01/20
2013/01/18 to 2013/01/27
2013/01/14 to 2013/01/19
2013/01/19 to 2013/01/25
2013/01/14 to 2013/01/26
2013/01/10 to 2013/01/26

我想找到与输入日期重叠的上述日期的所有可能组合(2013/01/10至2013/01/25)。

与输入日期重叠的日期范围的两个示例是:

{2013/01/09 to 2013/01/15, 2013/01/14 to 2013/01/26}
{2013/01/09 to 2013/01/15, 2013/01/14 to 2013/01/19, 2013/01/19 to 2013/01/25}

我没有列出与输入日期范围重叠的所有可能日期范围!
我想找到涵盖输入日期范围的所有日期的日期范围的组合!如果你看两个例子,我将日期范围组合起来覆盖输入日期的所有日子!
我不想检查一个输入日期范围为2的日期,如果日期范围涵盖输入日期的所有日期或者没有!,我想将日期范围组合到查找涵盖所有日期的所有可能的日期范围组合输入日期范围。
我想要一种算法来查找涵盖输入日期范围的所有日期的所有可能的日期组合。 有没有解决这个问题的快速算法?

3 个答案:

答案 0 :(得分:1)

如果您找到2013-01-09 to 2013-01-15,我不确定列表中缺少2013-01-09 to 2013-01-15的原因,但您可以根据需要修改此类

$range = [
        new DateManager("2013/01/08", "2013/01/10"),
        new DateManager("2013/01/09", "2013/01/15"),
        new DateManager("2013/01/10", "2013/01/20"),
        new DateManager("2013/01/18", "2013/01/27"),
        new DateManager("2013/01/14", "2013/01/19"),
        new DateManager("2013/01/19", "2013/01/25"),
        new DateManager("2013/01/14", "2013/01/26"),
        new DateManager("2013/01/10", "2013/01/26")
];

$find = new DateManager("2013/01/10", "2013/01/25");

foreach($range as $date) {
    $find->overlap($date) && print($date->getRange() . PHP_EOL);
}

输出

2013/01/09 to 2013/01/15 
2013/01/10 to 2013/01/20 
2013/01/14 to 2013/01/19 
2013/01/19 to 2013/01/25 

班级

class DateManager {
    private $start, $end;

    function __construct($start, $end, $format = "Y/m/d") {
        $this->start = $start instanceof DateTime ? $start : DateTime::createFromFormat($format, $start);
        $this->end = $end instanceof DateTime ? $end : DateTime::createFromFormat($format, $end);
    }

    function getStart() {
        return $this->start;
    }

    function getEnd() {
        return $this->end;
    }

    function getRange($format = "Y/m/d") {
        return sprintf("%s to %s ", $this->start->format($format), $this->end->format($format));
    }

    function between(DateManager $date) {
        return $date->getStart() >= $this->getStart() && $this->getEnd() <= $date->end;
    }

    function overlap(DateManager $date) {
        return (($date->getStart() >= $this->getStart() || $date->getEnd() > $this->getStart()) && $this->getEnd() >= $date->getEnd());
    }
}

答案 1 :(得分:1)

将(开始,结束)日期转换为系统时间(从1/1/70开始,步长= 1秒),然后 - 使用步骤= 24 * 60 * 60运行循环。将每个值解码为可打印格式,如果需要的话。

答案 2 :(得分:0)

我添加了几个数据项,使其更有趣:

$start = strtotime('2013/01/10');
$end = strtotime('2013/01/25');
$range[0]['start'] = "2013/01/08";
$range[0]['end'] = "2013/01/10";
$range[1]['start'] = "2013/01/09";
$range[1]['end'] = "2013/01/15";
$range[2]['start'] = "2013/01/10";
$range[2]['end'] = "2013/01/20";
$range[3]['start'] = "2013/01/18";
$range[3]['end'] = "2013/01/27";
$range[4]['start'] = "2013/01/14";
$range[4]['end'] = "2013/01/19";
$range[5]['start'] = "2013/01/19";
$range[5]['end'] = "2013/01/25";
$range[6]['start'] = "2013/01/14";
$range[6]['end'] = "2013/01/26";
$range[7]['start'] = "2013/01/10";
$range[7]['end'] = "2013/01/26";
$range[8]['start'] = "2013/01/9";
$range[8]['end'] = "2013/01/15";
$range[9]['start'] = "2013/01/13";
$range[9]['end'] = "2013/01/19";
$heads = array();
$tails = array();
$combos = array();
$h = 0;
foreach ($range as $key => $value){
    $r1 = strtotime($value['start']);
    $r2 = strtotime($value['end']);
    if ($r1 <= $start && $r2 >= $end){
        $combos[] = $key;
    } elseif($r1 <= $start && $r2 > $start) {
        $heads[$h]['r1'] = $r1;
        $heads[$h]['r2'] = $r2;
        $heads[$h]['seq'] = $key;
        $h++;
    } elseif($r1 > $start && $r1 < $end && $r1 < $r2) {
        $tails[$key]['r1'] = $r1;
        $tails[$key]['r2'] = $r2;
        $tails[$key]['seq'] = $key;
    }
}
while (count($tails) > 0){
    $heads2 = array();
    $tails2 = array();
    $h2 = 0;
    foreach ($heads as $key1 => $value1){
        foreach ($tails as $key2 => $value2){
            if (($value1['r1'] < $value2['r1']) && ($value1['r2'] >= $value2['r1']) && ($value1['r2'] < $value2['r2'])){
                $seq = $value1['seq'].':'.$value2['seq'];
                //keep tail alive, but don't change key
                $tails2[$key2]['r1'] = $value2['r1'];
                $tails2[$key2]['r2'] = $value2['r2'];
                $tails2[$key2]['seq'] = $key2;
                if ($value2['r2'] >= $end){
                    $combos[] = $seq;
                } else {
                    $heads2[$h2]['r1'] = $value1['r2'];
                    $heads2[$h2]['r2'] = $value2['r2'];
                    $heads2[$h2]['seq'] = $seq;
                    $h2++;
                }
            }
        }
    }
    $heads = $heads2;
    $tails = $tails2;
}

数组$combos包含一系列由冒号分隔的数组键,引用原始数组的数组键。这是显示输出的可能方法:

$html = '';
foreach ($combos as $key => $value){
    $p = '<p>';
    $seqs = explode(':', $value);
    foreach ($seqs as $key2 => $value2){
        $p .= $range[$value2]['start'].' - '.$range[$value2]['end'].'; ';
    }
    $html .= substr($p, 0, -2).'</p>';
}
echo $html;

显示以下内容:

2013/01/10 - 2013/01/26
2013/01/09 - 2013/01/15; 2013/01/14 - 2013/01/26
2013/01/10 - 2013/01/20; 2013/01/18 - 2013/01/27
2013/01/10 - 2013/01/20; 2013/01/19 - 2013/01/25
2013/01/10 - 2013/01/20; 2013/01/14 - 2013/01/26
2013/01/9 - 2013/01/15; 2013/01/14 - 2013/01/26
2013/01/09 - 2013/01/15; 2013/01/14 - 2013/01/19; 2013/01/18 - 2013/01/27
2013/01/09 - 2013/01/15; 2013/01/14 - 2013/01/19; 2013/01/19 - 2013/01/25
2013/01/09 - 2013/01/15; 2013/01/13 - 2013/01/19; 2013/01/18 - 2013/01/27
2013/01/09 - 2013/01/15; 2013/01/13 - 2013/01/19; 2013/01/19 - 2013/01/25
2013/01/9 - 2013/01/15; 2013/01/14 - 2013/01/19; 2013/01/18 - 2013/01/27
2013/01/9 - 2013/01/15; 2013/01/14 - 2013/01/19; 2013/01/19 - 2013/01/25
2013/01/9 - 2013/01/15; 2013/01/13 - 2013/01/19; 2013/01/18 - 2013/01/27
2013/01/9 - 2013/01/15; 2013/01/13 - 2013/01/19; 2013/01/19 - 2013/01/25