Получить повторяющиеся даты в пределах интервала дат - PullRequest
0 голосов
/ 06 мая 2019

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

Я работаю над приложением, которое позволяет вам получитьобзор ваших регулярных платежей (я знаю, этот материал уже существует, это всего лишь побочный проект) и ваших расходов.

API почти закончен, но я борюсь с этим разделом: мне нужно знать, еслипериодический платеж должен быть выполнен в течение заданного промежутка времени.

Периодические события (контракты) содержат среди прочих полей startDate, endDate, intervalType (enum [еженедельно, ежемесячно, ежеквартально, раз в два года, ежегодно])

В настоящее время у меня есть такой запрос для доктрины:

return $this->createQueryBuilder('e')
    ->andWhere('e.startDate >= :start')
    ->andWhere('e.endDate <= :end')
    ->andWhere('e.user = :user')
    ->setParameter('start', $start)
    ->setParameter('end', $end)
    ->setParameter('user', $user)
    ->getQuery()
    ->getResult();

, где параметры начало и конец - это интервал времени.

Но как я могу проверить, когда и как часто контракты должны выполняться в пределах выбранного фрейма?

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

Но я понятия не имею, как мне это сделать.

Пример данных:

[name, intervalType, startDate, endDate, amount]
rent, monthly, 2019-01-01, 2020-10-11, -500
utility, monthly, 2019-01-01, 2020-10-11, -150
salary, monthly, 2019-01-01, 2020-10-11, 1700
investment, biannually, 2019-02-10, null , 2500
food, weekly, 2019-01-01, null , -50

Если у меня есть временные рамки (2019-05-01 - 2019-05-31) этого месяца я получу эти контракты:

rent 2019-05-01
utility  2019-05-01
salary 2019-05-01
food 2019-05-01
food 2019-05-08
food 2019-05-15
food 2019-05-22
food 2019-05-29

Если бы я выбрал следующие 2 месяца (2019-07-01 - 2019-08-31), я получил бы это:

rent 2019-07-01
rent 2019-08-01
utility  2019-07-01
utility  2019-08-01
salary 2019-07-01
salary 2019-08-01
food 2019-07-01
food 2019-07-08
food 2019-07-15
food 2019-07-22
food 2019-07-29
food 2019-08-01
food 2019-08-08
food 2019-08-15
food 2019-08-22
food 2019-08-29
investment 2019-08-01

Возможно ли это с DateTime и DateInterval?

1 Ответ

0 голосов
/ 06 мая 2019

Логика с недельным интервалом мне кажется неправильной.По крайней мере, с моим банковским приложением еженедельное расписание всегда будет приходиться на один и тот же день недели с 7 днями между двумя последовательными событиями.

Так что с этой логикой вы можете использовать эту функцию:

function getScheduleBetween($data, $intervalStart, $intervalEnd) {
    $ref = [
        "yearly" => [12, "months"],
        "annually" => [12, "months"],
        "biannually" => [6, "months"],
        "quarterly" => [3, "months"],
        "monthly" => [1, "months"],
        "weekly" => [7, "days"],
        "daily" => [1, "days"]
    ];
    $intervalStart = new DateTime($intervalStart);
    $intervalEnd = new DateTime($intervalEnd);
    $result = [];
    foreach($data as $schedule) {
        // Convert start/end to DateTime
        $startDate = new DateTime($schedule["startDate"]);
        $endDate = $schedule["endDate"] ? min(new DateTime($schedule["endDate"]), $intervalEnd) : $intervalEnd;
        $name = $schedule["name"];
        $interval = $schedule["intervalType"];
        if (!isset($ref[$interval])) throw "Invalid interval type";
        list($coeff, $interval) = $ref[$interval];
        $match = clone $startDate;
        if ($match < $intervalStart) {
            $diff = $intervalStart->diff($match);
            $count = $interval == "days" ? $diff->format("%a") : $diff->m + $diff->y * 12 + ($diff->d ? 1 : 0);
            $count = ceil($count / $coeff) * $coeff;
            $match->modify("+$count $interval");
        }
        while ($match <= $endDate) {
            $temp = clone $match;
            $result[] = ["name" => $name, "date" => $temp->format("Y-m-d")];
            $match->modify("+$coeff $interval");
        }
    }
    array_multisort(array_column($result, "date"), $result);
    return $result;
}

Пример использования:

$data = [
    ["name" => "rent", "intervalType" => "monthly", "startDate" => "2019-01-01", "endDate" => "2020-10-11", "amount" => -500],
    ["name" => "utility", "intervalType" => "monthly", "startDate" => "2019-01-01", "endDate" => "2020-10-11", "amount" => -150],
    ["name" => "salary", "intervalType" => "monthly", "startDate" => "2019-01-01", "endDate" => "2020-10-11", "amount" => 1700],
    ["name" => "investment", "intervalType" => "biannually", "startDate" => "2019-02-10", "endDate" => null, "amount" => 2500],
    ["name" => "food", "intervalType" => "weekly", "startDate" => "2019-01-01", "endDate" => null, "amount" => -50],
];

$result = getScheduleBetween($data, "2019-05-01", "2019-05-31");
print_r($result);

С большим периодом:

$result = getScheduleBetween($data, "2019-05-01", "2019-08-31");
print_r($result);

Если у вас есть другие типы интервалов, я полагаю, что вы можете легко расширить эту функцию для поддержки их, даже с другой логикой"еженедельно".

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...