PHP | checkdate() Function The checkdate() function is a built-in function in PHP which checks the validity of the date passed in the arguments. It accepts the date in the format mm/dd/yyyy. The function returns a boolean value. It returns true if the date is a valid one, else it returns false.
Answer: Use the strtotime() Function You can first use the PHP strtotime() function to convert any textual datetime into Unix timestamp, then simply use the PHP date() function to convert this timestamp into desired date format. The following example will convert a date from yyyy-mm-dd format to dd-mm-yyyy.
date("Y/m/d") . "<br>"; echo "Today is " . date("Y.m.d") .
Try this.
$date="2012-09-12";
if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/",$date)) {
return true;
} else {
return false;
}
It's probably better to use another mechanism for this.
The modern solution, with DateTime
:
$dt = DateTime::createFromFormat("Y-m-d", $date);
return $dt !== false && !array_sum($dt::getLastErrors());
This validates the input too: $dt !== false
ensures that the date can be parsed with the specified format and the array_sum
trick is a terse way of ensuring that PHP did not do "month shifting" (e.g. consider that January 32 is February 1). See DateTime::getLastErrors()
for more information.
Old-school solution with explode
and checkdate
:
list($y, $m, $d) = array_pad(explode('-', $date, 3), 3, 0);
return ctype_digit("$y$m$d") && checkdate($m, $d, $y);
This validates that the input is a valid date as well. You can do that with a regex of course, but it's going to be more fuss -- and February 29 cannot be validated with a regex at all.
The drawback of this approach is that you have to be very careful to reject all possible "bad" inputs while not emitting a notice under any circumstances. Here's how:
explode
is limited to return 3 tokens (so that if the input is "1-2-3-4", $d
will become "3-4")ctype_digit
is used to make sure that the input does not contain any non-numeric characters (apart from the dashes)array_pad
is used (with a default value that will cause checkdate
to fail) to make sure that enough elements are returned so that if the input is "1-2" list()
will not emit a noticeyyyy-mm-dd :
/^((((19|[2-9]\d)\d{2})\-(0[13578]|1[02])\-(0[1-9]|[12]\d|3[01]))|(((19|[2-9]\d)\d{2})\-(0[13456789]|1[012])\-(0[1-9]|[12]\d|30))|(((19|[2-9]\d)\d{2})\-02\-(0[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))\-02\-29))$/g
yyyy/mm/dd :
/^((((19|[2-9]\d)\d{2})\/(0[13578]|1[02])\/(0[1-9]|[12]\d|3[01]))|(((19|[2-9]\d)\d{2})\/(0[13456789]|1[012])\/(0[1-9]|[12]\d|30))|(((19|[2-9]\d)\d{2})\/02\/(0[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))\/02\/29))$/g
mm-dd-yyyy :
/^(((0[13578]|1[02])\-(0[1-9]|[12]\d|3[01])\-((19|[2-9]\d)\d{2}))|((0[13456789]|1[012])\-(0[1-9]|[12]\d|30)\-((19|[2-9]\d)\d{2}))|(02\-(0[1-9]|1\d|2[0-8])\-((19|[2-9]\d)\d{2}))|(02\-29\-((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$/g
mm/dd/yyyy :
/^(((0[13578]|1[02])\/(0[1-9]|[12]\d|3[01])\/((19|[2-9]\d)\d{2}))|((0[13456789]|1[012])\/(0[1-9]|[12]\d|30)\/((19|[2-9]\d)\d{2}))|(02\/(0[1-9]|1\d|2[0-8])\/((19|[2-9]\d)\d{2}))|(02\/29\/((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$/g
dd/mm/yyyy :
/^(((0[1-9]|[12]\d|3[01])\/(0[13578]|1[02])\/((19|[2-9]\d)\d{2}))|((0[1-9]|[12]\d|30)\/(0[13456789]|1[012])\/((19|[2-9]\d)\d{2}))|((0[1-9]|1\d|2[0-8])\/02\/((19|[2-9]\d)\d{2}))|(29\/02\/((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$/g
dd-mm-yyyy :
/^(((0[1-9]|[12]\d|3[01])\-(0[13578]|1[02])\-((19|[2-9]\d)\d{2}))|((0[1-9]|[12]\d|30)\-(0[13456789]|1[012])\-((19|[2-9]\d)\d{2}))|((0[1-9]|1\d|2[0-8])\-02\-((19|[2-9]\d)\d{2}))|(29\-02\-((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$/g
Criteria:
Every year divisible by 4 is a leap year, except when it is divisible by 100 unless it is divisible by 400. So:
2004 - leap year - divisible by 4
1900 - not a leap year - divisible by 4, but also divisible by 100
2000 - leap year - divisible by 4, also divisible by 100, but divisible by 400
February has 29 days in a leap year and 28 when not a leap year
30 days in April, June, September and November
31 days in January, March, May, July, August, October and December
Test:
The following dates should all pass validation:
1976-02-29
2000-02-29
2004-02-29
1999-01-31
The following dates should all fail validation:
2015-02-29
2015-04-31
1900-02-29
1999-01-32
2015-02-00
Range:
We'll test for dates from 1st Jan 1000 to 31st Dec 2999. Technically the currently used Gregorian calendar only came into use in 1753 for the British Empire and at various years in the 1600s for countries in Europe, but I'm not going to worry about that.
Regex to test for a leap year:
The years divisible by 400:
1200|1600|2000|2400|2800
can be shortened to:
(1[26]|2[048])00
if you wanted all years from 1AD to 9999 then this would do it:
(0[48]|[13579][26]|[2468][048])00
if you're happy with accepting 0000 as a valid year then it can be shortened:
([13579][26]|[02468][048])00
The years divisible by 4:
[12]\d([02468][048]|[13579][26])
The years divisible by 100:
[12]\d00
Not divisible by 100:
[12]\d([1-9]\d|\d[1-9])
The years divisible by 100 but not by 400:
((1[1345789])|(2[1235679]))00
Divisible by 4 but not by 100:
[12]\d([2468][048]|[13579][26]|0[48])
The leap years:
divisible by 400 or (divisible by 4 and not divisible by 100)
((1[26]|2[048])00)|[12]\d([2468][048]|[13579][26]|0[48])
Not divisible by 4:
[12]\d([02468][1235679]|[13579][01345789])
Not a leap year:
Not divisible by 4 OR is divisible by 100 but not by 400
([12]\d([02468][1235679]|[13579][01345789]))|(((1[1345789])|(2[1235679]))00)
Valid Month and day excluding February(MM-DD):
((01|03|05|07|08|10|12)-(0[1-9]|[12]\d|3[01]))|((04|06|09|11)-(0[1-9]|[12]\d|30))
shortened to:
((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30))
February with 28 days:
02-(0[1-9]|1\d|2[0-8])
February with 29 days:
02-(0[1-9]|[12]\d)
Valid date:
(leap year followed by (valid month-day-excluding-february OR 29-day-february))
OR
(non leap year followed by (valid month-day-excluding-february OR 28-day-february))
((((1[26]|2[048])00)|[12]\d([2468][048]|[13579][26]|0[48]))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|[12]\d))))|((([12]\d([02468][1235679]|[13579][01345789]))|((1[1345789]|2[1235679])00))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|1\d|2[0-8]))))
So there you have it a regex for dates between 1st Jan 1000 and 31st Dec 2999 in YYYY-MM-DD format.
I suspect it can be shortened quite a bit, but I'll leave that up to somebody else.
That will match all valid dates. If you want it to only be valid when it contains just one date and nothing else, then wrap it in ^( )$
like so:
^(((((1[26]|2[048])00)|[12]\d([2468][048]|[13579][26]|0[48]))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|[12]\d))))|((([12]\d([02468][1235679]|[13579][01345789]))|((1[1345789]|2[1235679])00))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|1\d|2[0-8])))))$
If you want it for an optional date entry (ie. it can be blank or a valid date) then add ^$|
at the beginning, like so:
^$|^(((((1[26]|2[048])00)|[12]\d([2468][048]|[13579][26]|0[48]))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|[12]\d))))|((([12]\d([02468][1235679]|[13579][01345789]))|((1[1345789]|2[1235679])00))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|1\d|2[0-8])))))$
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With