. */ namespace FireflyIII\Support\Calendar; use Carbon\Carbon; use FireflyIII\Support\Calendar\Exceptions\IntervalException; /** * Class Calculator */ class Calculator { public const DEFAULT_INTERVAL = 1; private static array $intervals = []; private static ?\SplObjectStorage $intervalMap = null; /** * @return \SplObjectStorage */ private static function loadIntervalMap(): \SplObjectStorage { if (self::$intervalMap != null) { return self::$intervalMap; } self::$intervalMap = new \SplObjectStorage(); foreach (Periodicity::cases() as $interval) { $periodicityClass = __NAMESPACE__ . "\\Periodicity\\{$interval->name}"; self::$intervals[] = $interval->name; self::$intervalMap->attach($interval, new $periodicityClass()); } return self::$intervalMap; } /** * @param Periodicity $periodicity * @return bool */ private static function containsInterval(Periodicity $periodicity): bool { return self::loadIntervalMap()->contains($periodicity); } /** * @param Periodicity $periodicity * @return bool */ public function isAvailablePeriodicity(Periodicity $periodicity): bool { return self::containsInterval($periodicity); } /** * @param int $skip * @return int */ private function skipInterval(int $skip): int { return self::DEFAULT_INTERVAL + $skip; } /** * @param Carbon $epoch * @param Periodicity $periodicity * @param int $skipInterval * @return Carbon * @throws IntervalException */ public function nextDateByInterval(Carbon $epoch, Periodicity $periodicity, int $skipInterval = 0): Carbon { if (!self::isAvailablePeriodicity($periodicity)) { throw IntervalException::unavailable($periodicity, self::$intervals); } /** @var Periodicity\Interval $periodicity */ $periodicity = self::$intervalMap->offsetGet($periodicity); $interval = $this->skipInterval($skipInterval); return $periodicity->nextDate($epoch->clone(), $interval); } }