【问题标题】:DateTime "first day of last month" not returning the first dayDateTime“上个月的第一天”不返回第一天
【发布时间】:2013-08-30 22:02:27
【问题描述】:

我已经查看了this answer,它与我所拥有的非常接近。

这是我的 PHP 代码:

$start = new DateTime('0:00 first day of previous month', new DateTimeZone('UTC'));
/*
if (isset($_GET['year']) && isset($_GET['month']) && checkdate($_GET['month'], 1, $_GET['year'])) {
    $start = DateTime::createFromFormat('Y-m-d', $_GET['year'] . '-' . $_GET['month'] . '-1');
}*/
$middle = DateTime::createFromFormat('U', strtotime('first day of last month', $start->format('U')));
$middle->setTimezone(new DateTimeZone('UTC'));
$end = DateTime::createFromFormat('U', strtotime('first day of 2 months ago', $start->format('U')));
$end->setTimezone(new DateTimeZone('UTC'));

var_dump($start);
var_dump($middle);
var_dump($end);

今天是 8 月 27 日,所以我预计是 7 月 1 日、6 月 1 日和 5 月 1 日。以下是实际输出:

object(DateTime)[1]
  public 'date' => string '2013-07-01 00:00:00' (length=19)
  public 'timezone_type' => int 3
  public 'timezone' => string 'UTC' (length=3)

object(DateTime)[2]
  public 'date' => string '2013-05-02 00:00:00' (length=19)
  public 'timezone_type' => int 3
  public 'timezone' => string 'UTC' (length=3)

object(DateTime)[3]
  public 'date' => string '2013-04-02 00:00:00' (length=19)
  public 'timezone_type' => int 3
  public 'timezone' => string 'UTC' (length=3)

为什么它会为我返回每月的第二天?

我也尝试过不使用 new DateTimeZone('GMT') 作为初始 DateTime 的构造函数的第二个参数,但它仍然给我相同的结果,只是时间不同。

【问题讨论】:

    标签: php datetime datediff


    【解决方案1】:

    这部分无关 - 问题已编辑

    因为时区不同。 $start 以“Rainy River timezone”计算,而 $middle 和 $end 以 UTC 时间计算。 'Rainy River 时区有一个来自 UTC 的 -06:00 hour offset(正好是第一个与第二个和第三个结果之间的小时差)。

    更新 1 - 解决方案

    似乎问题出在 strtotime 附近。由于某种原因,它会产生一个偏移量为一天的结果(需要进一步解释)。一个简单的解决方案是从该日期减去一秒,它将产生正确的结果。

    $timezone = new DateTimeZone('UTC');
    $start = new DateTime('0:00 first day of previous month', $timezone );
    $middle = DateTime::createFromFormat('U', strtotime('first day of last month',($start  ->format('U'))-1),$timezone);
    echo $middle->format('Y-m-d')."\n";
    

    结果:

    2013-05-01
    

    更新 2 - 问题原因

    最终我发现问题源于第一个日期对象的实例化。这是一个插图。

    这将给出正确的结果:

    $original = new DateTime('2013-05-01');
    echo $original->format('Y-m-d')."\n";
    
    $previous= DateTime::createFromFormat('U', strtotime('first day of last month',($original->format('U'))),new DateTimeZone('UTC'));
    echo $previous->format('Y-m-d')."\n";
    

    结果(正常):

    2013-05-01
    2013-04-01   <--- OK
    

    但是,这不会(只有第一行不同,如原始代码):

    $original = new DateTime('0:00 first day of previous month', new DateTimeZone('UTC'));
    echo $original->format('Y-m-d')."\n";
    
    $previous= DateTime::createFromFormat('U', strtotime('first day of last month',($original->format('U'))),new DateTimeZone('UTC'));
    echo $previous->format('Y-m-d')."\n";
    

    结果:

     2013-07-01
     2013-05-02  <--- BAD
    

    【讨论】:

    • 我认为这是问题所在,但我也尝试将所有时间都设置为 UTC...见编辑后的帖子。
    • @rink.attendant.6 是否有理由以不同的方式实例化日期?为什么不 $start= new DateTime("上个月的第一天"); $middle = new DateTime("上个月的第一天"); $end = new DateTime("2个月前的第一天");
    • 因为您可以在我的注释代码中看到,$start 可能通过$_GET(特别是下拉框)传递,所以$middle$end 需要相对于@987654332 @.
    • +1 解释...每天都要学习新东西! :)
    • 这就是我们在这里的原因! :) 我仍然希望有一个在 php 方面更有经验的人就日期对象的行为方式/原因提供输入。似乎他们正在创建一些参考资料,但在文档或其他在线讨论中没有任何内容......
    【解决方案2】:

    看完the answer here,我有了一个更好的主意:

    $start = new DateTime('0:00 first day of previous month');
    /*
    if (isset($_GET['year']) && isset($_GET['month']) && checkdate($_GET['month'], 1, $_GET['year'])) {
        $start = DateTime::createFromFormat('Y-m-d', $_GET['year'] . '-' . $_GET['month'] . '-1');
    }*/
    $middle = clone $start;
    $middle->modify('first day of last month');
    $end = clone $start;
    $end->modify('first day of 2 months ago');
    
    var_dump($start);
    var_dump($middle);
    var_dump($end);
    

    输出:

    object(DateTime)[1]
      public 'date' => string '2013-07-01 00:00:00' (length=19)
      public 'timezone_type' => int 3
      public 'timezone' => string 'America/Rainy_River' (length=19)
    
    object(DateTime)[2]
      public 'date' => string '2013-06-01 00:00:00' (length=19)
      public 'timezone_type' => int 3
      public 'timezone' => string 'America/Rainy_River' (length=19)
    
    object(DateTime)[3]
      public 'date' => string '2013-05-01 00:00:00' (length=19)
      public 'timezone_type' => int 3
      public 'timezone' => string 'America/Rainy_River' (length=19)
    

    另外,我意识到DateTimeImmutable 将是$start 实例的更好选择(这样我就不必克隆其他两个),但我还没有访问 PHP 5.5 的权限。

    【讨论】:

    • 这是一个更干净的方式,竖起大拇指。然而 - 原始问题的一部分是为什么那段特定的代码不能正常工作,我试图详细说明。
    • 是的,所以对于 DateTimeImmutable 只需更改 DateTime > DateTimeImmutable,然后你可以这样做:$middle = $start->modify etc.
    猜你喜欢
    • 2017-08-03
    • 1970-01-01
    • 2015-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-30
    • 2011-09-29
    • 1970-01-01
    相关资源
    最近更新 更多