草庐IT

php - Magento 2 Collection Date 按一小时过滤掉

coder 2023-10-10 原文

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

最佳答案

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

Magento 打算将其所有日期以格林威治标准时间保存在 DB 中。
这种设计选择的原因很简单:Magento 允许您配置可能位于多个时区的多商店。

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

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

  • 现在,让我们举一个商业案例,如果我确实向我的客户 promise “我们在 48 小时内交付,世界范围内,基于您居住的国家/地区首都的时区。”。

    然后我在 5 月 1 日 16:15 收到 3 个订单,每个商店一个。
    这对我来说非常不方便,在管理员中,将所有三个订单都声明为 5 月 1 日 16:15,以履行我对客户的 promise ,因为我将不得不根据我在订单的管理网格中看到的商店。

    对我来说最好的就是看看
  • 上下了一个订单5 月 1 日 16:15,在伦敦店
  • 上下了一个订单4月30日20:15,东京店
  • 上下了一个订单5 月 1 日 21:15,纽约门店

  • 为了做到这一点,Magento 将从数据库中检索 GMT 中的日期,然后将当前 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 并且默认连接到 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 集合那样返回任何结果。time_zone Magento 的设置是在其默认 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绑定(bind)到您的服务器的时区。

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

    1.在客户中以格林威治标准时间保存日期

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

    并且您最终会在您的客户身上获得格林威治标准时间日期 ls_start_date
    或者你也可以用 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
    

    关于php - Magento 2 Collection Date 按一小时过滤掉,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57380394/

    有关php - Magento 2 Collection Date 按一小时过滤掉的更多相关文章

    1. ruby-on-rails - 事件管理员日期过滤器日期格式自定义 - 2

      是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s

    2. ruby-on-rails - 在 Controller 中干净地处理多个过滤器(参数) - 2

      我有一个名为Post的类,我需要能够适应以下场景:如果用户选择了一个类别,则只显示该类别的帖子如果用户选择了一种类型,则只显示该类型的帖子如果用户选择了一个类别和类型,则只显示该类别中该类型的帖子如果用户没有选择任何内容,则显示所有帖子我想知道我的Controller是否不可避免地会因大量条件语句而显得粗糙...这是我解决此问题的错误方法-有谁知道我如何才能做到这一点?classPostsController 最佳答案 您最好遵循“胖模型,瘦Controller”的惯例,这意味着您应该将这种逻辑放在模型本身中。Post类应该能够报告

    3. ruby-on-rails - 如何处理 Grape 中特定操作的过滤器之前? - 2

      我正在我的Rails项目中安装Grape以构建RESTfulAPI。现在一些端点的操作需要身份验证,而另一些则不需要身份验证。例如,我有users端点,看起来像这样:moduleBackendmoduleV1classUsers现在如您所见,除了password/forget之外的所有操作都需要用户登录/验证。创建一个新的端点也没有意义,比如passwords并且只是删除password/forget从逻辑上讲,这个端点应该与用户资源。问题是Grapebefore过滤器没有像except,only这样的选项,我可以在其中说对某些操作应用过滤器。您通常如何干净利落地处理这种情况?

    4. ruby-on-rails - Rails 3 - 过滤器链暂停为 :authentication rendered or redirected - 2

      我仍然收到标题中的“错误”消息,但不知道如何解决。在ApplicationController中,classApplicationController在routes.rb#match'set_activity_account/:id/:value'=>'users#account_activity',:as=>:set_activity_account--thisdoesn'tworkaswell..resources:usersdomemberdoget:action_a,:action_bendcollectiondoget'account_activity'endend和User

    5. ruby-on-rails - ActiveAdmin 自定义选择过滤器下拉名称 - 2

      对于用户模型,我有一个过滤器来检查用户的预订状态,该状态由整数值(0、1或2)表示。UserActiveAdmin索引页上的过滤器是通过以下代码实现的:filter:booking_status,as::select然而,这会导致下拉选项为0、1或2。当管理员用户从下拉列表中选择它们时,我更愿意自己将它们命名为“未完成”、“待定”和“已确认”之类的名称。有没有办法在不改变booking_status在模型中的表示方式的情况下做到这一点? 最佳答案 假设booking_status是模型中的枚举字段,您可以使用:过滤器:booking

    6. ruby-on-rails - 这个 C 和 PHP 程序员如何学习 Ruby 和 Rails? - 2

      按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我来自C、php和bash背景,很容易学习,因为它们都有相同的C结构,我可以将其与我已经知道的联系起来。然后2年前我学了Python并且学得很好,Python对我来说比Ruby更容易学。然后从去年开始,我一直在尝试学习Ruby,然后是Rails,我承认,直到现在我还是学不会,讽刺的是那些打着简单易学的烙印,但是对于我这样一个老练的程序员来说,我只是无法将它

    7. ruby - 如何通过 belongs_to 按外部 id 和本地属性进行过滤? - 2

      以下模型通过belongs_to链接:require'mongoid'classSensorincludeMongoid::Documentfield:sensor_id,type:Stringvalidates_uniqueness_of:sensor_idend...require'mongoid'require_relative'sensor.rb'classSensorDataincludeMongoid::Documentbelongs_to:sensorfield:date,type:Datefield:ozonMax1h,type:Floatfield:ozonMax8h

    8. ruby - Rails+ActiveAdmin - 使用 ransacker 过滤会抛出错误 PG::SyntaxError: ERROR: syntax error at or near "," - 2

      我在RubyonRails4.1.4上有一个项目,使用来自git://github.com/activeadmin/activeadmin的activeadmin1.0.0.pre,pg0.17.1,PostgreSQL9.3在项目中我有这些模型:类用户has_one:账户类账户属于:用户有很多:project_accountshas_many:项目,:through=>:project_accounts类项目#该项目有一个bool属性'archive'has_many:project_accounts类ProjectAccount属于:帐户属于:项目我有一个任务是在索引页面上实现一个

    9. ruby-on-rails - Rails 中的协同过滤 - 2

      按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我正在寻找一种在Rails中进行协作过滤的解决方案,甚至是可能的示例。到目前为止,我只发现了acts_as_recommendable,它看起来很有用,但我注意到它在过去2年中没有任何更新。有人知道任何其他解决方案和/或示例吗?

    10. ruby - 在 Jekyll 中过滤 site.related_posts - 2

      我对Jekyll和Ruby很陌生(但是,非常兴奋)。在不使用插件的情况下,我试图找到一种方法来过滤site.related_posts。例如,我正在阅读标题为Foo且类别为A、B的帖子。该站点总共包含3个帖子:Foo(类别:A、B)条形图(类别:A、C、D)动物园(类别:B、F)默认情况下,在Jekyll中我们这样做:{%forpostinsite.related_postslimit:5%}{%endfor%}但是,上面的代码返回所有(3)个帖子。一个帖子包含很多类别,所以类别应该是一个数组。如何修改代码并仅返回类别与当前帖子类别相交的类别?(在此示例中,我希望代码仅返回Foo和Zo

    随机推荐