4
\$\begingroup\$

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.
\$\endgroup\$
2
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

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.