DateTimeZone and DST in PHP

2017-12-03

I ran into something a little unexpected a few days ago at work and I thought I'd share (possibly in case I end up Googling this again myself later). I have a function like this:

function isDaylightSavingDifferent(DateTimeInterface $dt1, DateTimeInterface $dt2): bool
{
    return $dt1->format('I') !== $dt2->format('I');
}

It mostly does its job just fine, but I ran into some puzzling behavior when attempting to feed some data into it. The dates were in Atom (i.e. PHP's broken ISO-8601) format, ex. 2017-11-01T15:00:00-0500.

class DateTime#4319 (3) {
  public $date =>
  string(26) "2017-11-01 15:00:00.000000"
  public $timezone_type =>
  int(1)
  public $timezone =>
  string(6) "-05:00"
}

However, if I sent in another date after the DST switch (11-05 this year), the code dependent on that above function didn't fire. Why not?

class DateTime#4319 (3) {
  public $date =>
  string(26) "2017-11-06 15:00:00.000000"
  public $timezone_type =>
  int(1)
  public $timezone =>
  string(6) "-06:00"
}

The first one returns "0" for the "is DST" check, and the second returns... "0"??

wat

It turns out, PHP has 3 different ways to represent a DateTimeZone object, represented by $timezone_type in the var dump above. You can create one with a UTC offset (type 1, ex. -0500), a timezone abbreviation (type 2, ex. CDT), or a timezone name (type 3, ex. America/Chicago). All 3 are valid, and all represent the same moment in time, but only one of them contains enough context for PHP to understand DST. So while I knew it was CDT when I created the time string, when the code under test received it, it was formatted with a UTC offset, so the consuming code couldn't tell if DST had changed or not.

I ended up filling in the gaps using the context of the user, but when I initially tested it at the unit level, that wasn't there. Hence, the problem.

Comments