| <?php |
|
|
| |
| |
| |
| |
| |
| |
|
|
| namespace Piwik\Period; |
|
|
| use Exception; |
| use Piwik\Cache; |
| use Piwik\Common; |
| use Piwik\Container\StaticContainer; |
| use Piwik\Date; |
| use Piwik\Period; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| class Range extends Period |
| { |
| public const PERIOD_ID = 5; |
|
|
| protected $label = 'range'; |
| protected $today; |
|
|
| |
| |
| |
| protected $defaultEndDate; |
|
|
| protected $strPeriod; |
| protected $strDate; |
| protected $timezone; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public function __construct($strPeriod, $strDate, $timezone = 'UTC', $today = false) |
| { |
| $this->strPeriod = $strPeriod; |
| $this->strDate = $strDate; |
| $this->timezone = $timezone; |
| $this->defaultEndDate = null; |
|
|
| if ($today === false) { |
| $today = Date::factory('now', $this->timezone); |
| } |
|
|
| $this->today = $today; |
|
|
| $this->translator = StaticContainer::get('Piwik\Translation\Translator'); |
| } |
|
|
| public function __sleep() |
| { |
| return [ |
| 'strPeriod', |
| 'strDate', |
| 'timezone', |
| 'defaultEndDate', |
| 'today', |
| ]; |
| } |
|
|
| public function __wakeup() |
| { |
| $this->translator = StaticContainer::get('Piwik\Translation\Translator'); |
| } |
|
|
| private function getCache() |
| { |
| return Cache::getTransientCache(); |
| } |
|
|
| private function getCacheId() |
| { |
| $end = ''; |
| if ($this->defaultEndDate) { |
| $end = $this->defaultEndDate->getTimestamp(); |
| } |
|
|
| $today = $this->today->getTimestamp(); |
|
|
| return 'range' . $this->strPeriod . $this->strDate . $this->timezone . $end . $today; |
| } |
|
|
| private function loadAllFromCache() |
| { |
| $range = $this->getCache()->fetch($this->getCacheId()); |
|
|
| if (!empty($range)) { |
| foreach ($range as $key => $val) { |
| $this->$key = $val; |
| } |
| } |
| } |
|
|
| private function cacheAll() |
| { |
| $props = get_object_vars($this); |
|
|
| $this->getCache()->save($this->getCacheId(), $props); |
| } |
|
|
| |
| |
| |
| |
| |
| public function getLocalizedShortString() |
| { |
| return $this->getTranslatedRange($this->getRangeFormat(true)); |
| } |
|
|
| |
| |
| |
| |
| |
| public function getLocalizedLongString() |
| { |
| return $this->getTranslatedRange($this->getRangeFormat()); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public function getDateStart() |
| { |
| |
| $dateStart = parent::getDateStart(); |
|
|
| if (empty($dateStart)) { |
| throw new Exception("Specified date range is invalid."); |
| } |
|
|
| return $dateStart; |
| } |
|
|
| |
| |
| |
| |
| |
| public function getPrettyString() |
| { |
| $out = $this->translator->translate('General_DateRangeFromTo', array($this->getDateStart()->toString(), $this->getDateEnd()->toString())); |
| return $out; |
| } |
|
|
| protected function getMaxN(int $lastN): int |
| { |
| switch ($this->strPeriod) { |
| case 'day': |
| $lastN = min($lastN, 5 * 365); |
| break; |
|
|
| case 'week': |
| $lastN = min($lastN, 10 * 52); |
| break; |
|
|
| case 'month': |
| $lastN = min($lastN, 10 * 12); |
| break; |
|
|
| case 'year': |
| $lastN = min($lastN, 10); |
| break; |
| } |
| return $lastN; |
| } |
|
|
| |
| |
| |
| public function setDefaultEndDate(Date $oDate) |
| { |
| $this->defaultEndDate = $oDate; |
| } |
|
|
| |
| |
| |
| |
| |
| protected function generate() |
| { |
| if ($this->subperiodsProcessed) { |
| return; |
| } |
|
|
| $this->loadAllFromCache(); |
|
|
| if ($this->subperiodsProcessed) { |
| return; |
| } |
|
|
| parent::generate(); |
|
|
| if (preg_match('/^(last|previous)([0-9]*)$/', $this->strDate, $regs)) { |
| $lastN = $regs[2]; |
| $lastOrPrevious = $regs[1]; |
| if (!is_null($this->defaultEndDate)) { |
| $defaultEndDate = $this->defaultEndDate; |
| } else { |
| $defaultEndDate = $this->today; |
| } |
|
|
| $period = $this->strPeriod; |
| if ($period == 'range') { |
| $period = 'day'; |
| } |
|
|
| if ($lastOrPrevious == 'last') { |
| $endDate = $defaultEndDate; |
| } elseif ($lastOrPrevious == 'previous') { |
| if ('month' == $period) { |
| $endDate = $defaultEndDate->subMonth(1); |
| } else { |
| $endDate = $defaultEndDate->subPeriod(1, $period); |
| } |
| } |
|
|
| $lastN = $this->getMaxN((int) $lastN); |
|
|
| |
| $lastN--; |
| if ($lastN < 0) { |
| $lastN = 0; |
| } |
|
|
| $startDate = $endDate->addPeriod(-1 * $lastN, $period); |
| } elseif ($dateRange = Range::parseDateRange($this->strDate)) { |
| $strDateStart = $dateRange[1]; |
| $strDateEnd = $dateRange[2]; |
| $startDate = Date::factory($strDateStart); |
|
|
| |
| $timezone = null; |
| if (strpos($strDateEnd, '-') === false) { |
| $timezone = $this->timezone; |
| } |
|
|
| $endDate = Date::factory($strDateEnd, $timezone)->setTime("00:00:00"); |
| $maxAllowedEndDate = Date::factory(self::getMaxAllowedEndTimestamp()); |
|
|
| if ($endDate->isLater($maxAllowedEndDate)) { |
| $endDate = $maxAllowedEndDate; |
| } |
| } else { |
| throw new Exception($this->translator->translate('General_ExceptionInvalidDateRange', array($this->strDate, ' \'lastN\', \'previousN\', \'YYYY-MM-DD,YYYY-MM-DD\''))); |
| } |
|
|
| if ($this->strPeriod != 'range') { |
| $this->fillArraySubPeriods($startDate, $endDate, $this->strPeriod); |
| $this->cacheAll(); |
| return; |
| } |
|
|
| $this->processOptimalSubperiods($startDate, $endDate); |
| |
| |
| $this->endDate = $endDate; |
| $this->cacheAll(); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public static function parseDateRange($dateString) |
| { |
| $matched = preg_match('/^((?:[0-9]{4}-[0-9]{1,2}-[0-9]{1,2})|last[ -]?(?:week|month|year)),((?:[0-9]{4}-[0-9]{1,2}-[0-9]{1,2})|today|now|yesterday|last[ -]?(?:week|month|year))$/Di', trim($dateString), $regs); |
|
|
| if (empty($matched)) { |
| return false; |
| } |
|
|
| return $regs; |
| } |
|
|
| protected $endDate = null; |
|
|
| |
| |
| |
| |
| |
| public function getDateEnd() |
| { |
| if (!is_null($this->endDate)) { |
| return $this->endDate; |
| } |
|
|
| return parent::getDateEnd(); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| protected function processOptimalSubperiods($startDate, $endDate) |
| { |
| while ( |
| $startDate->isEarlier($endDate) |
| || $startDate == $endDate |
| ) { |
| $endOfPeriod = null; |
|
|
| $month = new Month($startDate); |
| $endOfMonth = $month->getDateEnd(); |
| $startOfMonth = $month->getDateStart(); |
|
|
| $year = new Year($startDate); |
| $endOfYear = $year->getDateEnd(); |
| $startOfYear = $year->getDateStart(); |
|
|
| if ( |
| $startDate == $startOfYear |
| && ($endOfYear->isEarlier($endDate) |
| || $endOfYear == $endDate |
| || $endOfYear->isLater($this->today) |
| ) |
| |
| |
| && !($endDate->isEarlier($this->today) |
| && $this->today->toString('Y') == $endOfYear->toString('Y') |
| && $this->today->compareYear($endOfYear) == 0) |
| ) { |
| $this->addSubperiod($year); |
| $endOfPeriod = $endOfYear; |
| } elseif ( |
| $startDate == $startOfMonth |
| && ($endOfMonth->isEarlier($endDate) |
| || $endOfMonth == $endDate |
| || $endOfMonth->isLater($this->today) |
| ) |
| |
| |
| && !($endDate->isEarlier($this->today) |
| && $this->today->toString('Y') == $endOfMonth->toString('Y') |
| && $this->today->compareMonth($endOfMonth) == 0) |
| ) { |
| $this->addSubperiod($month); |
| $endOfPeriod = $endOfMonth; |
| } else { |
| |
| |
| $week = new Week($startDate); |
| $startOfWeek = $week->getDateStart(); |
| $endOfWeek = $week->getDateEnd(); |
|
|
| $firstDayNextMonth = $startDate->addPeriod(2, 'month')->setDay(1); |
| $useMonthsNextIteration = $firstDayNextMonth->isEarlier($endDate); |
|
|
| if ( |
| $useMonthsNextIteration |
| && $endOfWeek->isLater($endOfMonth) |
| ) { |
| $this->fillArraySubPeriods($startDate, $endOfMonth, 'day'); |
| $endOfPeriod = $endOfMonth; |
| } elseif ( |
| $this->isEndOfWeekLaterThanEndDate($endDate, $endOfWeek) && |
| ($endOfWeek->isEarlier($this->today) || |
| $startOfWeek->toString() != $startDate->toString() || |
| $endDate->isEarlier($this->today)) |
| ) { |
| |
| $this->fillArraySubPeriods($startDate, $endDate, 'day'); |
| break 1; |
| } elseif ( |
| $startOfWeek->isEarlier($startDate) |
| && $endOfWeek->isEarlier($this->today) |
| ) { |
| $this->fillArraySubPeriods($startDate, $endOfWeek, 'day'); |
| $endOfPeriod = $endOfWeek; |
| } else { |
| $this->addSubperiod($week); |
| $endOfPeriod = $endOfWeek; |
| } |
| } |
| $startDate = $endOfPeriod->addDay(1); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| protected function fillArraySubPeriods($startDate, $endDate, $period) |
| { |
| $arrayPeriods = array(); |
| $endSubperiod = Period\Factory::build($period, $endDate); |
| $arrayPeriods[] = $endSubperiod; |
|
|
| |
| $endDate = $endSubperiod->getDateStart(); |
| while ($endDate->isLater($startDate)) { |
| $endDate = $endDate->addPeriod(-1, $period); |
| $subPeriod = Period\Factory::build($period, $endDate); |
| $arrayPeriods[] = $subPeriod; |
| } |
| $arrayPeriods = array_reverse($arrayPeriods); |
| foreach ($arrayPeriods as $period) { |
| $this->addSubperiod($period); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static function getLastDate($date = false, $period = false) |
| { |
| return self::getDateXPeriodsAgo(1, $date, $period); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static function getDateXPeriodsAgo($subXPeriods, $date = false, $period = false) |
| { |
| if ($date === false) { |
| $date = Common::getRequestVar('date'); |
| } |
|
|
| if ($period === false) { |
| $period = Common::getRequestVar('period'); |
| } |
|
|
| if (365 == $subXPeriods && 'day' == $period && Date::factory($date)->isLeapYear()) { |
| $subXPeriods = 366; |
| } |
|
|
| if ($period === 'range') { |
| $rangePeriod = new Range($period, $date); |
| $daysDifference = self::getNumDaysDifference($rangePeriod->getDateStart(), $rangePeriod->getDateEnd()); |
| $end = $rangePeriod->getDateStart()->subDay(1); |
| $from = $end->subDay($daysDifference); |
|
|
| return array("$from,$end", false); |
| } |
|
|
| |
| $strLastDate = false; |
| $lastPeriod = false; |
| if (!preg_match('/(last|previous)([0-9]*)/', $date, $regs)) { |
| if (strpos($date, ',')) { |
| |
|
|
| $rangePeriod = new Range($period, $date); |
|
|
| $lastStartDate = $rangePeriod->getDateStart()->subPeriod($subXPeriods, $period); |
| $lastEndDate = $rangePeriod->getDateEnd()->subPeriod($subXPeriods, $period); |
|
|
| $strLastDate = "$lastStartDate,$lastEndDate"; |
| } else { |
| $lastPeriod = Date::factory($date)->subPeriod($subXPeriods, $period); |
| $strLastDate = $lastPeriod->toString(); |
| } |
| } |
|
|
| return array($strLastDate, $lastPeriod); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public function getDayCount() |
| { |
| return (self::getNumDaysDifference($this->getDateStart(), $this->getDateEnd()) + 1); |
| } |
|
|
| private static function getNumDaysDifference(Date $date1, Date $date2) |
| { |
| $days = (abs($date1->getTimestamp() - $date2->getTimestamp())) / 60 / 60 / 24; |
| return (int) round($days); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static function getRelativeToEndDate($period, $lastN, $endDate, $site) |
| { |
| $timezone = $site->getTimezone(); |
| $last30Relative = new Range($period, $lastN, $timezone); |
|
|
| if (strpos($endDate, '-') === false) { |
| |
| $endDate = Date::factoryInTimezone($endDate, $timezone); |
| } else { |
| $endDate = Date::factory($endDate); |
| } |
| $last30Relative->setDefaultEndDate($endDate); |
|
|
| $date = $last30Relative->getDateStart()->toString() . "," . $last30Relative->getDateEnd()->toString(); |
| return $date; |
| } |
|
|
| private function isEndOfWeekLaterThanEndDate($endDate, $endOfWeek) |
| { |
| $isEndOfWeekLaterThanEndDate = $endOfWeek->isLater($endDate); |
|
|
| $isEndDateAlsoEndOfWeek = ($endOfWeek->toString() == $endDate->toString()); |
| $isEndOfWeekLaterThanEndDate = ($isEndOfWeekLaterThanEndDate |
| || ($isEndDateAlsoEndOfWeek |
| && $endDate->isLater($this->today))); |
|
|
| return $isEndOfWeekLaterThanEndDate; |
| } |
|
|
| |
| |
| |
| |
| |
| public function getRangeString() |
| { |
| $dateStart = $this->getDateStart(); |
| $dateEnd = $this->getDateEnd(); |
|
|
| return $dateStart->toString("Y-m-d") . "," . $dateEnd->toString("Y-m-d"); |
| } |
|
|
| public function getImmediateChildPeriodLabel() |
| { |
| $subperiods = $this->getSubperiods(); |
| return reset($subperiods)->getImmediateChildPeriodLabel(); |
| } |
|
|
| public function getParentPeriodLabel() |
| { |
| return null; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public static function getMaxAllowedEndTimestamp(): int |
| { |
| return strtotime( |
| date('Y-12-31 00:00:00', strtotime('+10 year', Date::getNowTimestamp())) |
| ); |
| } |
| } |
|
|