tworzenieweb/repetitive-date-estimator

View on GitHub
src/RepetitiveDateEstimator.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

namespace Tworzenieweb\CyclicDates;

use DateTime;

/**
 * Use this class for estimating events occuring on every month. For example having an estimation
 * of event occuring for every month in 2 weeks interval is as simple as
 * RepetitiveDateEstimator::build(new DateTime(), RepeatitiveInterval::twoWeeks())
 * This will show 2 dates for each month one scheduled for 15th and second for 29th
 *
 * @package Tworzenieweb\CyclicDates
 */
class RepetitiveDateEstimator
{
    /**
     * @var DateTime
     */
    private $referenceDate;

    /**
     * @var RepetitiveInterval
     */
    private $interval;



    /**
     * @param DateTime           $referenceDate
     * @param RepetitiveInterval $interval
     */
    private function __construct(DateTime $referenceDate, RepetitiveInterval $interval)
    {
        $this->referenceDate = $referenceDate;
        $this->interval = $interval;
    }



    /**
     * @param DateTime           $referenceDate
     * @param RepetitiveInterval $interval
     *
     * @return RepetitiveDateEstimator
     */
    public static function build(DateTime $referenceDate, RepetitiveInterval $interval)
    {
        $repetitiveDateEstimator = new self($referenceDate, $interval);

        return $repetitiveDateEstimator;
    }



    /**
     * @return DateTime
     */
    public function getNextDate()
    {
        $referenceWithoutTime = clone $this->referenceDate;

        $dayOfMonth = (int)$referenceWithoutTime->format('j');
        $repetitiveInterval = $this->interval->interval();
        $moduloInterval = $dayOfMonth % $repetitiveInterval;

        if ($moduloInterval === 0) {
            $daysToAdd = 1;
        } else {
            $daysToAdd = $repetitiveInterval - ($moduloInterval);
            $daysInCurrentMonth = (int)$referenceWithoutTime->format('t');

            if ($dayOfMonth + $daysToAdd >= $daysInCurrentMonth) {
                $daysToAdd += ($daysInCurrentMonth - $dayOfMonth - ($daysToAdd - $repetitiveInterval));
            }
            $daysToAdd++;
        }

        $nextDate = clone $referenceWithoutTime;
        $nextDate->modify(sprintf('+ %d days', $daysToAdd));

        return $nextDate;
    }
}