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

            // last1 means only one result ; last2 means 2 results so we remove only 1 to the days/weeks/etc
            $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);

            // we set the timezone in the Date object only if the date is relative eg. 'today', 'yesterday', 'now'
            $timezone = null;
            if (strpos($strDateEnd, '-') === false) {
                $timezone = $this->timezone;
            }
            $endDate = Date::factory($strDateEnd, $timezone);
        } 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);
        // When period=range, we want End Date to be the actual specified end date,
        // rather than the end of the month / week / whatever is used for processing this range
        $this->endDate = $endDate;
        $this->cacheAll();
    }