Я хочу создать таблицу со значениями на основе массива временных интервалов, показывающих даты начала и окончания каждого периода. Периоды могут иметь фиксированные интервалы, например (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, на который вы отвечаете, ближе всего к решению, которое я ищу, спасибо!
Почему вы изменили "<" в строкеif ($end <> NULL && $end < $f2) break; на " = "? А если сделать while (++$maxPeriods); то максимальное значение не получится - чего вы хотите добиться? и что еще более важно - какая проблема у вас с более короткими периодами?
@dWinder Я меняю «<» на «==», чтобы массив не создавал дополнительный период, кроме даты окончания. В рассматриваемом примере последнее значение даты окончания с «<»: 2023-02-01, но с «==» дата окончания: 2023-01-01, что является ожидаемым результатом. При этом меняются только интервалы и работает нормально. Когда я пытаюсь запустить с более короткими интервалами (P1D - плохой результат), (P1W, P15D скрипт не отвечает).
Я предполагаю, что P1W и P15D будут бесконечным циклом при использовании ==, потому что он может перескочить через точную дату окончания - я рекомендую использовать < и после того, как функция удалит последний элемент
@dWinder Я попытался добавить это условие `if (in_array($interval, array('P1D', 'P15D', 'P1W'))){ if ($end <> NULL && $end < $f2) break; } else { if ($end <> NULL && $end == $f2) break; } ` и удалить последний ключ в этом же случае, но в результате получится бесконечный цикл.
Я запустил это: print_r(interval('2019-01-01', 'P1D', 10000000, '2023-01-01')); в своем исходном коде, и он работал нормально. Пожалуйста, отредактируйте вопрос с вашим кодом (или откройте новый вопрос), чтобы мы могли попытаться найти вашу проблему. пожалуйста, избегайте if маленького интервала и просто удалите последний элемент в конце функции






Не помешала бы машина времени :) Если бы программа правильно считала прошлые периоды, то, если бы она "думала" сегодня '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"));
Обратите внимание, что первая роль сталкивается с прерыванием цикла (дата окончания или максимальное количество элементов).
Вам помог мой пост?