Question

I looked at this answer already, and it's quite close to what I have.

Here is my PHP code:

$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);

Today is August 27th, so I would expect July 1, June 1, and May 1. Here's what the actual output is:

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)

Why is it returning the second day of the months for me?

I've also tried it without the new DateTimeZone('GMT') as the second parameter of the constructor for the initial DateTime but it still gives me the same result, just with different times.

Was it helpful?

Solution

this part irrelevant - question was edited

Because of the timezone difference. $start is calculated in the 'Rainy River timezone', while $middle and $end are in UTC time. The 'Rainy River timezone has a -06:00 hour offset from UTC (exactly the difference in hours between the first with the second and third results).

update 1 - solution

It seems the problem lies somewhere around strtotime. For some reason it yields a result with an offset of one day (further explanation needed). A simple solution, is to subtract one second from that date and it will produce the correct result.

$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";

Result:

2013-05-01

update 2 - reason for problem

Eventually I find out that the problem originates from the instantiation of the fisrt date object. Here is an illustration.

This will give a correct result:

$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";

Result (OK):

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

However, this will not (only first line different, as in the original code):

$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";

Result:

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

OTHER TIPS

After reading the answer here, I had a better idea:

$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);

Output:

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)

Also, I realize that a DateTimeImmutable would be a better choice for the $start instance (so that I don't have to clone the other two), but I don't have access to PHP 5.5 yet.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top