DateTime“上个月的第一天”没有返回第一天

时间:2013-08-28 00:34:57

标签: php datetime datediff

我已经看过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的构造函数的第二个参数,但它仍然给我相同的结果,只是在不同的时间。

2 个答案:

答案 0 :(得分:3)

这部分无关紧要 - 问题被编辑

由于时区差异。 $ start在'Rainy River时区'中计算,而$ 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 - 问题原因

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

这将给出正确的结果:

$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

答案 1 :(得分:3)

阅读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尚未。