Массив периодов времени с начальной и конечной датами по диапазону

Я хочу создать таблицу со значениями на основе массива временных интервалов, показывающих даты начала и окончания каждого периода. Периоды могут иметь фиксированные интервалы, например (P1D, P1M и т. д.).

У меня уже есть функция, которая генерирует закрытые (прошлые) периоды, но я не могу найти способ сделать это с будущими датами.

Это функция:

function interval($start, $interval, $break = 0)
{
    $result = array();

    $today = date("Y-m-d");
    if ($break == 0) {
        if ($today < $start) $break = 1;
    }

    $date = new DateTime($start);
    $interval = new DateInterval($interval);

    $f1 = date('Y-m-d', strtotime($start));

    $x = TRUE;
    if ($break == 0) {
        do {
            $date->add($interval);
            $f2 = $date->format('Y-m-d');
            $result[] = array(
                'begin' => $f1,
                'end'    => $f2
            );
            $x = ($f1 <= $today && $f2 >= $today) ? TRUE : FALSE;
            $f1 = date('Y-m-d', strtotime('+1 day', strtotime($f2)));

        } while (!$x);

    } else {

        for ($n = 1; $n <= $break; $n++) {
            $date->add($interval);
            $f2 = $date->format('Y-m-d');
            $result[] = array(
                'begin' => $f1,
                'end'    => $f2
            );
            $f1 = date('Y-m-d', strtotime('+1 day', strtotime($f2)));
        }
    }
    return $result;
}

Для использования просто вызовите функцию:

print_r(interval('2019-01-01', 'P1M'));

И результат:

(
    [0] => Array
        (
            [begin] => 2019-01-01
            [end] => 2019-02-01
        )

    [1] => Array
        (
            [begin] => 2019-02-02
            [end] => 2019-03-01
        )

)

Я хочу сделать то же самое, но с будущими периодами, например, с 01-01-2019 по 01-01-2023 с интервалом в 1 месяц (P1M).

Обновлять

Ответ @dWinder наиболее близок к решению, которое я ищу без необходимости путешествовать во времени =), и он очень хорошо работает с месячными и годовыми интервалами, но с более короткими периодами (P15D, P1W, P1D), как показывает скрипт. проблемы, которые я не могу определить.

Я обновил функцию, которую вы опубликовали, чтобы она правильно проходила через массив и оставалась такой:

function interval($start, $interval, $maxPeriods = 100, $end = NULL) {
    $result = array();
    //$today = date("Y-m-d"); ** Variable not used, disable it **
    $date = new DateTime($start);
    $interval = new DateInterval($interval);

    $f1 = date('Y-m-d', strtotime($start));
    do {
        $date->add($interval);
        $f2 = $date->format('Y-m-d');
        $result[] = array( 'begin' => $f1, 'end' => $f2);
        if ($end <> NULL && $end == $f2) break; // if over the end date stop the loop
        $f1 = date('Y-m-d', strtotime('+1 day', strtotime($f2)));

    } while (++$maxPeriods); // I changed -- to ++ to avoid stopping the loop
    return $result;
}

Обновленная функция используется следующим образом:

print_r(interval('2019-01-01', 'P1M', NULL, '2023-01-01'));

И результаты:

(
    [0] => Array
        (
            [begin] => 2019-01-01
            [end] => 2019-02-01
        )

    [1] => Array
        (
            [begin] => 2019-02-02
            [end] => 2019-03-01
        )

    [2] => Array
        (
            [begin] => 2019-03-02
            [end] => 2019-04-01
        )

    [3] => Array
        (
            [begin] => 2019-04-02
            [end] => 2019-05-01
        )

    [4] => Array
        (
            [begin] => 2019-05-02
            [end] => 2019-06-01
        )

    [5] => Array
        (
            [begin] => 2019-06-02
            [end] => 2019-07-01
        )

    [6] => Array
        (
            [begin] => 2019-07-02
            [end] => 2019-08-01
        )

    [7] => Array
        (
            [begin] => 2019-08-02
            [end] => 2019-09-01
        )

    [8] => Array
        (
            [begin] => 2019-09-02
            [end] => 2019-10-01
        )

    [9] => Array
        (
            [begin] => 2019-10-02
            [end] => 2019-11-01
        )

    [10] => Array
        (
            [begin] => 2019-11-02
            [end] => 2019-12-01
        )

    [11] => Array
        (
            [begin] => 2019-12-02
            [end] => 2020-01-01
        )
)...

Вам помог мой пост?

dWinder 20.02.2019 16:36

@dWinder, на который вы отвечаете, ближе всего к решению, которое я ищу, спасибо!

Joseph Collins 20.02.2019 18:19

Почему вы изменили "<" в строкеif ($end <> NULL && $end < $f2) break; на " = "? А если сделать while (++$maxPeriods); то максимальное значение не получится - чего вы хотите добиться? и что еще более важно - какая проблема у вас с более короткими периодами?

dWinder 21.02.2019 07:17

@dWinder Я меняю «<» на «==», чтобы массив не создавал дополнительный период, кроме даты окончания. В рассматриваемом примере последнее значение даты окончания с «<»: 2023-02-01, но с «==» дата окончания: 2023-01-01, что является ожидаемым результатом. При этом меняются только интервалы и работает нормально. Когда я пытаюсь запустить с более короткими интервалами (P1D - плохой результат), (P1W, P15D скрипт не отвечает).

Joseph Collins 21.02.2019 15:00

Я предполагаю, что P1W и P15D будут бесконечным циклом при использовании ==, потому что он может перескочить через точную дату окончания - я рекомендую использовать < и после того, как функция удалит последний элемент

dWinder 21.02.2019 16:01

@dWinder Я попытался добавить это условие `if (in_array($interval, array('P1D', 'P15D', 'P1W'))){ if ($end <> NULL && $end < $f2) break; } else { if ($end <> NULL && $end == $f2) break; } ` и удалить последний ключ в этом же случае, но в результате получится бесконечный цикл.

Joseph Collins 21.02.2019 18:16

Я запустил это: print_r(interval('2019-01-01', 'P1D', 10000000, '2023-01-01')); в своем исходном коде, и он работал нормально. Пожалуйста, отредактируйте вопрос с вашим кодом (или откройте новый вопрос), чтобы мы могли попытаться найти вашу проблему. пожалуйста, избегайте if маленького интервала и просто удалите последний элемент в конце функции

dWinder 21.02.2019 19:01
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
7
236
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Не помешала бы машина времени :) Если бы программа правильно считала прошлые периоды, то, если бы она "думала" сегодня '2023-01-01' (плюс-минус), она правильно вычисляла бы прошлые периоды. Можете ли вы добавить аргумент $end по умолчанию сегодня?

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

Лучшее решение будет отправлять аргумент «конец» и проверять его (в дополнение к «maxPeriods»

Рассмотрим следующее (обратите внимание на комментарий в коде):

function interval($start, $interval, $maxPeriods = 100, $end = null) {
    $result = array();
    $today = date("Y-m-d");
    $date = new DateTime($start);
    $interval = new DateInterval($interval);

    $f1 = date('Y-m-d', strtotime($start));
    do {
            $date->add($interval);
            $f2 = $date->format('Y-m-d');
            $result[] = array( 'begin' => $f1, 'end' => $f2);
            if ($end && $end < $f2) break; // if over the end date stop the loop
            $f1 = date('Y-m-d', strtotime('+1 day', strtotime($f2)));

    } while (--$maxPeriods); //till you get max elements
    return $result;
}

Теперь назовите это с

print_r(interval('2019-01-01', 'P1M', 48));
//or
print_r(interval('2019-01-01', 'P1M', null, "2023-01-01"));

Обратите внимание, что первая роль сталкивается с прерыванием цикла (дата окончания или максимальное количество элементов).

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