0

I am creating a function where you can give a starting year, month and end year, month and the function will print every month and year in between the two given points.

I've already created a function that works perfectly but I believe this is not a good practice and there might be better way to do this. Now I am seeking your help to find a better way.

P.S. I can't get full date as input. Only can get month number and year.

Here is my code -

function get_all_months($monthstart = null, $yearstart = null, $monthend = null, $yearend = null) {
if (($monthstart === null) || ($yearstart === null) || ($monthend === null) || ($yearend === null)) {
    $monthend = date('m');
    $yearend = date('Y');
    if($monthend < 6) {
        $yearstart = $yearend - 1;
        $monthstart = (($monthend - 5) + 12);
    } else {
        $yearstart = $yearend;
        $monthstart = $monthend - 5;
    }
}
$month_array = array();
if ($yearstart > $yearend) {
    for ($m=$monthend; $m<=12; $m++) $month_array[] = array('month' => $m, 'year' => $yearend);
    for ($y=$yearend+1; $y<$yearstart; $y++) for ($m=1; $m<=12; $m++) $month_array[] = array('month' => $m, 'year' => $y);
    for ($m=1; $m<=$monthstart; $m++) $month_array[] = array('month' => $m, 'year' => $yearstart);
} elseif ($yearend > $yearstart) {
    for ($m=$monthstart; $m<=12; $m++) $month_array[] = array('month' => $m, 'year' => $yearstart);
    for ($y=$yearstart+1; $y<$yearend; $y++) for ($m=1; $m<=12; $m++) $month_array[] = array('month' => $m, 'year' => $y);
    for ($m=1; $m<=$monthend; $m++) $month_array[] = array('month' => $m, 'year' => $yearend);
} else {
    for ($m=$monthstart; $m<=$monthend; $m++) $month_array[] = array('month' => $m, 'year' => $yearstart);
}
return $month_array;
}

EDIT: Based on Nigel Ren's answer, this is the best way I could think of -

 function get_all_months($monthstart = null, $yearstart = null, $monthend = null, $yearend = null) {

if (($monthstart === null) || ($yearstart === null) || ($monthend === null) || ($yearend === null)) {
    $monthend = date('m');
    $yearend = date('Y');
    if($monthend < 6) {
        $yearstart = $yearend - 1;
        $monthstart = (($monthend - 5) + 12);
    } else {
        $yearstart = $yearend;
        $monthstart = $monthend - 5;
    }
}

$output = [];

if ($yearstart > $yearend) {
    $time   = strtotime($yearend."-".$monthend);
    $last   = date('m-Y', strtotime($yearstart."-".$monthstart));
} else {
    $time   = strtotime($yearstart."-".$monthstart);
    $last   = date('m-Y', strtotime($yearend."-".$monthend));
}
do {
        $cur_month_year = date('m-Y', $time);
        $month = date('m', $time);
        $year = date('Y', $time);
        $output[] =  array('month'=>$month,'year'=>$year);
        $time = strtotime('+1 month', $time);
    }
    while ($cur_month_year != $last);
    return $output;
}
b0xed
  • 72
  • 9

5 Answers5

2

You can use PHP's DateInterval class which is used for these purposes. Try this code

<?php
function getMonthsFromRange($start, $end, $format = 'M Y')
{

    $array = array();

    // Since you wanted 1 month it is Period = 1 Month
    $interval = new DateInterval('P1M');

    $realEnd = new DateTime($end);
    $realEnd->add($interval);

    $period = new DatePeriod(new DateTime($start), $interval, $realEnd);

    // Use loop to store date into array 
    foreach ($period as $date)
        $array[] = $date->format($format);

    // Return the array elements 
    return $array;
}

// Function call with passing the start date and end date 
$months = getMonthsFromRange('2010-10', '2011-11');

print_r($months);

It's output is:

Array ( [0] => Oct 2010 [1] => Nov 2010 [2] => Dec 2010 [3] => Jan 2011 [4] => Feb 2011 [5] => Mar 2011 [6] => Apr 2011 [7] => May 2011 [8] => Jun 2011 [9] => Jul 2011 [10] => Aug 2011 [11] => Sep 2011 [12] => Oct 2011 [13] => Nov 2011 )
keidakida
  • 713
  • 4
  • 12
1

probably you're looking for something like this:

function get_all_months($monthstart = null, $yearstart = null, $monthend = null, $yearend = null) {
    $month_array = [];
    if ($yearstart == $yearend)
        for ($m=$monthstart; $m<=$monthend; $m++) $month_array[] = array('month' => $m, 'year' => $yearstart);
    else {
        for ($m=$monthstart; $m<=12; $m++) $month_array[] = array('month' => $m, 'year' => $yearstart);
        for ($y=$yearstart+1; $y<$yearend; $y++) for ($m=1; $m<=12; $m++) $month_array[] = array('month' => $m, 'year' => $y);
        for ($m=1; $m<=$monthend; $m++) $month_array[] = array('month' => $m, 'year' => $yearend);
    }
    return $month_array;
}
Iłya Bursov
  • 23,342
  • 4
  • 33
  • 57
  • Same thing, my code has few if else to make sure even null input give last 6 months and if end/starting year is switched, it shouldn't break. – b0xed Aug 18 '20 at 18:55
  • I worked with this a little bit and was able to reduce the code a lot – b0xed Aug 18 '20 at 19:09
  • @b0xed if you need to support inverted values - just swap values in yearstart/end before loops – Iłya Bursov Aug 18 '20 at 19:12
1

Based on the answer here, it can be done simply by adding 1 month to the start date till you get to the end date...

function get_all_months($monthstart = null, $yearstart = null, $monthend = null, $yearend = null) {
    $output = [];
    $time   = strtotime($yearstart."-".$monthstart);
    $last   = date('m-Y', strtotime($yearend."-".$monthend));
    do {
        $month = date('m-Y', $time);
        $output[] =  $month;
        $time = strtotime('+1 month', $time);
    }
    while ($month != $last);
    return $output;
}

So

print_r(get_all_months(4,2008,2,2010));

gives...

Array
(
    [0] => 04-2008
    [1] => 05-2008
    [2] => 06-2008
    [3] => 07-2008
    [4] => 08-2008
    [5] => 09-2008
    [6] => 10-2008
    [7] => 11-2008
    [8] => 12-2008
    [9] => 01-2009
    [10] => 02-2009
    [11] => 03-2009
    [12] => 04-2009
    [13] => 05-2009
    [14] => 06-2009
    [15] => 07-2009
    [16] => 08-2009
    [17] => 09-2009
    [18] => 10-2009
    [19] => 11-2009
    [20] => 12-2009
    [21] => 01-2010
    [22] => 02-2010
)
Nigel Ren
  • 56,122
  • 11
  • 43
  • 55
0

If I understand correctly - you get the month as a number and the year as a number e.g 6 and 1997. If we assume that the start year is always less than the end year I would suggest going like this.

function distanceBetweenDates(int $sm, int $sy, int $em, int $ey) {
    $monthsBetweenYears = ($ey - $sy + 1) * 12;
    $distanceBetweenMonths = $monthsBetweenYears - $sm - (12 - $em);
    
    $startMonth = $sm + 1;
    $startYear = $sy;
    
    while ($distanceBetweenMonths > 0) {
        if ($startMonth <= 12) {
            echo $startMonth . ' - ' . $startYear;
        } else {
            $startMonth = 1;
            $startYear++;
            echo $startMonth . ' - ' . $startYear;
        }
        echo "\n";
        $startMonth++;
        $distanceBetweenMonths--;
    }
}

The only thing you might need to see is if the given months are included or excluded from the calculation.

The only thing missing here is the validation since it can be "skipped" of a sort if you use types inside the method.

g9m29
  • 373
  • 3
  • 16
0

With the DateTime class and DateInterval, you can create a generator of DateTime object of the specified range:

<?php

function get_all_months(int $monthstart, int $yearstart, int $monthend, int $yearend) {
        $onemonth = new DateInterval('P1M'); // one month interval
        $timeperiod = new DatePeriod(
                DateTime::createFromFormat('Y-m', "{$yearstart}-{$monthstart}"),
                $onemonth,
                DateTime::createFromFormat('Y-m', "{$yearend}-{$monthend}")->add($onemonth)
        );
        foreach ($timeperiod as $pos) {
                yield clone $pos;
        }
}

foreach (get_all_months(12, 2009, 1, 2020) as $month) {
        echo "{$month->format('Y-m')}\n";
}

DateTime should be more flexible to use than plain string or number array (see DateTime::format for more usage options).


Note: inspired by @keidakida, updated my answer.

Koala Yeung
  • 7,475
  • 3
  • 30
  • 50