-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Make DateInterval objects uncomparable #4039
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@nikic Thanks for your work here. Would it be possible to add a |
@frederikbosch What format would |
So this will completely break this? https://3v4l.org/O8Okj |
@Antnee Yes. All comparisons will be false and calling var_export() will have no effect on comparison (which it really shouldn't...). |
@nikic Return values could be |
@frederikbosch By special I meant things like |
Hmm, I can see that there are cases where we shouldn't be doing this, @nikic, especially as the behaviour is undefined. But I also know that in cases such as the example in my link where this does currently work, where the interval was calculated via a |
@Antnee To clarify, you are saying that you are calling |
I'm not at the moment, @nikic, but I know that some code that does this exists in production |
Okay, then their code will break (in a nice way: with a warning). If you put this kind of hack in production, then you need to be prepared to deal with the inevitable consequences. I'm happy to discuss alternatives here to allow DateInterval comparisons in some well-defined subset of cases, but the fact that some people thought it was a sensible idea to exploit a var_export side-effect should not prevent us from making a change here. |
I agree with you @nikic. The alternative could be to return the correct comparison when possible. This is possible if the interval represents a constant. That is: the interval represents the same amount of time at any given moment. This would be the case if the interval can be converted to (milli)seconds. Would that be possible (reasonably with the current code) at all? |
There shall not be a |
I'm thinking that the exception we can carve out here is something like this:
Would that be sound? The only potential issue I would see here are leap seconds. |
PHP doesn't care about leap-seconds, so that won't be a problem. I also don't think it's wise to allow comparisons for some intervals, but not others. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm OK with the patch as it stands.
@derickr Right, I think no |
I agree. I wrote some code that did this a few years ago, and I always expected that it would break one day, although I'd hoped that it would work without needing to be inspected first, rather than the comparison being taken completely away. I appreciate the oddities where If you only care about the days, you can currently compare those. So this (crude) example shows comparing whether January has more days than Feb, which is fine. So we can compute an interval and compare which one is larger by this property, and you don't need to inspect the object first - this just works (and we confirm the number of days later). If we need to look at something other than just days, though, it doesn't work. You have to check loads of properties manually, which is a pain. So comparing the objects works nicely: https://3v4l.org/5QC3W The alternative must surely be something like this, right? https://3v4l.org/QVSfC So we just calculate the total number of seconds and it works for me |
OK, so even that doesn't work... Needs some additional handling of the See, it's a pain to compare the two reliably |
The main case I'm concerned about here is comparison of DateIntervals produced by doing a Could we maybe say that only DateIntervals that are created by |
@nikic I still think it's messy. What I think would be best is that if we have a comparable DateInterval (ie. with a days property), we create a subclass (DateAbsoluteInterval?) that can then have a working conparator. The old DateInterval will than have one that always errors. |
@derickr That is a good consideration, and definitely better than having different behaviour when comparing different intervals. I still have one question. The reason I opened the bug report referenced in the start of the thread to begin with was to detect the fact that
When using |
@frederikbosch Apart from not being technically possible right now (there is no distinction between equality and inequality comparisons), restricting this to equality doesn't really help, because the P1M vs P30D issue stays the same. Those could be either equal or not equal. |
@nikic I was not proposing different comparison for equality and inequality. From my point of view those two (P1M and P30D) are always not equal while it is ambiguous who of the two is bigger than the other. My suggestion was to discover whether the following would be a considerable alternative. new DateInterval('P1M') == new DateInterval('P30D') // false
new DateInterval('P1M') != new DateInterval('P30D') // true
new DateInterval('P1M') == new DateInterval('P1M') // true
new DateInterval('P1M') != new DateInterval('P1M') // false
new DateInterval('P1M') < new DateInterval('P1M') // false
new DateInterval('P1M') > new DateInterval('P1M') // false
new DateInterval('P1M') > new DateInterval('P30D') // false + warning
new DateInterval('P1M') < new DateInterval('P30D') // false + warning So my suggestion would be that |
To be clear, by "inequality" I was referring to < > etc here, not !=.
That doesn't really make sense to me. Stepping away from that particular problematic example, I would very much expect that |
Merged as 3cfbbf2 into 7.4. I'll look into implementing Derick's suggestion as a followup. |
Thanks for the work! |
This does not have a NEWS entry yet. Maybe add it after #4063 was merged? |
Fixes https://bugs.php.net/bug.php?id=70810 among others.
The problem is that right now DateInterval uses normal object property comparison. As it doesn't have properties, all DateIntervals are considered equal, which is ... bad.
I've went through a couple iterations here, but in the end went with the simplest variant: When trying to compare two DateInterval objects, throw a warning and return 1 (uncomparable).
The problem here is that there are some intervals like
P1M
vsP30D
that just don't have a well-defined comparison (in absence of a start point). We could carve out some special rules (e.g. consider DateIntervals equal if all components are equal), but I think we might be better off forbidding this entirely. A well-defied comparison of DateIntervals is possible by adding them to a start DateTime and comparing the result.