Magento 2 Collection Date过滤一小时

时间:2019-08-06 16:28:34

标签: php mysql datetime magento magento2

我有一个问题,我按日期过滤了一个集合,并且期望得到的项目没有返回到集合中,但是,如果我打印出该集合使用的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(以后的一个小时),则代码将返回一个包含一个项目的集合。看起来与时区有关吗?也许是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();

3 个答案:

答案 0 :(得分:6)

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

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

让我们想象一下,我有一家在伦敦经营的3家门店。
这是我的商店:

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

现在,让我们以一个商业案例为例,我确实向我的客户保证:“我们将根据您居住的国家首都所在的时区在48小时内交付世界通行证。”

然后我得到3个订单,每个商店有5个订单,所有订单都在5月1日16:15。
在管理员中,如果我将所有三个订单声明于5月1日16点15分放置,以履行我对客户的承诺,那对我来说将是非常不便的,因为我将不得不基于商店,我在订单的管理网格中看到。

对我来说最好的就是看

  1. 5月1日1月15日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');
    }  
}

里克·詹姆斯有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的来源,如果您能够知道其时区,以便将其转换回格林尼治标准时间,以获取正确的格林尼治标准时间日期您的收藏夹过滤器。

例如,如果您知道您的时区为'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日期

或者您也可以使用DI进行Magento的更多操作:

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

答案 1 :(得分:2)

(评论太复杂; 可能会导致答案。)

在MySQL中,做

"\r\n\t\t\t\r\n\t\t\t\t08-07-2019, 11:37:16 AM\r\n\t\t\t\r\n\t\t\t\r\n\t\t\t"

特别是(?<!\\\\)\\+[\\w-]+SHOW VARIABLES LIKE "%zone%"; -- looking for how the timezone is set SHOW CREATE TABLE ... -- looking for datatypes used 还是created_at

答案 2 :(得分:-2)

首先,我将使用以下方法:

Mage_Sales_Model_Resource_Order_Collection::addFieldToFilter() 

直接而不是addAttributeToFilter()

第二,我将使用Mage_Core_Model_Date来转换日期和时区

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