简体   繁体   中英

Exclude date/time ranges from an array of date/time ranges with overlapping

Hope you are doing great!!

I'm kind of stuck with this, what I've done only work when there are not overlapping and anyway am not sure it's the correct approach.

So my current implementation is to merge all times (start_time and end_time as different entry in that array), sort them and remove duplicates if any.

Then I loop into that list and check for each if they are within $times ranges and not within restrictions ranges.

All those which pass these condition are added to another list. Then finally i'll loop through that list '2 item at a time' and build the final array of time ranges.

Codes: https://3v4l.org/2elDs (Laravel, using Collection and Carbon, so won't run there)

Sample without date/time overlapping:

$times = [
        [
            'start_time' => '2017-06-26 00:00:00',
            'end_time' => '2017-06-26 05:00:00',
        ],
        [
            'start_time' => '2017-06-26 13:00:00',
            'end_time' => '2017-06-26 18:00:00',
        ]
    ];

$timesToExclude= [
        [
            'start_time' => '2017-06-26 04:00:00',
            'end_time' => '2017-06-26 04:30:00',
        ],
        [
            'start_time' => '2017-06-26 07:00:00',
            'end_time' => '2017-06-26 10:00:00',
        ],
        [
            'start_time' => '2017-06-26 15:00:00',
            'end_time' => '2017-06-26 16:00:00',
        ]
    ];

Result to:

    $result = [
        [
            "start_time" => "2017-06-26 00:00:00",
            "end_time"   => "2017-06-26 04:00:00"
        ], [
            "start_time" => "2017-06-26 04:30:00",
            "end_time"   => "2017-06-26 05:00:00"
        ],
        [
            "start_time" => "2017-06-26 13:00:00",
            "end_time"   => "2017-06-26 15:00:00"
        ],
        [
            "start_time" => "2017-06-26 16:00:00",
            "end_time"   => "2017-06-26 18:00:00"
        ]
    ]

Sample with date/time overlapping

    $times = [
        [
            'start_time' => '2017-06-26 00:00:00',
            'end_time'   => '2017-06-26 10:00:00',
        ],
        [
            'start_time' => '2017-06-26 05:00:00',
            'end_time'   => '2017-06-26 20:00:00',
        ]
    ];

    $timesToExclude= [
        [
            'start_time' => '2017-06-26 04:00:00',
            'end_time'   => '2017-06-26 04:30:00',
        ],
        [
            'start_time' => '2017-06-26 07:00:00',
            'end_time'   => '2017-06-26 09:00:00',
        ],
        [
            'start_time' => '2017-06-26 15:00:00',
            'end_time'   => '2017-06-26 16:00:00',
        ]
    ];

Should result to:

$result = [
        [
            "start_time" => "2017-06-26 00:00:00",
            "end_time"   => "2017-06-26 04:00:00"
        ], [
            "start_time" => "2017-06-26 04:30:00",
            "end_time"   => "2017-06-26 05:00:00"
        ],
        [
            "start_time" => "2017-06-26 05:00:00",
            "end_time"   => "2017-06-26 07:00:00"
        ],
        [
            "start_time" => "2017-06-26 09:00:00",
            "end_time"   => "2017-06-26 10:00:00"
        ],
        [
            "start_time" => "2017-06-26 10:00:00",
            "end_time"   => "2017-06-26 15:00:00"
        ],
        [
            "start_time" => "2017-06-26 16:00:00",
            "end_time"   => "2017-06-26 20:00:00"
        ]
    ]

Anyone knows the correct algo/pseudo to deal with that?

Create common list of pairs {time; flag} {time; flag} , where flag is time_start, time_end, restriction_start or restriction_end .

Sort this list by time. In case of tie use flag as secondary key (for example, restr_start should go after time_end ).

Make $Active=0 , $Exclude=0

Walk through sorted list.

When you meet time_start , increment value of $Active {1}

When you meet time_end , decrement value of $Active {2}

When you meet restriction_start , increment value of $Exclude {3}

When you meet restriction_end , decrement value of $Exclude {4}

Open output interval in the next cases:
{1}: $Active becomes 1 and $Exclude = 0
{4}: $Exclude becomes 0 and $Active is non-zero

Close output interval in the next cases:
{2} $Active becomes 0 and $Exclude = 0
{3} $Exclude becomes 1 and $Active is non-zero

example: (don't know exact php syntax for complex conditions)

 case $TimeStart:
            $active = $active + 1;
            if ($active=1) and ($exclude=0)
                 $range['start_time'] = $mergedTime['time'];
            break;
 ....
  case $RestrictionEnd:
            $exclude = $exclude - 1;
            if ($exclude=0) and ($active > 0)
                 $range['start_time'] = $mergedTime['time'];
            break;
function excludeOverlaping($disponibilities,$plages) {

        if(count($disponibilities) == 0)
            return $disponibilities;
        if(count($plages) == 0)
            return $disponibilities;

                usort($disponibilities, function($a, $b)
                {
                    return strtotime($a->startDate) - strtotime($b->startDate);
                });
                usort($plages, function($a, $b)
                {
                    return strtotime($a->startDate) - strtotime($b->startDate);
                });

                for($i = 0; $i < count($disponibilities); $i++){
                    for($j=0;$j<count($plages);$j++){
                        $dispo = $disponibilities[$i];
                        if(isset($dispo->exclude)){
                            break;
                        }
                        $plage = $plages[$j]; 
                        if(strtotime($dispo->startDate)>=strtotime($plage->startDate) && strtotime($dispo->endDate)<=strtotime($plage->endDate)){
                                $disponibilities[$i]->exclude = true;

                        }else if( strtotime($dispo->startDate)<strtotime($plage->startDate) && strtotime($dispo->endDate) <= strtotime($plage->endDate) && strtotime($dispo->endDate) > strtotime($plage->startDate)){
                                 $disponibilities[$i]->endDate = $plage->startDate;
                        }else if( strtotime($dispo->startDate)>=strtotime($plage->startDate)  && strtotime($dispo->startDate)<=strtotime($plage->endDate) && strtotime($dispo->endDate) > strtotime($plage->endDate)){
                                 $disponibilities[$i]->startDate = $plage->endDate;
                        }else if( strtotime($dispo->startDate)<strtotime($plage->startDate) &&  strtotime($dispo->endDate) > strtotime($plage->endDate) ){
                              echo "[[[4]]]\n";
                                $tmp = new stdClass();
                                $tmp->startDate = $dispo->startDate;
                                $tmp->endDate = $plage->startDate;
                                $tmp2 = new stdClass();
                                $tmp2->startDate = $plage->endDate;
                                $tmp2->endDate = $dispo->endDate;
                                $disponibilities[$i]->exclude = true;
                                array_push($disponibilities,$tmp);
                                array_push($disponibilities,$tmp2);

                        } 
                    }
                }
                for($i=0;$i<count($disponibilities);$i++){
                    if(isset($disponibilities[$i]->exclude)){
                       array_splice($disponibilities,$i,1);
                       $i--;
                    }
                }
                var_dump($disponibilities);

    }

    $times = array();
    $restrictions = array();
    $obj = new stdClass();
    $obj->startDate = '2017-06-26 00:00:00';
    $obj->endDate =  '2017-06-26 07:00:00';
    array_push($times,$obj);
    $obj = new stdClass();
    $obj->startDate = '2017-06-26 00:00:00';
    $obj->endDate = '2017-06-26 24:00:00';
    array_push($times,$obj);


    $obj = new stdClass();
    $obj->startDate =  '2017-06-26 04:00:00';
    $obj->endDate = '2017-06-26 06:00:00';
    array_push($restrictions,$obj);


    $obj = new stdClass();
    $obj->startDate = '2017-06-26 00:00:00' ;
    $obj->endDate = '2017-06-26 01:00:00';
    array_push($restrictions,$obj);


    excludeOverlaping($times,$restrictions);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM