【问题标题】:Calculate the first date of a pay cycle based on odd/even weeks根据奇数/偶数周计算支付周期的第一个日期
【发布时间】:2013-10-27 17:44:54
【问题描述】:

我们正在创建一个非常通用的订购系统,该系统根据付款周期的当前一周向用户付款。

我们的支付周期是双周,因此我们认为第 1 周是奇数周,第 2 周是偶数周。

  • 第 1 周(奇数)[开始周期 A]
  • 第 2 周(偶数)[结束期 A]
  • 第 3 周(奇数)[开始周期 B]
  • 第 4 周(偶数)[结束期 B][支付期 A,本周星期五]
  • 第 5 周(奇数)[开始周期 C]
  • 第 6 周(偶数)[结束期 C] [支付期 B,本周星期五]

等等。


我们可以使用以下方法来确定是奇数周还是偶数周:

    self::$now = new DateTime();
    if (self::$now->format('W') % 2 === 0) {
        // Week is even -
    } else {
        // Week is odd
    }

如果是奇数周,我们希望将该周的星期日用作当前支付周期的“开始日期”。另一方面,如果是偶数周,我们希望取该周的星期六并将其用作当前支付周期的“结束日期”。

以前,我们计算这些开始日期和结束日期的方法相当粗糙。我们只是选择了一个任意日期作为两周支付周期的第一个日期,并且会使用一些凌乱的DateTime() 代码来计算差异等等。我们不想这样做,而是依赖于一周是偶数还是奇数。

这是我们用来计算之前开始和结束日期的代码:

public function getPreviousPeriodStart() {

    $daysIntoCurrentPeriod      = ((int)self::$now->diff(self::$refStart)->format('%a') % self::PERIOD_LENGTH);

    self::$prevPeriodStart      = new DateTime('2 weeks ago');
    self::$prevPeriodStart->sub(new DateInterval('P'.$daysIntoCurrentPeriod.'D'));

    return self::$prevPeriodStart;

}

public function getPreviousPeriodEnd() {

    $daysLeftCurrentPeriod      = self::PERIOD_LENGTH - ((int)self::$now->diff(self::$refStart)->format('%a') % self::PERIOD_LENGTH) - 1;

    self::$prevPeriodStart      = new DateTime('2 weeks ago');
    self::$prevPeriodStart->add(new DateInterval('P'.$daysLeftCurrentPeriod.'D'));

    return (self::$prevPeriodStart);

}

再一次,我知道这很糟糕而且草率,这就是为什么我想改进它!

一旦我们确定了当前支付周期的开始和结束日期,我们希望能够确定以下值:

  • 当前支付周期开始
  • 当前支付期结束
  • 上一个支付周期开始
  • 上一个支付期结束
  • 上一期支付日期(这将是上一支付期的支付日期 - 在当前支付期开始之前结束的那一天)[这将发生在下一个偶数支付期的星期五]
  • 当前周期支付日期(这将是当前支付周期的支付日期)[这将发生在下一个 EVEN 支付周期的星期五]

我正在努力寻找最干净、最明智的方法来处理这个问题。如果这种方法或方法不理想,我欢迎任何替代建议 - 我只是想确保这是准确且可维护的!

【问题讨论】:

  • 你的问题到底是什么?
  • 如果您查看我的帖子,您会看到“我们希望能够确定以下值”的部分:当前 pp 开始、结束、前一个 pp 开始、结束等。我想以与我们现在不同的方式来做这件事,使用 ODD/EVEN 方法而不是固定的参考日期。
  • 哦,我现在明白了。对不起。我很困惑。在这种情况下,请查看此链接 derickrethans.nl/iso-8601-weeks.html 并尝试获取当前周数并使用它通过转换类似“2013-WXX-7”的字符串来查找该周的开始/结束,其中 XX 是当前周。但是您应该知道 ISO8601 周从星期一开始而不是星期日,因此您也必须对此进行调整。
  • 还可以看看 Carbon php 库,它有很多非常有用的日期和时间函数

标签: php date datetime


【解决方案1】:

就像我在评论中所说,奇数/偶数方法将在奇数周数的年份失败,例如 2015 年,它有 53 周。所以 2015 年的最后一周将是奇数,然后下周(2016 年的第一周)也将是奇数。可能这不是故意的?

您的第一种方法更好。 选择一个日期作为参考,该参考是一个奇数的开始周期日期。基于此,您现在知道您当前的周期是奇数还是偶数,请参阅下面的方法isOdd()

例子:

class DateTimeExtended extends DateTime {

    # ref start date is odd
    const REF_START = '2013-W43-1';

    protected function isOdd(DateTime $dt)
    {
        $ref = new DateTime(self::REF_START);
        return floor($dt->diff($ref)->days / 7) % 2 == 0;
    }

    public function getCurrentPeriodStart()
    {
        $dt = new DateTime($this->format('o-\WW-1'));
        if (!$this->isOdd($dt)) {
            $dt->modify('-1 week');
        }
        return $dt;
    }

    public function getCurrentPeriodEnd()
    {
        $dt = new DateTime($this->format('o-\WW-7'));
        if ($this->isOdd($dt)) {
            $dt->modify('+1 week');
        }
        return $dt;
    }

    public function getPreviousPeriodStart()
    {
        $dt = $this->getCurrentPeriodStart();
        return $dt->modify('-2 week');
    }

    public function getPreviousPeriodEnd()
    {
        $dt = $this->getCurrentPeriodEnd();
        return $dt->modify('-2 week');
    }

}

使用 (demo):

$dt = new DateTimeExtended;

print_r( $dt->getCurrentPeriodStart() );   # 2013-10-21
print_r( $dt->getCurrentPeriodEnd() );     # 2013-11-03
print_r( $dt->getPreviousPeriodStart() );  # 2013-10-07
print_r( $dt->getPreviousPeriodEnd() );    # 2013-10-20

就像Dwayne Towell 在问题评论中已经说过的那样,ISO8601 周从星期一开始,而不是星期日,因此您需要调整代码以使其像那样工作。只需替换 ISO 格式,例如 this

【讨论】:

  • 您好。我知道这已经很多年了,但是,为了让它在 2017 年工作,需要对这段代码进行哪些更改?或者以后的几年?如何使此代码适用于 REF_START = 2017?
  • @Vini:输入你自己的参考日期?
  • 我注意到输入中有某种格式。
  • @Vini:它是标准的 ISO8601 格式。对于您的 REF 日期,您可以使用 DateTime 使用的任何已知格式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多