Correctly determine if date string is a valid date in that format
Asked Answered
S

21

249

I'm receiving a date string from an API, and it is formatted as yyyy-mm-dd.

I am currently using a regex to validate the string format, which works ok, but I can see some cases where it could be a correct format according to the string but actually an invalid date. i.e. 2013-13-01, for example.

Is there a better way in PHP to take a string such as 2013-13-01 and tell if it is a valid date or not for the format yyyy-mm-dd?

Shaunteshave answered 9/10, 2013 at 11:58 Comment(1)
Possible duplicate of php date validationHaunting
L
589

You can use DateTime::createFromFormat() for this purpose:

function validateDate($date, $format = 'Y-m-d')
{
    $d = DateTime::createFromFormat($format, $date);
    // The Y ( 4 digits year ) returns TRUE for any integer with any number of digits so changing the comparison from == to === fixes the issue.
    return $d && $d->format($format) === $date;
}

[Function taken from this answer. Also on php.net. Originally written by Glavić.]


Test cases:

var_dump(validateDate('2013-13-01'));  // false
var_dump(validateDate('20132-13-01')); // false
var_dump(validateDate('2013-11-32'));  // false
var_dump(validateDate('2012-2-25'));   // false
var_dump(validateDate('2013-12-01'));  // true
var_dump(validateDate('1970-12-01'));  // true
var_dump(validateDate('2012-02-29'));  // true
var_dump(validateDate('2012', 'Y'));   // true
var_dump(validateDate('12012', 'Y'));  // false

Demo!

Laburnum answered 9/10, 2013 at 11:59 Comment(19)
If you are using PHP 5.2.x, you should use strtotime to get the unix timestamp then date('Y-m-d', $t) to get the string date. Then you compare them just like this answer.Phrixus
@pedromanoel: for standard datetime input you can use strtotime, but for non-standard formats that strtotime doesn't recognizes, you will need some other solution. And for php version 5.2 support stopped on January 2011, for 5.3 support stopped on August 2014.Academician
consider this date var_dump( validateDate('2012-2-9'));Brushwork
The function works correctly. It returned false because thr format you specified was incorrect. If you want to use day and month without leading zeroes, then the format should be 'Y-n-j', @reignsly.Laburnum
I don't see the point of ` $d->format('Y-m-d') == $date` part since $d would already be false if it wasn't the correct format?Megaspore
@AntonyD'Andrea: no, it will not work without that part. Because $d will not be false in you give it date, which has overflown parts, like 13th month (2013-13-01). But it really depends what you want. If you need for example validateDate('2015-55-66') to be valid, then yes, you only need to check if $d is object or not.Academician
why this is false var_dump( validateDate('2013-13-01') ); this also a valid Y-d-m format dateAnticyclone
@arsh: Yes, it's a valid Y-d-m format date. But in the function, I've given Y-m-d.Laburnum
Sorry, I just wandering for a function to determine any string is it date or notAnticyclone
this will return false on a date like this '2016-12-11 10:15:00'Rhatany
@albanx: You can supply the date format as well. I have hardcoded it in the function, but you can change that however you want. Y-m-d H:i:s, for example.Laburnum
You, also should check about DateTime::getLastErrors()Caress
should be return $dt && (strtolower($dt->format($format)) == strtolower($string)); to make it work with 'january 2017' like formatsBawbee
You should prepend '!' to the date format string to reset any non-defined date-time properties to 00 (unix epoch time). Otherwise with a pattern like 'Y-m' createFromFormat() will use the current time, which can lead to issues when converting timezones.Subcontinent
@Glavić Do you think the strict comparison is required here?Laburnum
@AmalMurali: I have not used strict comparison in my answers; I even used string and int in my examples as valid (output true). It is all based on user requirements, but I think most users will not need strict comparisons.Academician
Nonsense, why 12012 would be a bad year? WTH? Do they expect Earth or callendars are not gonna exist in the year of 12012?Gris
does not work for var_dump(validateDate('0018-08-23'));Samekh
var_dump(validateDate('9:00')); // falseTralee
A
99

Determine if any string is a date

function checkIsAValidDate($myDateString){
    return (bool)strtotime($myDateString);
}
Anticyclone answered 25/6, 2014 at 6:15 Comment(10)
This validates a whole range of valid date formats not just yyyy-mm-dd.Helgoland
@MichelAyres this is because php sees 2015-02-30 as a valid date because when the day given is greater than the number of days in the given month (or negative) php rolls over to the next month. Since the date is guaranteed to be of the format yyyy-mm-dd this can be fixed by changing the return to return (bool)strtotime($myDateString) && date("Y-m-d", strtotime($myDateString)) == $myDateString;.Haland
Why does (bool)strtotime('s') come out as true?Voroshilovgrad
this also returns 1 checkIsAValidDate("F");Taradiddle
we can use $myDateString = str_replace("/", '-', $myDateString); before return if the date string contains slashes (/) like :- dd/mm/yyyyPusher
@vaibhav,@Dainis echo "$str == " . date('l dS \o\f F Y h:i:s A', $timestamp);Zwinglian
Important to note that this won't work with a British (d/m/Y) format as it will assume it's converting the American (m/d/Y). It will seem to work only if the day is lower than 12.Immaterial
This is not correct! Will validate if you give it any integer or decimal! Use this only if you are sure that a string is being fed into the function.Gill
this is incorrect (bool)strtotime("foo") returns trueSupple
This accepted answer is an error - why is the error answer accepted - and this not the answer? how do we fix this? We are sending people off with the wrong answer and to potential doom :/ and ... go figure - the person who posted this is aware it is an error and allows himself to collect points for an incorrect answer... figures.Supple
P
50

Use in simple way with php prebuilt function:

function checkmydate($date) {
  $tempDate = explode('-', $date);
  // checkdate(month, day, year)
  return checkdate($tempDate[1], $tempDate[2], $tempDate[0]);
}

Test

   checkmydate('2015-12-01'); //true
   checkmydate('2015-14-04'); //false
Polyphagia answered 17/3, 2015 at 7:39 Comment(6)
Again, when using a test, inside an if to return simply true or false, return the test itself.Blither
This assumes there are at least 3 elements in the $tempDate array.Vaudevillian
@person27: return sizeof($tmpDate) == 3 && checkdate($tmpDate[1]...Impresario
@Polyphagia - this fails. If the year is 2, 20, 202, 2020 or even if the year is 20201 - it returns true every time.Danielson
d.m.Y is standart for almost every european country - and there are cases where it's something like Y/m/d [ although i don't understand how you could print day after month lol ] - you don't want to explode every possible input string.Damoiselle
This throws notices if checkmydate('thisIsWrong!')Alejandraalejandrina
D
19

Determine if string is a date, even if string is a non-standard format

(strtotime doesn't accept any custom format)

<?php
function validateDateTime($dateStr, $format)
{
    date_default_timezone_set('UTC');
    $date = DateTime::createFromFormat($format, $dateStr);
    return $date && ($date->format($format) === $dateStr);
}

// These return true
validateDateTime('2001-03-10 17:16:18', 'Y-m-d H:i:s');
validateDateTime('2001-03-10', 'Y-m-d');
validateDateTime('2001', 'Y');
validateDateTime('Mon', 'D');
validateDateTime('March 10, 2001, 5:16 pm', 'F j, Y, g:i a');
validateDateTime('March 10, 2001, 5:16 pm', 'F j, Y, g:i a');
validateDateTime('03.10.01', 'm.d.y');
validateDateTime('10, 3, 2001', 'j, n, Y');
validateDateTime('20010310', 'Ymd');
validateDateTime('05-16-18, 10-03-01', 'h-i-s, j-m-y');
validateDateTime('Monday 8th of August 2005 03:12:46 PM', 'l jS \of F Y h:i:s A');
validateDateTime('Wed, 25 Sep 2013 15:28:57', 'D, d M Y H:i:s');
validateDateTime('17:03:18 is the time', 'H:m:s \i\s \t\h\e \t\i\m\e');
validateDateTime('17:16:18', 'H:i:s');

// These return false
validateDateTime('2001-03-10 17:16:18', 'Y-m-D H:i:s');
validateDateTime('2001', 'm');
validateDateTime('Mon', 'D-m-y');
validateDateTime('Mon', 'D-m-y');
validateDateTime('2001-13-04', 'Y-m-d');
Desjardins answered 17/10, 2016 at 10:24 Comment(2)
When using a test, inside an if to return simply true or false, return the test itself.Blither
But 2018-3-24 is returning false,the method receive 2018-3-24, when the format is apply return 2018-03-24; how I can return true in two ways?Callous
B
17

This option is not only simple but also accepts almost any format, although with non-standard formats it can be buggy.

$timestamp = strtotime($date);
return $timestamp ? $date : null;
Battement answered 30/6, 2015 at 8:32 Comment(9)
This should have been the accepted answer! Much, much simpler.Cardiomegaly
Important to note that this won't work with a British (d/m/Y) format as it will assume it's converting the American (m/d/Y). It will seem to work only if the day is lower than 12!Immaterial
@Battement - i tested this and it fails certain values. If $date = '202-03-31' it returns true. But thats not a valid date. I found that if you alter the year to a non-valid year it always returns true.Danielson
@Danielson strange... maybe it’s seeing 202AD? What year’s timestamp does ‘strtotime’ give?Battement
@Battement - not certain, but 202 returns a negative number - which still passes the test.Danielson
@Danielson I can see a case where someone born in say 1950 would want their birthday to pass as a valid date, and that value would also probably be negativeBattement
the cleanest and simplest answer is by user4600953 below: 'function isValidDate($date) { return date('Y-m-d', strtotime($date)) === $date; }'Danielson
@Danielson it's slower and more code for the same result. '202-03-31' was a real date in the 3rd century, I don't understand why you claim it's not a valid date.Battement
why is the error answer accepted - and this not the answer? how do we fix this? We are sending people off with the wrong answer and to potential doom :/Supple
Q
14

The easiest way to check if given date is valid probably converting it to unixtime using strtotime, formatting it to the given date's format, then comparing it:

function isValidDate($date) {
    return date('Y-m-d', strtotime($date)) === $date;
}

Of course you can use regular expression to check for validness, but it will be limited to given format, every time you will have to edit it to satisfy another formats, and also it will be more than required. Built-in functions is the best way (in most cases) to achieve jobs.

Qualifier answered 3/7, 2017 at 19:16 Comment(3)
I feel that this answer is pretty low quality, especially considering there are already strtotime answers.Legitimacy
This is the shortest answer and it works. It is a bit harsh to say that it is low quality.Sabinasabine
@Qualifier - this is the easiest one that works. Lots of others are saying to use checkdate() - but I am finding checkdate fails if the year is ANY value: 2, 20, 202, 2020, 20201 - all return true. I am going with your solution!Danielson
C
13

I'm afraid that most voted solution (https://mcmap.net/q/115901/-correctly-determine-if-date-string-is-a-valid-date-in-that-format) is not working properly. The fourth test case (var_dump(validateDate('2012-2-25')); // false) is wrong. The date is correct, because it corresponds to the format - the m allows a month with or without leading zero (see: http://php.net/manual/en/datetime.createfromformat.php). Therefore a date 2012-2-25 is in format Y-m-d and the test case must be true not false.

I believe that better solution is to test possible error as follows:

function validateDate($date, $format = 'Y-m-d') {
    DateTime::createFromFormat($format, $date);
    $errors = DateTime::getLastErrors();

    return $errors['warning_count'] === 0 && $errors['error_count'] === 0;
}
Coenzyme answered 28/2, 2019 at 7:35 Comment(0)
W
12

You can also Parse the date for month date and year and then you can use the PHP function checkdate() which you can read about here: http://php.net/manual/en/function.checkdate.php

You can also try this one:

$date="2013-13-01";

if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/",$date))
    {
        echo 'Date is valid';
    }else{
        echo 'Date is invalid';
    }
Whom answered 9/10, 2013 at 12:2 Comment(2)
in case of february (as commented by elitechief21) function isValidDate($date) { return preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/",$date) && date("Y-m-d", strtotime($date)) == $date; }Psyche
This solution is very poor, as it doesn't check the date validity in any sense. Any month can have 31 days, including February. Any year can have 29 of February. To validate dates using RegExp would demand something much more complex, with back-references and negative look aheads.Blither
S
9

I have this thing that, even with PHP, I like to find functional solutions. So, for example, the answer given by @migli is really a good one, highly flexible and elegant.

But it has a problem: what if you need to validate a lot of DateTime strings with the same format? You would have to repeat the format all over the place, what goes against the DRY principle. We could put the format in a constant, but still, we would have to pass the constant as an argument to every function call.

But fear no more! We can use currying to our rescue! PHP doesn't make this task pleasant, but it's still possible to implement currying with PHP:

<?php
function validateDateTime($format)
{
    return function($dateStr) use ($format) {
        $date = DateTime::createFromFormat($format, $dateStr);
        return $date && $date->format($format) === $dateStr;
    };
}

So, what we just did? Basically we wrapped the function body in an anonymous and returned such function instead. We can call the validation function like this:

validateDateTime('Y-m-d H:i:s')('2017-02-06 17:07:11'); // true

Yeah, not a big difference... but the real power comes from the partially applied function, made possible by currying:

// Get a partially applied function
$validate = validateDateTime('Y-m-d H:i:s');

// Now you can use it everywhere, without repeating the format!
$validate('2017-02-06 17:09:31'); // true
$validate('1999-03-31 07:07:07'); // true
$validate('13-2-4 3:2:45'); // false

Functional programming FTW!

Sherd answered 6/2, 2017 at 16:17 Comment(3)
The best answer by a landslide IMHO(solves the specific problem of the OP with greater flexibility and pretty much the same amount of code as the rest of the answers)Fra
IMHO this is really ugly programming, just put your values in an array and loop through them to validate, but please don't do this!Zuniga
I'm curious @Tim, could you give us an example of your array/loop validation?Blither
E
8

Accordling with cl-sah's answer, but this sound better, shorter...

function checkmydate($date) {
  $tempDate = explode('-', $date);
  return checkdate($tempDate[1], $tempDate[2], $tempDate[0]);
}

Test

checkmydate('2015-12-01');//true
checkmydate('2015-14-04');//false
Encarnacion answered 29/2, 2016 at 19:25 Comment(2)
You'll need to test if count($tempDate) === 3 thoughFatling
@Fatling no you don't, checkdate will bomb if they are missing, you could write isset() for every single one but i'd simply suppress the warningsSkipbomb
H
6

Validate with checkdate function:

$date = '2019-02-30';

$date_parts = explode( '-', $date );

if(checkdate( $date_parts[1], $date_parts[2], $date_parts[0] )){
    //date is valid
}else{
    //date is invalid
}
Hadik answered 11/6, 2019 at 11:47 Comment(1)
Documentation for checkdate: w3schools.com/php/func_date_checkdate.aspHadik
A
4

How about this one?

We simply use a try-catch block.

$dateTime = 'an invalid datetime';

try {
    $dateTimeObject = new DateTime($dateTime);
} catch (Exception $exc) {
    echo 'Do something with an invalid DateTime';
}

This approach is not limited to only one date/time format, and you don't need to define any function.

Ariannaarianne answered 7/9, 2017 at 10:53 Comment(2)
this will not work for datetime value 0000-00-00 00:00:00Subjunctive
@NaseeruddinVN '0000-00-00 00:00:00' is a valid datetime value. It's just the first the value. However, the date property of the datetime object will be '-0001-11-30 00:00:00'.Ariannaarianne
M
1

Tested Regex solution:

    function isValidDate($date)
    {
            if (preg_match("/^(((((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])))))$/", $date)) {
                    return $date;
            }
            return null;
    }

This will return null if the date is invalid or is not yyyy-mm-dd format, otherwise it will return the date.

Melodize answered 14/3, 2017 at 20:17 Comment(0)
Q
1
/*********************************************************************************
Returns TRUE if the input parameter is a valid date string in "YYYY-MM-DD" format (aka "MySQL date format")
The date separator can be only the '-' character.
*********************************************************************************/
function isMysqlDate($yyyymmdd)
{
    return checkdate(substr($yyyymmdd, 5, 2), substr($yyyymmdd, 8), substr($yyyymmdd, 0, 4)) 
        && (substr($yyyymmdd, 4, 1) === '-') 
        && (substr($yyyymmdd, 7, 1) === '-');
}
Quaky answered 9/12, 2018 at 20:41 Comment(0)
A
1

To add onto the accepted answer, you can further check for a valid date or DateTime by checking if the formatted date is an instanceof DateTime.

$date = DateTime::createFromFormat('Ymd', $value);
$is_datetime = ($date instanceof DateTime);
$is_valid_datetime_format = $is_datetime
  ? ($date->format('Ymd') === $value)
  : false;

if (!$is_datetime || !$is_valid_datetime_format) {
  // Not a valid date.
  return false;
}

This will catch any values that are not a DateTime such as random strings or an invalid date such as 20202020.

Atkins answered 22/4, 2021 at 15:41 Comment(0)
H
0
    /**** date check is a recursive function. it's need 3 argument 
    MONTH,DAY,YEAR. ******/

    $always_valid_date = $this->date_check($month,$day,$year);

    private function date_check($month,$day,$year){

        /** checkdate() is a php function that check a date is valid 
        or not. if valid date it's return true else false.   **/

        $status = checkdate($month,$day,$year);

        if($status == true){

            $always_valid_date = $year . '-' . $month . '-' . $day;

            return $always_valid_date;

        }else{
            $day = ($day - 1);

            /**recursive call**/

            return $this->date_check($month,$day,$year);
        }

    }
Harvest answered 1/9, 2017 at 10:43 Comment(1)
Code without any explanation isn't very useful.Leitmotif
L
0

Try and let me know it works for me

$date = \DateTime::createFromFormat('d/m/Y', $dataRowValue);
if (!empty($date)) {
//Your logic
}else{
//Error
}

if you pass any alpha or alphanumberic values it will give you the empty value in return

Lateen answered 22/9, 2021 at 13:12 Comment(0)
S
0

Regex solution

function verify_date($date){
  /* correct format = "2012-09-15 11:23:32" or "2012-09-15"*/
  if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])( (0[0-9]|[1-2][0-4]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9]))?$/",$date)) {
    return true;
  } else {
    die("Wrong date format: it should be '2012-09-15 11:23:32' or '2012-09-15', date received is: ".$date);
  }
}
Sloatman answered 1/6, 2022 at 11:59 Comment(0)
P
0

Try this:

$myday = '2022-1-30'; 
if (($timestamp = strtotime($myday)) === false) {
    echo 'The string ('.$myday.') is not date';
} else {
    echo 'The string ('.$myday.') is date = ' . date('l dS \o\f F Y h:i:s A', $timestamp);
}
Psychomancy answered 28/9, 2022 at 11:45 Comment(0)
V
0

Use date_parse

The array includes warning_count and warnings fields.
(snip)
The array also contains error_count and errors fields.
PHP: date_parse - Manual

function isValidDate($input) {
    $a = date_parse($input);
    return $a["warning_count"] == 0 && $a["error_count"] == 0;
}

For example:

var_dump(isValidDate("2023-11-30"));  // true
var_dump(isValidDate("2023-11-31"));  // false (warnings: "The parsed date was invalid")
var_dump(isValidDate("2023-13-01"));  // false (errors: "Unexpected character")
Vendible answered 12/12, 2023 at 11:0 Comment(0)
L
-1

Give this a try:

$date = "2017-10-01";


function date_checker($input,$devider){
  $output = false;

  $input = explode($devider, $input);
  $year = $input[0];
  $month = $input[1];
  $day = $input[2];

  if (is_numeric($year) && is_numeric($month) && is_numeric($day)) {
    if (strlen($year) == 4 && strlen($month) == 2 && strlen($day) == 2) {
      $output = true;
    }
  }
  return $output;
}

if (date_checker($date, '-')) {
  echo "The function is working";
}else {
  echo "The function isNOT working";
}
Linseylinseywoolsey answered 10/11, 2017 at 16:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.