0

I have a loop within PHP that is supposed to get the last 12 full months that have passed. Today, May 31st, the labels for those months seem to be duplicating.

March replaces February December replaces November October replaces September

$count = 1;
$date_array = array();
while ($count <= 12) {

    // Calculate date
    $query_year = date('Y', strtotime('-' . $count . ' months America/Chicago'));
    $query_month = date('m', strtotime('-' . $count . ' months America/Chicago'));
    $display_month = date('M', strtotime('-' . $count . ' months America/Chicago'));

    $date_array[] = array(
        'query_year' => $query_year,
        'query_month' => $query_month,
        'display_month' => $display_month
    );

    $count ++;

}

print_r($date_array);

Here's the code in action: http://ideone.com/dBklOH

Any idea why this is happening? It only started today, the 31st of May, and will likely resolve itself by tomorrow.

Jeff Walden
  • 311
  • 1
  • 4
  • 19
  • 5
    What do you expect if you subtract 1 month from 31st May..... should PHP tell you 31st April? – Mark Baker May 31 '16 at 13:53
  • 1
    Possible duplicate of [How to get previous month and year relative to today, using strtotime and date?](http://stackoverflow.com/questions/5489502/how-to-get-previous-month-and-year-relative-to-today-using-strtotime-and-date) – Patrick Q May 31 '16 at 13:55
  • @MarkBaker I would expect PHP to find the last date of the previous month, April 30th. Same with subtracting 1 month from March 31st, I expect to see February 28/29th. 1 month != number of days in current month – Jeff Walden May 31 '16 at 14:10
  • 2
    @JeffWalden - then your expectation is wrong, as explained in Matt's answer.... PHP Is effectively decrementing the month part of a date 2016-03-31 to give 2016-02-31 (adjusting by decrementing the year for month 1), but not adjusting the day.... incredibly logical if you think about it from a computer's perspective – Mark Baker May 31 '16 at 14:11
  • This is pretty well documented behavior and has been asked and answered on SO multiple times. – Patrick Q May 31 '16 at 14:12
  • @MarkBaker - thanks, and makes sense, just not what I would expect when subtracting "1 month". Appreciate your help explaining this within the context of my specific question even though it's been asked and answered multiple times on every programming forum imaginable. – Jeff Walden May 31 '16 at 14:22

4 Answers4

4

Programmatically determining the date N months in the past or the future is a non-trivial question when today's day of the month (31) didn't exist in the month in question. Is 1 month after 31 January: 28 February or 2 March? Obviously a human knows it depends what you mean by "1 month" but PHP chooses the latter.

Your safest bet to get hold of the last twelve months is to first set your initial date to the first day of the current month.

for ($index = 0, $date = new DateTime("first day of this month", new DateTimeZone("America/Chicago"));
     ++$index <= 12; $date->modify("-1 month")) {
    $dates[] = [
        "query_year" => $date->format("Y"),
        "query_month" => $date->format("m"),
        "display_month" => $date->format("M")
    ];
}

Or, using PHP's date function instead of the DateTime class:

for ($index = 0, $date = strtotime("first day of this month America/Chicago");
     ++$index <= 12; $date = strtotime("-1 month", $date)) {
    $dates[] = [
        "query_year" => date("Y", $date),
        "query_month" => date("m", $date),
        "display_month" => date("M", $date)
    ];
}
Matt Raines
  • 4,149
  • 8
  • 31
  • 34
1
<?php

$date = new DateTime('first day of this month', new DateTimeZone('America/Chicago'));
$dates = [];
for($i = 12; $i>0;$i--) {
    $dates[] = [
        'query_year' => $date->format('Y'),
        'query_month' => $date->format('m'),
        'display_month' => $date->format('M'),
    ];
    $date->sub(new DateInterval("P1M"));
}
var_dump($dates);
Pyton
  • 1,291
  • 8
  • 19
1

The problem with your code is that you must use the first day of the month as the start point. Some months have 31 days, and some months have 30 or 29 days. The reduce day number in string "-1 months" based on previous month and not current month.

 <?php
         date_default_timezone_set("America/Chicago");
         $count = 0;
         $data_array = array();
         $year_month = date("Y-m");
         $first_day = strtotime("$year_month-01 00:00:00");
         while ($count < 12) {
             $query_year = date("Y", strtotime("-".$count." months", $first_day));
             $query_month = date("m", strtotime("-".$count." months", $first_day));
             $display_month = date("M", strtotime("-".$count." months", $first_day));
             $date_array[] = array(
                 "query_year" => $query_year,
                 "query_month" => $query_month,
                 "display_month" => $display_month
             );
             $count++;
        }
        print_r($date_array);
    ?>
Hunter Turner
  • 6,804
  • 11
  • 41
  • 56
w169q169
  • 11
  • 2
0

I use this simple code to loop through months

$year_month = date("Ym");
for($i=0;$i<12;$i++)
{
     $year_month = date("Ym",strtotime("-$i month",strtotime($year_month)));
     print $i." - ".$year_month."<br/>";
}
vgijre
  • 1
  • 2