PHP Regex for checking dates in the format YYYY-MM-DD

I am trying to verify that dates entered by end users are in YYYY-MM-DD. Regex has never been my forte, I keep getting a false return value for preg_match () I have a setting.

So, I guess I created a regex disorder, described in detail below.

$date="2012-09-12"; if (preg_match("^[0-9]{4}-[0-1][0-9]-[0-3][0-9]$",$date)) { return true; }else{ return false; } 

Any thoughts?

+79
php regex date-format
Nov 02 '12 at 11:27
source share
20 answers

Try it.

 $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; } 
+170
Nov 02 '12 at 11:33
source share
β€” -

It is probably best to use a different mechanism for this.

Modern solution with DateTime :

 $dt = DateTime::createFromFormat("Ymd", $date); return $dt !== false && !array_sum($dt::getLastErrors()); 

It also checks the input: $dt !== false ensures that the date can be parsed with the specified format, and the array_sum trick is a brief way to array_sum that PHP did not "shift the month" (for example, note that January 32 - 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 confirms that the entry is also a valid date. Of course, you can do this with a regular expression, but it will be more vain - and February 29 you can’t check with a regular expression at all.

The disadvantage of this approach is that you have to be very careful to reject all possible β€œbad” data without sending a notification under any circumstances. Here's how:

  • explode limited to return 3 tokens (so if you enter "1-2-3-4", $d will become "3-4")
  • ctype_digit used to make sure the input does not contain any non-numeric characters (except for dashes)
  • array_pad (with a default value that will cause checkdate to fail) to ensure that enough items are returned so that when you enter "1-2" list() does not give a notification
+85
Nov 02 '12 at 11:32
source share

Criteria:

Each year divisible by 4 is a leap year, except when it is divisible by 100, if it is not 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 in 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 must pass verification:

 1976-02-29 2000-02-29 2004-02-29 1999-01-31 

The following dates must pass verification:

 2015-02-29 2015-04-31 1900-02-29 1999-01-32 2015-02-00 

Range:

We will check the dates from January 1 to December 31, December 29, 2999. The Gregorian calendar, currently used technically, was used only in 1753 for the British Empire and in different years in the 1600s for European countries, but I will not worry about that.

Lege year Regex:

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 

Years divisible by 4:

 [12]\d([02468][048]|[13579][26]) 

Years divisible by 100:

 [12]\d00 

Not divisible by 100:

 [12]\d([1-9]\d|\d[1-9]) 

Years divisible by 100, but not by 400:

 ((1[1345789])|(2[1235679]))00 

Divisible by 4, but not 100:

 [12]\d([2468][048]|[13579][26]|0[48]) 

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, you have a regular expression for dates between January 1, 1000 and December 31, 2999 in the format YYYY-MM-DD.

I suspect that it can be reduced a little, but I will leave it to someone else.

This will correspond to all valid dates. If you want it to be valid only if it contains only one date and nothing else, then wrap it in ^( )$ as follows:

 ^(((((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 an additional date entry (that is, it can be an empty or a valid date), add ^$| at the beginning, for example:

 ^$|^(((((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])))))$ 
+34
Apr 02 '15 at 16:14
source share

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

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

+33
Jun 03 '16 at 10:01
source share

You can do it as follows:

 if (preg_match("/\d{4}\-\d{2}-\d{2}/", $date)) { echo 'true'; } else { echo 'false'; } 

but it is better to use it:

 $date = DateTime::createFromFormat('Ym-d', $date); if ($date) { echo $date -> format('Ym-d'); } 

in this case, you get an object that is easier to use than just strings.

+14
Nov 02
source share

You can use preg_match with checkdate php function

 $date = "2012-10-05"; $split = array(); if (preg_match ("/^([0-9]{4})-([0-9]{2})-([0-9]{2})$/", $date, $split)) { return checkdate($split[2], $split[3], $split[1]); } return false; 
+7
Nov 02
source share

I know this is an old question. But I think I have a good solution.

 $date = "2016-02-21"; $format = "Ymd"; if(date($format, strtotime($date)) == date($date)) { echo "true"; } else { echo "false"; } 

You can try. If you change the date to 02/21/2016, the echo will be false. And if you change the format after that to dmY, the echo will be true.

With this simple code, you can check which date format is being used without checking it with a regular expression.

Maybe there is a person who will check it on every occasion. But I think that my idea as a whole is valid. It seems logical to me.

+6
Feb 20 '16 at 23:11
source share

preg_match needs / or another char as a separator.

 preg_match("/^[0-9]{4}-[0-1][0-9]-[0-3][0-9]$/",$date) 

you should also check the validity of this date so that you do not receive something like 9999-19-38

 bool checkdate ( int $month , int $day , int $year ) 
+4
Nov 02
source share

you can use

 function validateDate($date, $format = 'Ymd H:i:s') { $d = DateTime::createFromFormat($format, $date); return $d && $d->format($format) == $date; } $date="2012-09-12"; echo validateDate($date, 'Ym-d'); // true or false 
+3
Apr 13 '18 at 13:38
source share

You can also do it like this:

 if (DateTime::createFromFormat('Ym-d', $date)->format('Ym-d') === $date) { // date is correctly formatted and valid, execute some code } 

This will check not only the format, but also the validity of the date, as DateTime will only create valid dates, and this should match the input.

+2
Feb 12 '18 at 15:47
source share

If you want to combine this type of date, use:

 preg_match("~^\d{4}-\d{2}-\d{2}$~", $date) 
+1
Nov 02
source share

This should tell you if the format is valid, and if the input date is valid.

  $datein = '2012-11-0'; if(preg_match('/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/', $datein)){ echo 'good'; }else{ echo 'no good'; } 
+1
Nov 02
source share

Check and approve date YYYY-MM-DD in one line

 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; } 

The output will be:

 var_dump(isValidDate("2018-01-01")); // bool(true) var_dump(isValidDate("2018-1-1")); // bool(true) var_dump(isValidDate("2018-02-28")); // bool(true) var_dump(isValidDate("2018-02-30")); // bool(false) 

Days and a month without a leading zero are allowed. If you do not want to allow this, the regular expression should be:

 "/^(\d{4})-(\d{2})-(\d{2})$/" 
+1
Nov 19 '17 at 16:22
source share

If this helps, here is a regular expression for jnY format (year should be more than 2018):

 if (preg_match('/^([1-9]|[1-2][0-9]|[3][0-1])\-([1-9]|[1][0-2])\-(?:20)([1][8-9]|[2-9][0-9])$/', $date)) { // Do stuff } 
+1
07 Oct '18 at 10:18
source share

It all depends on how strictly you want this feature to be. For example, if you do not want to allow months above 12 and days above 31 (not depending on the month, which will require a date-logic record), this can become quite complicated:

 function checkDate($date) { $regex = '/^' . '(' . // Allows years 0000-9999 '(?:[0-9]{4})' . '\-' . // Allows 01-12 '(?:' . '(?:01)|(?:02)|(?:03)|(?:04)|(?:05)|(?:06)|(?:07)|(?:08)|(?:09)|(?:10)|' . '(?:11)|(?:12)' . ')' . '\-' . // Allows 01-31 '(?:' . '(?:01)|(?:02)|(?:03)|(?:04)|(?:05)|(?:06)|(?:07)|(?:08)|(?:09)|(?:10)|' . '(?:11)|(?:12)|(?:13)|(?:14)|(?:15)|(?:16)|(?:17)|(?:18)|(?:19)|(?:20)|' . '(?:21)|(?:22)|(?:23)|(?:24)|(?:25)|(?:26)|(?:27)|(?:28)|(?:29)|(?:30)|' . '(?:31)' . ')' . '$/'; if ( preg_match($regex, $date) ) { return true; } return false; } $result = checkDate('2012-09-12'); 

Personally, I would just go for: /^([0-9]{4}\-([0-9]{2}\-[0-9]{2})$/

0
Nov 02 '12 at 12:37
source share

To work with dates in php, you must follow the php standard, so this regex will ensure that you have a valid date that can work with PHP.

  preg_match("/^([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/",$date) 
0
Sep 23 '14 at 4:49
source share

This method can be useful for checking dates in PHP. The current method is for mm / dd / yyyy format. You should update the sequence of parameters in checkdate according to your format and separator in explode .

  function isValidDate($dt) { $dtArr = explode('/', $dt); if (!empty($dtArr[0]) && !empty($dtArr[1]) && !empty($dtArr[2])) { return checkdate((int) $dtArr[0], (int) $dtArr[1], (int) $dtArr[2]); } else { return false; } } 
0
May 29 '17 at 11:52 a.m.
source share

[If you are using Symfony 4.1.2, try this] [1]

  $validDate = explode("-",$request->get('date')); if (checkdate(filter_var($validDate[1],FILTER_SANITIZE_NUMBER_INT),filter_var($validDate[0],FILTER_SANITIZE_NUMBER_INT),filter_var($validDate[2],FILTER_SANITIZE_NUMBER_INT))){ $date = date_create(filter_var($request->get('date'),FILTER_SANITIZE_SPECIAL_CHARS)); }else{ return $this->redirectToRoute('YOUR_ROUTE'); } 
0
Jan 31 '19 at 14:08
source share

Starting with Laravel 5.7 and the date format, that is: 12/31/2009

 function checkDateFormat(string $date): bool { return preg_match("/^(0[1-9]|1[0-2])\/(0[1-9]|[1-2][0-9]|3[0-1])\/[0-9]{4}$/", $date); } 
0
Jun 26 '19 at 10:30
source share

Format 1: $ format1 = "2012-12-31";

Format 2: $ format2 = "31-12-2012";

 if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/",$format1)) { return true; } else { return false; } if (preg_match("/^(0[1-9]|[1-2][0-9]|3[0-1])-(0[1-9]|1[0-2])-[0-9]{4}$/",$format2)) { return true; } else { return false; } 
0
Jul 25 '19 at 11:42
source share



All Articles