spatie / period

Complex period comparisons
https://spatie.be/open-source
MIT License
1.6k stars 72 forks source link

Help about adding new period in a range #40

Closed MrEldin closed 5 years ago

MrEldin commented 5 years ago

I have a problem, I tried to achieve this with this package, but I need help:

These are periods of dates:

$periods = new PeriodCollection(
    Period::make('2019-01-01', '2019-01-02', null, Boundaries::EXCLUDE_NONE),
    Period::make('2019-01-03', '2019-01-05', null, Boundaries::EXCLUDE_NONE),
    Period::make('2019-01-06', '2019-01-13', null, Boundaries::EXCLUDE_NONE),
    Period::make('2019-01-14', '2019-01-20', null, Boundaries::EXCLUDE_NONE),
    Period::make('2019-01-21', '2019-01-25', null, Boundaries::EXCLUDE_NONE),
    Period::make('2019-01-26', '2019-02-10', null, Boundaries::EXCLUDE_NONE)
)

And I want to add this new period:

$c = Period::make('2019-01-03', '2019-01-15', null,Boundaries::EXCLUDE_NONE )

But after calculation was made I want to get these results:

[
        ['start_time' => '2019-01-01', 'end_time' => '2019-01-02'],
        ['start_time' => '2019-01-03', 'end_time' => '2019-01-15'],
        ['start_time' => '2019-01-16', 'end_time' => '2019-01-20'],
        ['start_time' => '2019-01-16', 'end_time' => '2019-01-20'],
        ['start_time' => '2019-01-21', 'end_time' => '2019-01-25'],
        ['start_time' => '2019-01-26', 'end_time' => '2019-02-10'],
]

Could you help to achieve this?

I tried to do this with:

$clearPeriods = $periods->boundaries()->diff($c);
$clearPeriods = $clearPeriods->add($c);

And this is fine when you have only 3 periods, but with multiple, I will have to calculate range of periods, so is there a better way to do this?

brendt commented 5 years ago

I'm not sure what exactly you're trying to achieve with

But after calculation was made I want to get these results

What calculation do you mean? Or do you want me to help you figure that out?

If so, it would be helpful to write your examples visually like so:

/*
 * A              [============]
 * B                   [==]
 * C                  [=======]
 *
 * OVERLAP             [==]
 */

(The package adds a visualiser to help you with that: https://github.com/spatie/period#visualizing-periods)

MrEldin commented 5 years ago

Hi @brendt ,

Thank you for your answer, I will try to explain better, so I have that first period collection, and now I want to add new period in that period collection. So after new period is added, I want that period to be added and to fill complete overlap.

I will show that visually:

/*
 * A  [=====]
 * B        [====]
 * C             [====]
 * D                  [===]
 * E                      [===]
 * F      [====]
 */

After making some arrangement, I want to get this? Is there easy way to do this?

RESULT

/*
 * A  [===]
 * B           [==]
 * C              [====]
 * D                   [===]
 * E                       [===]
 * F      [====]
 *
 */
brendt commented 5 years ago

You want to change the order of the periods in a collection, did I understand that correct?

MrEldin commented 5 years ago

No, in my example F is a new period that I want to add in that first period, so One more time:

/*
 * A  [=====]
 * B        [====]
 * C             [====]
 * D                  [===]
 * E                      [===]
 * F      [====] <-- This is new period
 */

So this new period have to cut first two periods and to be added in that gap?

RESULT

/*
 * A  [===]
 * B           [==]
 * C              [====]
 * D                   [===]
 * E                       [===]
 * F      [====] <- This is new period added in gap
 */
brendt commented 5 years ago

Got it! There's no oneliner to do this, but from what I can tell, it shouldn't be too hard:

$newCollection = new PeriodCollection();

/*
 * A                    [=====]
 * F                        [====]
 *
 * OVERLAP                  [=]
 *
 * DIFF(A and OVERLAP)  [==]
 */
foreach ($oldCollection as $A) {
    if (!$A->overlapsWith($F)) {
        $newCollection[] = $A;

        continue;
    }

    $overlap = $F->overlapSingle($A);

    $diff = $A->diff($overlap);

    $newCollection = array_merge($newCollection, $diff);
}

I haven't tested this code, but here's the gist of it:

At the end you can add the F period. I might have overlooked some edge cases in my code sample, so be sure to write some tests for it. I'll close this issue now, but be sure to let me know if this worked, or if I got it wrong :)

MrEldin commented 5 years ago

I had couple of unit tests prepered, so I just add this code, at first it's not worked, but I made some changes, now it is working.

This is working code, well tested:

        $newCollection = [];

        foreach ($periods as $A) {
            if (!$A->overlapsWith($c)) {
                array_push($newCollection, $A);
                continue;
            }

            $overlap = $c->overlapSingle($A);

            $diff = $A->diff($overlap);

            foreach ($diff as $item) {
                array_push($newCollection, $item);
            }
        }

        array_push($newCollection, $c);

        $result = [];

        foreach ($newCollection as $period) {
            array_push($result, [
                'start_time' => $period->getStart()->format('Y-m-d'),
                'end_time' => $period->getEnd()->format('Y-m-d'),
            ]);
        }

        usort($result, [$this, 'sortFunction']);

        $this->assertEquals($expectedResult, $result);

@brendt Thank you so much, and thanks for great package.