back to blog

It's About Time We Talk About PHP's DateTime.

September 30th, 2014 by Daniel Pepin

Time. A developer's worst fear. No, not the worry of draining budgets and tight deadlines, but the fear of the unknown. Time can be quite a mystery; a set of rules and international regulations that differ between continents, countries, and even individual states.

A few things to be concerned about:

That's a lot of information for a new developer to learn about, so let's start out with some basics.

The first things to try and understand is how your web server is handling datetime.

Setup

To figure out what timezone PHP is set to by default, you can run this command:

> php -r "echo date_default_timezone_get() . PHP_EOL;"

If your timezone was set to Eastern Standard Time (EST) you may see output like:

> America/New_York

However, we can change the default timezone for PHP by modifying the date.timezone property in our php.ini file.
Lookup valid timezone values.

Basics

Now that we have our timezone set properly we can start utilizing the powerful PHP DateTime class.

The first thing we can do is create a new instance of DateTime:

$now = new DateTime('now');

The first parameter to DateTime must be a valid date or time format. This is a very powerful feature. Here's a small script to display some datetime formats:

$valid_time_formats = ['now', 'jan 1 2016', '12 hours ago', 'next year', 'first day of next month', 'last day of this month', '12/25/2015', '@612835200'];

$mask = "|%25.25s | %-22.22s |\n";
printf($mask, 'Format', 'Result');
foreach($valid_time_formats as $format) {
  $date = new DateTime($format);
  printf($mask, $format, $date->format('m-d-Y h:i:sA'));
}

The output should look like:

|                   Format | Result                 |
|                      now | 09-30-2014 10:24:32AM  |
|               jan 1 2016 | 01-01-2016 12:00:00AM  |
|             12 hours ago | 09-29-2014 10:24:32PM  |
|                next year | 09-30-2015 10:24:32AM  |
|  first day of next month | 10-01-2014 10:24:32AM  |
|   last day of this month | 09-30-2014 10:24:32AM  |
|               12/25/2015 | 12-25-2015 12:00:00AM  |
|               @612835200 | 06-03-1989 12:00:00AM  |

When creating a new instance of DateTime, the second argument, a timezone, is optional:

public __construct ([ string $time = "now" [, DateTimeZone $timezone = NULL ]] )

So if we know the exact timezone we want to use, we can instantiate like:

$pst = new DateTime('now', new DateTimezone('America/Los_Angeles'));

Notice that the timezone must be an instance of DateTimezone.

Formatting

Another great feature of DateTime is the ability to format the output. View formatting documentation.

Here are some examples of formats:

$formats = ['Y', 'm-d-Y', 'H:i:s', 'h:i:sA', 'z', 'W', 'M jS, Y', DATE_RSS, DATE_ISO8601];

$mask = "|%17.17s | %-25.25s |\n";
printf($mask, 'Format', 'Result');
$date = new DateTime('now');
foreach($formats as $format) {
  printf($mask, $format, $date->format($format));
}

Here is the output:

|           Format | Result                    |
|                Y | 2014                      |
|            m-d-Y | 09-30-2014                |
|            H:i:s | 16:07:37                  |
|           h:i:sA | 04:07:37PM                |
|                z | 272                       |
|                W | 40                        |
|          M jS, Y | Sep 30th, 2014            |
| D, d M Y H:i:s O | Tue, 30 Sep 2014 16:07:37 |
|    Y-m-d\TH:i:sO | 2014-09-30T16:07:37-0400  |
Use the built-in DateTime constants to quickly format your datetime into a commonly accepted format.

Modifying

One of the most powerful features with the DateTime class is the ability to modify a date by adding/removing seconds, minutes, hours, days, weeks, months, years, etc.

Here are some examples of how you can modify a given datetime:

$modifiers = ['now', '+10 seconds', '+10 minutes', '+1 day', 'last second tomorrow', 'last day of this month', '12/31', '+3 months', '+1 year'];

$mask = "|%25.25s | %-22.22s |\n";
printf($mask, 'Modifier', 'Result');
$date = new DateTime('now');
foreach($modifiers as $modifier) {
  $date->modify($modifier);
  printf($mask, $modifier, $date->format('m-d-Y h:i:sA'));
}

And here's the output. Notice that each additional modifier to the DateTime object will be applied directly to the object.

|                 Modifier | Result                 |
|                      now | 09-30-2014 03:06:41PM  |
|              +10 seconds | 09-30-2014 03:06:51PM  |
|              +10 minutes | 09-30-2014 03:16:51PM  |
|                   +1 day | 10-01-2014 03:16:51PM  |
|     last second tomorrow | 10-01-2014 11:59:59PM  |
|   last day of this month | 10-31-2014 11:59:59PM  |
|                    12/31 | 12-31-2014 11:59:59PM  |
|                +3 months | 03-31-2015 11:59:59PM  |
|                  +1 year | 03-31-2016 11:59:59PM  |
If you don't want the original object to be modified, you can use the DateTimeImmutable class. That does the same thing as DateTime, but instead of modifying the original object, it returns a new instance.

Advanced

DateTime is extremely powerful. Another tough subject that it helps a lot with is calculating time differences. Using DateTime::diff() we can get a DateInterval object where we can calculate the difference in seconds, minutes, hours, days, years, etc.

The difference that is calculated does not include overflow values. This means that when calculating the difference in seconds, we are calculating the difference between 0-59 of the two dates. It does not take into account that it might be a different hour, day, or even year.

Let's take a look at some examples:

$date = new DateTime('now');
$future = new DateTime('December 31 2015');

echo 'Now: ' . $date->format('m/d/Y h:i:sA') . PHP_EOL;
echo 'Future: ' . $future->format('m/d/Y h:i:sA') . PHP_EOL;

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

$formats = ['%s seconds','%i minutes', '%h hours', '%a days', '%m months'];

$mask = "|%15.15s | %-22.22s |\n";
printf($mask, 'Format', 'Result');
foreach($formats as $format) { 
  printf($mask, $format, $interval->format($format));
}

Here is the output:

Now: 09/30/2014 04:28:52PM
Future: 12/31/2015 12:00:00AM

|         Format | Result                 |
|     %s seconds | 8 seconds              |
|     %i minutes | 31 minutes             |
|       %h hours | 7 hours                |
|        %a days | 456 days               |
|      %m months | 3 months               |
Notice that %a is special in that it displays the total # of days difference between the dates. However, note that the other formats are relative to their own types, such as hours (%h) only calculates the difference in daily hours.

Extending

Limited by PHP's built-in DateTime class, or just want a consistent way of handling unique methods across your application? Simply extend the DateTime class (or interface) and make your own.

class CustomDateTime extends DateTime {

  public function __toString() {
    return $this->format(DATE_ISO8601);
  }

  public function age($diff = 'now') {
    return $this->diff(new DateTime($diff))->format('%y');
  }

}

$bday = new CustomDateTime('12/25/1988');
echo 'Birthday: ' . $bday . PHP_EOL; // Notice we don't have to format here.
echo 'Age: ' . $bday->age() . PHP_EOL;

The output looks like:

Birthday: 1988-12-25T00:00:00-0500
Age: 25

That concludes our brief intro to PHP's DateTime class. Play around with the examples to get a better grasp of what is possible with DateTime. Although it will seem daunting at first, it greatly simplifies the work that a developer has to do.

 

Recommendations

PHP's DateTime documentation is a great read! There's a lot to learn, including DateTime, DateTimezone, DateInterval, and DatePeriod.