php - 检查更广泛范围内的差距的多个日期范围

时间:2015-01-06 01:32:37

标签: php mysql date

我有一个在线会员制度,当地执法部门要求我们为所有会员提供5年的地址历史记录。在这5年内,允许重叠,但可能没有任何差距。成员输入他们的地址,存储在mysql数据库中,地址和来自&到目前为止每个地址。成员将数据作为月份和年份输入。这将存储为“从”日期的月份中的第一个月以及“到”日期的月份的最后一个月。

我的$ array是从数据库的mysqli查询中为特定成员编译的,以提取他们提供的地址的所有日期范围。这个数组很好地填充了我的循环。

$array[] = array("from"=>$row['date_from'],"to"=>$row['date_to']);

我尝试将这些范围中的每一个转换为天数并添加所有范围,但地址重叠(允许和必要),即使地址历史中存在空白,也可能超过5年。

我所需要的只是$ complete完全是真或假,我不需要天数差距。

打开以将其作为mysql查询或php。

我很抱歉没有提供半工作代码 - 我唯一的想法是循环1825天(5年),看看是否每个人都有代表。

1 个答案:

答案 0 :(得分:2)

这个递归函数将遍历您的范围数组,将重叠的范围组合起来,直到它们尽可能地组合。如果结果超过一个范围,那么您就会有差距。我还使用辅助功能使查找重叠更清晰/更容易,我假设您的日期看起来像'Y-m-d'

<?php

// First, here's how to use it. If we can't combine all the ranges, and the 
// final combined range doesn't reach back 5 years or to the present,
// we have a gap.
$mergedRanges = combineRanges($myArray);
if (count($mergedRanges) > 1
    || $mergedRanges[0]['from'] > date('Y-m-d', time() - 157680000) // 5 years ago
    || $mergedRanges[0]['to'] < date('Y-m-d')) // present
    echo 'Gaps found';

/**
 * Recursive function to combine ranges.
 *
 * @param array $ranges
 * @return array Array of combined ranges (has only 1 element if no gaps)
 */
function combineRanges(array $ranges)
{
    $mergedRanges = array();
    $usedKeys = array();

    // Nested foreach compares each unique pair of ranges for overlap.
    // If the a range has already been accounted for, it can be skipped.
    foreach ($ranges as $k1 => $range1) {
        if (!in_array($k1, $usedKeys)) {
            foreach ($ranges as $k2 => $range2) {
                if (!in_array($k1, $usedKeys) && $k1 > $k2) {

                    // If ranges overlap, combine them and make a note that
                    // they've already been included
                    if (rangesOverlap($range1, $range2)) {
                        $newRange = array(
                            'from' => min($range1['from'], $range2['from']),
                            'to' => max($range1['to'], $range2['to'])
                        );
                        // It's possible the resulting range could already
                        // be accounted for by a different combo of ranges,
                        // so check first
                        if (!in_array($newRange, $mergedRanges))
                            $mergedRanges[] = $newRange;
                        $usedKeys[] = $k1;
                        $usedKeys[] = $k2;

                    // Otherwise, add the 2nd range to $mergedRanges
                    } elseif (!in_array($k2, $usedKeys)) {
                        $mergedRanges[] = $range2;
                        $usedKeys[] = $k2;
                    }

                    // If $range1 didn't have any overlaps, add it here
                    if (!in_array($k1, $usedKeys)) {
                        $mergedRanges[] = $range1;
                        $usedKeys[] = $k1;
                    }
                }
            }
        }
    }

    // If $ranges and $mergedRanges have the same # of elements,
    // or if $ranges only had 1 element to begin with,
    // that means we couldn't merge any more. Otherwise, recurse!
    if (count($ranges) == 1)
        return $ranges;
    return count($mergedRanges) == 1 || (count($ranges) == count($mergedRanges))
        ? $mergedRanges
        : combineRanges($mergedRanges);
}

/**
 * Helper function to see if 2 ranges overlap.
 *
 * @param array $range1
 * @param array $range2
 * @return boolean
 */
function rangesOverlap(array $range1, array $range2)
{
    // Find the day before each range in order to combine ranges
    // that don't overlap but are right next to each other.
    $overlap = false;
    $range1Before = date('Y-m-d', strtotime('-1 day', strtotime($range1['from'])));
    $range2Before = date('Y-m-d', strtotime('-1 day', strtotime($range2['from'])));

    // Account for when $range1 is first or when $range 2 is first
    if ($range1['from'] <= $range2['from'] && $range1['to'] >= $range2Before
        || $range2['from'] <= $range1['from'] && $range2['to'] >= $range1Before)
       $overlap = true;

    return $overlap;
}