Laravel Version: 5.5
PHP Version: 7.2.0-2+ubuntu16.04.1+deb.sury.org+2
Database Driver & Version: -
When I create a ISO String in javascript for the backend, the only possible way I found to match the format was to use 'Y-m-d\TH:i:s.u\Z'. According to the PHP documentation since version 7.0 you can supplement u with v and get the exact results that Javascript is producing. Using u actually add 000 to the milliseconds to make microseconds. I tried everything I can to use the v but it does not want to work for my php 7.2 installation (might be an issue with php 7.2).
Never the less, PHP does not mind the small difference and neither does JS. What I mean to say is that I can use u and consume JS ISO format and JS can consume the extra 000.
The problem is not one that many people will encounter (I just like the precision). However this might be a requirement for some applications.
The way I see it there is one of two options:
Make sure to use PHP 7.2 (not sure about rest)
Post a javascript (new Date()).toISOString() to an api.
Validate that string using any PHP date string. It seems insurmountable. Example:
[
...
// does not work
'deadline' => 'date_format:Y-m-d\TH:i:s.u\Z',
// should work but does not work
'starting_date' => 'date_format:Y-m-d\TH:i:s.v\Z',
...
]
I am happy to handle this edge case in my App or write code for handling the problem for the framework. That is if I will be allowed to do so and can get a few +1's to confirm.
If you are like me and would like a temporary solution that might not adhere to the ISO8601 standard (who does?), then you can use this regex.
'date' => 'regex:/\d{4}-[01][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-5][0-9].\d{3}Z/'
Laravel uses the DateTime::createFromFormat method to validate it, that method doesn't support v, it's from PHP
http://php.net/manual/en/datetime.createfromformat.php
This is the validation code:
public function validateDateFormat($attribute, $value, $parameters)
{
$this->requireParameterCount(1, $parameters, 'date_format');
if (! is_string($value) && ! is_numeric($value)) {
return false;
}
$format = $parameters[0];
$date = DateTime::createFromFormat('!'.$format, $value);
return $date && $date->format($format) == $value;
}
Should probably request in the php repo for it to be added
Yes, that is the code that is used by laravel internally. I appreciate that you pointed it out.
However I am not ever using v. I use DateTime:ISO8601 as the format. This gives me a format string of "Y-m-d\TH:i:sO".
To my mind the issue is either in Javascript/Php's interpretation of what the ISO standard is. I don't know the standards and I guess that that would be the place to start looking.
As I mentioned the format PHP uses adds 000. I think it could be beneficial to handle this as a loose comparison and not a strict comparison.
This is a javascript iso string: 2018-01-23T21:53:12.783Z
This is a PHP iso string: 2018-01-23T21:53:12+0000
v only works with DateTime::format() but not with DateTime::createFromFormat().
Here is the bug report: https://bugs.php.net/bug.php?id=75577
I'm going to close this, as it seems to be a bug in PHP or the DateTime objects - and not Laravel itself.
Feel free to let me know if you think this should be re-opened
This has been fixed in PHP 7.3.
@laurencei @staudenmeir @dewwwald
I would like to open this issue again because is not PHP related problem.
The "v" argument is not used in this case.
Validating the following string 2019-01-30T23:00:00.000Z against the format below:
date_format:Y-m-d\TH:i:s.u\Z
returns:
The birthday does not match the format Y-m-d\TH:i:s.u\Z.
I'm using "u" argument, not "v".
But if I try to:
$data = \Carbon\Carbon::createFromFormat('Y-m-d\TH:i:s.u\Z', '2019-01-31T23:00:00.000Z');
dump($data);
$data2 = \DateTime::createFromFormat('Y-m-d\TH:i:s.u\Z', '2019-01-31T23:00:00.000Z');
dump($data2);
I have the corret result:
Carbon @1548975600 {#707 â–¼
date: 2019-01-31 23:00:00.0 UTC (+00:00)
}
DateTime @1548975600 {#726 â–¼
date: 2019-01-31 23:00:00.0 UTC (+00:00)
}
So it seems to be a problem with Validator.
The validation uses the following code
date = DateTime::createFromFormat('!'.$format, $value);
return $date && $date->format($format) == $value;
So it just creates a date using the passed value then confirms that it is a date with that format. Try those couple of lines in tinker with a date and see if it returns true.
Thank you @devcircus!
Now I understand the problem, it's really a PHP problem:
The "u" format return 6 digits milliseconds, while the pattern is 3 digits.
For the future people looking for a solution PHP <= 7.2:
date_format:Y-m-d\TH:i:s.\0\0\0\Z
Most helpful comment
Thank you @devcircus!
Now I understand the problem, it's really a PHP problem:
For the future people looking for a solution PHP <= 7.2: