10

How do I make the expression which checks the birthday input to match a format like this dd/mm/yyyy? Below is what I came out so far, but it takes this too if I put 99/99/9999!

if (!preg_match("/[0-9]{2}\/[0-9]{2}\/[0-9]{4}/", $cnt_birthday))
  {
   $error = true;
   echo '<error elementid="cnt_birthday" message="BIRTHDAY - Only this birthday format - dd/mm/yyyy - is accepted."/>';
  }

How can I make sure that its only 01 to 31 for dd and 01 to 12 for mm? but I am sure how to restrict yyyy... I think theoritical 9999 should be acceptable... let me know if you have a better idea!

thanks, Lau

Andy Lester
  • 91,102
  • 13
  • 100
  • 152
Run
  • 54,938
  • 169
  • 450
  • 748

11 Answers11

26

I would suggest using checkdate() for this instead:

if (preg_match("/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/", $cnt_birthday, $matches)) {
    if (!checkdate($matches[2], $matches[1], $matches[3])) {
        $error = true;
        echo '<error elementid="cnt_birthday" message="BIRTHDAY - Please enter a valid date in the format - dd/mm/yyyy"/>';
    }
} else {
    $error = true;
    echo '<error elementid="cnt_birthday" message="BIRTHDAY - Only this birthday format - dd/mm/yyyy - is accepted."/>';
}

So regexp validates the format, checkdate validates the actual date.

Tim Fountain
  • 33,093
  • 5
  • 41
  • 69
  • 1
    Preg_match are not used for that, a 20lines pattern could do the job, but it would be complex as it would require to "hard code" the number of days of each month, and check if there is a February 29th, checkdate do exactly what you want, you should use it. – Dominique Sep 15 '10 at 19:21
  • The above would accept invalid dates such as -30/12/2021 or 30/12/2021aaa hence a slight modification is needed to ensure first/last characters start with/are final and only digits: `/^([0-9]{2})\/([0-9]{2})\/([0-9]{4})$/`. If you want to make your regex stricter i.e. limit days to 31, months to 12 and e.g. year 1950-2099 then `/^(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[0-2])\/(19[5-9]\d|20[0-9]\d|2099)$/`. Or if you want to change year range from 1900-2099 then `/^(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[0-2])\/(19|20)\d{2}$/`. More explanation: https://regex101.com/r/z7RsdU/1/ – webcoder.co.uk Sep 30 '21 at 10:25
26

Based on Tim's checkdate based solution:

The extraction of day, month and year can easily be done using explode as:

list($dd,$mm,$yyyy) = explode('/',$cnt_birthday);
if (!checkdate($mm,$dd,$yyyy)) {
        $error = true;
}
Community
  • 1
  • 1
codaddict
  • 445,704
  • 82
  • 492
  • 529
  • 1
    $yyy should be $yyyy in list($dd,$mm,$yyy). (and the if block is missing a closing bracket.) – catgofire Sep 15 '10 at 20:24
  • 1
    The answer below is the actual correct one. If you enter '01/01/80' the function will pass even though there are only two and not four digits of the year defined. http://3v4l.org/uQ2oH – emeraldjava May 01 '13 at 17:08
  • I'm fairly certain that codaddict is intending to simplify the extraction of the date components. Hence when he says, "Based on Tim's `checkdate` based solution... – Mlagma Jun 30 '13 at 06:33
  • 1
    Very helpful but does not answer the question: an "expression" aka regular expression. – Xaxis Aug 16 '13 at 17:22
3
if(preg_match("/(\d{2})\/(\d{2})\/(\d{4})$/", $date,$matches)){         
return (checkdate((int) $matches[2],(int)$matches[1],(int) $matches[3]) );
}else{
return false ;
}
  • preg_match to validate pattern dd/mm/yyyy
  • checkdate to validate date values
Yehia
  • 386
  • 2
  • 9
  • According to this if I provide 12/31/1992 this will also valid but it is not. So is there any max value check or something. – Sunil Pachlangia Sep 15 '17 at 14:32
  • the pattern match will pass that but the checkdate wont. – Yehia Sep 16 '17 at 15:54
  • Use pattern DD (01-31), MM (01-12) and YYYY (1950-2099): `/^(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[0-2])\/(19|20)\d{2}$/` or with an extended YYYY range 1900-2099 `/^(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[0-2])\/(19|20)\d{2}$/`. – webcoder.co.uk Sep 30 '21 at 10:30
3

Consider using strtotime() and reformat it with date(). It will provide more flexibility for users while entering a date and let's you use whatever formats you need in different places.

Personally, I am pretty lazy when it comes to accurate date calculation and abuse it like strtotime("-10 day",$timestamp). This has the benefit of lower possibility of getting sued by an annoyed parent becuse you calculated their little daughters age to be just above 18, not accounting for leap years correctly, and let her to your adult themed site, however ridiculous it may sound.

artificialidiot
  • 5,309
  • 29
  • 27
  • +1 for flexible input. Users are notoriously lazy, if not also less intelligent. Making them have to stop to think can be hazardous to your health. – Kevin Vaughan Sep 16 '10 at 02:09
2
$ok  = DateTime::createFromFormat('d/m/Y',$datestring)->format('d/m/Y') == $datestring;

PHP >= 5.3

Wrikken
  • 69,272
  • 8
  • 97
  • 136
1

maybe something like this would help

 list($month,$day,$year)=explode("/",$date);
if(checkdate($month,$day,$year))
{
echo "good";
}
else{echo "bad";}
Sujay sreedhar
  • 3,498
  • 2
  • 21
  • 28
1

To be really anal-retentive it might be easier to use your current regex, parse the numbers, then verify they're in range with checkdate(), but for kicks, here's the regex that ensures dates = 01-31 (and 1-9), and month = 01-12 (and 1-9).

preg_match("/([012]?[1-9]|[12]0|3[01])\/(0?[1-9]|1[012])\/([0-9]{4})/", $date_string)

Couple things of note

  • I've used grouping on all, required for the ORing (|) within, but also useful to extract those values if you want to do specific things with them
  • 0000 doesn't make much sense as a date, but I've left the explosion of that regex as an excersise to the reader. If you really want this to verify birthdates (and you're expecting currently or relatively recently deceased people) restrict that to say 1900+ or 1800+, or whatever is an acceptable range for you. If you might be parsing historical figures' birthdays... your call.
  • This still doesn't check that the date range is correct for the month in question! so for that use checkdate()
Chadwick
  • 12,555
  • 7
  • 49
  • 66
  • 1
    A fine example why not to use a regex. For even leap year (sort of) awareness: http://regexadvice.com/blogs/mash/archive/2004/04/02/300.aspx – Wrikken Sep 15 '10 at 23:33
0

Probably not the best solution, but here's my try.

You convert it to a time, and then reformat it back to the m/d/Y. If the string is unchanged, then, it was in the correct format to begin with.

$transformedDate = date("m/d/Y", strtotime($myDate));

if($transformedDate == $myDate){        
    return true;    
} else{         
    return false;           
}
NL3294
  • 984
  • 1
  • 10
  • 27
0

Only accepting a strictly formatted string is probably a bad practice. Assuming you're getting input from a webpage, it would be better to have separate fields for month, day, and year. They could just be text boxes, but it might be preferable to have drop-down menus, which would solve your limits problem (i.e. the only choices for month are 1,2,...,12). Requiring that users enter 01/01/2001 and not accepting 1/1/2001 is lazy programming. And only accepting "/" as a separator is awkward.

But, to touch on your original question even if you decide to stick with formatted strings — since it's a birthdate field, you should probably restrict the yyyy to:

if($yyyy > date('Y')) {
  echo '<error elementid="cnt_birthday" message="BIRTHDAY - Year must be less than or equal to the current year."/>';
}

Otherwise people could have negative ages :)

catgofire
  • 603
  • 5
  • 6
0

Try this regex : ([0-9]{2}\/[0-9]{2}\/[0-9]{4})

Sebastian Lenartowicz
  • 4,695
  • 4
  • 28
  • 39
dialyy
  • 9
  • 1
  • 1
0

I'm late to see this, but this solved my problem

    if (1 !== preg_match('/(0[1-9]|1[0-9]|2[0-9]|3(0|1))\/(0[1-9]|1[0-2])\/\d{4}/',$value)) {
        $this->form_validation->set_message('validate_emaildate','Date needs to have a valid date format - dd/mm/yyyy');
        return FALSE;
    }

Courtesy the following posts:

Regex format here.. thanks Abin.

Function to check the format [here] (Codeigniter's regex match) .. thanks Hashem

+cheers

Community
  • 1
  • 1
devak23
  • 419
  • 1
  • 4
  • 6