53

Im trying to to set up a php date validation (MM/DD/YYYY) but I'm having issues. Here is a sample of what I got:

$date_regex = '%\A(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.](19|20)\d\d\z%'; 

$test_date = '03/22/2010'; 
if (preg_match($date_regex, $test_date,$_POST['birthday']) ==true) {
    $errors[] = 'user name most have no spaces';`
Nicolás Ozimica
  • 9,481
  • 5
  • 38
  • 51
Pablo Lopez
  • 741
  • 1
  • 8
  • 11
  • 4
    consider how the date is supplied in the first instance.A client side date picker will greatly reduce the change of getting wrongly formatted dates. –  Aug 19 '12 at 23:43
  • Just try to create a DateTime object with the supplied string. If that fails then the date was invalid – GordonM Jun 29 '17 at 08:37
  • 2
    @user557846 You shouldn't depend on client side to ensure correct data. It's so easily bypassed it should not be considered reliable. It should only be considered an aid to the user, in that it helps them make fewer invalid form submissions. – GordonM Jun 29 '17 at 08:39

13 Answers13

91

You could use checkdate. For example, something like this:

$test_date = '03/22/2010';
$test_arr  = explode('/', $test_date);
if (checkdate($test_arr[0], $test_arr[1], $test_arr[2])) {
    // valid date ...
}

A more paranoid approach, that doesn't blindly believe the input:

$test_date = '03/22/2010';
$test_arr  = explode('/', $test_date);
if (count($test_arr) == 3) {
    if (checkdate($test_arr[0], $test_arr[1], $test_arr[2])) {
        // valid date ...
    } else {
        // problem with dates ...
    }
} else {
    // problem with input ...
}
Nicolás Ozimica
  • 9,481
  • 5
  • 38
  • 51
  • 3
    This is the best solution as it also checks for invalid dates, e.g. Feb 29. A pure regex approach based on formatting would allow invalid dates. – Cody Caughlan Aug 19 '12 at 23:42
  • 2
    +1 for a regexless solution. Not all problems need a regex solution. – Madara's Ghost Aug 19 '12 at 23:43
  • regex solution also checked for a format, while this doesn't. If string `foobar` is passed - you'll get notices – zerkms Aug 19 '12 at 23:49
  • 37
    Let's come into the 21st century. [`DateTime::createFromFormat()`](http://php.net/datetime.createfromformat) and [`DateTime::getLastErrors()`](http://php.net/datetime.getlasterrors). Thank me later. – salathe Aug 19 '12 at 23:55
  • 2
    @salathe has the best approach. Nicolás should really update his answer as it's providing an old approach of doing this. – Sk446 Dec 19 '12 at 10:40
  • Everyone, in multiple posts, keeps saying to use checkdate. But checkdate fails if the year is : 2, 20, 202, 2020 or even 20201 - it returns true every time. – rolinger May 07 '20 at 20:51
  • 1
    @rolinger Why should checkdate fail with such years??? First: are they invalid years? Second: the documentation for `checkdate` explicitly states that: "The year is between 1 and 32767 inclusive." – Nicolás Ozimica Sep 15 '20 at 22:46
55

You can use some methods of the DateTime class, which might be handy; namely, DateTime::createFromFormat() in conjunction with DateTime::getLastErrors().

$test_date = '03/22/2010';

$date = DateTime::createFromFormat('m/d/Y', $test_date);
$date_errors = DateTime::getLastErrors();
if ($date_errors['warning_count'] + $date_errors['error_count'] > 0) {
    $errors[] = 'Some useful error message goes here.';
}

This even allows us to see what actually caused the date parsing warnings/errors (look at the warnings and errors arrays in $date_errors).

salathe
  • 51,324
  • 12
  • 104
  • 132
  • 15
    It's worth to mention that you can check it straight with `DateTime::createFromFormat()` only, by checking if it's false, so `if(DateTime::createFromFormat('m/d/Y', $test_date) === false) exit('bad date format');` – s3m3n Mar 06 '13 at 22:02
  • 5
    Only very crudely @s3m3n. For example, without checking for warnings, "44/33/2211" is a **valid** `m/d/Y` date (it is `2nd Sep 2214` btw). – salathe Mar 06 '13 at 23:22
  • You are right, but in my case I'm only validating if moderator didn't make literal mistake in date format which is going directly to database instead of logical sense of given date. Someone else might need the same thing. – s3m3n Mar 07 '13 at 17:09
  • 1
    DateTime::createFromFormat(PHP 5 >= 5.3.0) – Nitsan Baleli Aug 03 '14 at 13:56
  • 2
    This is not **always** the best approach. If you receive the input already in separated values, but you don't know how exactly they are formatted (`M` vs. `MM`, `YYYY` vs. `YY`), I think `checkdate` would be more flexible and accept anything that resembles a valid date. Additionaly, with `DateTime`, if I write `31` as the **month** it will be considered valid, and simply add `2` years and set it to July (since `31%12 = 7`). I really don't think this would be expected behaviour by many people… – o0'. Jun 19 '15 at 09:20
32

Though checkdate is good, this seems much concise function to validate and also you can give formats. [Source]

function validateDate($date, $format = 'Y-m-d H:i:s') {
    $d = DateTime::createFromFormat($format, $date);
    return $d && $d->format($format) == $date;
}

function was copied from this answer or php.net


The extra ->format() is needed for cases where the date is invalid but createFromFormat still manages to create a DateTime object. For example:

// Gives "2016-11-10 ..." because Thursday falls on Nov 10
DateTime::createFromFormat('D M j Y', 'Thu Nov 9 2016');

// false, Nov 9 is a Wednesday
validateDate('Thu Nov 9 2016', 'D M j Y');
Community
  • 1
  • 1
Joel
  • 1,650
  • 2
  • 22
  • 34
  • 1
    Why/when is the extra verification `$d->format($format) == $date` needed? – Gras Double Jan 29 '16 at 05:20
  • @Gras Double - Validation. He's returning true/false, but ensuring that the DateTime object was created and that the formatted DateTime equals the function input $date. – jjwdesign Feb 12 '16 at 17:52
  • Sure, but a `return (bool) $d;` would do it. My question was, as the createFromFormat succeeded, in what scenario could the `$d->format()` be different from the input? – Gras Double Feb 12 '16 at 20:15
  • I have found this: `validateDate('Mon, 21-Jan-2041 15:24:52 GMT', DateTime::COOKIE);`. Returns false because the `->format()` produces "Monday" instead of "Mon". Though, it would be better to return true, as the input is valid. I'm updating the answer. – Gras Double Feb 12 '16 at 20:31
  • Found out a purpose for the `->format()` verification. Answer updated again. – Gras Double Feb 12 '16 at 21:05
9

Instead of the bulky DateTime object .. just use the core date() function

function isValidDate($date, $format= 'Y-m-d'){
    return $date == date($format, strtotime($date));
}
d.raev
  • 9,216
  • 8
  • 58
  • 79
4

Use it:

function validate_Date($mydate,$format = 'DD-MM-YYYY') {

    if ($format == 'YYYY-MM-DD') list($year, $month, $day) = explode('-', $mydate);
    if ($format == 'YYYY/MM/DD') list($year, $month, $day) = explode('/', $mydate);
    if ($format == 'YYYY.MM.DD') list($year, $month, $day) = explode('.', $mydate);

    if ($format == 'DD-MM-YYYY') list($day, $month, $year) = explode('-', $mydate);
    if ($format == 'DD/MM/YYYY') list($day, $month, $year) = explode('/', $mydate);
    if ($format == 'DD.MM.YYYY') list($day, $month, $year) = explode('.', $mydate);

    if ($format == 'MM-DD-YYYY') list($month, $day, $year) = explode('-', $mydate);
    if ($format == 'MM/DD/YYYY') list($month, $day, $year) = explode('/', $mydate);
    if ($format == 'MM.DD.YYYY') list($month, $day, $year) = explode('.', $mydate);       

    if (is_numeric($year) && is_numeric($month) && is_numeric($day))
        return checkdate($month,$day,$year);
    return false;           
}         
3

Nicolas solution is best. If you want in regex,

try this,

this will validate for, 01/01/1900 through 12/31/2099 Matches invalid dates such as February 31st Accepts dashes, spaces, forward slashes and dots as date separators

(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.](19|20)[0-9]{2}
FirmView
  • 3,130
  • 8
  • 34
  • 50
3

REGEX should be a last resort. PHP has a few functions that will validate for you. In your case, checkdate is the best option. http://php.net/manual/en/function.checkdate.php

Jared Drake
  • 982
  • 4
  • 12
3

This function working well,

function validateDate($date, $format = 'm/d/Y'){
    $d = DateTime::createFromFormat($format, $date);
    return $d && $d->format($format) === $date;
}
1

I know this is an older post, but I've developed the following function for validating a date:

function IsDateTime($aDateTime) {
    try {
        $fTime = new DateTime($aDateTime);
        $fTime->format('m/d/Y H:i:s');
        return true;
    }
    catch (Exception $e) {
        return false;
    }
}
Rich R
  • 367
  • 4
  • 15
1

Try This

/^(19[0-9]{2}|2[0-9]{3})\-(0[1-9]|1[0-2])\-(0[1-9]|1[0-9]|2[0-9]|3[0-1])((T|\s)(0[0-9]{1}|1[0-9]{1}|2[0-3]{1})\:(0[0-9]{1}|1[0-9]{1}|2[0-9]{1}|3[0-9]{1}|4[0-9]{1}|5[0-9]{1})\:(0[0-9]{1}|1[0-9]{1}|2[0-9]{1}|3[0-9]{1}|4[0-9]{1}|5[0-9]{1})((\+|\.)[\d+]{4,8})?)?$/

this regular expression valid for :

  • 2017-01-01T00:00:00+0000
  • 2017-01-01 00:00:00+00:00
  • 2017-01-01T00:00:00+00:00
  • 2017-01-01 00:00:00+0000
  • 2017-01-01

Remember that this will be cover all case of date and date time with (-) character

David Nguyen
  • 21
  • 1
  • 4
  • 1
    This question has already had plenty of far superior answers, what's more it's years old. Your answer adds nothing. Please don't indulge in necromancing. – GordonM Jun 29 '17 at 08:43
0

Not sure if this answer the question or going to help....

$dt = '6/26/1970' ; // or // '6.26.1970' ;

$dt = preg_replace("([.]+)", "/", $dt);

$test_arr  = explode('/', $dt);

if (checkdate($test_arr[0], $test_arr[1], $test_arr[2]) && preg_match("/[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{4}/", $dt))

     { echo(date('Y-m-d', strtotime("$dt")) . "<br>"); }

   else

     { echo "no good...format must be in mm/dd/yyyy"; }
Berke Cagkan Toptas
  • 1,034
  • 3
  • 21
  • 31
Dennis
  • 1
0

We can use simple "date" input type, like below:

Birth date: <input type="date" name="userBirthDate" /><br />

Then we can link DateTime interface with built-in function 'explode':

public function validateDate()
    {
        $validateFlag = true;
        $convertBirthDate = DateTime::createFromFormat('Y-m-d', $this->birthDate);
        $birthDateErrors = DateTime::getLastErrors();

        if ($birthDateErrors['warning_count'] + $birthDateErrors['error_count'] > 0)
        {
            $_SESSION['wrongDateFormat'] = "The date format is wrong.";
        }

        else
        {
            $testBirthDate = explode('-', $this->birthDate);
            if ($testBirthDate[0] < 1900)
            {
                $validateFlag = false;
                $_SESSION['wrongDateYear'] = "We suspect that you did not born before XX century.";
            }
        }

        return $validateFlag;
    }

I tested it on Google Chrome and IE, everything works correctly. Furthemore, Chrome display simple additional interface. If you don't write anything in input or write it in bad format (correctly is following: '1919-12-23'), you will get the first statement. If you write everything in good format, but you type wrong date (I assumed that nobody could born before XX century), your controller will send the second statement.

Plusce
  • 117
  • 2
  • 14
0

I think it will help somebody.

function isValidDate($thedate) {
    $data = [
        'separators' => array("/", "-", "."),
        'date_array' => '',
        'day_index' => '',
        'year' => '',
        'month' => '',
        'day' => '',
        'status' => false
    ];

    // loop through to break down the date
    foreach ($data['separators'] as $separator) {
        $data['date_array'] = explode($separator, $thedate);
        if (count($data['date_array']) == 3) {
            $data['status'] = true;
            break;
        }
    }

    // err, if more than 4 character or not int
    if ($data['status']) {
        foreach ($data['date_array'] as $value) {
            if (strlen($value) > 4 || !is_numeric($value)) {
                $data['status'] = false;
                break;
            }
        }
    }

    // get the year
    if ($data['status']) {
        if (strlen($data['date_array'][0]) == 4) {
            $data['year'] = $data['date_array'][0];
            $data['day_index'] = 2;
        }elseif (strlen($data['date_array'][2]) == 4) {
            $data['year'] = $data['date_array'][2];
            $data['day_index'] = 0;
        }else {
            $data['status'] = false;
        }
    }

    // get the month
    if ($data['status']) {
        if (strlen($data['date_array'][1]) == 2) {
            $data['month'] = $data['date_array'][1];
        }else {
            $data['status'] = false;
        }
    }

    // get the day
    if ($data['status']) {
        if (strlen($data['date_array'][$data['day_index']]) == 2) {
            $data['day'] = $data['date_array'][$data['day_index']];
        }else {
            $data['status'] = false;
        }
    }

    // finally validate date
    if ($data['status']) {
        return checkdate($data['month'] , $data['day'], $data['year']);
    }

    return false;
}