How I Learned To Stop Using strtotime() And Love PHP DateTime

The DateTime classes in PHP have been available since version 5.2, but I have largely ignored them until recently. This was partly due to the fact that I was working in PHP 5.1 environments a lot (don't ask) but mostly because I was just used to using the standard date functions that have always been a part of PHP (well, since version 4). I wanted to explain why I will be using the new DateTime classes more from now on and why you shouldn't be hesitant to use them.

Using a combination of strtotime() and date() can handle most things and is a good method to quickly grab a date.

echo strtotime("02/23/2013 12:00"); // prints 1361620800
echo strtotime("today"); // prints 1362096000
echo strtotime("2013-02-23"); // prints 1362096000

You can get real problems, however, when the string isn't quite what strtotime() expects, which is especially true when trying to parse a date in a non-US date format. If the date string isn't understood by strtotime() then it will return false.

var_dump(strtotime("23/02/2013")); // prints bool(false)

When this happens you can try a few things to force strtotime() to parse the date correctly. Sometimes it's something as simple as swapping the slashes for dashes, which forces strtotime() to parse the date in a different way.

$date = "23/02/2013";
$timestamp = strtotime($date);
if ($timestamp === FALSE) {
  $timestamp = strtotime(str_replace('/', '-', $date));
}
echo $timestamp; // prints 1361577600

However, this gets more complicated when the date string consists of more than just the date. Take the following date string, which I found being returned from a web service I was using.

Thu, 23/02/2012 - 15:18

Trying the previous trick of attempting to replace slashes doesn't work here, so further steps needed to be taken. After a couple of minutes of playing around with the string I found that removing the dash (-) between the date and the time caused the strtotime() function to accept the string and parse it correctly.

$timestamp = strtotime(trim(str_replace('/', '-', str_replace('-', '', $date))));

It was at this point that I questioned what I was trying to do. In my attempt to get the date string parsed I had used three expensive string manipulation functions, just to try and get the date time string into a state that strtotime() could understand. I therefore resolved to find a better solution to this problem and after a bit of research I re-discovered the PHP DateTime classes. Of particular interest was a method called DateTime::createFromFormat() that takes a format and a string and creates a date object. Here is an example of using the createFromFormat() method to parse the above date time value correctly.

$str_date = 'Thu, 23/02/2012 - 15:18';
$obj_date = DateTime::createFromFormat('D, d/m/Y - H:i', $str_date);
echo $obj_date->getTimestamp(); // prints 1330010280

My ultimate goal in all this was to simply compare one date with another as part of a usort() function call. What I initially started doing was converting the time into a timestamp and then compared those timestamps. With the PHP DateTime classes this isn't needed as the objects are directly comparable (at least since version 5.2.2). Here is an example of comparing two different DateTime objects.

$str_date = 'Thu, 23/02/2012 - 15:18';
$obj_date_1 = DateTime::createFromFormat('D, d/m/Y - H:i', $str_date);

$str_date = 'Thu, 08/11/2012 - 12:57';
$obj_date_2 = DateTime::createFromFormat('D, d/m/Y - H:i', $str_date);

var_dump($obj_date_1 < $obj_date_2); // prints bool(true)

Rather than attempt to hack the string into a working format and push it into strtotime() it is best to use the DateTime classes so that you are absolutely sure of what dates you get from your inputs. In fact, if you are unsure of the input format of your date then you need to take a step back and understand why this is the case. You have to be sure about what input parameters are being passed to any program, and if you are receiving a mix of different date formats then things can go horribly wrong very quickly.

The good thing about the createFromFormat() method is that if you do give it the wrong date format then it will return false. This means that you do have some leeway when creating DateTime objects and can correctly detect if things are not as they appear to be.

$str_date = 'Thu, 23/02/2012';
$obj_date = DateTime::createFromFormat('D, d/m/Y - H:i', $str_date);
var_dump($obj_date); //prints bool(false)

This is much more robust than using the DateTime class constructor which will throw an exception if you supply it with an invalid date format. Here is an example of producing an exception with the PHP DateTime class.

try {
    $date = new DateTime('11n5fgfgh');
} catch (Exception $e) {
    echo $e->getMessage();
    exit(1);
}

This produces the following output.

DateTime::__construct(): Failed to parse time string (11n5fgfgh) at position 0 (1): Unexpected character

The bottom line in all of this is that if you are accepting user input (or input of any kind) then you need to do all you can to make sure it is in the correct format before you start working on it. If you are using a web service and the date format keeps changing then you need to tell them about this as that is really poor practice.

A couple of years ago I had first hand experience of maintaining a system (I didn't build it, I hasten to add!) where there was a single function to manipulate dates, but I couldn't be sure in what format the date would be passed in due to different parts of the system recording dates differently. The system would call this single function and either pass a null value or one of four different date formats. This meant a few occasions where strtotime() would fail and return false, which was then returned as Jan 1st 1970 by the date() function, which was then saved back to the database. I eventually had to track down every place where this function was called from and make sure it was passing the correct value. That pain would have been far easier if I had swapped out the function with a DateTime() object as then I would know for sure if the date was read correctly.

Comments

Thanks!
Permalink
Thank you
Permalink
Thanks a looooootttt excelent explanation!!
Permalink
Thanks, it really helped.
Permalink
thanks!
Permalink
In your example: $str_date = 'Thu, 23/02/2012 - 15:18'; I would like to get the date to be a string so that I can insert it be something along the lines of yesterday's date at 11:59 PM. It would need to be dynamic though. Any ideas?
Permalink
I think you want something like this:$dateinterval = DateInterval::createFromDateString('-1 day');
Name
Philip Norton
Permalink
thanks In your example: $str_date = 'Thu, 23/02/2012 - 15:18'; I would like to get the date to be a string , I want to get month from it I try $variable_added=date("m", strtotime ($list_item -> field_birthday ) ); but it failed , Any Ideas?
Permalink
This is great. Appreciate the info. Learned something new about DateTime which I like a lot!
Permalink
Thanks for the info, really helped.
Permalink

One thing to be aware of is that DateTime will accept an invalid date in some cases and will intelligently make it a correct date without any warnings or errors. This can be problematic in some cases.

php > $date = DateTime::createFromFormat('Y-m-d H:i:s', '2021-01-99 02:41:37');
php > echo  $date->format('Y-m-d H:i:s');
2021-04-09 02:41:37

 

Permalink

That is a good point Brad. How would you solve that? Maybe adding some validation on the date before using it would do the trick.

Name
Philip Norton
Permalink

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
10 + 1 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.