thephpleague / period

PHP's time range API

Home Page:https://period.thephpleague.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Difference between sequences

Bibendus83 opened this issue Β· comments

Hi everyone.
I noticed there is a diff() method in the Period class but it's missing in Sequence.
It would be nice to be able to substract a sequence of periods from another sequence.

Here is a use case example.
I have a sequence containing all the standard time presences of a worker in a month (ex. mon-fri from 8:00 to 12:00 and from 13:00 to 17:00).
My worker decides to take a half day permit and, later the same month, a full week of holydays.
In that case it would be nice to create a sequence of the absence days so I can substract them from the presences sequence and obtain only the real presences for that month.

@Bibendus83 thanks for using the library.

Can't the use of the Sequence::filter method be used to achieve that πŸ€”

Of course it can be done but it's not immediate and it seems a basic operation to me that should be implemented in the library.
The Sequence::filter method would not be the proper way to do it however because a difference is not always a filter.
What if I want to create an absence on a day between 9:00 and 10:00?

Maybe I could use the Sequence::map method but I'm not sure about that because sometimes I would need to reduce the size of a period, sometimes I would need to split it in 2 periods, sometime I would need to remove the period completely.

Hence the reason why it's not in the package because different rules may endup with a "different" definition of Sequence::diff. Now to be clear, I'm not opposed to it but I would first need to see a PR about it so I can evaluate it's need or inclusion.
One of the reason I avoid its addition was that I was unclear on its implementation to be general purpose. So If you could provide a preliminary work we could try to add it if I do find it enough general and applicable.

I think the same logic about Period:diff should be applied.
In this case, all periods from one sequence should be substracted by all the periods in the other sequence.

Here is a graphical example of what I mean.
image

Oh well... I just discovered that Period::diff doesn't work as I expected or maybe I found a bug.
Here is an example:
$period1: 14:00 - 19:00
$period2: 15:00 - 21:00
$diff = $period1->diff($period2) =

Expected:
14:00 - 15:00

Real result:
[14:00 - 15:00, 19:00 - 21:00]

Is that working as intended?
In the Period::diff method documentation I don't understand what "if both objects share no datepoint" means.

Your are mistaking:

  • gap;
  • diff;
  • .. and intersect;

Please refer to the doc for the differences. πŸ˜‰

Nope, the gap between 14:00 - 19:00 and 15:00 - 21:00 would be empty because they are overlapping and contiguous periods.
Intersect would return the common time period of the 2 ranges (15:00 - 19:00) and it's not what I'm looking for.

What I mean is that when you do subtractions between 2 sets you should get only one result like this:
image

With Period::diff I'm getting both P-Q and Q-P and I'm not even assured that the first value of the returned array contains P-Q.

I should get 2 different results depending on the order of my operation
$p = 14:00 - 19:00;
$q = 15:00 - 21:00;
$p->diff($q) => P - Q => 14:00 - 15:00
$q->diff($p) => Q - P => 19:00 - 21:00

Currently if you do

$q->diff($p) + $q->intersect($p) = $q->merge($p);

The result of diff has never been affected by the order since its introduction in the package. It may be a considered a bug and we may introduce new methods to deal with your case scenario but ideally diff should have returned a Sequence object but the Sequence object was introduced after diff so...

In the next major version this will be changed ;) .

I know that changing a used method could be a great issue for whoever uses this library.
I agree that the old diff function should return a sequence instead of an array, however the new diff should simply return a Period or null.

Considering I'm going to contribute on the code, do you have any idea on how to name the new method name for a proper diff function?

What about diffOnStart and diffOnEnd πŸ€”

Also the methods should throw and not return null if the periods do not overlap like diff does currently.

I was thinking something like substract or sub. It's hard to find a proper name because the mathematic operation of substraction of sets is called difference so diff should be the correct name but unfortunately it's already used.

I can't throw an exception when Q and P don't overlap because it's a valid set operation.

Example 1
P: [1, 5, 7]
Q: [8, 9]
P - Q = P because Q does not overlap with P (no elements in common)

Example 2
P: [1, 3]
Q: [1, 3, 6]
P - Q = empty (NULL) because all elements of P are present in Q

sub used to exist in the lib so substract may do. Start with that we will think about a definitive name while reviewing the PR... Naming thing is always hard πŸ˜‰

I have a problem.
I'm using Sequence::map to substract all periods from another sequence. It may happen that with the substraction a period from the first sequence may become empty.
How can I unset the period while inside the map function?

Why don't you use foreach πŸ€”

@Bibendus83 just to understand how many new methods/PR are you working on ?

  • Period::substract
  • Sequence::diff

If the answer is yes to both methods please create 2 PR for each method separately. this way it will be easier for me to review. Also I believe that Period::substract is simple to implement since you can reuse most of Period::diff implementation code πŸ˜‰

my 2 cents.

Considering they are the same operation I'm calling them both substract and yes, I will create separate PRs.
I completed both methods but Period::substract is using Period::diff at the moment (I'm in hurry to get my feature done but I don't like this solution).
Do you want the unit testing files too with the PR or are you adding them later?

if you can include Unit tests then yes please do it will be easier for me to review them then πŸ‘

Consider that ATM I had to adapt the library to work on my PHP 7.0 environment.
Considering I don't have a working 7.1 envorinment I will have to improvise :)

I believe most of the test should work on 7.0 too ... anyway if you already got a working implementation you can always submit it ... if making the test is too complicated... I'll work around it ... also good point on naming both methods substract ... after a good night sleep seems the right approach to me. Currently diff is commutative while substract won't be so they complement each other nicely πŸ‘

I still need to test my code before I can release it.

I need a method to obtain the sum of the durations of all the periods contained in a sequence.
For example:
[8:00-12:00, 15:00-16:00] will result in 4h+1h=5h

Considering I don't think this method exists, how should i name it?
I would have called it Sequence::getDuration or Sequence::getTotalDuration but a Period::getDuration already exists and has a total different purpose.

Does this method needs to be public or it can be internal ? If it is the latter call it as you wish as it will be private as long as it is self explained it's OK.

Public because it gives utility.
I want to know how many hours of worktime are left after I remove vacations from presences.

you can use the Sequence::reduce method to obtain this information I believe πŸ€”

But I need a time duration, not a period.
I think the best way is to cycle all periods in a sequence using the method Period::getDateInterval.
I would prefer to ignore possible overlaps and just sum all durations.
If someone wants to know the duration without overlaps he can use Sequence::unions first.

reduce can return any type of data no just period πŸ˜„ ... so if you want DateInterval you'll get them or timestamp depends on what function you will give it ... check the documentation.

I don't know, it seems overly complicated when you just need a foreach :)

$func = function (int $carry, Period $interval): int {
    return $carry + $interval->getTimestampInterval();
};
$sequence->reduce($func, 0);

yes but also means that you don't really need a public method for that πŸ˜‰

the package should no try to cover all possible function/method otherwise you'll get a class with 200 methods πŸ˜„

Oh thanks.
BTW don't you think that all basic functions included in Period should be available in Sequence too?

Which methods ?

Period::getTimestampInterval
It would be nice to have a Sequence::getTimestampInterval that obtains the total

At first glance I'd say no ... but this is another discussion/issue πŸ˜‰

I'll add a PR with this function too, then

I'm adding a PR with Period::substract and its tests

I made a first implementation for Sequence::substract, it needs polishment but I need your travis to test my code on PHP 7.1 so I will commit it anyway :)

I'm closing this issue as it has been resolved and an implementation has landed on the master branch.