Skip to content

DateTime::diff Not handling DST changes correctly #1059

@asharris

Description

@asharris

From manual page: https://php.net/datetime.diff

The code snippet below sets two times, the first is in BST (i.e. UTC + 1 Hours) and the second is in UTC.

<?php

date_default_timezone_set('Europe/London');
$dt1 = new DateTime('2021-10-30 09:00:00');
$dt2 = new DateTime('2021-10-31 09:00:00');
echo $dt1->format('Y-m-d H:i:s e') . "\n";
echo $dt2->format('Y-m-d H:i:s e') . "\n";
$diff = $dt1->diff($dt2, true);
print_r($diff);
$seconds = $dt2->getTimestamp() - $dt1->getTimestamp();
$minutes = intdiv($seconds, 60);
$hours = intdiv($seconds, 60 * 60);
$days = intdiv($seconds, 60 * 60 * 24);
echo "Total Days: $days\nTotal Hours: $hours\nTotal Minutes: $minutes\nTotal Seconds: $seconds\n";

This produces the following output:

2021-10-30 09:00:00 Europe/London
2021-10-31 09:00:00 Europe/London
DateInterval Object
(
    [y] => 0
    [m] => 0
    [d] => 1
    [h] => 0
    [i] => 0
    [s] => 0
    [f] => 0
    [weekday] => 0
    [weekday_behavior] => 0
    [first_last_day_of] => 0
    [invert] => 0
    [days] => 1
    [special_type] => 0
    [special_amount] => 0
    [have_weekday_relative] => 0
    [have_special_relative] => 0
)
Total Days: 1
Total Hours: 25
Total Minutes: 1500
Total Seconds: 90000

As can be seen, the diff shows only an exact number 1 of days and no hours, whereas the actual time difference is 25 hours.
Experimenting with different times for $dt2 (such as '2021-10-31 08:30:00') gives in excess of $diff->h of 24 and $diff->i of 30, so diff does seem to be aware of the change from DST but once the total exceeds 25 hours, the extra hour is lost in the calculation.

So, using 08:30 as the end time after the clocks change gives the following output:

2021-10-30 09:00:00 Europe/London
2021-10-31 08:30:00 Europe/London
DateInterval Object
(
    [y] => 0
    [m] => 0
    [d] => 0
    [h] => 24
    [i] => 30
    [s] => 0
    [f] => 0
    [weekday] => 0
    [weekday_behavior] => 0
    [first_last_day_of] => 0
    [invert] => 0
    [days] => 0
    [special_type] => 0
    [special_amount] => 0
    [have_weekday_relative] => 0
    [have_special_relative] => 0
)
Total Days: 1
Total Hours: 24
Total Minutes: 1470
Total Seconds: 88200

Using getTimestamp() on the date objects correctly handles the DST change.

I do believe the Interval generated by DateTime::diff should ALWAYS reflect the ABSOLUTE time difference between two objects and it is inconsistent that it correctly shows 24 hours and 30 minutes when the end time is 08:30, but 1 day and 30 minutes when the end time is 09:30:

2021-10-30 09:00:00 Europe/London
2021-10-31 09:30:00 Europe/London
DateInterval Object
(
    [y] => 0
    [m] => 0
    [d] => 1
    [h] => 0
    [i] => 30
    [s] => 0
    [f] => 0
    [weekday] => 0
    [weekday_behavior] => 0
    [first_last_day_of] => 0
    [invert] => 0
    [days] => 1
    [special_type] => 0
    [special_amount] => 0
    [have_weekday_relative] => 0
    [have_special_relative] => 0
)
Total Days: 1
Total Hours: 25
Total Minutes: 1530
Total Seconds: 91800

I have tested with: PHP 8.0.12 (cli) (built: Oct 21 2021 14:38:26) ( NTS )
and also: PHP 7.3.31 (cli) (built: Sep 21 2021 10:24:03) ( NTS )

I can find nothing in the documentation to explain this behaviour, and therefore believe it to be a bug


Antony Harris

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions