8

I am trying to make a date regex validator. The issue I'm having is that I'm using an input field with "date" type, which works like a charm in Chrome; it opens a calendar-like in Chrome, but in the rest it does nothing, so I decided to go for a manual input of the date for the rest.

This is my error throwing message (I'm looking for YYYY-MM-DD format):

$date_regex ='#^(19|20)\d\d[\- /.](0[1-9]|1[012])[\- /.](0[1-9]|[12][0-9]|3[01])$#';
$hiredate = $_POST['hiredate'];
if (!preg_match($date_regex, $hiredate)){
    $errors[] = 'Your hire date entry does not match the YYYY-MM-DD required format.';
}

I know there are a lot of examples about this, but I tried like 20 already and I couldn't solve it. Maybe I'm missing something.

Here's the input field, if somewhat relevant:

<input type="date" name="hiredate" />
11
  • 1
    Why not explode by - and use checkdate() instead? Commented Nov 4, 2013 at 17:26
  • Is there a standard procedure ? I found it in a different post but didn't work for me. FYI, I want to let them insert like YYYY/MM/DD or YYYY.MM.DD or YYYY-MM-DD. In order to not restrain them too much. Commented Nov 4, 2013 at 17:29
  • 3
    Would it be easier to provide 3 separate <input> boxes with an appropriate maxlength and digit-only validate which gets concatednated into the proper format on the backend? You can add listeners to each input box to go to the next one if a person keypressed 2 digits. The user doesn't give an rats behind about what format you need and you will only frustrate them at best if it is not their personal native format Commented Nov 4, 2013 at 17:31
  • I actually thought of that, but wouldn't it make things more complicated keeping in mind some months have 28-29 days, and others 31 ? My guess is that the regex is okay, since I've seen it in a lot of posts. Maybe my error handling is all wrong... But you might have a point. Commented Nov 4, 2013 at 17:34
  • I would build in logic that says If insert fails then slap user in face...I would hope that a person knows not to go out of bounds in a date field or else you probably shouldn't hire them :) Commented Nov 4, 2013 at 17:37

3 Answers 3

16

Do not use regex for this, you can get the same result by using DateTime::createFromFormat

// specify your date's original format, in this example m/d/Y (e.g. 08/31/2013)
$format = "m/d/Y";
$hireDate = DateTime::createFromFormat($format, $_POST['hiredate']);
if(!$hireDate) {
 // createFromFormat returns false if the format is invalid;
} else {
   //change it to any format you want with format() (e.g. 2013-08-31)
   echo $hireDate->format("Y-m-d");
}

you can read more here:

http://php.net/manual/en/datetime.createfromformat.php

However, it seems like the issue is totally unrelated to PHP.

PHP runs on the back end, and it seems like you have a front end problem.

I also doubt the problem is the input type you use. If one browser doesn't support the input type you specified, then it defaults to text. See it here:

http://jsfiddle.net/FKGCA/

My browser doesn't know what the <input type="whatever" /> is, so it defaults the input type to "text". If I wrap those 4 inputs in a <form action="myForm.php" method="POST"></form> tag, the browser sends the inputs to the server because the server doesn't care/know if the inputs were hidden, radio buttons, selects, texts, or password. The servers only receives raw-data.

More than likely, your issue is with your Javascript, and not with your PHP. Try to see if the browser that doesn't display your widget tells you that there's an error of some kind in your page.

Safari and Firefox have development/debugging tools, not so sure about IE.

Sign up to request clarification or add additional context in comments.

2 Comments

That was exactly what I was stating. The "widget" works in Chrome and is converted to a text input type in the others. That's exactly what I am trying to validate. I have up the date format and split the field into three and will go from there.
gotcha, for cases like this is when jQuery comes in handy. Otherwise, you will need to test for support: diveintohtml5.info/detect.html#input-types and if it's not supported. You will have to use javascript-based alternatives (like jQuery)
10

Your regex didn't work because you had unescaped / delimiter.

The regex that would validate date in format YYYY-MM-DD as follows:

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

It will validate that the year starts with 19 or 20, that the month is not greater than 12 and doesn't equal 0 and that the day is not greater than 31 and doesn't equal 0.

Example Online

Using your initial example, you could test it like this:

$date_regex = '/^(19|20)\d\d[\-\/.](0[1-9]|1[012])[\-\/.](0[1-9]|[12][0-9]|3[01])$/';
$hiredate = '2013-14-04';

if (!preg_match($date_regex, $hiredate)) {
    echo '<br>Your hire date entry does not match the YYYY-MM-DD required format.<br>';
} else {
    echo '<br>Your date is set correctly<br>';      
}

Example Online

1 Comment

Yep, this worked okay. I used so many variants of the regex I must have altered it badly. Great eye you have there Ilia Rostovtsev.
0

Check and validate YYYY-MM-DD date in one line statement

function isValidDate($date) {
    return preg_match("/^(\d{4})-(\d{1,2})-(\d{1,2})$/", $date, $m)
        ? checkdate(intval($m[2]), intval($m[3]), intval($m[1]))
        : false;
}

See the details in my answer here.

Don't use blindly DateTime::createFromFormat to validate dates. Let's take non-existent date 2018-02-30 and see:

$d = DateTime::createFromFormat("Y-m-d", "2018-02-30");
var_dump((bool) $d); // bool(true)

Yes, it returns true, not false as you may expected. More interesting:

$d = DateTime::createFromFormat("Y-m-d", "2018-99-99");
var_dump((bool) $d); // bool(true)

Also true... So, it validates just the number of digits. One more try:

$d = DateTime::createFromFormat("Y-m-d", "ABCD-99-99");
var_dump($d); // bool(false)

At last false.

What is going on here we can see from this snippet:

$d = DateTime::createFromFormat("Y-m-d", "2018-02-30");
var_dump($d);

// var_dump OUTPUT
object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2018-03-02 16:41:34.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(3) "UTC"
}

As you can see when we pass non-existent 2018-02-30, the DateTime object contains 2018-03-02. I assume that it's because February 2018 has 28 days, i.e. the maximum date is 2018-02-28, and when we pass the day 30, createFromFormat just adds 30 days to the 2018-02-01 and create new date without any preceding date validation.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.