getting all months between 2 dates
Asked Answered
S

5

14

I created a function that returns an array containing each month, starting from a supplied carbon date and ending on the current date.

Although the function is doing what it is supposed to do, it looks hideous. Clearly my programming skills arent yet what they're supposed to be. Surely there must be a better way to achieve what i want.

My code looks like this:

    class DateUtilities {
    protected $months = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'];

    public function getMonthListFromDate(Carbon $date)
    {
        $monthArray = array();
        $today = Carbon::today();

        $currentYear = $today->copy()->format('Y');
        $currentMonth = strtolower($today->copy()->format('F'));

        $startYear = $date->copy()->format('Y');
        $startMonth = strtolower($date->copy()->format('F'));

        for($i = $startYear; $i <= $currentYear; $i ++) {
            foreach($this->months as $monthIndex => $month) {
                if (($monthIndex >= array_search($startMonth, $this->months) && $i == $startYear) ||
                    ($monthIndex <= array_search($currentMonth, $this->months) && $i == $currentYear) ||
                    ($i != $startYear && $i != $currentYear)) {
                    $formattedMonthIndex = ($monthIndex + 1);

                    if($formattedMonthIndex < 10) {
                        $monthArray['0' . $formattedMonthIndex . '-' . $i] = $month . ' ' . $i;
                    } else {
                        $monthArray[$formattedMonthIndex . '-' . $i] = $month . ' ' . $i;
                    }
                }
            }
        }

        return $monthArray;
    }
}

and the result is:

array:25 [▼
  "03-2013" => "march 2013"
  "04-2013" => "april 2013"
  "05-2013" => "may 2013"
  "06-2013" => "june 2013"
  "07-2013" => "july 2013"
  "08-2013" => "august 2013"
  "09-2013" => "september 2013"
  "10-2013" => "october 2013"
  "11-2013" => "november 2013"
  "12-2013" => "december 2013"
  "01-2014" => "january 2014"
  "02-2014" => "february 2014"
  "03-2014" => "march 2014"
  "04-2014" => "april 2014"
  "05-2014" => "may 2014"
  "06-2014" => "june 2014"
  "07-2014" => "july 2014"
  "08-2014" => "august 2014"
  "09-2014" => "september 2014"
  "10-2014" => "october 2014"
  "11-2014" => "november 2014"
  "12-2014" => "december 2014"
  "01-2015" => "january 2015"
  "02-2015" => "february 2015"
  "03-2015" => "march 2015"
]

Can anyone help me improve this code?

EDIT:

After the great tips i ended up with the following:

class DateUtilities {

public function getMonthListFromDate(Carbon $start)
{
    $start = $start->startOfMonth();
    $end   = Carbon::today()->startOfMonth();

    do
    {
        $months[$start->format('m-Y')] = $start->format('F Y');
    } while ($start->addMonth() <= $end);

    return $months;
}

}

Thank you for the help guys!!

Sabulous answered 17/3, 2015 at 18:21 Comment(2)
what does the input look like?Vicegerent
Its a Carbon date itemSabulous
L
15

With DateTime this could be easily achieved.

  1. Create a datetime object for each start and end date
  2. Setup an interval of 1 month
  3. Get a set of date beetween the start date and the end date with 1 month interval

Example :

public function getMonthListFromDate(Carbon $date)
{
    $start    = new DateTime(); // Today date
    $end      = new DateTime($date->toDateTimeString()); // Create a datetime object from your Carbon object
    $interval = DateInterval::createFromDateString('1 month'); // 1 month interval
    $period   = new DatePeriod($start, $interval, $end); // Get a set of date beetween the 2 period

    $months = array();

    foreach ($period as $dt) {
        $months[] = $dt->format("F Y");
    }

    return $months;
}

See it in action : http://3v4l.org/smS3N

Ladyfinger answered 17/3, 2015 at 18:32 Comment(1)
Your 3v4l demo is not identical to your posted snippet. I find the use of ->modify('first day of this month') to be VERY necessary for stability, but you do not mention this adjustment in your answer. Pretty darn related: https://mcmap.net/q/274234/-how-to-list-all-months-between-two-datesVagrancy
C
22

Late to this party, but worth adding that Carbon already has this covered. Using CarbonPeriod would reduce the whole class to ...

use Carbon\Carbon;
use Carbon\CarbonPeriod;

class DateUtilities
{
    public function getMonthListFromDate(Carbon $start)
    {
        $start->setDay(1);
        foreach (CarbonPeriod::create($start, '1 month', Carbon::today()) as $month) {
            $months[$month->format('m-Y')] = $month->format('F Y');
        }
        return $months;
    }
}
Character answered 21/5, 2019 at 8:59 Comment(2)
I like this way of doing things. Ran into an issue with it today though; February was not returned as a result since it's the 29th, and since last month was February (which doesn't have a 29th). I was able to resolve that by adding $start->setDay(1); as the first line within the function.Mohock
Good point. Answer edited to incorporate this.Character
L
15

With DateTime this could be easily achieved.

  1. Create a datetime object for each start and end date
  2. Setup an interval of 1 month
  3. Get a set of date beetween the start date and the end date with 1 month interval

Example :

public function getMonthListFromDate(Carbon $date)
{
    $start    = new DateTime(); // Today date
    $end      = new DateTime($date->toDateTimeString()); // Create a datetime object from your Carbon object
    $interval = DateInterval::createFromDateString('1 month'); // 1 month interval
    $period   = new DatePeriod($start, $interval, $end); // Get a set of date beetween the 2 period

    $months = array();

    foreach ($period as $dt) {
        $months[] = $dt->format("F Y");
    }

    return $months;
}

See it in action : http://3v4l.org/smS3N

Ladyfinger answered 17/3, 2015 at 18:32 Comment(1)
Your 3v4l demo is not identical to your posted snippet. I find the use of ->modify('first day of this month') to be VERY necessary for stability, but you do not mention this adjustment in your answer. Pretty darn related: https://mcmap.net/q/274234/-how-to-list-all-months-between-two-datesVagrancy
A
10

Use CarbonPeriod class todo same

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

foreach ($period as $dt) {
     echo $dt->format("Y-m") . "<br>\n";
}
Agglutinin answered 16/3, 2020 at 10:20 Comment(1)
How to get distinct month?Waterfront
A
2

Here's an example that's much shorter and simpler. You can adapt it to the conventions of your Carbon class easily:

$start = strtotime('last month', strtotime('2013-03'));
$now = time();

while(($start = strtotime('next month', $start)) <= $now) {
    $result[date('m-Y', $start)] = strtolower(date('F Y', $start));
}
Armillas answered 17/3, 2015 at 18:37 Comment(0)
D
2
while ($start->lte($end)) {
            $months[$start->format('m-Y')] = $start->format('F Y');
            $start->addMonth();
        }
        return $months;
Duodiode answered 20/8, 2019 at 11:35 Comment(1)
This answer is missing its educational explanation.Vagrancy

© 2022 - 2024 — McMap. All rights reserved.