12

Is there any way to find the month difference in PHP? I have the input of from-date 2003-10-17 and to-date 2004-03-24. I need to find how many months there are within these two days. Say if 6 months, I need the output in months only. Thanks for guiding me for day difference.

I find the solution through MySQL but I need it in PHP. Anyone help me, Thanks in advance.

Karthik
  • 3,221
  • 5
  • 28
  • 38

13 Answers13

79

The easiest way without reinventing the wheel. This'll give you the full months difference. I.e. the below two dates are almost 76 months apart, but the result is 75 months.

date_default_timezone_set('Asia/Tokyo');  // you are required to set a timezone

$date1 = new DateTime('2009-08-12');
$date2 = new DateTime('2003-04-14');

$diff = $date1->diff($date2);

echo (($diff->format('%y') * 12) + $diff->format('%m')) . " full months difference";
deceze
  • 510,633
  • 85
  • 743
  • 889
  • great work:just a small question into this how to get number of days using this? – noobie-php Jul 26 '12 at 07:36
  • 2
    @noobie Please [RTFM for `DateInterval::format`](http://php.net/manual/en/dateinterval.format.php) to find all possible formatting options. – deceze Jul 26 '12 at 10:22
  • 1
    Weird results: From: 2013-03-01, Until: 2013-04-01, diff year: 0, diff month: 1, diff days: 3. Months with less then 31 days also give diff in months=0 – thehpi Feb 08 '13 at 12:24
  • @thehpi Interesting. But this is bound to be imprecise whatever you do, since "a month" is an terribly vague unit of time. Should it count as one month as soon as a month boundary is crossed between two dates? Or only if a full month lies between them? What is "a full month"? 28 days? 30? 31? – deceze Feb 08 '13 at 13:41
  • 4
    Use with caution! Difference in months between 01.01.2013 and 31.03.2013 will be 2 months (not 3 as maybe expected!) – Valentin Despa Nov 14 '13 at 12:49
  • 1
    @ValentinDespa Actually the difference between 01.01.2013 and 31.03.2013 is [2 months and 30 days](http://ideone.com/B9qv5O), which is accurate. You would be kind of an optimistic person if you would expect a difference of exactly 3 months :-) – Alexandru Guzinschi Oct 03 '14 at 10:11
  • To get rid of 'Wrong string concatenation operator' in PhpStorm cast string to int: (((int) $diff->format('%y') * 12) + (int) $diff->format('%m')); – Ehsan Jan 06 '16 at 00:55
  • 2018-04-01 and 2018-07-01 Should be 3 months difference but got 2 months. – vee Jul 06 '18 at 07:53
  • Well, it comes down to how you define "one month". It's a wishy-washy definition. Apparently PHP's definition and yours differ. I'd inspect the remaining day difference and round up if necessary/close enough. – deceze Jul 06 '18 at 07:56
  • Would be nice to point out that you can add %R to the format to get negative values depending of which date is greater `$diff->format('%R%y') * 12) + $diff->format('%R%m')` – Juanjo Martinez Jul 30 '20 at 10:14
28

After testing tons of solutions, putting all in a unit test, this is what I come out with:

/**
 * Calculate the difference in months between two dates (v1 / 18.11.2013)
 *
 * @param \DateTime $date1
 * @param \DateTime $date2
 * @return int
 */
public static function diffInMonths(\DateTime $date1, \DateTime $date2)
{
    $diff =  $date1->diff($date2);

    $months = $diff->y * 12 + $diff->m + $diff->d / 30;

    return (int) round($months);
}

For example it will return (test cases from the unit test):

  • 01.11.2013 - 30.11.2013 - 1 month
  • 01.01.2013 - 31.12.2013 - 12 months
  • 31.01.2011 - 28.02.2011 - 1 month
  • 01.09.2009 - 01.05.2010 - 8 months
  • 01.01.2013 - 31.03.2013 - 3 months
  • 15.02.2013 - 15.04.2013 - 2 months
  • 01.02.1985 - 31.12.2013 - 347 months

Notice: Because of the rounding it does with the days, even a half of a month will be rounded, which may lead to issue if you use it with some cases. So DO NOT USE it for such cases, it will cause you issues.

For example:

  • 02.11.2013 - 31.12.2013 will return 2, not 1 (as expected).
Valentin Despa
  • 40,712
  • 18
  • 80
  • 106
8

I just wanted to add this if anyone is looking for a simple solution that counts each touched-upon month in stead of complete months, rounded months or something like that.

// Build example data
$timeStart = strtotime("2003-10-17");
$timeEnd = strtotime("2004-03-24");
// Adding current month + all months in each passed year
$numMonths = 1 + (date("Y",$timeEnd)-date("Y",$timeStart))*12;
// Add/subtract month difference
$numMonths += date("m",$timeEnd)-date("m",$timeStart);

echo $numMonths;
MaX
  • 1,765
  • 13
  • 17
6

Wow, way to overthink the problem... How about this version:

function monthsBetween($startDate, $endDate) {
    $retval = "";

    // Assume YYYY-mm-dd - as is common MYSQL format
    $splitStart = explode('-', $startDate);
    $splitEnd = explode('-', $endDate);

    if (is_array($splitStart) && is_array($splitEnd)) {
        $difYears = $splitEnd[0] - $splitStart[0];
        $difMonths = $splitEnd[1] - $splitStart[1];
        $difDays = $splitEnd[2] - $splitStart[2];

        $retval = ($difDays > 0) ? $difMonths : $difMonths - 1;
        $retval += $difYears * 12;
    }
    return $retval;
}

NB: unlike several of the other solutions, this doesn't depend on the date functions added in PHP 5.3, since many shared hosts aren't there yet.

CLWill
  • 148
  • 1
  • 5
  • I just edited this line ```$retval = ($difDays > 0) ? $difMonths : $difMonths - 1;``` to ```$retval = ($difDays >= 0) ? $difMonths : $difMonths - 1;``` So the difference between 2017-02-01 and 2017-03-01 is just 1 – jona303 Jan 15 '17 at 13:33
  • not correct. bettwen 2022-06-15 2023-06-15 = 12 Months. Your code retunt value 11 – Pavel8289 Jun 24 '22 at 08:06
2

http://www.php.net/manual/en/datetime.diff.php

This returns a DateInterval object which has a format method.

graydot
  • 136
  • 5
2
$datetime1 = date_create('2009-10-11');

$datetime2 = date_create('2013-1-13');

$interval = date_diff($datetime1, $datetime2);

echo $interval->format('%a day %m month %y year');
Manish Jesani
  • 1,339
  • 5
  • 20
  • 36
Siddharth Shukla
  • 1,063
  • 15
  • 14
0
// get year and month difference

$a1 = '20170401';

$a2 = '20160101'

$yearDiff = (substr($a1, 0, 4) - substr($a2, 0, 4));

$monthDiff = (substr($a1, 4, 2) - substr($a2, 4, 2));

$fullMonthDiff = ($yearDiff * 12) + $monthDiff;

// fullMonthDiff = 16
Mikhail_Sam
  • 10,602
  • 11
  • 66
  • 102
0

This is my enhanced version of @deceze answer:

 /**
 * @param string $startDate
 * Current date is considered if empty string is passed
 * @param string $endDate
 * Current date is considered if empty string is passed
 * @param bool $unsigned
 * If $unsigned is true, difference is always positive, otherwise the difference might be negative
 * @return int
 */
public static function diffInFullMonths($startDate, $endDate, $unsigned = false)
{
    $diff = (new DateTime($startDate))->diff(new DateTime($endDate));
    $reverse = $unsigned === true ? '' : '%r';
    return ((int) $diff->format("{$reverse}%y") * 12) + ((int) $diff->format("{$reverse}%m"));
}
Ehsan
  • 1,022
  • 11
  • 20
0

The best way.

function getIntervals(DateTime $from, DateTime $to)
{
    $intervals = [];
    $startDate = $from->modify('first day of this month');
    $endDate = $to->modify('last day of this month');
    while($startDate < $endDate){
        $firstDay = $startDate->format('Y-m-d H:i:s');
        $startDate->modify('last day of this month')->modify('+1 day');
        $intervals[] = [
            'firstDay' => $firstDay,
            'lastDay' => $startDate->modify('-1 second')->format('Y-m-d H:i:s'),
        ];
        $startDate->modify('+1 second');
    }
    return $intervals;
}
$dateTimeFirst = new \DateTime('2013-01-01');
$dateTimeSecond = new \DateTime('2013-03-31');
$interval = getIntervals($dateTimeFirst, $dateTimeSecond);
print_r($interval);

Result:

Array
(
    [0] => Array
        (
            [firstDay] => 2013-01-01 00:00:00
            [lastDay] => 2013-01-31 23:59:59
        )

    [1] => Array
        (
            [firstDay] => 2013-02-01 00:00:00
            [lastDay] => 2013-02-28 23:59:59
        )

    [2] => Array
        (
            [firstDay] => 2013-03-01 00:00:00
            [lastDay] => 2013-03-31 23:59:59
        )

)
Lebnik
  • 628
  • 8
  • 11
0

In my case I needed to count full months and day leftovers as month as well to build a line chart labels.

/**
 * Calculate the difference in months between two dates
 *
 * @param \DateTime $from
 * @param \DateTime $to
 * @return int
 */
public static function diffInMonths(\DateTime $from, \DateTime $to)
{
    // Count months from year and month diff
    $diff = $to->diff($from)->format('%y') * 12 + $to->diff($from)->format('%m');

    // If there is some day leftover, count it as the full month
    if ($to->diff($from)->format('%d') > 0) $diff++;

    // The month count isn't still right in some cases. This covers it.
    if ($from->format('d') >= $to->format('d')) $diff++;
}
John Linhart
  • 1,746
  • 19
  • 23
-1
<?php
  # end date is 2008 Oct. 11 00:00:00
  $_endDate = mktime(0,0,0,11,10,2008);
  # begin date is 2007 May 31 13:26:26
  $_beginDate = mktime(13,26,26,05,31,2007);

  $timestamp_diff= $_endDate-$_beginDate +1 ;
  # how many days between those two date
  $days_diff = $timestamp_diff/2635200;

?>

Reference: http://au.php.net/manual/en/function.mktime.php#86916

Russell Dias
  • 70,980
  • 5
  • 54
  • 71
-1
function monthsDif($start, $end)
{
    // Assume YYYY-mm-dd - as is common MYSQL format
    $splitStart = explode('-', $start);
    $splitEnd = explode('-', $end);

    if (is_array($splitStart) && is_array($splitEnd)) {
        $startYear = $splitStart[0];
        $startMonth = $splitStart[1];
        $endYear = $splitEnd[0];
        $endMonth = $splitEnd[1];

        $difYears = $endYear - $startYear;
        $difMonth = $endMonth - $startMonth;

        if (0 == $difYears && 0 == $difMonth) { // month and year are same
            return 0;
        }
        else if (0 == $difYears && $difMonth > 0) { // same year, dif months
            return $difMonth;
        }
        else if (1 == $difYears) {
            $startToEnd = 13 - $startMonth; // months remaining in start year(13 to include final month
            return ($startToEnd + $endMonth); // above + end month date
        }
        else if ($difYears > 1) {
            $startToEnd = 13 - $startMonth; // months remaining in start year 
            $yearsRemaing = $difYears - 2;  // minus the years of the start and the end year
            $remainingMonths = 12 * $yearsRemaing; // tally up remaining months
            $totalMonths = $startToEnd + $remainingMonths + $endMonth; // Monthsleft + full years in between + months of last year
            return $totalMonths;
        }
    }
    else {
        return false;
    }
}
  • 2
    Ammended - changed $startToEnd = 12 - $startMonth; to $startToEnd = 13 - $startMonth; As before it wasn't counting the duration of december – Adam Fekete Jan 12 '12 at 10:26
-8

Here's a quick one:

$date1 = mktime(0,0,0,10,0,2003); // m d y, use 0 for day
$date2 = mktime(0,0,0,3,0,2004); // m d y, use 0 for day

echo round(($date2-$date1) / 60 / 60 / 24 / 30);
Tower
  • 98,741
  • 129
  • 357
  • 507
  • 26
    This will become more and more inaccurate the farther apart the two dates are and may eventually round the wrong way... – deceze Apr 21 '10 at 09:22
  • 7
    What will happened if month has `31 days` in case `January, March, May, July, August, October, December` or `28 days` in case `February`, coz you divided by 30. – Frank Dec 27 '12 at 06:53