PHP regular expression to check if MM/DD are within proper range - php

I have a form that keeps throwing an error to users because they don't understand that their birthdate needs to be in MM/DD format. I'm not asking for the year, but some put it in as MM/YY and don't realize I want the day not the year.
I tried to do this with regex but couldn't figure it out, so I wrote this
$ckdate = explode("/", $mySub['Birthday']);
if($ckdate[0]>12 || $ckdate[0]<1){
echo 'Please make sure your birthdate is in a valid format MM/DD, we ask for month and day only';
return;
}
if($ckdate[1]>31 || $ckdate[1]<1){
echo 'Please make sure your birthdate is in a valid format MM/DD, we ask for month and day only';
return;
}
Is this the best way to do this? It seems inefficient. I'm wondering if regex is the way, or is there a PHP function to do this?
Thanks in advance.

You could use checkdate() and just enter a dummy year. For example: checkdate($m, $d, 2000).
Note: you probably want to use a leap year as the dummy year so those born on February 29th aren't left out in the cold.

Related

PHP date, how to prevent php date for auto convert to next day? [duplicate]

This question already has answers here:
Correctly determine if date string is a valid date in that format
(20 answers)
Closed 4 years ago.
As you may know, php date will return a valid date on these dates:
year-04-31
year-06-31
year-09-31
I have read other SO thread here about the non existed date, but this thread did not solve my problem. My goal is to prevent php to automatically let these date convert to next day. For example, if user input date 2018-04-31, the system will prompt an error.
$value = '2018-06-31'; // assume user input this date
if(date is 04-31, 06-31 or 09-31) // i have no idea what to put here
echo 'wrong date, please enter again';
EDIT
someone actually posted this question as duplicate for for this thread in SO...which part it says something about 4-31, 6-31, 9-31? please dont simply mark this as duplicate, make your comparison between my question and the others before mark. thank you
Try the following
<?php
$date = '2018-06-31';
function checkIsAValidDate($date){
return (bool)strtotime($date) && date("Y-m-d", strtotime($date)) == $date;
}
if(checkIsAValidDate($date) == 1){
echo 'valid';
}else{
echo 'not valid';
}
?>
This will validate the format, and that the date is real. IE, there are only 30 days in the month of June.
strtotime will return FALSE in case it cannot convert string to timestamp. So you can try:
if (strtotime('<your date time string here>') === false) {
// TODO.
}
----- EDIT -----
Thanks to #Nick.
strtotime may wrong in some case as '02-30'. So I suggest another solution:
$date = explode('-', '<date time string>');
if (checkdate($date[1], $date[2], $date[0]) {
// TODO
}
I guess you don't to allow users to input 31st when the month is of 30 days. In that case why don't you simple use a datepicker? Instead of allowing users to enter the date make them choose one. If you are scared about editing after the date is chosen then use a javascript function to disable text input in the filed and allow only clicks which makes datepicker to appear.

How do you validate a date format?

I want my users to be able to change the date format to whatever they like:
$dateformat = $_POST['format'];
$datestamp = date($dateformat,$timestamp);
How do I check if $dateformat is a correct valid date format string?
if( strtotime($_POST['format']) !== FALSE)
{
echo 'valid date';
}
else
{
echo 'invalid date';
}
But will not work on dates having year > 2048 as not defined in current PHP
checkdate() will tell you whether the date can be parsed should you decide to stick with a certain format:
strtotime() will let you know if a date string can be parsed - you can pass it any string. If the date cannot be parsed, the function returns false.
As a side note: I wouldn't let the user choose how to enter dates. This will make it very complicated for you, obviously - but also as user I would ask myself how I am supposed to enter the date, since most of the users will be used to instruction with regard to dates. Why not provide them with a simple drop down for year, month and date, and then use these three parameters with checkdate()?
I'd recommend you provide your users with a select drop down of various date formats then storing it in a database. You can then pull their selected format and insert it into the first parameter of the date() function.
Have a look into Zend_Date, but without knowing to interpret 06-03-2012 as 6th march or 3rd june it is not possible.
You know? 12/12/12 is the date, were the europeans and the americans can talk about the day without misunderstanding ;-)
Here's a regex implementation:
$input = 'M d Y';
if(preg_match('#(dd|DD|m|M|mm|MM|y|yy|YY|-|\s)+#', $input)) {
$valid = true;
print date($input);
} else {
print "invalid date format";
}
It assumes that the space char and dash chars are allowed.
It specifies flags listed here: http://php.net/manual/en/datetime.formats.date.php
More here: http://php.net/manual/en/function.date.php

Check if variable is a valid date with PHP

I am working on a script that will import some data from a CSV file. As I am doing this I want to be able to check a variable to see if it is a valid date string.
I have seen several ways to check if a sting is a date, but most of them require you to now the format. I will not know the format that the date will in.
right now I am using strtotime(), but this fails to easily
$field ="May";
if(strtotime($field)){
echo "This is a date";
}
In this case, "May" was the persons first name, and not a date at all.
Can any one recommend more reliable function?
Edit based on questions from some of you.
For a variable to pass as a "date" in my case, it would need to be specific to a day/month/year, so just "May" would be to vague to count.
Based on that and Pauls good point below we can also test to see if the string contains a number, such as
$field ="May";
if(strtotime($field) && 1 === preg_match('~[0-9]~', $field)){
echo "This is a date";
}else{
echo "Nope not a date";
}
This seems to cover my immediate needs, but can any one spot any issues or suggest improvements?
Use date_parse and check the values of the returned array
$date = date_parse("May")
// ["year"] == FALSE
// ["month"] == 5
// ["day"] == FALSE
You can also pass those into checkdate.
$date = date_parse($someString);
if ($date["error_count"] == 0 && checkdate($date["month"], $date["day"], $date["year"]))
echo "Valid date";
else
echo "Invalid date";
I don't think there is a all-in-one answer to this problem. You may have different strategy depending on your use case.
Your strtotime() is a perfect solution, but as you say, you may end up with false positive. Why? Because may may be a word or a name. However, what is the result of strtotime('May')?
echo date(DateTime::ISO8601, strtotime('May'));
2012-05-21T00:00:00+0200
So giving only the month will return a date of the current year and the current day starting at midnight with the given month. A possible solution would be to check if your string has the current day and/or the current year included, this way, you may check against to make sure your date is a fully qualified date and valid.
echo date(DateTime::ISO8601, strtotime('May Day')); // (strtotime() returns false)
1970-01-01T01:00:00+0100
echo date(DateTime::ISO8601, strtotime('May 21'));
2012-05-21T00:00:00+0200
A simple strpos() or even a regex should do the trick.
However it is a bit odd and should be used only if you have no other way to do.
I believe that a better solution would be to define a set of valid format and interpolate the result to make sure that the date is valid.
$validDateFormatPatterns = array(
'[0-9]{1,2}-[0-9]{1,2}-[0-9]{4}', // 21-05-2012, obviously this pattern is simple and would accept 05-21-2012,
'the [0-9]{1,2}(th|st|nd|rd) (January|February|...|May|...|December) [0,9]{4}', // The 21st May 2012
);
You should try to cover most of the case and I'm sure you will be able to find regex that checks for most current date format.
In any case, you may need to adapt your function from time to time because there is no easy way to make it bulletproof.
I know this was asked a long time ago, but looking around for this and trying to avoid regex, I came up with this:
function checkInputIsDate($date) {
return (bool)strpbrk($date,1234567890) && strtotime($date);
}
This works because it takes away the issues posted above where only a month is passed into strtotime by making sure there are numbers in the string with strpbrk as well as verifying strtotime outputs a date.
And learned about a function I didn't know existed.
Hope this helps someone.

preg_match: check birthday format (dd/mm/yyyy)

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
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;
}
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.
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.
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
$ok = DateTime::createFromFormat('d/m/Y',$datestring)->format('d/m/Y') == $datestring;
PHP >= 5.3
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()
maybe something like this would help
list($month,$day,$year)=explode("/",$date);
if(checkdate($month,$day,$year))
{
echo "good";
}
else{echo "bad";}
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 :)
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;
}
Try this regex : ([0-9]{2}\/[0-9]{2}\/[0-9]{4})
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

Correct Regular expressions to match a date

What is the correct regular expression to use to validate a date like. 2009-10-22 or 2009-01-01 etc. Platform PHP
This (from regexplib.com) will match what you want, and perform checks for leap years, days-per-month etc. It's a little more tolerant of separators than you want, but that can be easily fixed. As you can see, it's rather hideous.
Alternatively (and preferably in my opinion) you may want to simply check for figures in the correct places, and then perform leap year and days-per-month checks in code. Sometimes one regexp isn't so understandable and there's greater clarity in performing the checks in code explicitly (since you can report precisely what's wrong - "only 30 days in November", rather than a "doesn't match pattern" message, which is next to useless)
If you want something simple that does a little more than just validates format, but doesn't go as far as validating how many days is in the month that is entered, or leap years, you can use this:
^(19|20)[0-9]{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$
This example allows years 19xx and 20xx
As you have to deal with accepting 2009-02-28 but not 2009-02-29 but accept 2008-02-28 you need more logic that 1 think a regex can give. (But if someone can show it I would be impressed)
I would try to convert it to a date and report if the conversion failed or if you you language has a check date function use that.
\d{4}-\d{2}-\d{2} would match string in that form, but to check if date is valid, you'd had to break that string to year, month and date (you can use this regexp for that) parts and check each of them.
You can additionally, make sure that year must start with 1 or 2: [12]\d{3}-\d{2}-\d{2}, and you can also do the same for month and day: [12]\d{3}-[01]\d-[0123]\d (but I would go with the first regexp and compare parts "manually")
found this on the web tested it with a few dates and looks stable, for dates between 1900 and 2000:
(19|20)\d\d[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])
OK, a regex that will validate month and day ranges could be
[0-9]{4}-(?:1[0-2]|[1-9])-(?:3[01]|[12][0-9]|[1-9])
If you want to restrict the years, say, from 1900 to 2050, you could end up with
(?:2050|20[0-4][0-9]|19[0-9]{2})-(?:1[0-2]|[1-9])-(?:3[01]|[12][0-9]|[1-9])
They will not catch "subtly wrong" dates like February 31st, so it's really quite clear that a sanity check needs to be performed outside of the regex.
In .NET Regex:
\d{4}\-\d{2}\-\d{2}
[0-9]{4}-[0-9]{2}-[0-9]{2}
or
\d\d\d\d-\d\d-\d\d
or
...
simply read first regex tutorial
^\d{4}-\d{2}-\d{2}$
but no regular expression can prevent someone to enter "9867-39-56"
For a complete validation (which would include verifying that the day, month and year parts are valid) a Regex is not the tool of choice. Apart from month issues you'd get into trouble with leap years...
So, if you just want to check if the rough format is correct, or isolate the different parts (year-month-day), a regex is fine.
([0-9]{1,4})-(1[012]|0?[1-9])-([12][0-9]|3[01]|0?[1-9])
This is already pretty exact and captures the year (0..9999), month and day into capture groups, ready for parsing...
If you can rely on more than a regular expression, an hybrid solution by using Posix functions date() and time() delivered with PHP could look like this:
<?php
date_default_timezone_set("GMT");
function validateDate($date)
{
if (preg_match("^[0-9]{4}-[0-9]{2}-[0-9]{2}^", $date))
{
return date('Y-m-d', strtotime($date)) === $date;
}
return false;
}
// Some tests
$dates = array(
'2009-09-09', '2009-09-32', '2009-31-00', '2035-01-02',
);
foreach($dates AS $date)
{
echo $date .': '. (validateDate($date) ? 'OK' : 'FAILS') ."\n";
}
?>
It's not elegant plus you'll be limited by Unix Epoch time (from January 1 1970 00:00:00 GMT to January 19 2038 03:14:07 GMT), but it's reliable and it's well supported in PHP.

Categories