Ошибка php date_diff с 2021-12-01, а не с часовым поясом UTC

Сделайте 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 18.03.2022 18:50

@IMSoP я обновил свой код с дополнительными комментариями о выводе и ожиданиях вывода

man 18.03.2022 19:17
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
0
2
37
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поэтому я изменил ваш код на:

$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, и это очень хороший ответ, который решает мою проблему!

man 21.03.2022 16:52

Другие вопросы по теме