How to list all months between two dates
Asked Answered
B

6

71

I'm trying to list all months between two dates.

For example; start date is: 2010-12-02 and last date is: 2012-05-06

I want to list something like this:

2010-12
2011-01
2011-02
2011-03
2011-04
.
.
.
2012-04
2012-05

This is what I have tried and it is not working at all:

    $year_min = 2010;
    $year_max = 2012;
    $month_min = 12;
    $month_max = 5;
    for($y=$year_min; $y<=$year_max; $y++)
    {
        for($m=$month_min; $m<=$month_max; $m++)
        {
            $period[] = $y.$m;
        }
    }
Baksheesh answered 11/9, 2013 at 13:45 Comment(0)
H
238

PHP 5.3

$start    = new DateTime('2010-12-02');
$start->modify('first day of this month');
$end      = new DateTime('2012-05-06');
$end->modify('first day of next month');
$interval = DateInterval::createFromDateString('1 month');
$period   = new DatePeriod($start, $interval, $end);

foreach ($period as $dt) {
    echo $dt->format("Y-m") . "<br>\n";
}

See it in action

PHP 5.4 or newer

$start    = (new DateTime('2010-12-02'))->modify('first day of this month');
$end      = (new DateTime('2012-05-06'))->modify('first day of next month');
$interval = DateInterval::createFromDateString('1 month');
$period   = new DatePeriod($start, $interval, $end);

foreach ($period as $dt) {
    echo $dt->format("Y-m") . "<br>\n";
}

The part where we modify the start and end dates to the first of the month is important. If we didn't, and the current day higher then the last day in February (i.e. 28 in non-leap years, 29 in leap years) this would skip February.

Hards answered 11/9, 2013 at 13:46 Comment(7)
Isn't there a mistake in version for PHP 5.4 or newer? In version below 5.4 there is $end->modify('first day of next month'); and above 5.4 is ->modify('first day of this month');Flowerdeluce
Neat solution, what about reversed order? Switching $start with $end does nothing...Religieux
@Religieux Just put the results in an array and reverse itHards
swap new DatePeriod($end, $interval, $start) and array_reverse(iterator_to_array($period))Simone
echo(iterator_count($period));Hards
Not an issue for this question (since it only specifies dates), but I've ran into the issue that it adds an additional month at the end when the $start time is smaller than the $end time. Using $start->setTime(0,0); and $end->setTime(0,0); seems to fix it.Nares
this is the best way to solve if between dates too close, example var date1 = "2022-09-29"; var date2 = "2022-10-10";Panier
B
13
function getMonthsInRange($startDate, $endDate)
{
    $months = array();

    while (strtotime($startDate) <= strtotime($endDate)) {
        $months[] = array(
            'year' => date('Y', strtotime($startDate)),
            'month' => date('m', strtotime($startDate)),
        );

        // Set date to 1 so that new month is returned as the month changes.
        $startDate = date('01 M Y', strtotime($startDate . '+ 1 month'));
    }

    return $months;
}
Benefaction answered 13/8, 2015 at 3:24 Comment(1)
Can you clarify why this is helpful please?Benthamism
D
10

You must make a difference between two months of the same year and two months of different years.

$year_min = substr($row['contractStart'], 0, 4);
$year_max = substr($row['contractEnd'], 0, 4);
$month_min = substr($row['contractStart'], 5, 2);
$month_min = substr($row['contractEnd'], 5, 2);
$period = array();
try {
  if ($year_min > $year_max)
    throw new Exception();
  else if ($year_min == $year_max)
    if ($month_min > $month_max)
      throw new Exception();
    for ($month = $month_min; $month <= $month_max; $month++) {
      $period[] = $month . '-' . $year;
    }
  else {
    for ($month = $month_min; $month <= 12; $month++) {
      $period[] = $month . '-' . $year_min;
    }
    for ($year = $year_min + 1; $year < $year_max; $year++) {
      for ($month = $month_min; $month <= $month_max; $month++) {
        $period[] = $month . '-' . $year;
      }
    }
    for ($month = 1; $month <= $month_max; $month++) {
      $period[] = $month . '-' . $year_max;
    }
  }
  implode("<br />\r\n", $period);
}
catch (Exception $e) {
  echo 'Start date occurs after end date.'
}

That's for the hard way. Now there is a quick and easy way that is already given as an answer which I recommend you to choose.

Dutra answered 11/9, 2013 at 13:59 Comment(0)
L
6

This was my solution since DateTime is not available in my server environment.

$a = "2007-01-01";
$b = "2008-02-15";

$i = date("Ym", strtotime($a));
while($i <= date("Ym", strtotime($b))){
    echo $i."\n";
    if(substr($i, 4, 2) == "12")
        $i = (date("Y", strtotime($i."01")) + 1)."01";
    else
        $i++;
}

Try it out: http://3v4l.org/BZOmb

Leomaleon answered 26/1, 2015 at 13:6 Comment(0)
A
3

In Laravel,

$period = \Carbon\CarbonPeriod::create('2017-06-28', '1 month', '2019-06-01');

foreach ($period as $dt) {
     echo $dt->format("Y-m") . "<br>\n";
}
Abirritant answered 31/8, 2020 at 16:22 Comment(1)
Mentioned in 2019 @ https://mcmap.net/q/276228/-getting-all-months-between-2-datesScat
P
0

October 2021 Update
If you have dates selected by the user, here's a solution

$from = date('Y-m-d', strtotime($_POST['from']));
$to = date('Y-m-d', strtotime($_POST['to']));

$counter = 1;
$max_date = strtotime($to);
$current_date = strtotime($from);
$dates = [];
$months = [];
$loop = true;
while($loop) {
    if(strtotime(date('Y-m-d',$current_date)." +".$counter."days") >= $max_date) $loop = false;
    else {
        $current_date = strtotime(date('Y-m-d', $current_date)." +".$counter."days");
        $date = date('Y-m-d', $current_date);
        $dates[] = $date;
        $months[] = date('Y-m', $current_date);
        $counter++;
    }
}
$months = array_unique($months);
echo '<pre>';
print_r($dates);
echo '<br>';
print_r($months);
echo '</pre>';
Personify answered 16/10, 2021 at 1:38 Comment(1)
Even if you have dates selected by the user, you should still be using datetime objects. This answer is too convoluted and is missing its educational explanation.Scat

© 2022 - 2024 — McMap. All rights reserved.