I need a menu with buttons of months previous and after a given month, something like this:

| month | month | month | current month | month | month | month |

Can this function further be improved?

* Generates array of months previous and after given date
* @param $p_base_data       - array of base year and months, 
         if not provided current date is used ['year'=>false, 'month'=>0]
* @param $p_nr_prev_months - number of previous mounts to calculate
* @param $p_nr_post_months - number of upcoming mounts to calculate
* @return populated array

function buildListOfMonths( array $p_base_date = ['year'=>false, 'month'=>0], 
                            int $p_nr_prev_months = 5, 
                            int $p_nr_post_months = 6) : array
    // init var
    $months_list    = [];
    // setting base year and date if parameter not passed
    $base_year  = $p_base_date['year'] ? $p_base_date['year'] : date('Y');
    $base_month     = $p_base_date['month'] > 0 
        ? (int)$p_base_date['month'] 
        : date('n');

    // looping through prev to post number of months
    for ($i = -$p_nr_prev_months; $i <= $p_nr_post_months; $i++) {
        // creating date object of actual date
        $date_obj       = strtotime(date($base_year.'-'.$base_month.'-01')." +$i months");
        // building array of months
        $months_list[]  = [ 'year'       => date('Y', $date_obj), 
                               'nr_month'   => date('n', $date_obj), 
                               'month_name' => date("M", $date_obj),
                               'current'    => (date('n', $date_obj) == $base_month) ? true : false ];
    } // end for

    return $months_list;
} // end func.
New contributor
Szekelygobe is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

Be aware that in some scenarios, strtotime() may have a limitation. Furthermore using property DateTime objects will provide greater utility if you decide to extend functionality in the future.

This task is well-suited for DatePeriod. It will serve you up a collection of date objects that you can format however you wish.

Code: (Demo)

function rangeOfMonths($prior, $ahead, $baseYear = null, $baseMonth = null) {
    $baseYear = $baseYear ?: date('Y');
    $baseMonth = $baseMonth ?: date('m');

    $rangeStart = new DateTime($baseYear . '-' . $baseMonth . '-01 23:59:59');
    $rangeEnd = clone($rangeStart);

    $rangeStart->modify("-{$prior} month");
    $rangeEnd->modify("+{$ahead} month +1 day");

    $period = new DatePeriod($rangeStart, new DateInterval('P1M'), $rangeEnd);
    $monthsList = [];
    foreach ($period as $i => $dateObject) {
        $monthsList[] = [
            'year'       => $dateObject->format('Y'), 
            'nr_month'   => $dateObject->format('n'), 
            'month_name' => $dateObject->format('M'),
            'current'    => $i == $prior
    return $monthsList;

var_export(rangeOfMonths(5, 6));
  • Instead of bloating the function declaration syntax with an array type argument which has two elements (with potentially falsey / non-date-oriented values), write these optional values as individually served arguments and write them as the final arguments. This will allow you to call this function without needing to pass the array structure. If your incoming data is already an indexed array with upto two elements, just ... (splat operator) the array when calling the function.

  • Either way, using any falsey default values will benefit your script by allowing you to leverage the brief ?: (elvis operator).

+1 day is to make the date range inclusive of the final month.

*Timezone impact was not included in my review, you may want to investigate fringe cases and set the timezone per your application's requirements.


Your Answer

Szekelygobe is a new contributor. Be nice, and check out our Code of Conduct.

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.