DateTime Magic in PHP

The new DateTime object isn't so new anymore, having been around since v5.1, but looking around the web at code questions and examples you wouldn't know it. The same old "divide by the number of seconds in a year" answers proliferate Stackoverflow and other forums. Really, it is so much easier!

So let's first work with some basic objects still inside our comfort zones, then see how easy our typical manipulation requirements are with this "new" object.

First, let's clarify what we are and are not working with. The date() function you might be used to working, from here http://php.net/manual/en/function.date.php, works as so:

//date() requires format, timestamp=optional

$old_style_date = date('Y-m-d h:i:s');
var_dump($old_style_date);

You see the date formatted as a string. There's the problem. You see, there's not so much you can do with a string except chop it up with formatting and do a bunch of math on it. Plus, before you even get started, you have to convert your date to a timestamp with strtotime() - so you'd better hope your date is in a format it accepts, or there's more pre-processing.

Ugly.

Let's see how our DateTime object is different ( http://www.php.net/manual/en/class.datetime.php )

$new_style_date = new DateTime();

var_dump($new_style_date);

var_dump(get_class_methods($new_style_date));

You see that, although the date is still stored as a string, it is a variable of an object with a lot of helpful methods. The first one is just its __construct - much more useful than the old way:

$new_style_date = new DateTime();
echo $new_style_date->format('Y-m-d h:i:s');

$new_style_date = new DateTime('2013-03-24 08:30:30');
echo $new_style_date->format('Y-m-d');

$new_style_date = new DateTime('@1363071114');
echo $new_style_date->format('Y-m-d');

Feed it a formatted time or a timestamp - or just let it default to time(). Notice when we want to give it a timestamp, we tell it this by using a preceeding "@" symbol - this lets the class know it's not a badly formatted date

So now we've studied the basic class, let's look at doing a few common tasks. Suppose we'd like to see how long ago a certain event occurred:

Please note: There's more than one way to skin a cat. One of the great things about php, and programming in general, is the flexibility each developer has. You're welcomed to come up with different ways of doing the following examples - just make sure the cat gets skinned.

$date = new DateTime('2011-02-22 08:30:30');
echo $date->format('Y-m-d');

$now = new DateTime();

$interval = $date->diff($now);
var_dump($interval);

echo $interval->days;

Now that's different! No more long functions to divide & label years, months, days... ago. The interval is returned as anothor object, so you just reference the broken out variables. Let's get fancier:

$date = new DateTime();
echo $date->format('Y-m-d');

$future = new DateTime('first day of next month');

$interval = $date->diff($future);
var_dump($interval);

echo $interval->days;

Excellent! No more calculating all those future & past dates - just tell it in straightforward English using Relative Formats There are loads of clever combinations you can make, just be careful of your syntax (notice, for example, "of" in the above example. Try it without to see the difference).

So I'll leave you to play with that a bit, and move on to a bit more complicated example just to finish up. Let's say I need a sales report showing the data broken out for the last 12 months, including this one. Dynamic, of course - months need to rollover ever 1st of month. How might I set something like that up?

Believe it or not, we are actually building on the last example. Remember $interval? That is actually a class object as well - a DateInterval class So, we can make our date do things like cycle over regular periods of time:


// notice our relative date format - we want to start 12 months ago. I'm saying on the 
// "first day" because we probably will do more with this, including ranges for queries	
// need more or less time, change this starting point
$date = new DateTime('first day of -12 months' ); 

//let's just take a peek at what we've got
echo $date->format('Y-m-d');

// array_map is a handy way to cycle over an array. Think of this as foreach (range(1,12))
$months_array = array_map(
    function($val) use ($date) 
    {
        // Here's the good bit. We use the add() method on the DateTime object, and we 
        // are adding a DateInterval 
	// Syntax 'P1M' means "plus 1 month", then we format to 3-letter month (Jan, etc)
	return $date->add(new DateInterval('P1M'))->format('M');
    }, range(1,12)
);

// let's have a look
var_dump($months_array);

When you look through the links I've included, you'll see there's an almost endless list of things you can do using these two classes together. Hopefully this article has been enough to get you comfortable and playing with them; once you've reached that point, you'll see how much less tortuous working dates in php can be!

Note: There's a great library called Carbon that provides a wrapper for the DateTime class, with a lot of additions and gotchas sorted out. Worth taking a look as a next step to this post

Hope that helped!

Contact me