Calculate the number of months between two dates in PHP?
Asked Answered
S

17

52

Without using PHP 5.3's date_diff function (I'm using PHP 5.2.17), is there a simple and accurate way to do this? I am thinking of something like the code below, but I don't know how to account for leap years:

$days = ceil(abs( strtotime('2000-01-25') - strtotime('2010-02-20') ) / 86400);
$months = ???;

I'm trying to work out the number of months old a person is.

Serriform answered 16/11, 2012 at 12:47 Comment(5)
Does that also rule out the DateTime class?Ilailaire
any reason you didnt like the solution on php.net? php.net/manual/en/datetime.diff.php#107434 this oneCrossstaff
What exactly do you mean with number of months? What's the difference of months between 5 October and 3 November of the same year? And what about 31st of October and 1 November?Abell
Count the number of years, multiply it by 12, subtract the beginning month from the end month, then do the same with the days. -edit- or do what deceze suggested ;)Crosstie
Thanks all, I've updated the question now which should answer your queries. @NappingRabbit, its extremely long!Serriform
S
124
$date1 = '2000-01-25';
$date2 = '2010-02-20';

$ts1 = strtotime($date1);
$ts2 = strtotime($date2);

$year1 = date('Y', $ts1);
$year2 = date('Y', $ts2);

$month1 = date('m', $ts1);
$month2 = date('m', $ts2);

$diff = (($year2 - $year1) * 12) + ($month2 - $month1);

You may want to include the days somewhere too, depending on whether you mean whole months or not. Hope you get the idea though.

Sirius answered 16/11, 2012 at 12:52 Comment(2)
Actually this approach works better than others. For example date_diff() produces a diff of "7 months and 30 days" between 2009-09-01 and 2010-05-01 while this solutions produces "8 months" always.Aloud
You can use date('n', $ts1) to get numbers without leading zeros....Wordless
L
33

Here is my solution. It checks both years and months of dates and find difference.

 $date1 = '2000-01-25';
 $date2 = '2010-02-20';
 $d1=new DateTime($date2); 
 $d2=new DateTime($date1);                                  
 $Months = $d2->diff($d1); 
 $howeverManyMonths = (($Months->y) * 12) + ($Months->m);
Lawmaker answered 9/4, 2019 at 12:48 Comment(2)
the cleanest solution in my opinion BUT set up the time zone parameter $d1=new DateTime($date2, new DateTimeZone('Europe/Paris') )Paeon
the perfect solution, way cleannerQualm
K
27

This is a simple method I wrote in my class to count the number of months involved into two given dates :

public function nb_mois($date1, $date2)
{
    $begin = new DateTime( $date1 );
    $end = new DateTime( $date2 );
    $end = $end->modify( '+1 month' );

    $interval = DateInterval::createFromDateString('1 month');

    $period = new DatePeriod($begin, $interval, $end);
    $counter = 0;
    foreach($period as $dt) {
        $counter++;
    }

    return $counter;
}
Kaunas answered 5/9, 2014 at 11:24 Comment(3)
Instead of looping over the period, you can simply use iterator_count($period)Marxism
I really like this solution, especially with @Marxism 's replacement for the loop.Allocate
I changed this to $end = $end->modify( '+1 day' ); in my code and worked greatRealgar
J
5

This is how I ended up solving it. I know I'm a little late but I hope this saves someone a lot time and lines of code.
I used DateInterval::format to display a human readable countdown clock in years, months, and days. Check https://www.php.net/manual/en/dateinterval.format.php for the format table to see your options on how to modify your return values. Should give you what you're looking for.

$origin = new DateTime('2020-10-01');
$target = new DateTime('2020-12-25');
$interval = $origin->diff($target);
echo $interval->format('%y years, %m month, %d days until Christmas.');

Outputs: 0 years, 2 month, 24 days

Josh answered 1/10, 2020 at 17:57 Comment(0)
B
4

Like this:

$date1 = strtotime('2000-01-25');
$date2 = strtotime('2010-02-20');
$months = 0;

while (($date1 = strtotime('+1 MONTH', $date1)) <= $date2)
    $months++;

echo $months;

If you want to include days to, then use this:

$date1 = strtotime('2000-01-25');
$date2 = strtotime('2010-02-20');

$months = 0;

while (strtotime('+1 MONTH', $date1) < $date2) {
    $months++;
    $date1 = strtotime('+1 MONTH', $date1);
}

echo $months, ' month, ', ($date2 - $date1) / (60*60*24), ' days'; // 120 month, 26 days
Buckeen answered 16/11, 2012 at 12:52 Comment(3)
This would tell you that between 2012-01-01 and 2012-02-01 there are two months. Even if you changed <= to < it would yield two months for the Jan-1 --> Feb-2 timespan, which might not be the best solution.Poet
Using the dates you specified I get 1 returned because it updates $date1 before it checks if it is less and adds a month.Buckeen
All those using this, do take care of the while statement with some limiting value for its iteration. I'm not saying if the code is wrong, it is just bit safe with bit precautions with iterative statements.Bandage
S
3

my function to resolve issue

function diffMonth($from, $to) {

        $fromYear = date("Y", strtotime($from));
        $fromMonth = date("m", strtotime($from));
        $toYear = date("Y", strtotime($to));
        $toMonth = date("m", strtotime($to));
        if ($fromYear == $toYear) {
            return ($toMonth-$fromMonth)+1;
        } else {
            return (12-$fromMonth)+1+$toMonth;
        }

    }
Swage answered 9/10, 2015 at 4:18 Comment(0)
B
2

Here is my solution. It only checks years and months of dates. So, if one date is 31.10.15 and other is 02.11.15 it returns 1 month.

function get_interval_in_month($from, $to) {
    $month_in_year = 12;
    $date_from = getdate(strtotime($from));
    $date_to = getdate(strtotime($to));
    return ($date_to['year'] - $date_from['year']) * $month_in_year -
        ($month_in_year - $date_to['mon']) +
        ($month_in_year - $date_from['mon']);
}
Bisexual answered 29/12, 2015 at 13:36 Comment(0)
A
1

I recently needed to calculate age in months ranging from prenatal to 5 years old (60+ months).

Neither of the answers above worked for me. The first one I tried, which is basically a 1 liner for deceze's answer

$bdate = strtotime('2011-11-04'); 
$edate = strtotime('2011-12-03');
$age = ((date('Y',$edate) - date('Y',$bdate)) * 12) + (date('m',$edate) - date('m',$bdate));
. . .

This fails with the set dates, obviously the answer should be 0 as the month mark (2011-12-04) hasn't been reached yet, how ever the code returns 1.

The second method I tried, using Adam's code

$bdate = strtotime('2011-01-03'); 
$edate = strtotime('2011-02-03');
$age = 0;

while (strtotime('+1 MONTH', $bdate) < $edate) {
    $age++;
    $bdate = strtotime('+1 MONTH', $bdate);
}
. . .

This fails and says 0 months, when it should be 1.

What did work for me, is a little expansion of this code. What I used is the following:

$bdate = strtotime('2011-11-04');
$edate = strtotime('2012-01-04');
$age = 0;

if($edate < $bdate) {
    //prenatal
    $age = -1;
} else {
    //born, count months.
    while($bdate < $edate) {
        $age++;
        $bdate = strtotime('+1 MONTH', $bdate);
        if ($bdate > $edate) {
            $age--;
        }
    }
}
Alber answered 31/10, 2013 at 18:2 Comment(0)
H
1

How about this:

$d1 = new DateTime("2009-09-01");
$d2 = new DateTime("2010-09-01");
$months = 0;

$d1->add(new \DateInterval('P1M'));
while ($d1 <= $d2){
    $months ++;
    $d1->add(new \DateInterval('P1M'));
}

print_r($months);
Herron answered 24/1, 2015 at 23:4 Comment(0)
L
1

My solution was mix of a few answers. I did not want to do a loop especially when I have all the data in the $interval->diff, I just did the math, and the number of months could be negative if in my case so this was my approach.

    /**
     * Function will give you the difference months between two dates
     *
     * @param string $start_date
     * @param string $end_date
     * @return int|null
     */
    public function get_months_between_dates(string $start_date, string $end_date): ?int
    {
        $startDate = $start_date instanceof Datetime ? $start_date : new DateTime($start_date);
        $endDate = $end_date instanceof Datetime ? $end_date : new DateTime($end_date);
        $interval = $startDate->diff($endDate);
        $months = ($interval->y * 12) + $interval->m;
        
       return $startDate > $endDate ? -$months : $months;
        
    }
Latinism answered 22/1, 2021 at 17:4 Comment(0)
H
0

Follow up on the answer of @deceze (I've upvoted on his answer). Month will still count as a whole even if the day of the first date didn't reached the day of the second date.

Here's my simple solution on including the day:

$ts1=strtotime($date1);
$ts2=strtotime($date2);

$year1 = date('Y', $ts1);
$year2 = date('Y', $ts2);

$month1 = date('m', $ts1);
$month2 = date('m', $ts2);

$day1 = date('d', $ts1); /* I'VE ADDED THE DAY VARIABLE OF DATE1 AND DATE2 */
$day2 = date('d', $ts2);

$diff = (($year2 - $year1) * 12) + ($month2 - $month1);

/* IF THE DAY2 IS LESS THAN DAY1, IT WILL LESSEN THE $diff VALUE BY ONE */

if($day2<$day1){ $diff=$diff-1; }

The logic is, if the day of the second date is less than the day of the first date, it will reduce the value of $diff variable by one.

Heavensent answered 20/11, 2014 at 6:14 Comment(0)
L
0
$date1 = '2000-01-25';
$date2 = '2010-02-20';

$ts1 = strtotime($date1);
$ts2 = strtotime($date2);

$year1 = date('Y', $ts1);
$year2 = date('Y', $ts2);

$month1 = date('m', $ts1);
$month2 = date('m', $ts2);

$diff = (($year2 - $year1) * 12) + ($month2 - $month1);

If Month switched from Jan to Feb above code will return you the $diff = 1 But if you want to consider next month only after 30 days then add below lines of code along with above.

$day1 = date('d', $ts1);
$day2 = date('d', $ts2);

if($day2 < $day1){ $diff = $diff - 1; }
Luing answered 27/2, 2019 at 4:48 Comment(0)
C
0

To calculate the number of CALENDAR MONTHS (as also asked here) between two dates, I usually end up doing something like this. I convert the two dates to strings like "2020-05" and "1994-05", then get their respective result from the below function, then run a subtraction of those results.

/**
 * Will return number of months. For 2020 April, that will be the result of (2020*12+4) = 24244
 * 2020-12 = 24240 + 12 = 24252
 * 2021-01 = 24252 + 01 = 24253
 * @param string $year_month Should be "year-month", like "2020-04" etc.
 */
static private function calculate_month_total($year_month)
{
    $parts = explode('-', $year_month);
    $year = (int)$parts[0];
    $month = (int)$parts[1];
    return $year * 12 + $month;
}
Chuppah answered 3/7, 2020 at 15:46 Comment(0)
C
0
function date_duration($date){
    $date1 = new DateTime($date);
    $date2 = new DateTime();
    $interval = $date1->diff($date2);
    if($interval->y > 0 and $interval->y < 2){
        return $interval->y.' year ago';
    }else if($interval->y > 1){
        return $interval->y.' years ago';
    }else if($interval->m > 0 and $interval->m < 2){
        return $interval->m.' month ago';
    }else if($interval->m > 1){
        return $interval->y.' months ago';
    }else if($interval->d > 1){
        return $interval->d.' days ago';
    }else{
        if($interval->h > 0 and $interval->h < 2){
            return $interval->h.' hour ago';
        }else if($interval->h > 1){
            return $interval->h.' hours ago';
        }else{
            if($interval->i > 0 and $interval->i < 2){
                return $interval->i.' minute ago';
            }else if($interval->i > 1){
                return $interval->i.' minutes ago';
            }else{
                return 'Just now';
            }
        }
    }
}

Returns 11 Months ago, 2 years ago, 5 minutes ago style of date differentiation

Example:

echo date_duration('2021-02-28 14:59:00.00');

Will return '1 Month ago' depending on your current month

Capel answered 31/3, 2021 at 14:9 Comment(0)
C
0

I just want to share the function I wrote. You can modify it to get both months and years by entering the related datetime format to define the modifier aka modify("+1 something").

/**
 * @param DateTimeInterface $start a anything using the DateTime interface
 * @param DateTimeInterface|null $end to calculate the difference to
 * @param string $modifier for example: day or month or year.
 * @return int the count of periods.
 */
public static function elapsedPeriods(
                       DateTimeInterface $start, 
                       DateTimeInterface $end = null, 
                       string            $modifier = '1 month'
): int
{

    // just an addition, in case you just want the periods up untill now
    if ($end === null ) {
        $end = new DateTime();
    }

    // we clone the start, because we dont want to change the actual start
    // (objects are passed by ref by default, so if you forget this you might 
    // mess up your data)
    $cloned_start = clone $start;

    // we create a period counter, starting at zero, because we want to count 
    // periods, assuming the first try, makes one period. 
    // (week, month, year, et cetera) 
    $period_count = 0;
    
    // so while our $cloned_start is smaller to the $end (or now).
    // we will increment the counter
    while ($cloned_start < $end) {
        // first off we increment the count, for the first iteration could end 
        // the cycle
        $period_count++;
        // now we modify the cloned start
        $cloned_start->modify(sprintf("+1 %s", $modifier));
    }

    return $period_count; // return the count
}

cheers

Chewy answered 29/8, 2021 at 20:44 Comment(0)
P
0

$d1 = date('Y-m-d', strtotime('2015-02-13'));

$d2 = date('Y-m-d');

$d1 = new DateTime($d1); $d2 = new DateTime($d2);

$interval = $d2->diff($d1);

$interval->m = ($interval->m + (12 * $interval->y));

$months = $interval->format('%m');

Philander answered 20/5, 2022 at 7:29 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Foltz
S
0

You can make the calculations manually, for which I created a simple class. You input the start and end dates, and get the amount of months between those two dates.

The formulate converts the days into decimal point values taking into account the amount of days the corresponding month had in that specific year.

Here is the class:

    class TimeHelper
{

    private $years = 0;

    private $months = 0;

    private $days = 0;

    public function getMonthsDiff($start, $end) : float
    {
        $this->resetDifValues();
        $start = new \DateTime($start);
        $end = new \DateTime($end);

        $this->getYears($end, $start);
        $this->getMonths($end, $start);
        $this->getDays($end, $start);

        $totalMonths = ($this->years * 12) + $this->months;
        $totalMonths = floatval($totalMonths) + (floatval($this->days) / floatval($this->getDaysInStartMonth($start)));
        return $totalMonths;
    }

    /**
     * @return void
     */
    private function resetDifValues(): void
    {
        $this->years = 0;
        $this->months = 0;
        $this->days = 0;
    }

    /**
     * @param \DateTime $end
     * @param \DateTime $start
     * @return void
     */
    private function getYears(\DateTime $end, \DateTime $start): void
    {
        $this->years = intval($end->format('Y')) - intval($start->format('Y'));
        if ($this->years < 0) {
            throw new \InvalidArgumentException('The end date must be greater than the start date');
        }
    }

    /**
     * @return void
     */
    private function convertYearToMonths(): void
    {
        if ($this->years <= 0) {
            throw new \InvalidArgumentException('The end date must be greater than the start date');
        }
        $this->years--;
        $this->months += 12;
    }

    /**
     * @param \DateTime $end
     * @param \DateTime $start
     * @return void
     */
    private function getMonths(\DateTime $end, \DateTime $start): void
    {
        $this->months = intval($end->format('m')) - intval($start->format('m'));
        if ($this->months < 0) {
            $this->convertYearToMonths();
        }
    }

    /**
     * @param \DateTime $end
     * @return void
     */
    private function convertMonthInDays(\DateTime $end, \DateTime $start): void
    {
        if ($this->months <= 0) {
            $this->convertYearToMonths();
        }
        $this->months--;
        $daysInMonthBeforeEndDate = $this->getDaysInStartMonth($start);
        $this->days += $daysInMonthBeforeEndDate;
    }

    /**
     * @param \DateTime $end
     * @param \DateTime $start
     * @return void
     */
    private function getDays(\DateTime $end, \DateTime $start): void
    {
        $this->days = intval($end->format('d')) - intval($start->format('d'));
        if ($this->days < 0) {
            $this->convertMonthInDays($end, $start);
        }
    }


    /**
     * @param \DateTime $start
     * @return int
     */
    private function getDaysInStartMonth(\DateTime $start): int
    {
        $monthBeforeEndDate = intval($start->format('m'));
        return cal_days_in_month(CAL_GREGORIAN, $monthBeforeEndDate, $start->format('Y'));
    }

}

To get the result you just have to instantiate the class and call the method getMonthsDiff.

$timeHelper = new TimeHelper();
$monthsDiff = $timeHelper->getMonthsDiff($project->dt_start, $project->dt_end);

This will give you the amount of months in float format like 1.0 or 10.935...

Sydneysydnor answered 26/1 at 18:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.