【问题标题】:Magento 2 Collection Date Filter out by one hourMagento 2 Collection Date 按一小时过滤
【发布时间】:2019-12-14 06:20:17
【问题描述】:

我有一个问题,我按日期过滤集合,而我希望获得的项目没有在集合中返回,但是如果我打印出集合使用的 SQL 并针对我的数据库运行该 SQL,则返回项目.

$from = new \DateTime($lsDate);
$orders = $this->_orderCollectionFactory->create()
        ->addFieldToSelect(['grand_total', 'created_at'])
        ->addAttributeToFilter('created_at', array('gteq' => $from->format('Y-m-d H:i:s')))
        ->addAttributeToFilter('customer_id',$customer->getId())
        ->setPageSize(10)
        ->setOrder('created_at', 'desc');

$from->format('Y-m-d H:i:s') // Lets say this is 2019-08-06 15:33:00
$this->logger->info(count($orders)); // This is 0

如果我打印出它生成的 SQL,它看起来像这样:

SELECT `main_table`.`entity_id`, `main_table`.`grand_total`, `main_table`.`created_at` FROM `sales_order` AS `main_table` WHERE (`created_at` >= '2019-08-06 15:33:21')

应返回的订单created_at日期为2019-08-06 15:34:00

如果我在我的数据库上运行上述查询,它会返回上面的一个订单,但是正如您在我上面的代码中看到的那样,该集合是空的。

如果我将订单日期更改为2019-08-06 16:34:21(未来一小时),则代码将返回包含一项的集合。看起来它与时区 somwehere 有关?也许是 DST(夏令时)?

编辑

这里是有关$lsDate 变量的更多信息。

$lsDate 来自客户属性。我这样存储日期:

 $newDate = new \DateTime();
 $customer->setCustomAttribute('ls_start_date', $newDate->format('Y-m-d H:i:s'));

然后这样获取日期:

$lsDate = $customer->getCustomAttribute('ls_start_date')->getValue();

【问题讨论】:

  • 如何从您的集合中取出该查询?它至少还应该有一个来自您的代码的AND customer_id = 123 order by created_at desc LIMIT 10,它在您的查询中没有。为确保从集合中获得真正的查询,您可能应该在打印 SQL 之前对其调用 ->load() 函数
  • 请注意您在ls_start_date 上有一套,在ls_stamp_date 上有一套。所以属性不一样:)
  • 抱歉打错了

标签: php mysql datetime magento magento2


【解决方案1】:

首先,也许我们需要一个通用的 Magento 日期处理说明。

Magento 打算将其所有日期保存在 GMT 的数据库中。
这种设计选择的原因很简单:Magento 允许您配置可能位于多个时区的多商店。

假设我有一个 Magento,有 3 家商店,我在伦敦经营。
这是我的商店:

  • 我的伦敦商店,配置在Europe/London;这也是我的 Main Store 配置
  • 日本商店,配置为Asia/Tokyo 时区
  • 北美商店,配置时区America/New_York

现在,让我们来看一个商业案例,如果我确实向我的客户承诺“我们会在 48 小时内交付,全球范围内,基于您所居住国家/地区首都的时区。” .

然后我收到了 3 个订单,每个商店都有一个订单,都是 5 月 1 日 16:15 的订单。
这对我来说是极其不方便的,在管理员中,将所有三个订单声明为 5 月 1 日 16:15 下达,以履行我对客户的承诺,因为我必须根据我在订单的管理网格中看到的商店。

对我来说最好的就是看到

  1. 5 月 1 日 16:15 在伦敦商店下单
  2. 4 月 30 日 20:15 在东京商店下单
  3. 5 月 1 日 21:15 在纽约商店下单

为了做到这一点,Magento 会从数据库中检索 GMT 日期,然后将当前的时区应用于日期。
很简单。

想象一下,如果他们确实将时区日期存储在数据库中会有多复杂... Magento 需要同时存储日期和时区,并为您必须显示的任何单个日期进行来回转换或时区计算.
非常疯狂的工作。

因此,为了遵循 Magento 的工作方式,您最好的选择是将您的日期存储在 GMT 的数据库中,并以这种方式创建您的客户日期:

use Magento\Framework\Stdlib\DateTime\DateTimeFactory;
use Magento\Customer\Model\Customer;   

class CustomerLsDate {
    private $dateTimeFactory; 

    public function __construct(DateTimeFactory $dateTimeFactory) {
        $this->dateTimeFactory = $dateTimeFactory;
    }

    public function setLsDate(Customer $customer): CustomerLsDate {
        $customer->setCustomAttribute('ls_start_date', $this->dateTimeFactory->create()->gmtDate('Y-m-d H:i:s'));

        return $this;
    }
}

那么,当你想查询这个日期时,就照原样使用它。
如果你想在商店时区向某人说,那么:

use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
use Magento\Customer\Model\Customer;

class CustomerLsDate {
    private $timezone;

    public function __construct(TimezoneInterface $timezone) {
        $this->timezone = $timezone;
    }

    public function getLsDate(Customer $customer): string {
        $date = $this->timezone->date(
            new \DateTime(
                $customer->getCustomAttribute('ls_start_date')->getValue(),
                new \DateTimeZone('GMT')
            )
        );

        Zend_Debug::dump($date->format('Y-m-d H:i:s'));

        return $date->format('Y-m-d H:i:s');
    }  
}

这确实是最适合 Magento 哲学的方法

完整的CustomerLsDate类:

use Magento\Framework\Stdlib\DateTime\DateTimeFactory;
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
use Magento\Customer\Model\Customer;   

class CustomerLsDate {
   private $dateTimeFactory; 
   private $timezone;

   public function __construct(DateTimeFactory $dateTimeFactory, TimezoneInterface $timezone) {
       $this->timezone = $timezone;
       $this->dateTimeFactory = $dateTimeFactory;
   }

   public function setLsDate(Customer $customer): CustomerLsDate {
       $customer->setCustomAttribute(
           'ls_start_date', 
           $this->dateTimeFactory->create()->gmtDate('Y-m-d H:i:s')
       );

       return $this;
   }

   public function getLsDate(Customer $customer): string {
       $date = $this->timezone->date(
           new \DateTime(
               $customer->getCustomAttribute('ls_start_date')->getValue(),
               new \DateTimeZone('GMT')
           )
       );

       Zend_Debug::dump($date->format('Y-m-d H:i:s'));
       return $date->format('Y-m-d H:i:s');
    }  
}

Rick James 有part of the answer

由于created_attimestamp 并且与MySQL 的默认连接将apply the server timezone to a timestamp,因此您的手动查询有效。

但是现在如果你像 Magento 一样去做

SET time_zone = '+00:00'; 
SELECT `main_table`.`entity_id`, `main_table`.`grand_total`, `main_table`.`created_at` FROM `sales_order` AS `main_table` WHERE (`created_at` >= '2019-08-06 15:33:21');

您的查询不会像您的 Magento 集合那样返回任何结果。
Magento 的 time_zone 设置在其默认 PDO 适配器实现中完成:

/**
 * Creates a PDO object and connects to the database.
 *
 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
 * @SuppressWarnings(PHPMD.NPathComplexity)
 *
 * @return void
 * @throws \Zend_Db_Adapter_Exception
 * @throws \Zend_Db_Statement_Exception
 */
protected function _connect()
{
    // extra unrelated code comes here...

    // As we use default value CURRENT_TIMESTAMP for TIMESTAMP type columns we need to set GMT timezone
    $this->_connection->query("SET time_zone = '+00:00'");

    // extra unrelated code comes here...
}

来源:Magento/Framework/DB/Adapter/Pdo/Mysql

从那里开始,您的答案就在于您的变量 $lsDate 来自哪里,以及您是否能够知道它的时区,以便将其转换回 GMT,以便将正确的 GMT 日期提供给您的收藏过滤器。

例如,如果您知道您的时区是'Europe/London',您可以这样做

$date = new \DateTime('2019-08-06 15:33:21', new \DateTimeZone('Europe/London'));
$date->setTimezone(new \DateTimeZone('GMT'));
echo $date->format('Y-m-d H:i:s'); // echoes 2019-08-06 14:33:21

根据您的编辑,当您创建 new \DateTime() 时,您将获得绑定到服务器时区的 DateTime


因此,根据您的喜好,您可以将日期保存在 GMT 的自定义客户字段中,也可以保存时区和日期。

1。在客户中保存 GMT 日期

PHP 方式

$newDate = new \DateTime('now',new \DateTimeZone('GMT'));
$customer->setCustomAttribute('ls_start_date', $newDate->format('Y-m-d H:i:s'));

您最终会在您的客户ls_start_date 上获得 GMT 日期

或者您也可以使用更多 Magento 方式,使用 DI:

use Magento\Framework\Stdlib\DateTime\DateTimeFactory;    

class Whatever {
   private $dateTimeFactory; 

   public function __construct(DateTimeFactory $dateTimeFactory) {
       $this->dateTimeFactory = $dateTimeFactory;
   }

   public function assignThatLsDate($customer) {
       $customer->setCustomAttribute('ls_start_date', $this->dateTimeFactory->create()->gmtDate('Y-m-d H:i:s'));
   }
}

2。在客户中以本地时区保存日期

$newDate = new \DateTime();
$customer->setCustomAttribute('ls_start_date', $newDate->format('Y-m-d H:i:s'));
$customer->setCustomAttribute('ls_start_date_timezone', $newDate->getTimezone ());

然后

$from = new \DateTime(
    $customer->getCustomAttribute('ls_start_date')->getValue(),
    $customer->getCustomAttribute('ls_start_date_timezone')->getValue()
)->setTimezone(new \DateTimeZone('GMT'));

// query to your collection is unchanged

【讨论】:

  • 我绝对认为您在正确的轨道上。我已经更新了我的问题,以提供有关 $lsDate 变量的更多详细信息。
  • @GlenRobson 编辑了更多输入,但必须查看第三个选项
  • 感谢您的更新!我会尝试第一个选项,看看是否可行。我更喜欢选项 3,但根据我的研究,您不能将时间戳存储为属性。
  • @GlenRobson 更深入地了解重新编辑的答案。现在我仍然缺少的是上下文。您何时以及为什么要创建此日期?是客户单击按钮设置lsDate 吗?它是来自其他模型的事件吗?不然呢?
  • 我接受了非常详细且有据可查的答案,非常感谢。我有一个您可能能够回答的问题是将日期存储为 GMT 而不是 UTC 的原因?
【解决方案2】:

(对于评论来说太复杂了;可能会导致答案。)

在 MySQL 中,做

SHOW VARIABLES LIKE "%zone%";  -- looking for how the timezone is set

SHOW CREATE TABLE ...  -- looking for datatypes used

具体来说,created_atDATETIME 还是 TIMESTAMP

【讨论】:

  • 是否会导致答案,我会说
【解决方案3】:

首先我会使用方法:

Mage_Sales_Model_Resource_Order_Collection::addFieldToFilter() 

直接而不是addAttributeToFilter()

其次,我会使用Mage_Core_Model_Date 来转换日期和时区

(Mage::getSingleton('core/date'))

【讨论】:

  • 您的建议似乎指向 magento 1 的实现。
  • 对不起。是的,我没有注意到您的问题是关于 Magento 2 的。
猜你喜欢
  • 2021-10-12
  • 2014-02-22
  • 2018-02-26
  • 1970-01-01
  • 1970-01-01
  • 2017-06-28
  • 2015-09-04
  • 1970-01-01
  • 2022-06-13
相关资源
最近更新 更多