Link to home
Start Free TrialLog in
Avatar of tofat
tofat

asked on

dd/mm/yyyy php date validation

I found some cool code that is meant to validate the date in dd/mm/yyyy format and also check that the day of the month actually exists i.e.: you can't type in 31/02/2016. But even if I put in a perfectly valid date it still throws the error that the date is invalid for some reason?

 function isDate($string) {
    $matches = array();
    $pattern = '/^([0-9]{1,2})\\/([0-9]{1,2})\\/([0-9]{4})$/';
    if (!preg_match($pattern, $string, $matches)) return false;
    if (!checkdate($matches[2], $matches[1], $matches[3])) return false;
    return true;
}
        if (!isDate($date)) {
    $error .= "invalid date.<br>";
}

Open in new window

Avatar of Marco Gasi
Marco Gasi
Flag of Spain image

Your regex gives error because you're using as delimiter the slash which is used i regex and dates. Change the delimiter to #
To check regex online you can use https://www.regex101.com/
Avatar of tofat
tofat

ASKER

Great link, thanks for that. I changed it to:

#^([0-9]{1,2})\\#([0-9]{1,2})\\#([0-9]{4})$#

and it does't throw any errors any more. However, when I put in a test string of 30/06/2016 it says,

"Your pattern does not match the subject string."

I also tried changing it to :

#^([0-9]{2})\\#([0-9]{2})\\#([0-9]{4})$#
 
because it said that 1,2 was greedy. But that didn't work either.
Tofat, you have to change only delimiters not all slashes :)

#^([0-9]{1,2})\\/([0-9]{1,2})\\/([0-9]{4})$#
Date / Time processing in PHP is fairly well documented here.  The PHP built-in functions and classes have certain rules that work perfectly.  No need for regular expressions at all!  However if you want to use dd/mm/yyyy you would want to translate the slashes into dashes.  Details of the allowable formats are here:
http://php.net/manual/en/datetime.formats.date.php

Translation process:
$ddmmyyyy = str_replace('/', '-', $ddmmyyyy);

Open in new window


Procedural Details
https://www.experts-exchange.com/articles/201/Handling-Date-and-Time-in-PHP-and-MySQL-Procedural-Version.html

Object-Oriented Details
https://www.experts-exchange.com/articles/20920/Handling-Time-and-Date-in-PHP-and-MySQL-OOP-Version.html
$pattern = '#^([0-9]{1,2})\\/([0-9]{1,2})\\/([0-9]{4})$#';
SOLUTION
Avatar of Olaf Doschke
Olaf Doschke
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
For a complete regex, see here: http://stackoverflow.com/questions/15491894/regex-to-validate-date-format-dd-mm-yyyy and it fails for php anyway.

I wouldn't want to put this into my code, then later come back and say - "ah yes". You would rather come back to this code and say "huh?" That feeling also can't really be fixed by a comment. If an error happens and you'd need to fix it, then what?

The short regex is good for prefiltering any strings not even close to a good date format, so you don't try to convert 'hello' into a date. But that's all it may provide.

Bye, Olaf.
@Olaf: the correct date check is done through the checkdate() function calles at line 5. Good point insted for the double escaping: I don't know how I could miss that which result even from here: https://www.regex101.com/r/kN2yE9/1 

So, summarizing, the correct regex is the one pointed out by Olaf: /^[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,4}$/ Once matched, checkdate() should work as expected :)
I see, the preg_match is just extracing the day, month, and year.

Still Ray has the simplest solution, you simply try to insert into a date or datetime (after replacing slashes with dashes) amd handle problems. It may not be the taste of some to not avoid sql errors in advance rather than handle them, many other things but the date may be wrong and so prechecking can be easier.

Bye, Olaf.
Avatar of tofat

ASKER

Thank you all for your comments. I like Ray's post that says I don't have to use regular expressions but I don't know how to implement that. I have quickly put this together but wondered 2 things, how can I change the format from dd/mm/yy to dd/mm/yyyy and how would i validate the form to make it check that the format dd/mm/yyyy is correct?

<html>

<head>
    <?php

    $pretty_date = date('d/m/y');
    $mydate = ($_POST['mydate']);
     
?>
</head>

<body>
        <form method="post" action="<?php echo htmlspecialchars($_SERVER[" PHP_SELF "]);?>">
            <input type="text" name="mydate">
            <input type="submit" name="submit" value="Submit"> Your date is:<?php echo $mydate; ?>
        </form>
</body>

</html>

Open in new window

To format the date for a 4 digit yesr just use Y instead of y
$pretty_date = date('d/m/Y');

Open in new window

But to check if the user used the requested format I think you have to go back to a regex...
Avatar of tofat

ASKER

Haha, I was just thinking that. But then I don't understand Ray's post where he suggests I don't need to use regex. Unless I misunderstood?
Otherwise you could replace your text input with 2 select for days and months and a text input for the year checking if is 4 characters long
Avatar of tofat

ASKER

By the way, I changed the regex to this and it still doesn't work

        function isDate($string) {
    $matches = array();
    $pattern = '^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4})$';
    if (!preg_match($pattern, $string, $matches)) return false;
    if (!checkdate($matches[2], $matches[1], $matches[3])) return false;
    return true;
}
        if (!isDate($date)) {
    $error .= "invalid date.<br>";
}

Open in new window

You missed the delimiters:
$pattern = '/^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4})$/';

Open in new window

<?php
/**
 * https://www.experts-exchange.com/questions/28955059/dd-mm-yyyy-php-date-validation.html#a41686219
 */
error_reporting(E_ALL);

// WRONG: DATE IS JANUARY 7, 2016 IF FORMATTED AS DD/MM/YYYY
$ddmmyyyy = '07/01/2016';
echo PHP_EOL . date('r', strtotime($ddmmyyyy));

// RIGHT
$ddmmyyyy = str_replace('/', '-', $ddmmyyyy);
echo PHP_EOL . date('r', strtotime($ddmmyyyy));

Open in new window

Outputs:
Fri, 01 Jul 2016 00:00:00 -0500
Thu, 07 Jan 2016 00:00:00 -0600

Open in new window

Avatar of tofat

ASKER

It is still saying invalid date:

 function isDate($string) {
    $matches = array();
    $pattern = '/^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4})$/';
    if (!preg_match($pattern, $string, $matches)) return false;
    if (!checkdate($matches[2], $matches[1], $matches[3])) return false;
    return true;
}
        if (!isDate($date)) {
    $error .= "invalid date.<br>";
}

Open in new window

What works with strtotime()?
https://iconoun.com/demo/strtotime.php
<?php // demo/strtotime.php
/**
 * Show what works with strtotime()
 *
 * http://php.net/manual/en/function.strtotime.php
 * http://php.net/manual/en/function.date.php
 * http://php.net/manual/en/datetime.formats.php
 * http://php.net/manual/en/datetime.formats.relative.php
 *
 */
error_reporting(E_ALL);

// RESPONSE STRING HERE
$obuff = NULL;

// MAN PAGE http://php.net/manual/en/function.date-default-timezone-set.php
date_default_timezone_set('America/Chicago');

// IF WE HAVE INPUT FROM THE URL QUERY STRING
if (!empty($_GET["s"]))
{
    $urlink = '<a href="' . $_SERVER['REQUEST_URI'] . '">' . $_GET['s'] . '</a>';

    // COLLECT THE OUTPUT BUFFER
    ob_start();

    // USE strtotime() FUNCTION TO MAKE A TIMESTAMP
    $unix_timestamp = strtotime($_GET["s"]);

    // TEST FOR SUCCESS OR FAILURE
    if ($unix_timestamp === FALSE)
    {
        echo "<strong>HONK!</strong><br /> <u>$urlink</u> NOT USEFUL WITH strtotime() <br/><br/><br/><br/>";
    }

    // ON SUCCESS, PRINT THE RESULTS
    else
    {
        echo "<strong>BINGO</strong><br /> <strong><u>$urlink</u></strong> WORKS WITH strtotime() <br/>";
        echo "THE INTEGER TIMESTAMP VALUE IS ";
        echo number_format($unix_timestamp) . "<br />";

        // FORMAT ISO AND HUMAN-READABLE DATES
        $y = date('c', $unix_timestamp);
        echo "THE ISO8601 DATETIME STRING IS $y<br />";
        $z = date('l, \t\h\e jS \o\f F Y g:i:s A', $unix_timestamp);
        echo "THE TEXTUAL DATE IS $z<br />";
    }

    // RECOVER THE RESPONSE FROM THE OUTPUT BUFFER
    $obuff = ob_get_clean();

} // END OF PROCESSING INPUT

$tzone = date_default_timezone_get();
$phpvs = phpversion();

// CREATE THE HTML FORM USING HEREDOC NOTATION
$htmls = <<<HTMLS
<!DOCTYPE html>
<html dir="ltr" lang="en-US">
<head>
<meta charset="utf-8" />
<title>PHP strtotime()</title>
</head>

<body onload="document.f.s.focus()">

$obuff

<form name="f" method="get">
<br />TO TEST A STRING FOR A VALID DATE/TIME, TYPE IT HERE:<input name="s" />
<input type="submit" value="GO" />
</form>

<br/><strong>Note: Your local time may vary.  This server is in $tzone</strong>
<br/>Current PHP version is $phpvs
<br/>TRY TESTING SOME OF THESE STRINGS (CLICK THE LINK, OR COPY AND PASTE INTO THE FORM):
HTMLS;

echo $htmls;

// DEFINE A FUNCTION TO CREATE A TEST CASE
function t($str)
{
    echo PHP_EOL
    . '<br/>'
    . '<a href="http://iconoun.com/demo/strtotime.php?s='
    . urlencode($str)
    . '">'
    . $str
    . '</a>'
    ;
}

// USE THE FUNCTION TO CREATE A FEW TEST CASES
t('09/01/2014');
t('09-01-2014');
t('- 3 hours');
t('tomorrow');
t('tomorrow 3:15:25 pm');
t('March 15, 1986');
t('yesterday');
t('yesterday + 1 week');
t('next year');
t('now');
t('now + 627 hours 15 minutes');
t('tomorrow midnight');
t('tomorrow 1:35pm');
t('last Tuesday');
t('three days ago');
t('last Friday + 2 weekdays');
t('- 3 days');
t('A TIME');
t('A BOGUS TIME');
t('s time');
t('t time');
t('u time');
t("Avogadro's Constant");
t("Wednesday November, 10 2010 1:01pm"); echo " a misplaced comma";
t("Wednesday, November 10 2010 1:01pm");
t("First Tuesday 2005"); echo " works, but not the way you might think";
t("Last day of January + 1 Month"); echo " works, but not the way you might think";
t("January 31 + 1 Month"); echo " works, but not the way you might think";
t('-1000000000 seconds'); echo " one billion";
t('+1000000000 seconds');
t('42'); echo " and thanks for all the fish!";

// END OF PAGE
echo '</body></html>';

Open in new window

I promise you it will be worth your time to read the articles I mentioned above.  We've all plowed these fields before, and you don't have to reinvent anything here!
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Changed the code so you can do some test changing the querystring value:
http://test.webintenerife.com/check_date.php?date=12/12/2012
Try it like this.  You don't have to write a function to decode a date.  PHP already has one!  It returns the Unix timestamp on success, and FALSE on failure.  Easy!
<?php // demo/tofat_a.php
/**
 * https://www.experts-exchange.com/questions/28955059/dd-mm-yyyy-php-date-validation.html#a41686243
 *
 * http://php.net/manual/en/function.strtotime.php
 *
 * https://www.experts-exchange.com/articles/20920/Handling-Time-and-Date-in-PHP-and-MySQL-OOP-Version.html
 * https://www.experts-exchange.com/articles/201/Handling-Date-and-Time-in-PHP-and-MySQL-Procedural-Version.html
 */
error_reporting(E_ALL);

$bogus = 'foo';
if (!$x = strtotime($bogus)) echo "$bogus is not a date ";

$valid = 'July 4, 1776';
if ($x = strtotime($valid)) echo "$valid is a date ";

Open in new window

Avatar of tofat

ASKER

Hmm. How interesting. I tried this which works:

$bogus = 'test';
if (!$x = strtotime($bogus)) echo "$bogus is not a date ";

$valid = '01-07-2016';
if ($x = strtotime($valid)) echo "$valid is a date ";

Open in new window


But I still want to format it like this 01/07/2016 or is that not possible? If I try it with your code instead of the dashes it throws the error "is not a date"
Please give yourself a little time to read the man page here carefully and for understanding, then think about the question a little bit more.  This stuff has been working correctly in PHP for many, many years.  You just need to copy the best practices shown in the PHP.net web site.
http://php.net/manual/en/datetime.formats.date.php

Once you have a Unix timestamp you can use date() to create the readable version of the date in almost any format imaginable.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Besides not copying {1,4} or at least {2,4} to also be able to test for 2 digit years, as we arrived at strtotime() already and it works for Ray and not you, why don't we take a closer look at the manual?

The manual says a / slash automatically turns interpretation to m/d/y instead of d-m-y with dashes or dots. That's why I also though Rays first post was a good answer.

01/07 works in any case, but in general with swapped month and day portions the dates of course can get wrong, eg 15-07-2016 exists, but not 15/07/2016.

Bye, Olaf.
Avatar of tofat

ASKER

Okay, that worked now, Thanks Ray. I am trying to get this validation right but there is obviously something wrong with this as it isn't working.

$bogus = 'foo';

Open in new window


if (empty($_POST["date"])) {
            $error .="Please select a date<br>";
        }
    
       else if (($date = strtotime($bogus)) === false) {

      $error .="That date is invalid.<br>"
    }

Open in new window

Do you echo $error?

Bye, Olaf.
Avatar of tofat

ASKER

Yes, I do.

 if ($error != "") {

            $error = '<div class="contact-error"><p><strong>There were error(s) in your form:</strong></p>' . $error . '</div>';

            echo $error;

Open in new window

Well, that would have been the obvious reason...
So 'foo' is converted to what $date, then?

Bye, Olaf.
Avatar of tofat

ASKER

Sorry, I am not really sure. I was trying to use part of this example:

<?php
$str = 'Not Good';

// previous to PHP 5.1.0 you would compare with -1, instead of false
if (($timestamp = strtotime($str)) === false) {
    echo "The string ($str) is bogus";
} else {
    echo "$str == " . date('l dS \o\f F Y h:i:s A', $timestamp);
}
?>

Open in new window

Tofat, what about my comment #a41686260?
Well, you have $date computed from 'foo', it is not reported as error, so what does an echo of $date show? That's all I'm asking. If it's empty, you have another indicator of a bogus date. strtotime might not alwways fail, but turn some string to empty/null,0 or an ultimo date, whatever.

Bye, Olaf.
Avatar of tofat

ASKER

Hi Marco,

Sorry, I was quite keen on the method without using regex which is why I am exploring it. If I don't get a workable answer on that soon then I will revert back to regex and definitely try your solution out.
Avatar of tofat

ASKER

Olaf,

I am using ajax for form submission and the reason I can tell the code isn't working is because normally on submit, the errors will appear but now when I submit the loading spinner just spins forever and it does't get further than that. How can I see the echo of date?
Avatar of tofat

ASKER

Just to elaborate:

$date = ($_POST['date']);
    $valid = '01/07/2016';
    $bogus = 'foo';

Open in new window


But I don't know if I am meant to call $valid or not?
I wouldn't test that part of code in the whole environment, that's bound to fail. Seems you wired something totally wrong.

Bye, Olaf.
Straightly doing
$bogus = 'foo';
if (($date = strtotime($bogus)) === false) echo "That date is invalid.<br>";

Open in new window

Results in that message.

Bye, Olaf.
Avatar of tofat

ASKER

I have reverted back to Marco's solution as I am not winning with the other way which I had really wanted to try. I have had 3 people really try to help me now so I don't know how to split up the points. I will try to spread them out fairly between the three of you if that's okay.
Split as you want :)
Avatar of tofat

ASKER

Thanks guys, I appreciate all the assistance!
Very fine. In the end something is working.

Splitting the single values into $matches[2], $matches[1], $matches[3] and putting them into setDate - http://php.net/manual/de/datetime.setdate.php via setDate($matches[3], $matches[2], $matches[1]) may be a solution instead of only checking with checkdate.

That way you accept less formats but if mm/dd/yyyy is your goal you get it that way.

Bye, Olaf.
In an online forum like this it's sometimes hard to know the level of experience each participant has.  Here are a couple of articles that may make your use of E-E richer and your learning path in PHP smoother.  Best of luck with your project, ~Ray

https://www.experts-exchange.com/articles/18625/A-NICE-Approach-to-Dialog-at-E-E.html

https://www.experts-exchange.com/articles/11769/And-by-the-way-I-am-New-to-PHP.html