Magento 2 收集日期过滤一小时
Magento 2 Collection Date Filter out by one hour
我遇到了一个问题,我按日期过滤了一个集合,但我希望得到的项目没有返回到集合中,但是如果我打印出集合使用的 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
。
如果我 运行 我的数据库上面的查询它 returns 上面的一个订单但是正如你在我上面的代码中看到的那样,集合是空的。
如果我将订单日期更改为 2019-08-06 16:34:21
(未来一小时)代码,则 returns 包含一个项目的集合。好像跟这里的时区有关系?也许是 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();
(评论太复杂;可能 会得出答案。)
在MySQL中做
SHOW VARIABLES LIKE "%zone%"; -- looking for how the timezone is set
SHOW CREATE TABLE ... -- looking for datatypes used
特别是 created_at
是 DATETIME
还是 TIMESTAMP
?
首先,也许我们需要一个通用的 Magento 日期处理说明。
Magento 打算将其所有日期以格林威治标准时间保存在数据库中。
选择这种设计的原因很简单:Magento 允许您配置 multi-store 可以在多个时区。
假设我有一个 Magento,有 3 家商店,我在伦敦经营。
这是我的商店:
- 我的伦敦店,配置在
Europe/London
;这也是我的主要商店配置
- 日本商店,配置在
Asia/Tokyo
时区
- 北美商店,配置时区
America/New_York
现在,让我们来看一个商业案例,我向我的客户承诺 "We deliver in 48 hour, worldwilde, based on the timezone of the capital city of the country you live in.".
然后我收到 3 个订单,每个商店一个,都是在 5 月 1 日 16:15。
这对我来说非常不方便,在管理员中,将所有三个订单都声明为 5 月 1 日在 16:15 下达,以履行我对客户的承诺,因为我将不得不根据疯狂的计算我在商店的订单管理网格中看到。
对我来说最好的是看到
- 5 月 1 日 于 16:15 在伦敦商店
下了一个订单
- 4 月 30 日在 20:15 东京商店
下的一个订单
- 5 月 1 日 在 21:15 纽约商店
下了一个订单
Magento,为了做到这一点,将从数据库中检索格林威治标准时间的日期,然后将当前的 sotre 时区应用到日期。
很简单。
想象一下,如果他们在数据库中存储时区日期会有多复杂...Magento 需要存储日期和时区,并为您拥有的任何单个日期进行来回转换或时区计算显示。
非常疯狂的工作。
因此,为了遵循 Magento 的工作方式,您最好的选择是将您的日期存储在格林威治标准时间的数据库中,因此以这种方式创建您的客户日期:
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
class:
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_at
是 timestamp
and that a default connection to MySQL will 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');
您的查询不会 return 任何像您的 Magento collection 那样的结果。
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 日期给你的 collection 过滤器。
例如,如果您知道您的时区是'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'));
您最终会在您的客户上获得 GMT 日期 ls_start_date
或者你也可以使用 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
我遇到了一个问题,我按日期过滤了一个集合,但我希望得到的项目没有返回到集合中,但是如果我打印出集合使用的 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
。
如果我 运行 我的数据库上面的查询它 returns 上面的一个订单但是正如你在我上面的代码中看到的那样,集合是空的。
如果我将订单日期更改为 2019-08-06 16:34:21
(未来一小时)代码,则 returns 包含一个项目的集合。好像跟这里的时区有关系?也许是 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();
(评论太复杂;可能 会得出答案。)
在MySQL中做
SHOW VARIABLES LIKE "%zone%"; -- looking for how the timezone is set
SHOW CREATE TABLE ... -- looking for datatypes used
特别是 created_at
是 DATETIME
还是 TIMESTAMP
?
首先,也许我们需要一个通用的 Magento 日期处理说明。
Magento 打算将其所有日期以格林威治标准时间保存在数据库中。
选择这种设计的原因很简单:Magento 允许您配置 multi-store 可以在多个时区。
假设我有一个 Magento,有 3 家商店,我在伦敦经营。
这是我的商店:
- 我的伦敦店,配置在
Europe/London
;这也是我的主要商店配置 - 日本商店,配置在
Asia/Tokyo
时区 - 北美商店,配置时区
America/New_York
现在,让我们来看一个商业案例,我向我的客户承诺 "We deliver in 48 hour, worldwilde, based on the timezone of the capital city of the country you live in.".
然后我收到 3 个订单,每个商店一个,都是在 5 月 1 日 16:15。
这对我来说非常不方便,在管理员中,将所有三个订单都声明为 5 月 1 日在 16:15 下达,以履行我对客户的承诺,因为我将不得不根据疯狂的计算我在商店的订单管理网格中看到。
对我来说最好的是看到
- 5 月 1 日 于 16:15 在伦敦商店 下了一个订单
- 4 月 30 日在 20:15 东京商店 下的一个订单
- 5 月 1 日 在 21:15 纽约商店 下了一个订单
Magento,为了做到这一点,将从数据库中检索格林威治标准时间的日期,然后将当前的 sotre 时区应用到日期。
很简单。
想象一下,如果他们在数据库中存储时区日期会有多复杂...Magento 需要存储日期和时区,并为您拥有的任何单个日期进行来回转换或时区计算显示。
非常疯狂的工作。
因此,为了遵循 Magento 的工作方式,您最好的选择是将您的日期存储在格林威治标准时间的数据库中,因此以这种方式创建您的客户日期:
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
class:
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_at
是 timestamp
and that a default connection to MySQL will 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');
您的查询不会 return 任何像您的 Magento collection 那样的结果。
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 日期给你的 collection 过滤器。
例如,如果您知道您的时区是'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'));
您最终会在您的客户上获得 GMT 日期 ls_start_date
或者你也可以使用 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