Shouldn't catalog price rule calculation always happen 3 days in advance?

No.

The cron job calls the observer method Mage_CatalogRule_Model_Observer::dailyCatalogUpdate(). This one calls Mage_CatalogRule_Model_Resource_Rule::applyAllRulesForDateRange() without arguments.

If applyAllRulesForDateRange() is called without arguments, one day +/- the current date is assumed.

So you can create newer or older dates but the nightly cron job does not.


I spend sometime to understand this :)

And The script calculate prices for the current day, the previous day and the next day.

In Which, Updates include interval 3 days [in Magento 2.2.X]

  • current day - 1 days before + 1 days after

Daily update catalog price rule by cron

/vendor/magento/module-catalog-rule/Cron/DailyCatalogUpdate.php

  • Update include interval 3 days - current day - 1 days before + 1 days after
  • This method is called from cron process, cron is working in UTC time and
  • we should generate data for interval -1 day ... +1 day

Reindex product prices according rule settings.

/vendor/magento/module-catalog-rule/Model/Indexer/ReindexRuleProductPrice.php

public function execute(
    $batchCount,
    \Magento\Catalog\Model\Product $product = null,
    $useAdditionalTable = false
) {
    $fromDate = mktime(0, 0, 0, date('m'), date('d') - 1);
    $toDate = mktime(0, 0, 0, date('m'), date('d') + 1);

    /**
     * Update products rules prices per each website separately
     * because of max join limit in mysql
     */

    -
    -
    -

$ruleData['from_time'] = $this->roundTime($ruleData['from_time']);
$ruleData['to_time'] = $this->roundTime($ruleData['to_time']);
/**
 * Build prices for each day
 */
for ($time = $fromDate; $time <= $toDate; $time += IndexBuilder::SECONDS_IN_DAY) {
    if (($ruleData['from_time'] == 0 ||
            $time >= $ruleData['from_time']) && ($ruleData['to_time'] == 0 ||
            $time <= $ruleData['to_time'])
    ) {
        $priceKey = $time . '_' . $productKey;

        if (isset($stopFlags[$priceKey])) {
            continue;
        }

        if (!isset($dayPrices[$priceKey])) {
            $dayPrices[$priceKey] = [
                'rule_date' => $time,
                'website_id' => $ruleData['website_id'],
                'customer_group_id' => $ruleData['customer_group_id'],
                'product_id' => $ruleProductId,
                'rule_price' => $this->productPriceCalculator->calculate($ruleData),
                'latest_start_date' => $ruleData['from_time'],
                'earliest_end_date' => $ruleData['to_time'],
            ];
        } else {
            $dayPrices[$priceKey]['rule_price'] = $this->productPriceCalculator->calculate(
                $ruleData,
                $dayPrices[$priceKey]
            );
            $dayPrices[$priceKey]['latest_start_date'] = max(
                $dayPrices[$priceKey]['latest_start_date'],
                $ruleData['from_time']
            );
            $dayPrices[$priceKey]['earliest_end_date'] = min(
                $dayPrices[$priceKey]['earliest_end_date'],
                $ruleData['to_time']
            );
        }

        if ($ruleData['action_stop']) {
            $stopFlags[$priceKey] = true;
        }
    }
}

So the calculation duration

$fromDate = mktime(0, 0, 0, date('m'), date('d') - 1);
$toDate = mktime(0, 0, 0, date('m'), date('d') + 1);

In Magento 1.X this bug occurs if you are living in a timezone which is more than +01:00 from GMT. The default cronjob is running at 01:00 and is using GMT time to set the rule_date. In said case the date will be "yesterday" so there would be no price rule for the current day.

Example:

Current Datetime: 2017-07-19 01:00:00 (at current timezone)
Current Datetime at GMT: 2017-07-18 23:00:00
At 01:00 cronjob runs to write price rules with "$coreDate->gmtTimestamp('Today');"
Function will return "2017-07-18" which in this example is "yesterday"

Solution in 2.2.X

/vendor/magento/module-catalog-rule/Model/Indexer/IndexBuilder.php

$fromTime = strtotime($rule->getFromDate());
$toTime = strtotime($rule->getToDate());
$toTime = $toTime ? $toTime + self::SECONDS_IN_DAY - 1 : 0;

References

  1. http://www.divisionlab.com/solvingmagento/magento-catalog-price-rules/
  2. https://github.com/magento/magento2/issues/6610
  3. https://ipfs-sec.stackexchange.cloudflare-ipfs.com/magento/A/question/3161.html
  4. https://support.google.com/merchants/answer/6069284?hl=en