-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Form] Disallow transform dates beyond the year 9999 #25781
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
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.
Artificial limit of max timestamp with a constant is not a right approach. To keep being compatible with environments where PHP correctly parses even such high values, this fix should instead improve detection when PHP is unable to correctly parse such date and only then throw such exception.
You posted several code snippets in referenced issue which give you pointers how to do that (exception thrown / returned false)
@ostrolucky those code snippets are the result of an hour of research convincing me that this is the only viable implementation. Every other attempt at solving the issue resulted in test cases breaking because, at its core, all of PHP simply cannot support 5 digit years. The The problem in this very specific case is that there's no earlier detection mechanism. The Intl extension is out of our control, and this is the localized transformer class. It may be parsing Klingon Star Dates for all we know. What we do know is that it'll get unreliable once we put it in a So no, I do not believe there is a better alternative than the current fix. All others, like regexping, will boil down to slower implementations to the same effect - we have to act on the timestamp coming out of Intl. Integer comparison is just fastest. |
Here you go https://3v4l.org/qK9BQ Support for such dates should be now just a matter of using this snippet here |
That snippet does not solve the issue, and that is that the
Does not equal the much cleaner:
Because I'm not contesting there are other, prettier ways to fundamentally fix the behavior, but it would be a far more invasive change than what's being done now, and that's a bad idea imo to just fix a simple bug in 2.7. Please keep in mind that all other DateTime transformers are already implicitly broken for 5+ digit years, and that the DateValidator and related classes do a regexp check to ensure the year consists of 4 digits. There is no point in doing a better fix here than what I proposed. There is no functional difference between doing a timestamp-compare with a well-defined and -commented constant, or doing a |
Ok how about this? Passes the unit tests and still preserves 5 digit year @@ -125,16 +125,16 @@
}
try {
- if ($dateOnly) {
- // we only care about year-month-date, which has been delivered as a timestamp pointing to UTC midnight
- $dateTime = new \DateTime(gmdate('Y-m-d', $timestamp), new \DateTimeZone($this->outputTimezone));
- } else {
- // read timestamp into DateTime object - the formatter delivers a timestamp
- $dateTime = new \DateTime(sprintf('@%s', $timestamp));
- }
+ $dateTime = new \DateTime(sprintf('@%s', $timestamp));
+ $timezone = new \DateTimeZone($this->outputTimezone);
// set timezone separately, as it would be ignored if set via the constructor,
// see http://php.net/manual/en/datetime.construct.php
- $dateTime->setTimezone(new \DateTimeZone($this->outputTimezone));
+ $dateTime->setTimezone($timezone);
+
+ if ($dateOnly) {
+ // supplied timestamp in UTC midnight has been shifted by timezone, we need to revert that
+ $dateTime->sub(\DateInterval::createFromDateString($timezone->getOffset($dateTime).' seconds'));
+ }
} catch (\Exception $e) {
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
}
That's kinda right. That might be battles for another pull requests if someone creates issue for them similarly as has been created for this one.
DateValidator skips the check if it receives DateTimeInterface instance already. That poses another problem, but again, that's a battle for different time IMO suporting > 4 digit years might be useful in science and we should strive to support it if we can. There is also no limit on year digits for HTML5 date input, Firefox lets me input there whichever year I want, so there shouldn't be such artificial limit in backend either. There aren't much requests for such support (for now) but giving up right away by throwing exception is not a way. This PR might be good opportunity to start supporting it and lead the way for future support. edit: found this relevant wiki article https://en.wikipedia.org/wiki/Year_10,000_problem, this problem is interesting |
But it would be a functional change, so
I don't think we disagree on any of the core points, I think we definitely should support 5 and even 6 digit years in Symfony. But right now we don't and functionality is broken. This PR fixes the data corruption in 2.7 in the least invasive way possible. The fundamental fixes should go in master. |
Btw this line from that Wikipedia link is interesting:
Limiting to 4 digits is technically adhering to standards right now. |
Not really. If someone raises bug report about date input mangled by transformer (as was the issue this PR fixes), we can fix that by supporting > 4 digits.
My patch fixes those
My interpretation is that year is written with four digits initially, but you are free to add more of them. Like HTML 5 date input does. But ok, this should be agreed on in RFC |
this looks reasonable to me @ogizanagi @HeahDude What do you think? |
Sounds reasonable for me too. A proper support for 5 and greater digits should have a dedicated feature PR. |
I agree. Let's do this (and create a new issue for handling 5+ digits) |
Already done at #25790 😄 |
@@ -22,6 +22,9 @@ | |||
*/ | |||
class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer | |||
{ | |||
// Midnight of last day of 9999 to prevent 5 digit years from being transformed | |||
const MAX_TIMESTAMP = 253370674800; |
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.
This seems to be the last day of the year 9998. Shouldn't this be 253402297199
(or at least 253402210800
)?
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.
Uhm you're right, I must've c/p'ed the wrong line in my tests haha. I did consciously go for UTC midnight of the 31st, as there are +13h timezones and this means it will never be the year 10000 at any (currently known) timezone for the given timestamp. The correct value would be 253402214400 which is 9999-12-31T00:00:00+00:00
.
@@ -22,6 +22,9 @@ | |||
*/ | |||
class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer | |||
{ | |||
// Midnight of last day of 9999 to prevent 5 digit years from being transformed | |||
const MAX_TIMESTAMP = 253402214400; |
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.
Should be a private static
variable so that we won't have to maintain a new public const.
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.
or just placed inlined as it is used only once (a private static
would force the compiler to consider it mutable)
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.
Agreed, sucks that we don't have private const
back in 2.7.
Thank you @curry684. |
…y684) This PR was squashed before being merged into the 2.7 branch (closes #25781). Discussion ---------- [Form] Disallow transform dates beyond the year 9999 Fixes #14727 | Q | A | ------------- | --- | Branch? | 2.7 | Bug fix? | yes | New feature? | no | BC breaks? | not really | Deprecations? | no | Tests pass? | yes | Fixed tickets | #14727 | License | MIT Explicitly locked out submission of dates beyond December 31st 9999 in forms as PHP is highly incapable of consistently handling such dates. Before this patch dates were randomly transformed or mangled. Technically there is a BC break as this will now cause validation to fail on input that was *accepted* before, but it was mangled. Not my call but I prefer the rejection over data corruption: ``` // Old behavior $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, null, \IntlDateFormatter::GREGORIAN, 'yyyy-MM-dd'); $result = $transformer->reverseTransform('20107-03-21'); // $result is now 2007-03-21 ``` Commits ------- 70cc969 [Form] Disallow transform dates beyond the year 9999
Fixes #14727
Explicitly locked out submission of dates beyond December 31st 9999 in forms as PHP is highly incapable of consistently handling such dates. Before this patch dates were randomly transformed or mangled.
Technically there is a BC break as this will now cause validation to fail on input that was accepted before, but it was mangled. Not my call but I prefer the rejection over data corruption: