L8.15.0 - Validation for RFC3339_EXTENDED date format doesn't work
vlauciani opened this issue · comments
- Laravel Framework 8.15.0
- PHP Version: 7.4
Description:
The validation for RFC3339_EXTENDED PHP date format, doesn't work correctly.
Steps To Reproduce:
<?php
// ./routes/web.php
use Illuminate\Support\Facades\Route;
Route::get('test_date', function () {
$array = [
'mydate1' => '2018-01-29T20:36:01.123Z',
'mydate2' => '2018-01-29T20:36:01.123+00:00'
];
Validator::make($array, [
'mydate1' => 'required|date_format:"' . \DateTimeInterface::RFC3339_EXTENDED . '"',
'mydate2' => 'required|date_format:"' . \DateTimeInterface::RFC3339_EXTENDED . '"'
])->validate();
return "Ok";
});
Both mydate1 and mydate2 are a valid RFC3339_EXTENDED PHP date format:
but calling the test_date route on your browser, Laravel Validation return an error on mydate1.
The validator just uses DateTime::createFromFormat behind the scenes so this isn't a Laravel issue.
@driesvints DateTime::createFromFormat works; the date are "valid". Please, check the link to the sandbox.
@vlauciani although both dates are created with no errors when calling DateTIme::createFromFormat(...), the one ending with Z does not match the format string specified in the DateTimeInterface::RFC3339_EXTENDED constant.
If you run:
echo DateTimeInterface::RFC3339_EXTENDED;You will get
Y-m-d\TH:i:s.vP
In PHP docs' date format page where they list all the format tokens P is described as:
| format character | Description | Example returned values |
|---|---|---|
| P | Difference to Greenwich time (GMT) with colon between hours and minutes | Example: +02:00 |
Reference: https://www.php.net/manual/en/datetime.format.php
So P should not match Z. Actually Z is meant to be accepted by using p instead (lower case p) if you look in the table within the link above.
I don't know why DateTime::createFromFormat(...) doesn't fail with this mismatch between the format string and the value provided. Might be a bug or some intended behavior to widen up date strings processing. Maybe you could try PHP's externals/internals mail list to learn the rationale around it.
But, in my opinion, it is not a bug in how Laravel handles this validation. Actually Laravel's validation for date and time format is pretty straightforward:
framework/src/Illuminate/Validation/Concerns/ValidatesAttributes.php
Lines 391 to 393 in ca0b82a
Just for the record I tested without the leading exclamation mark and it still fails validation with the date ending in Z.
As a workaround, if you need to accept both formats you could try a custom rule:
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Str;
use Illuminate\Validation\Concerns\ValidatesAttributes;
class DateFormat implements Rule
{
use ValidatesAttributes;
private string $format;
public function __construct(string $format)
{
$this->format = $format;
}
public function passes($attribute, $value)
{
if ($this->format === \DateTimeInterface::RFC3339_EXTENDED && Str::endsWith($value, 'Z')) {
$value = Str::replaceLast('Z', '+00:00', $value);
}
return $this->validateDateFormat($attribute, $value, [$this->format]);
}
public function message()
{
return Str::replaceFirst(':format', $this->format, \trans('validation.date_format'));
}
}The use it as:
$array = [
'mydate1' => '2018-01-29T20:36:01.123Z',
'mydate2' => '2018-01-29T20:36:01.123+00:00',
];
$validator = \Illuminate\Support\Facades\Validator::make($array, [
'mydate1' => ['required' , new \App\Rules\DateFormat(\DateTimeInterface::RFC3339_EXTENDED) ],
'mydate2' => ['required' , new \App\Rules\DateFormat(\DateTimeInterface::RFC3339_EXTENDED) ],
])->validate();
return 'ok';Hope this helps.
Thank you very much @rodrigopedra