12

I'm obviously not using filter_var() correctly. I need to check that the user has entered a valid date, in the form "dd/mm/yyyy".

This simply returns whatever I passed as a date, while I expected it to return either the date or 0/null/FALSE in case the input string doesn't look like a date:

$myregex = "/\d{2}\/\d{2}\/\d{4}/";
print filter_var("bad 01/02/2012 bad",FILTER_VALIDATE_REGEXP,array("options"=>array("regexp"=> $myregex)));

If someone else uses this function to check dates, what am I doing wrong? Should I use another function to validate form fields?

Thank you.

Gulbahar
  • 5,343
  • 20
  • 70
  • 93

10 Answers10

27

Using regex to validate date is a bad idea .. Imagine 99/99/9999 can easily be seen as a valid date .. you should checkdate

bool checkdate ( int $month , int $day , int $year )

Simple Usage

$date = "01/02/0000";
$date = date_parse($date); // or date_parse_from_format("d/m/Y", $date);
if (checkdate($date['month'], $date['day'], $date['year'])) {
    // Valid Date
}
Baba
  • 94,024
  • 28
  • 166
  • 217
  • 2
    **Attention** if you perform `date_parse( '2019-02-313' );` the result has `31` as day value. Maybe in this case you need to check the value of the field `$date['error_count']` – Zauker May 14 '19 at 09:12
16
$myregex = '~^\d{2}/\d{2}/\d{4}$~';

The regex matched because you just require that pattern anywhere in the string. What you want is only that pattern and nothing else. So add ^ and $.

Note that this still doesn't mean the value is a valid date. 99/99/9999 will pass that test. I'd use:

if (!DateTime::createFromFormat('d/m/Y', $string))
deceze
  • 510,633
  • 85
  • 743
  • 889
5

A simple and convenient way to verify the date in PHP is a strtotime function. You can validate the date with only one line:

strtotime("bad 01/02/2012 bad"); //false
strtotime("01/02/2012"); //1325455200 unix timestamp
Cmyker
  • 2,318
  • 1
  • 26
  • 29
  • 1
    Great for a short fix. But be careful of MM/DD/YYYY being read backwards to DD/MM/YYYY or other types of traps. – Sablefoste Dec 27 '16 at 04:50
  • Did you try also with this value '2019-02-31' ? :) `strtotime` respond with a Unix Timestamp, but maybe you have to consider invalid this date. – Zauker May 14 '19 at 09:16
4

Why using a heavy RegEx, while there is a native DateTime PHP-class?

$value = 'badbadbad';

try {
    $datetime = new \DateTime($value);

    $value = $datetime->format('Y-m-d');
} catch(\Exception $e) {
    // Invalid date
}

Always returns the format you were expecting, if a valid datetime string was inserted.

Tim
  • 2,805
  • 25
  • 21
1

The better solution is posted by @baba, but the code posted doesn't work fine in certain conditions.

For example if you try to validate this date "2019-12-313";

$date = date_parse("2019-12-313");

the function returns this array:

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

    [error_count] => 1
    [errors] => Array
        (
            [10] => Unexpected character
        )

    [is_localtime] => 
)

so, if you perform the command:

checkdate($date['month'], $date['day'], $date['year']);

checkdate will returns a true value because the value of $date['day'] is 31 instead of 313.

to avoid this case maybe you have to add a check to the value of $date['error_count']

the @baba example updated

$date = "2019-12-313";
$date = date_parse($date); // or date_parse_from_format("d/m/Y", $date);
if ($date['error_count'] == 0 && checkdate($date['month'], $date['day'], $date['year'])) {
    // Valid Date
}
Zauker
  • 2,344
  • 3
  • 27
  • 36
1

You can check date by using this function:

function is_date_valid($date, $format = "Y-m-d"){
    $parsed_date = date_parse_from_format($format, $date);
    if(!$parsed_date['error_count'] && !$parsed_date['warning_count']){
        return true;
    }

    return false;
}

How to use it:

$is_valid = is_date_valid("2021-8-1"); // it will return boolean.

Note: You can change the format of the date via pass the format in the second parameter.

Mohamad Hamouday
  • 2,070
  • 23
  • 20
0

Try setting the start and end for your regex like this:

$myregex = "/^\d{2}\/\d{2}\/\d{4}$/";
Benjamin Paap
  • 2,744
  • 2
  • 21
  • 33
0

$myregex = "~^(19|20)\d\d[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$~";

print filter_var("bad 01/02/2012 bad",FILTER_VALIDATE_REGEXP,array("options"=>array("regexp"=> $myregex)));
0

A more succinct way of validating date would be to use DateTime::createFromFormat

Here's a function that adequately handles it:

    function isValidDate(string $date, string $format = 'Y-m-d')
    {
        $dateObj = DateTime::createFromFormat($format, $date);
        
        return ($dateObj && $dateObj->format($format) == $date) ? true : false;
    }

Check date is valid using:

    $dateString = "";
    
    if(isValidDate($dateString, 'Y-m-d H:i:s'))
    {
        //Date is valid
    }
    else
    {
        //Date is not valid
    }

Just substitute with desired format(s), and that's it.

-1

You received a lot of answers that indicated the RegEx would allow for an answer of 99/99/9999. That can be solved by adding a little more to the RegEx. If you know you'll never have a date outside of the years 1900-2099... you can use a RegEx like this:

/^(0[1-9]|[12][0-9]|3[01])[/](0[1-9]|1[012])[/](19|20)\d\d$/

That will validate a date of this format: dd/mm/YYYY

Between: 01/01/1900 - 31/12/2099

If you need more years, just add them near the end. |21|22|23|24|25 (would extend it to the year 2599.)

AJ Quick
  • 161
  • 14