Advertisement

Dates and Time - The OOP Way

by

The Date/Time PHP extension is a set of classes that allow you to work with almost all of the date and time related tasks. It's been available since the release of PHP 5.2 and the extension introduced several new classes, all of which are mapped to real life scenarios:

  • A date or a time is represented by a DateTime object.
  • A timezone of the world is represented by a DateTimeZone object.
  • DateInterval objects represent an interval. For example, when we say two days from now on, the "two days" is the interval. The DateInterval object doesn't rely on a specific date or time.
  • DatePeriod objects represent a period between two dates.

Now don't let the last two tip you off, we'll be looking at real world usage of these two in a moment.


From date() to DateTime

Whenever we want to show a date we used date(), it's simple and it works. You just need to pass the date format you need. But it's a real pain to manipulate, a good example is formatting dates and times according to a custom timezone.

DateTime does more than just return a formatted date, but before we go any further, you need to initiate a new DateTime object which represents your date and or time. Then we can do all sorts of cool stuff with it. A new instance is created just like any other PHP class.

$date = new DateTime();

The constructor of DateTime accepts a string parameter which defaults to "now", the current time and date. To create an object for a specific date, you should pass the specific date and time to it. Formatting for the parameter is self explanatory in most cases. Below you can find a few different examples of constructing your DateTime object:

new DateTime('2013, March 24') //DateTime representing 2013, March 24
new DateTime('2013-03-24') //DateTime representing 2013, March 24
new DateTime('+2 days') //DateTime representing 2 days from now on.
new DateTime('tomorrow')

When PHP is having a hard time understanding the format, it will throw an exception. A full list of the available formatting can be found in in the documentation

If there is no formatting that matches your needs, you can specify your own format by using DateTime::createFromFormat

DateTime::createFromFormat('j-M-Y', '24-Mar-2013');

Now that we have a DateTime object in hand we can do whole bunch of stuff, rather easily.

Unix Timestamp

$date->getTimestamp(); //returns a unix timestamp

Modifying Date/Times

$date->setDate(2013, 12, 30); //yyyy, mm, dd will set the the specified date
$date->setTime(12, 3, 20); //hh, mm, ss (optional) will modify the time
$date->modify('tomorrow'); //string based manipulation
$date->setTimestamp(1364798550); //modify using a unix timestamp

Note that when out-of-range values are set, PHP will modify the date accordingly. For example, $date->setDate(2013, 12, 35); will generate 2014-01-04, the same goes for time.


Working With Multiple Dates

Now that you're obsessed with DateTime, the next thing you know, your apps will be filled with DateTime objects. You'll start to love dates and times like never before. From now on, you'll be dealing with DateTime objects, not "strings" that you have to pass to the strtotime function when you need to do a little math.

Say for example that you need to compare two birthdays:

$sheldon = new DateTime('May 20th, 1980');
$neo     = new DateTime('March 11th, 1962');

if ($sheldon > $neo)
    echo 'Sheldon is younger than neo';

Another scenario might be comparing two dates. We can compare dates against one another like so:

$diff = $neo->diff($sheldon);

The diff call returns a DateInterval object. If we dump out the return value:

DateInterval Object
(
    [y] => 18
    [m] => 2
    [d] => 9
    [h] => 0
    [i] => 0
    [s] => 0
    [invert] => 0
    [days] => 6645
)

These are public properties. You can generate some friendly output from a DateInterval object:

$diff->format('Neo is older by %Y years and %m months older'); //Neo is older by 18 years and 2 months

What's best about the DateInterval object is that you can apply the interval to another DateTime object:

$neo->add($diff); //neo's birthday changed to sheldon's

Note: Modifications to DateTime, such as adding doesn't return new DateTime objects, it affects the original object. Always keep this in mind when passing around DateTime objects throughout your app. PHP 5.5 introduced a new class that returns new objects upon modification.

diff isn't the only place where you can generate a DateInterval object. Since it's a class, new objects can be initiated as usual:

$new_diff = new DateInterval('P2Y');

The amount of years/months/days etc., are passed in as a string to the constructor. More information can be found in the constructor's documentation.


Working With Timezones

When creating new DateTime objects, the second argument of the constructor defines a timezone. If we skip this, a default timezone will be grabbed from the php.ini's date.timezone. You can modify this at runtime by calling date_default_timezone_set:

date_default_timezone_set('America/New_York');
new DateTime('today'); //datetime object is on New York time

You can also change timezones on the fly. As you may have guessed, first we need a Timezone object.

$timezone = new DateTimeZone('America/New_York');
$sheldon->setTimezone($timezone); //sheldon's birthday now on to New York

You can define the timezone while creating your new DateTime object:

$sheldon = new DateTime('May 20th, 1980', $timezone);

However, an important thing to note is that setTimezone modifies the original DateTime object. What we're basically asking is, "format this date, to this timezone" whenever we call the setTimezone method. Now on the other hand, in the last example where we pass the timezone into the DateTime constructor, we're saying, "the date I'm creating is in this timezone".

A list of valid timezones are available in the online documentation.


DatePeriods

I think the official manual provides the best explanation:

A date period allows iteration over a set of dates and times, recurring at regular intervals, over a given period.

DatePeriod allows us to generate a set of DateTimes using two days that we define between an interval. We pass a starting date, an interval and an ending date. On each interval a new DateTime object is created.

Let's say that we want to get all of Sheldon's birth dates, since his birth:

//since birthdays occur every year, the interval is one year
$interval = new DateInterval('P1Y');

//third argument is the end date, new DateTime() == right now
$period   = new DatePeriod($sheldon, $interval, new DateTime());

foreach($period as $dt) {
    //DateTime objects
    echo $dt->format('Y-m-d - D'), "\n";    
}

The result would be:

1981-05-20 - Wed
1982-05-20 - Thu
1983-05-20 - Fri
1984-05-20 - Sun
1985-05-20 - Mon
1986-05-20 - Tue
...

Now by default, the DatePeriod includes the starting date that we pass in. However, the fourth argument to the constructor allows us to skip the start date:

$period   = new DatePeriod($sheldon, $interval, new DateTime(), DatePeriod::EXCLUDE_START_DATE);

Let's see how many birthday parties Neo has had before Sheldon was born:

$bdays   = new DatePeriod($neo, $interval, $sheldon, DatePeriod::EXCLUDE_START_DATE);
echo iterator_count($bdays);

Extending

All of the classes that we've covered today can be extended to use with your own methods. One popular usage is extending the DateTime with a __toString method so that you can properly print out a DateTime object without calling format.


A Couple Usage Scenarios

  • One of my personal approaches to using DateTime objects, is when dealing with date/time columns in databases. All of the dates are stored as UTC timezone dates. The app code only works with DateTime objects, but before the end query is generated, all of the dates are formatted to UTC. This approach has allowed me to work with multiple timezone inputs easily.

    I can pass in a New York time object and completely forget about formatting it, before going to the database. I can easily switch between Unix timestamps and regular date-time formatting in my database at anytime, my app code doesn't care as long as it gets a DateTime object.

  • I've also used DateInterval to simplify subscription payment logic. Using DateInterval objects to define the time between the subscription has made things really easy. I just need to apply the interval to last payment date.

Do you have any good date/time usage examples? Share them in the comments.


Wrap Up

The date time extension has so much to offer, if you're on the bleeding edge, there are new classes and interfaces introduced since PHP 5.5. Be sure to checkout the manual. Thanks for reading.

Advertisement