Как сравнить даты, когда летнее время меняется с Laravel / Carbon

У меня есть коллекция объектов с полем времени с временным шагом 30 минут:

Я просматриваю свой список на предмет дыр в данных, то есть в этом списке он должен вернуть:

dataHoleStart:    2020-03-29 04:30
dataHoleEnd  :    2020-03-29 08:30

Дело в том, что 29 числа этого года у меня возникает проблема, потому что это день, когда мы меняем летнее время.

There is no hole:2020-03-29 01:00
There is no hole:2020-03-29 01:30
There is no hole:2020-03-29 03:00
There is no hole:2020-03-29 03:30
New hole: 2020-03-29 03:00
Existing hole: 2020-03-29 03:30
Existing hole: 2020-03-29 04:00
Existing hole: 2020-03-29 04:30
Existing hole: 2020-03-29 05:00

Но, заглянув в свою базу данных Postgres, я вижу, что данные довольно регулярные, нет никакой дыры.

Вот мой код:

foreach ($measures as $key => $measure) {
            if ($key == 0) continue;
            $nextTS = (clone $ts_cursor)->addMinutes(30);
            if ($measure->time->eq($nextTS)) {
                $ts_cursor = $nextTS;
                echo "There is no hole:". $measure->time->format('yy-m-d H:i'). "<br/>";
                continue;
            }
            // Here we have a hole.
            if ($missingDataIni != null) {
                // We are in an existing hole
                $ts_cursor = $nextTS;
                echo('Existing hole: ' .$measure->time->format('yy-m-d H:i'). "<br/>");
            } else {
                // We create a new hole
                $missingDataIni = $nextTS;
                $ts_cursor = $nextTS;
                echo 'New hole: '.$measure->time->format('yy-m-d H:i'). "<br/>";
            }

        }

Я попытался преобразовать поля углерода в метку времени, но у меня все еще есть та же проблема.

Кроме того, в config/app.php мой часовой пояс определен следующим образом:

'timezone' => 'Europe/Paris',

Как мне обрабатывать сравнение дат, включая летнее время, с Carbon?


person Juliatzin    schedule 25.08.2020    source источник
comment
Дело в том, что работать с 'timezone' => 'Europe/Paris', в вашем config/app.php - плохая идея. См. medium.com/@kylekatarnls/ Европа / Paris должен появиться, когда вы хотите отформатировать или обработать даты в конкретном контексте пользователя. В противном случае обрабатывайте время независимым образом, используя UTC.   -  person KyleK    schedule 25.08.2020
comment
Основная проблема при записи даты и времени в часовом поясе на летнее время в своей базе данных: когда строка 25 октября 2020 г. 02:30 (для часового пояса Парижа), вы не знаете, 2:30 это зимнее время или летнее время. Таким образом, у вас должен быть часовой пояс UTC (GMT) для вашей БД и вашей конфигурации Laravel, чтобы он был выровнен, тогда, если вам нужно отобразить дату по парижскому времени, легко ->tz('Europe/Paris') на экземпляре Carbon.   -  person KyleK    schedule 25.08.2020
comment
Я согласен с вами, что не работаю в UTC. Для записи, все мои даты в БД указаны в формате UTC. Но для управления DST я должен работать с часовым поясом, иначе я не смог бы получить переходы, которые зависят от часового пояса.   -  person Juliatzin    schedule 26.08.2020
comment
ооо, ты был прав, я закончил тем, что сменил часовой пояс на UTC   -  person Juliatzin    schedule 26.08.2020


Ответы (1)


Решаю с помощью getTransitions

Вот код:

// We get the transition at timezone.
$transitions = collect($tz->getTransitions($start->format('U'), $end->format('U')))
    ->map(function ($transition) use ($tz) {
        return (new Carbon($transition['time']))->timezone($tz)->toAtomString();
    })->toArray();

foreach ($measures as $key => $measure) {
    if ($key == 0) continue;
    $nextTS = (clone $ts_cursor)->addMinutes(30);
    if ($measure->time->timezone($tz)->eq($nextTS)) {
        $ts_cursor = $nextTS;
        echo "There is no hole:" . $measure->time->timezone($tz)->toAtomString() . "<br/>";
        continue;
    }
    if (in_array($measure->time->timezone($tz)->toAtomString(), $transitions)) {
        echo "Transition setting ts_cursor:".$ts_cursor->timezone($tz)->toAtomString(). "<br/>";
        $ts_cursor = $ts_cursor->addMinutes(-30);
        continue;
    }

    // Here we have a hole.
    if ($missingDataIni != null) {
        // We are in an existing hole
        echo('Existing hole: ' . $measure->time->timezone($tz)->toAtomString() . "<br/>");
        $ts_cursor = $nextTS;
    } else {
        // We create a new hole
        $ts_cursor = $nextTS;
        $missingDataIni = $ts_cursor;
        echo 'New hole: ' . $ts_cursor->timezone($tz)->toAtomString() . "<br/>";
    }
}
person Juliatzin    schedule 25.08.2020