Сделайте diff, а затем примените его к той же дате, в результате мы хотим увидеть ту же дату, но результат может быть непредсказуемым.
Проверено на php 8.0.16, 7.4.7
<?php
date_default_timezone_set('Europe/Amsterdam');
$dateTimeNow = new DateTimeImmutable('2022-03-18 00:00:00.000000');
$dateTimeOld = new DateTimeImmutable('2021-12-01 00:00:00.000000');
$timeInterval = $dateTimeOld->diff($dateTimeNow);
var_dump($dateTimeNow); //output 2022-03-18
var_dump($dateTimeOld->add($timeInterval)); //output 2022-03-16 but must be 2022-03-18!
$dateTimeNow = new DateTimeImmutable('2022-03-01 00:00:00.000000');
$dateTimeOld = new DateTimeImmutable('2021-12-01 00:00:00.000000');
$timeInterval = $dateTimeOld->diff($dateTimeNow);
var_dump($dateTimeNow); //output 2022-03-01
var_dump($dateTimeOld->add($timeInterval)); //output 2022-03-02 but must be 2022-03-01!
date_default_timezone_set('UTC');
$dateTimeNow = new DateTimeImmutable('2022-03-18 00:00:00.000000');
$dateTimeOld = new DateTimeImmutable('2021-12-01 00:00:00.000000');
$timeInterval = $dateTimeOld->diff($dateTimeNow);
var_dump($dateTimeNow); //output 2022-03-18
var_dump($dateTimeOld->add($timeInterval)); //output 2022-03-18 correct!
$dateTimeNow = new DateTimeImmutable('2022-03-01 00:00:00.000000');
$dateTimeOld = new DateTimeImmutable('2021-12-01 00:00:00.000000');
$timeInterval = $dateTimeOld->diff($dateTimeNow);
var_dump($dateTimeNow); //output 2022-03-01
var_dump($dateTimeOld->add($timeInterval)); //output 2022-03-01 correct!
ПРИМЕЧАНИЕ. На пхп 8.1.3 работает без ошибок
Как это можно объяснить и что я должен использовать для этой цели, чтобы убедиться, что он действителен и стабилен?
@IMSoP я обновил свой код с дополнительными комментариями о выводе и ожиданиях вывода
Поэтому я изменил ваш код на:
$tzs = [
new DateTimezone('Europe/Amsterdam'),
new DateTimezone('UTC')
];
$base = '2021-12-01 00:00:00.000000';
$dates = [
'2022-03-18 00:00:00.000000',
'2022-03-01 00:00:00.000000',
];
foreach( $tzs as $tz ) {
$basedate = new DateTimeImmutable($base, $tz);
foreach( $dates as $date ) {
$curdate = new DateTimeImmutable($date, $tz);
$diff = $basedate->diff($curdate);
$test = $basedate->add($diff);
printf("%s %s %5s/%4s %s %s\n",
$basedate->format('c'),
$curdate->format('c'),
$diff->format('%mm%dd'),
$diff->format('%ad'),
$test->format('c'),
($curdate == $test) ? '1' : '0'
);
}
}
Что выводит в php>=8.1:
2021-12-01T00:00:00+01:00 2022-03-18T00:00:00+01:00 3m17d/107d 2022-03-18T00:00:00+01:00 1
2021-12-01T00:00:00+01:00 2022-03-01T00:00:00+01:00 3m0d/ 90d 2022-03-01T00:00:00+01:00 1
2021-12-01T00:00:00+00:00 2022-03-18T00:00:00+00:00 3m17d/107d 2022-03-18T00:00:00+00:00 1
2021-12-01T00:00:00+00:00 2022-03-01T00:00:00+00:00 3m0d/ 90d 2022-03-01T00:00:00+00:00 1
и в php <8.1:
2021-12-01T00:00:00+01:00 2022-03-18T00:00:00+01:00 3m15d/107d 2022-03-16T00:00:00+01:00 0
2021-12-01T00:00:00+01:00 2022-03-01T00:00:00+01:00 2m29d/ 90d 2022-03-02T00:00:00+01:00 0
2021-12-01T00:00:00+00:00 2022-03-18T00:00:00+00:00 3m17d/107d 2022-03-18T00:00:00+00:00 1
2021-12-01T00:00:00+00:00 2022-03-01T00:00:00+00:00 3m0d/ 90d 2022-03-01T00:00:00+00:00 1
Что, по-видимому, отличается от того, как DateTime->diff()
или DateInterval
определяют «месяц». Могу поспорить, что эта разница как-то связана с данными часового пояса и/или переходом на летнее время, отсюда и разница между UTC и Amesterdam TZ.
Мое решение здесь всегда будет «всегда хранить и вычислять даты в формате UTC, все остальные часовые пояса просто для человеческого глаза».
Но если это невыполнимо, то для эта конкретная ситуация, где вы не имеете дело с чем-то более подробным, чем количество дней, вы, вероятно, сойдете с рук:
$diff = $old->diff($new);
$diff = new DateInterval($diff->format('P%aD'));
Что должно убрать туманные «месяцы и оставшиеся дни» и оставить только «общее количество дней», что дает согласованные результаты во всех текущих версиях PHP. Но если в вашем фактическом коде есть компонент времени, вам придется найти этот вызов формата, чтобы получить правильную спецификацию интервала.
Есть подробный список исправлений Date в выпуске 8.1, но я не могу сказать, какой из них применим.
Я бы добавил часы, минуты, секунды и инвертировал $diff->format('P%aDT%hH%iM%sS')
и $diff->invert = $oldDiff->invert
, и это очень хороший ответ, который решает мою проблему!
Этому примеру довольно сложно следовать; Можете ли вы добавить некоторые комментарии/описание, объясняющие, что показывает каждый тестовый пример, и каков ожидаемый результат?