Asked  7 Months ago    Answers:  5   Viewed   29 times

Hi I seem to be having trouble getting the datetime method to work as expected? I may be doing something wrong?

// Passes OK 
$dateTime = DateTime::createFromFormat('d/m/Y', '12/12/2012' );
var_dump($dateTime);

// should fail but returns  - 2016-09-25 
$dateTime = DateTime::createFromFormat('d/m/Y', '56/56/2012' );
var_dump($dateTime);

// correctly returns False 
$dateTime = DateTime::createFromFormat('d/m/Y', '56/56/fail' );
var_dump($dateTime);

// should fail but returns 2019-08-29 09:58:10
$dateTime = DateTime::createFromFormat('m/d/Y', '90/90/2012' );
var_dump($dateTime);

enter image description here

 Answers

67

The thing about DateTime::createFromFormat is that there are two kinds of unexpected input it recognizes: the kind that generates errors, and the kind that generates warnings.

Input such as '56/56/fail' produces an error, so false is returned and everything is good. However, '56/56/2012' gives not an error but a warning, and is actually parsed as the 56th day of the 56th month of 2012. Since 2012 does not have 56 months, PHP internally changes this to 2016 + 8 months = Aug 2016. And since that month does not have 56 days, we have another compensation to Sep 2016 + (56 - 31) days = 25 Sep 2016. So while unexpected, this is in fact correct.

If you want to disallow this automatic adjustment, you have to wrap the DateTime factory method and use DateTime::getLastErrors as reference:

$dateTime = DateTime::createFromFormat('d/m/Y', '56/56/2012');
$errors = DateTime::getLastErrors();
if (!empty($errors['warning_count'])) {
    echo "Strictly speaking, that date was invalid!n";
}

See it in action.

Wednesday, March 31, 2021
 
mschuett
answered 7 Months ago
48

I afraid you've created DateTime object like this:

$date = new DateTime('01/18/2016 00:00 AM America/New_York');

That is not a supported/valid datetime format!

If you want to create a DateTime object from another format you must call DateTime::createFromFormat() instead, look:

$timezone = new DateTimeZone('America/New_York');
$strdate  = '01/18/2016 00:00 AM';
$date     = DateTime::createFromFormat('m/d/Y H:i A', $strdate, $timezone);

PHP doc states:

DateTime::createFromFormat / date_create_from_format — Returns new DateTime object formatted according to the specified format

Wednesday, March 31, 2021
 
Tucker
answered 7 Months ago
84

You need to add an @ for the timestamp in the DateTime class, like this:

$dt = new DateTime("@" . $supportDetails["Reported"]);
                  //^ See here

You can also see this in the manual. And a quote from there:

Unix Timestamp "@" "-"? [0-9]+ "@1215282385"

Also note that the current timezone is getting ignored, which you can also see in the manual:

Note: The $timezone parameter and the current timezone are ignored when the $time parameter either is a UNIX timestamp (e.g. @946684800) or specifies a timezone (e.g. 2010-01-28T15:00:00+02:00).

Wednesday, March 31, 2021
 
eliotlencelot
answered 7 Months ago
37

By default, PHP will populate missing date values with those of the current date/time; so

$date = DateTime::createFromFormat('m/Y', '02/2017');

will populate the missing day value with the current date; and as 31st February is an invalid date, it will roll forward into March. Likewise, hours/minutes/seconds will be populated with the missing time values based on the current time.

If you want to force the behaviour of forcing to the beginning of the month/time, then modify your mask with a leading !

$date = DateTime::createFromFormat('!m/Y', '02/2017');

This will populate the missing day with the 1st of the month, and the time with 00:00:00

Alternatively, a trailing | will have the same effect

$date = DateTime::createFromFormat('m/Y|', '02/2017');
Thursday, August 5, 2021
 
Pravi
answered 3 Months ago
82

The reason for this behaviour is that when you don't specify the missing parts of a date/time input to DateTime::createFromFormat, it uses the values from the current local date and time. In Auckland, that is October 31st and so it tries to make a date out of September 31 2019, which comes out as October 1 2019. To avoid this problem, use a ! at the start of the format string; this will instead substitute values from January 1 1970, 00:00:00 (the Unix Epoch) as required for those that are not specified in the time value:

echo DateTime::createFromFormat('!F-Y', 'September-2019')
    ->setTimeZone(new DateTimeZone('Pacific/Auckland'))
    ->format('Y-m-d');

Output:

2019-09-01

Demo on 3v4l.org

Tuesday, August 31, 2021
 
Keith
answered 2 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :