Steve Grunwell

Open-source contributor, speaker, and electronics tinkerer

Grayscale close-up of a watch face

For a Great Time, Make it a DateTime

Let’s acknowledge something right out of the gate: working with dates and times can be a slog. Not a year goes by without some app breaking due to Daylight Saving Time or an assistant not realizing that March 31st exists.

For those of us working in PHP—especially more recent versions of the language—dates and times don’t need to be a source of pain. Instead of strtotime() this and date() that, we have functionality baked into PHP that dramatically simplifies the work of parsing, converting, and formatting dates and times.

Meet the DateTimeInterface

PHP’s datetime extension (built into PHP by default) defines the DateTimeInterface which, as you might have guessed from its name, is an interface related to dates and times. Who says naming things has to be hard?

PHP also ships with two implementations of this interface: DateTime and DateTimeImmutable. Once again, their differences should be pretty clear from the description on the tin: a DateTimeImmutable object functions exactly like a DateTime except that it cannot be modified. Instead, attempting to modify a DateTimeImmutable object will result in a new DateTimeImmutable instance.

There are good use-cases for both implementations, but as a general rule of thumb you should default to DateTimeImmutable: this prevents operations from manipulating the underlying date/time directly, which becomes increasingly important if (for example) you’re passing a datetime through a series of middleware layers. DateTime should really only be used in cases where you need to manipulate the date and/or time within the same instance of the object.

Let’s Make a Date(Time)!

The constructors of the DateTime classes are pretty familiar if you’ve used PHP’s strtotime() function, as they works in much the same way:

Notice that all of the examples above are in Universal Coordinated Time (UTC): because we didn’t specify a time zone, PHP defaulted to the system time zone which—on a reasonable, *nix-based server—will be UTC.

We specify time zones by constructing DateTimeZone objects:

Note that the constructor will also attempt to automatically set the time zone if it can be inferred from the date string:

If we have a Unix timestamp, we can work with those, too!

Notice that Unix timestamps are prefixed with an ampersat (“@”, or “at sign”) character. Additionally, the time zone argument is ignored, as Unix timestamps are meant to represent the number of seconds that have passed since 1970-01-01 00:00:00 +0000, also known as the Unix Epoch.

If you know the format a date is supposed to be in, you can also parse it using the static createFromFormat() method:

Working with DateTime Objects

Once you have a valid DateTime object, there are a whole slew of options at your fingertips. Some of the most common include:

Modifying & Comparing DateTimes

Imagine you run an ecommerce store with a 30 day return policy, and need to determine whether or not an order is eligible for returns:

There are a few things to note about that example:

  • Using the modify() method, we can pass strtotime()-like strings
  • DateTime objects can be compared with one another out of the box

In that example, I chose to add 30 days to the original order date, but this could have just as easily been reversed:

While both of those examples work, PHP also includes the DateInterval class, which is designed to handle situations just like this:

Note the “P30D” argument passed to the DateInterval constructor: this may seem confusing at first, but it represents a period of 30 days. A full explanation can be found in the docs, but for your convenience:

Designator Description
Y Years
M Months
D Days
W Weeks (7 days)
H Hours
M Minutes
S Seconds

Notice that “M” is used for both months and minutes: every time period will start with “P”, and anything that comes after “T” (if present) will be interpreted as time:

Formatting Dates & Times

When you’re finally ready to render a date (or save it to a database), call on DateTimeInterface::format(). It accepts all the arguments you’re used to from PHP’s date() function:

Time Zone Conversions & Localization

One of the really nice parts about PHP’s datetime functionality is that it understands a ton of different time zones. Developers operating primarily in North America and the European Union have it easy, but not every country has it so easy.

Did you know that Asia/Kabul (Afghanistan) is UTC +4:30? Or that Pacific/Chatham (Chatham Islands of New Zealand) is UTC +12:45 or UTC +13:45, depending on Daylight Saving Time?

There are a entire countries that can’t be properly addressed using an integer UTC offset, and you don’t want to be responsible for keeping track of it all. Fortunately, the Internet Assigned Numbers Authority (IANA) maintains a database of all of this information, which is shipped with PHP.

Since PHP is aware of time zones and how they relate to UTC, we can also use DateTime objects to localize dates and times for the user:

As if that wasn’t handy enough, I’ve been burying the lede: using PHP DateTime objects means you don’t need to worry about Daylight Saving Time!

Note that I didn’t need to specify that Christmas falls during Eastern Standard Time (EST, UTC -5:00) and the 4th of July is in Eastern Daylight Time (EDT, UTC -4:00). Instead, I told it “hey, create objects representing these dates for the America/New_York time zone” and PHP figured out the rest!

Working with DateTime Objects vs Date Strings & Integer Timestamps

Let’s face it: working with date strings and Unix timestamps can be messy, and doing math with them is even worse. How many times have you seen (or written) something like this?

It’s a trivial example, but is still not the friendliest to read. What are the numbers going into $seven_days?† Why does $date hold a Unix timestamp?

These problems only multiply when you start passing date strings and/or Unix timestamps around as arguments for functions, because these strings and integers lack context. Additionally, every time you convert a date to a timestamp or back again, you run the risk of losing precision (at best) or confusing the local time zone for UTC. DateTime objects, on the other hand, act as PHP value objects that represent a specific date and time in a specific time zone.

The next time you find yourself converting dates to timestamps and vice versa, do yourself a favor and consider a DateTime instance instead!

Only the First Date!

This post only scratches the surface of what PHP’s datetime library offers. Whether it’s dealing with recurring events or handling errors with date parsing, PHP’s datetime functionality makes working with dates and times much easier.

If you need even more flexibility, you might also consider the Carbon library, which also uses the DateTimeInterface under the hood (though, in my experience, does add some overhead).

Now go forth and save the date!

† 60 seconds * 60 minutes * 24 hours * 7 days, but you’d know that at a glance if you were using stevegrunwell/time-constants!

Previous

Strict Equality for Better Code

Next

R.I.P. to the Best Girl, Shelby

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Be excellent to each other.