1

I have the following to loop through each month of the year. However, it seems to skip February.

$start = new DateTime('2015-01-01');
$start->modify('last day of this month');

$current = new DateTime('now');
$end = new DateTime('2018-01-01');

$interval = DateInterval::createFromDateString('1 month');
$period = new DatePeriod($start, $interval, $end);

$timestamps = array();

foreach ($period as $dt) {
    $dt->modify('last day of this month');

    echo 'C:' . $current->format('d F Y') . '<br>';
    echo 'S:' . $start->format('d F Y') . '<br>';
    echo 'D:' . $dt->format('d F Y') . '<br>';
    echo '<br><br>';

}

However, the above outputs:

C:17 March 2015
S:31 January 2015
D:31 January 2015

C: 17 March 2015
S:31 January 2015
D:31 March 2015

C: 17 March 2015
S:31 January 2015
D:30 April 2015

Can anyone spot my mistake? I expected the second D to have a value of the 28 February 2015.

I just want a list of months that have already been passed.

Update

The problem highlighted by MLeFevre in the comments is that working with date intervals can be tricky. See Example #3 Beware when adding months http://php.net/manual/en/datetime.add.php.

Abs
  • 56,052
  • 101
  • 275
  • 409
  • 1
    If you down vote, please explain why so that I can improve my question. – Abs Mar 17 '15 at 11:09
  • 1. Please add how your current output the start and the end loos like 2. Also add what output you would expect and if possible mark with a comment in the current output where you expect something else – Rizier123 Mar 17 '15 at 11:15
  • Interesting question. It seems that working with month DateInterval can be tricky, so I would use another approach. See also [this question](http://stackoverflow.com/questions/9282287/what-can-go-wrong-when-adding-months-with-a-dateinterval-and-datetimeadd). – Furgas Mar 17 '15 at 11:18
  • 1
    Your problem could be related to this example from the manual `Example #3 Beware when adding months` http://php.net/manual/en/datetime.add.php, a classic manual moment of not go into more detail other than `beware`, but i'm assuming there's a set distinct number of days for a period of 1 month (eg: 30days), rather than it being an arbitrary period of "1 month" which is why it skips Feb. You could prove this in your example by changing your end date to `$end = new DateTime('2015-05-03');` vs `$end = new DateTime('2015-05-04');`, the latter will include an additional month. – SubjectCurio Mar 17 '15 at 11:20

3 Answers3

3

Rather than use a DatePeriod, why not just use the modify method slightly differently like this:

$current = new DateTime('now');
$end = new DateTime('2018-01-01');

while($current < $end) {
    $current->modify('last day of next month');
    echo 'C:' . $current->format('d F Y') . '<br>';
}

In your question, you're firstly adding a month, then going to the end of that month. This doesn't work, as the length of each month varies.

Sample output:

C:30 April 2015
C:31 May 2015
C:30 June 2015
C:31 July 2015
C:31 August 2015
C:30 September 2015
C:31 October 2015
C:30 November 2015
C:31 December 2015
C:31 January 2016
C:29 February 2016
C:31 March 2016
// etc.

To loop from $start to $current, you could change the logic slightly like this:

$start = new DateTime('2015-01-31'); // start from end of month
$current = new DateTime('now');

do {
    echo 'C:' . $start->format('d F Y') . '<br>';    
} while($start->modify('last day of next month') < $current);

Output:

C:31 January 2015
C:28 February 2015
Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
  • I am just playing around with your solution. The output I am looking for is `January, February` and that is it. At the end of March the output will be `January, February and March`. – Abs Mar 17 '15 at 11:28
  • 1
    I've edited my answer to show how you could do that. – Tom Fenech Mar 17 '15 at 11:35
  • That is awesome, so much less code than my pathetic excuse for a solution! – Abs Mar 17 '15 at 11:38
0

It happen because February has 28 days and your interval is 1 month (30 days). So it skips 30 days from 30 January to 2 March. Then it move to last day of March.

Change

$start->modify('last day of this month');

to

$start->modify('first day of this month');
Damian Polac
  • 911
  • 9
  • 20
-1

Your first date is 31-Jan-2015. Since February has no 31st, it's going to March 3rd. Then you are telling it to go to the end of that month which is why you are getting the end of March after January and not February.

sg-
  • 2,196
  • 1
  • 15
  • 16