How to validate dates without leading zero on day and month?

Sergio Santopietro picture Sergio Santopietro · Jun 14, 2017 · Viewed 10.3k times · Source

I need to validate a precise date format in php.

In my case the optimum date format should be: e.g. 1/1/2017 (without leading zeros) and the code should allow me to avoid the rest of date's format.

The following code is what i wrote but with no result:

if(preg_match("/([1-9]|[1-2][0-9]|3[0-1]-([1-9]|1[0-2])-^[0-9]{4})$/", $date){
    // insert into etc. etc....
}else{
    // update etc. etc....
}

The problem is that the code doesn't properly validate the date; it accepts every kind of date format.

Answer

mickmackusa picture mickmackusa · Jul 31, 2017

I noticed that the OP's submitted answer was not bulletproof, so I started to research some of the functions mentioned in the comments under the question and some of my own thoughts.

I agree with apokryfos in that using regex to validate a date expression is not best practice. A pure regex solution is going to be an increasingly verbose and decreasingly comprehensible pattern because it will have to factor leap years both in the past and future. For this reason, I've investigated a few date functions that php has on offer.

  1. date_parse_from_format() as suggested by Alex Howansky. Unfortunately, it will not be a stand-alone solution for this case because the format parameter obeys the syntax/rules of DateTime::createFromFormat():

enter image description here

See how d and j are grouped together, as are m and n. Consequently, this function will catch invalid dates, but not the unwanted leading zeros.

  1. strptime() as suggested by Casimir et Hippolyte. This function affords matching days without leading zeros using %e. This function does not have a character that matches non-zero-leading month numbers. Furthermore, there can be hiccups with this function bases on the operating system.

  2. check_date() seems like a logical choice, but again it will not flinch at zero-led numbers. Furthermore, this function requires a bit more data preparation than the others, because it requires the month, day, and year values to be individualized.

  3. if(!DateTime::createFromFormat('j/n/Y', $date)) suggested by deceze regarding a slightly different scenario will not work in this case. createFromFormat() will go to great lengths to construct a valid date -- even a string like 99/99/9999 will make it through.


In an attempt to create the most concise expression, here is what I can offer as a seemingly bulletproof solution using a DateTime class:

if (($d=DateTime::createFromFormat('n/j/Y',$date)) && $date==$d->format('n/j/Y')){
    // valid
}else{
    // invalid
}

strtotime() can be used with a single condition for this case because the OP's date format uses slashes as a delimiter and this function will correctly parse the date expression "American-style".

This seems to be the simplest way to check for a valid date without leading zeros: Demo Link

if(date('n/j/Y',strtotime($date))==$date){
    // valid
}else{
    // invalid
}

If you are dealing with datetime expressions and don't wish to check the time portion, you can call this line before conditional line:

$date = explode(' ', $date)[0];  // create an array of two elements: date and time, then only access date

Demo Link

or

$date = strstr($date, ' ', true); // extract substring prior to first space