What is the difference between 2 days and 48 hours in php

Viewed 612

$new_date = date('Y-m-d', strtotime("+2 days"));

or

$new_date = date('Y-m-d', strtotime("+48 hours"));

Are they same or different

2 Answers

Are they same or different

They are quite different. Objectively speaking, 2 days does not always equal 48 hours.

Consider crossing a daylight-savings boundary.

For example, 2019-04-07T02:00:00+1100 (AEDT -> AEST)

$twoDays = new DateInterval('P2D');
$fortyEightHours = new DateInterval('PT48H');

$ref = new DateTimeImmutable('2019-04-07T01:00:00',
        new DateTimeZone('Australia/Melbourne'));

echo 'Reference:     ', $ref->format('r'), PHP_EOL;
echo 'Plus 2 days:   ', $ref->add($twoDays)->format('r'), PHP_EOL;
echo 'Plus 48 hours: ', $ref->add($fortyEightHours)->format('r'), PHP_EOL;

This produces

Reference:     Sun, 07 Apr 2019 01:00:00 +1100
Plus 2 days:   Tue, 09 Apr 2019 01:00:00 +1000
Plus 48 hours: Tue, 09 Apr 2019 00:00:00 +1000

Note, HHVM produces a different result for some reason

Demo ~ https://3v4l.org/L4tKo

This list of common date / time related falsehoods is worth checking out ~ https://github.com/kdeldycke/awesome-falsehood#dates-and-time


It's worth pointing out that using the same reference date and manipulating it with strtotime() produces different results to those above.

Demo ~ https://3v4l.org/O6MTd

I suspect this is because its relative time calculations aren't as nuanced as DateInterval.

Semantically they are different, as demonstrated by Phil in the other answer. This means that in some other libraries that account for this the difference is real.

However, the implementation of strtotime() does not take this into account, so given your example, it makes no difference.

I was interested if it can be followed up through source code, so here goes:

Implementation of strtotime() does this:

  1. strtotime() starts, calling timelib_strtotime() to create the initial ts construct
  2. timelib_strtotime() calls scan() to parse the string
  3. it reads the string you've given to it, and one of the tokens it gets is your +2 days or +48 hours, which is considered as relativetext.
  4. for relativetext, among other things, timelib_set_relative() is called
  5. in timelib_set_relative() it does a lookup from using timelib_lookup_relunit(), which uses timelib_relunit_lookup array, finding that you've provided either a construct with type TIMELIB_DAY, value 1, multiplier 2, or construct TIMELIB_HOUR, value 1, multiplier 24
  6. It saves the information from previous call to s->time->relative construct
  7. this construct gets passed on, until strtotime calls timelib_update_ts with it (and with timezone info!)
  8. in timelib_update_ts we call do_adjust_relative() to act on it. We do it without using time zone parameter, which I would think is the critical mistake
  9. do_adjust_relative() calls timelib_do_normalize()
  10. timelib_do_normalize() for hours does do_range_limit(0, 24, 24, &time->h, &time->d), increasing days and decreasing hours if hours are over the hardcoded limit which is 24, and for days does do_range_limit_days(), increasing months and decreasing days if we are over the limit, taking into account leap time (but NOT time zones!).
  11. on returning to timelib_update_ts, we call do_adjust_timezone with timezone parameter, but at this point we've already made all the adjustments and we know no more if it was done with +48 hours or with +2 days. Thus it makes no difference.

TLDR: strtotime() does not take time zones into account when doing the conversion, so in that function it makes no difference, even though it really should.

Related