Framework: Relative dates validation

Created on 21 Apr 2017  路  20Comments  路  Source: laravel/framework

  • Laravel Version: 5.4.6
  • PHP Version: 7.1
  • Database Driver & Version:

Description:

Relative dates validation doesn't work despite the official documentation:

date

The field under validation must be a valid date according to the strtotime PHP function.

https://laravel.com/docs/5.4/validation#rule-date

strtotime works correctly with relative dates like "+1 week +1 hour" or "yesterday", but the Laravel date validation rule does not.

If a user wants to get a date in a strict format he must use date_format, not date. If he uses the date rule he must parse and format it anyway. For example, like this:

$start = new \DateTime($request->input('start'));
$start_formatted = $start->format('Y-m-d H:i:s');

So there is no difference between relative and "normal" dates if you format them using DateTime and format. An you must do that if you already use date.

Currently, date validation rule is successfully passed by the very different arguments:

01/02/2012
2012-01-02
2006-12-12 10:00:00.5 +1 week +1 hour
Feb 2010

So if somebody didn't care about formatting after date validation he is already in danger.

Steps To Reproduce:

?start=yesterday

$this->validate($request, [
    'start' => 'sometimes|required|date',
]);

Fails with The start is not a valid date.

P.S. In my API I really need flexible relative dates support.

Most helpful comment

I think it does not matter what he needs, docs says valid date according to the strtotime.

All 20 comments

Cause you are passing a string with value "yesterday" ?

@Dylan-DPC the string 'yesterday' is a valid input for strtotime.

Ya i see where the problem lies. Working on a fix for it

It could be an issue with PHP date_parse function not working well with timestamps. Digging into this

@Dylan-DPC apparently, when a relative date is provided to the date_parse() function it returns an array with the only relative section. Other fields are empty.

print_r(date_parse('yesterday'));

```
Array
(
[year] =>
[month] =>
[day] =>
[hour] => 0
[minute] => 0
[second] => 0
[fraction] => 0
[warning_count] => 0
[warnings] => Array
(
)

[error_count] => 0
[errors] => Array
    (
    )

[is_localtime] => 
[relative] => Array
    (
        [year] => 0
        [month] => 0
        [day] => -1
        [hour] => 0
        [minute] => 0
        [second] => 0
    )

)

So no wonder
```php
checkdate($date['month'], $date['day'], $date['year'])

returns false.

@chimit Yes i'm aware of that.. date_parse() accepts timestamp strings which the code is passing if you pass the above string. So i'm looking at that

I don't know why @taylorotwell decided to use so complicated function instead of simple strtotime check:

/**
 * Validate that an attribute is a valid date.
 *
 * @param  string  $attribute
 * @param  mixed   $value
 * @return bool
 */
protected function validateDate($attribute, $value)
{
    if (strtotime($value)) {
        return true;
    }

    return false;
}

@chimit your code fails for input '2000-01-01'

@Dylan-DPC really? I don't see any problems:

echo strtotime('2000-01-01'); // 946713600

i tried it and ran it across the framework testcases for date validation. It failed for the above input.

any updates on this?

@chimit Your method will fail on 1970-01-01

@Dylan-DPC @chimit any solutions to get this working?

sorry have been busy with the other things. Do your really need to pass it as yesterday?

I think it does not matter what he needs, docs says valid date according to the strtotime.

Think the docs need to be changed here. Seems like accepting relative date strings directly as valid dates would be a nightmare for BC.

ya you can make a PR to laravel/docs repo

@ntzm yes, it may fail for GMT 0, so we need to make a strict comparison:

/**
 * Validate that an attribute is a valid date.
 *
 * @param  string  $attribute
 * @param  mixed   $value
 * @return bool
 */
protected function validateDate($attribute, $value)
{
    if (strtotime($value) !== false) {
        return true;
    }

    return false;
}

But there are more problems with Unit tests. For example,

$v = new Validator($trans, ['x' => '1325376000'], ['x' => 'date']);
$this->assertTrue($v->fails());

This rule is passed, because

var_dump(strtotime('1325376000')); // int(127187443537)

But @taylorotwell set it to be failed.

Another problem with this assertion:

$v = new Validator($trans, ['x' => new DateTime()], ['x' => 'date']);
$this->assertTrue($v->passes());

strtotime failed, because it takes only strings, not DateTime objects.

So it looks like Taylor's vision of date validation differs a lot with strtotime behavior. The only way to save backward compatibility is to update existing validateDate method by adding relative dates support.

Closing since the PR was refused, you can however add a custom rule in your project if you want.

Thanks everyone :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

YannPl picture YannPl  路  3Comments

JamborJan picture JamborJan  路  3Comments

kerbylav picture kerbylav  路  3Comments

Fuzzyma picture Fuzzyma  路  3Comments

lzp819739483 picture lzp819739483  路  3Comments