Take the following PHP code:
<?php
$timezone = new DateTimeZone('UTC');
$startDate = new DateTime('2022-09-26T00:00:00', $timezone);
$endDate1 = new DateTime('2023-02-28T00:00:00', $timezone);
$endDate2 = new DateTime('2023-03-01T00:00:00', $timezone);
$interval1 = $endDate1->diff($startDate);
$interval2 = $endDate2->diff($startDate);
print_r($interval1);
print_r($interval2);
And here's the output:
DateInterval Object
(
[y] => 0
[m] => 5
[d] => 2
[h] => 0
[i] => 0
[s] => 0
[f] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
[invert] => 1
[days] => 155
[special_type] => 0
[special_amount] => 0
[have_weekday_relative] => 0
[have_special_relative] => 0
)
DateInterval Object
(
[y] => 0
[m] => 5
[d] => 5
[h] => 0
[i] => 0
[s] => 0
[f] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
[invert] => 1
[days] => 156
[special_type] => 0
[special_amount] => 0
[have_weekday_relative] => 0
[have_special_relative] => 0
)
Can someone please explain why first interval's y-m-d
is 0-5-2 when days
count is 155, but second interval's y-m-d
is 0-5-5 even if only 1 day was added (infact the days
count is 156)?
I tried to port the same code in other languages (JavaScript and python to be precise) and the result for the first interval is the same as PHP's one: 5 months and 2 days. However the result for the second interval is 5 months and 3 days (as i would expect) and not 5 months and 5 days as PHP says.
PHP finds the difference between two dates by naively computing the difference between all components of a timestamp. (years, months, days, etc. (ref)) So the second interval would look like (y=1, m=-6, d=-25)
at this stage. However, this makes little sense to us humans, so PHP tries to normalize this interval by adjusting each field if the next one down would over- or underflow. (ref). But months do not contain a consistent amount of days, so PHP tries to be smart and does day-to-month adjustments based on the length of the month we started in. (ref)
So if the first date is in February, the days field will be adjusted by 28 days: (y=1, m=-7, d=2)
, while March has 31 days, yielding (y=1, m=-7, d=5)
. The next step for both intervals is the month adjustment, which is identical. A year has twelve months, so we get (y=0, m=5, d=2)
and (y=0, m=5, d=5)
.
Knowing this, we can have some more fun with PHP being weird
$start = new DateTime('2022-09-26');
$end = new DateTime('2023-03-01');
$start->diff($end); // +5m 3d
$end->diff($start); // -5m 5d
EDIT: My first assumption on how PHP worked internally turned out to be incorrect, so the answer above has been rewritten after @KIKO Software posed their question.