15

I'm having trouble using the PHP DateTime class, and more specifically the DateTime::createFromFormat().

I get a date from a string, then try to instanciate a DateTime object, using DateTime::createFromFormat(). But, when I give this function a date that cannot exist, it is still working, returning me a valid DateTime object, with a valid date, which isn't the date I gave it.

Code example :

$badDate = '2010-13-03';
$date = DateTime::createFromFormat('Y-m-d', $badDate);

var_dump($date);

/*
object(DateTime)#284 (3) {
["date"]=>
string(19) "2011-01-03 10:01:20"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/Berlin"
}
*/

Any ideas? I really need a way to check date validity.

Thank you.

Edit:

I just found why, see my answer.

6 Answers 6

16

DateTime::createFromFormat doesn't throw exception/return false when the given date is impossible. It try to guess the expected date.

If you give it '2010-01-32' (as in Januar, 32th), it will return a DateTime object containing Februar, 1st (Januar 31th + 1 day). Must be logical... in some weird twisted way.

To check the validity, you must check the DateTime::getLastErrors() which contains warning, like for my case :

array(4) {
  ["warning_count"]=>
  int(1)
  ["warnings"]=>
  array(1) {
    [10]=>
    string(27) "The parsed date was invalid"
  }
  ["error_count"]=>
  int(0)
  ["errors"]=>
  array(0) {
  }
}

This behavior seems to come from the UNIX timestamp which PHP calculate, based on the year, month and date you give it (even if the date is invalid).

Sign up to request clarification or add additional context in comments.

2 Comments

"Must be logical... in some weird twisted way." ==> Oh, we see that a lot in PHP.
Anyone reading this remember to check for both warnings AND errors, especially if you write your own isValidDate function to wrap around this.
16

I found example where validation based on DateTime::getLastErrors() will fail, so better solution would be to compare inputed date with generated date.

Code:

function validateDate($date, $format = 'Y-m-d')
{
    $dt = DateTime::createFromFormat($format, $date);
    return $dt && $dt->format($format) == $date;
}

Use example:

var_dump(validateDate('2012-02-28')); # true
var_dump(validateDate('2012-02-30')); # false

# you can validate and date/time format
var_dump(validateDate('14:50', 'H:i')); # true
var_dump(validateDate('14:99', 'H:i')); # false

# this is the example where validation with `DateTime::getLastErrors()` will fail
var_dump(validateDate('Tue, 28 Feb 2012 12:12:12 +0200', DateTime::RSS)); # true
var_dump(validateDate('Tue, 27 Feb 2012 12:12:12 +0200', DateTime::RSS)); # false

4 Comments

ouch, nice catch. What version of PHP are you using for this test? Maybe this was fixed along the way?
@ClementHerreman: this result is on all PHP version that support DateTime::createFromFormat, that is all above php >= 5.3.0. See it in action here. I never said that DateTime::getLastErrors() should report warning or error in this case, probably this works as intended (ignoring string days), but IMO, if we are doing validation function, it should matter, because 27 Feb 2012 is not Tuesday, it is Monday.
In this example DateTime::getLastErrors() reports error only in case if textual day cannot be found or it is invalid, see.
Much nicer solution, as this should handle both errors and warnings. Thanks!
15

You have to make use of DateTime::getLastErrors() which will contain the error The parsed date was invalid.

$badDate = '2010-13-03';
$date = DateTime::createFromFormat('Y-m-d', $badDate);
if( DateTime::getLastErrors()['warning_count'] > 0 ){
 //not a correct date
}else{
 //correct date
 print $date->format('Y-m-d');
}

Comments

4

Look for checkdate() function at php documentation. It will help you :)

1 Comment

This should have been the answer.
1

You can do that :

DateTime::getLastErrors()

Comments

-2

change

$date = DateTime::createFromFormat('Y-m-d', $badDate);

into

$date = DateTime::createFromFormat('Y-d-m', $badDate);

Your given format is wrong, you properly mixed up days and months...

To understand what is behind the magical calculation of dates read this very very good article by Derick Rethans and all comments ;)

5 Comments

The format isn't wrong, it really is `Y-m-d', I want to ensure that date validity is checked.
But when m is the second part and you pass 13: its calculated as January in the next year. Everything is fine here! This is the way PHP calculates with overlapping numbers on dates.
Indeed, that's what I just discovered, but 2 questions rise : 1. where the hell is it documented ? 2. Am I the only that doesn't find this behavior logical/expectable ?
I added a link to an article.
Nice article, but maybe you should also edit your answer about me using a bad format =).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.