Краткий ответ: да, это ошибка.
Я упростил ваш тестовый пример, чтобы сделать его более очевидным:
$timeZones = ['GMT', 'Europe/Paris'];
foreach ($timeZones as $timeZone) {
date_default_timezone_set($timeZone);
$startDate = new \DateTime('2020-07-01');
$calcDateA = (new \DateTime('2020-07-01'))->modify('+1 month -1 day');
$diffA = $startDate->diff($calcDateA);
echo("Using $timeZone\n");
echo $startDate->format('r'), "\n";
echo $calcDateA->format('r'), "\n";
echo $diffA->format('Days: %d / Months: %m'), "\n\n";
}
Using GMT
Wed, 01 Jul 2020 00:00:00 +0000
Fri, 31 Jul 2020 00:00:00 +0000
Days: 30 / Months: 0
Using Europe/Paris
Wed, 01 Jul 2020 00:00:00 +0200
Fri, 31 Jul 2020 00:00:00 +0200
Days: 0 / Months: 1
Я предполагал, что, как вы предполагаю, вычисления выполняются в UT C:
$utc = new DateTimeZone('UTC');
foreach ($timeZones as $timeZone) {
date_default_timezone_set($timeZone);
$startDate = new \DateTime('2020-07-01');
$startDate->setTimezone($utc);
$calcDateA = (new \DateTime('2020-07-01'))->modify('+1 month -1 day');
$calcDateA->setTimezone($utc);
$diffA = $startDate->diff($calcDateA);
echo("Using $timeZone\n");
echo $startDate->format('r'), "\n";
echo $calcDateA->format('r'), "\n";
echo $diffA->format('Days: %d / Months: %m'), "\n\n";
}
Это приводит к потере некоторого контекста:
Using GMT
Wed, 01 Jul 2020 00:00:00 +0000
Fri, 31 Jul 2020 00:00:00 +0000
Days: 30 / Months: 0
Using Europe/Paris
Tue, 30 Jun 2020 22:00:00 +0000
Thu, 30 Jul 2020 22:00:00 +0000
Days: 0 / Months: 1
В системе отслеживания ошибок PHP есть несколько тикетов. В один из них участник объясняет:
это не связано с переходом на летнее время (на этот раз). Но случается, что PHP сначала преобразует его во время UT C, что в итоге приводит к сравнению 2016-12-31 23:00 с 2017-09-30 22:00 - а затем алгоритм становится еще более запутанным и беспорядочным. вверх.
Этот код сложный, и, к сожалению, его нужно переписать. Это займет некоторое время.