【问题标题】:Get recurring dates within date interval获取日期间隔内的重复日期
【发布时间】:2019-05-06 13:34:17
【问题描述】:

我想我被这个问题困住了,所以我希望你能给我一些想法,我可以如何解决这个任务。

我正在开发一个应用程序,让您可以概览您的定期付款(我知道,这些东西已经存在,这只是一个副项目)和您的费用。

API 快完成了,但我在这部分遇到了困难:我需要知道定期付款是否在给定的时间范围内到期。

周期性事件(合同)包括其他字段 startDate、endDate、intervalType(枚举 [每周、每月、每季度、每半年、每年])

目前我有这个作为我的学说查询:

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();

其中参数start和end是间隔的帧。

但我如何检查合同是否在所选框架内到期以及多久到期?

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 吗?

【问题讨论】:

  • 那么当跨月边界时,每周并不是真正的每周? 07-29 和 08-01 仅相隔 3 天...在闰年甚至可能仅相隔 1 天...您确定这个逻辑吗?

标签: php date time


【解决方案1】:

每周间隔的逻辑在我看来是错误的。至少在我的银行应用程序中,每周计划总是在同一个工作日,两次连续发生之间有 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);

如果您有其他类型的间隔,我相信您可以轻松扩展此功能以支持它们,即使使用“每周”的其他逻辑。

【讨论】:

  • 是的,每周数据错误(c/p 失败),我的错。感谢您的出色回答:)
猜你喜欢
  • 2016-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-09
  • 2017-04-03
相关资源
最近更新 更多