Элегантный способ получить количество месяцев между двумя датами?

Предположим, у меня есть две даты в переменных, например

$date1 = "2009-09-01"; $date2 = "2010-05-01"; 

Мне нужно получить количество месяцев между $date2 и $date1 ( $date2 >= $date1 ). Т.е. мне нужно получить 8 .

Есть ли способ получить его, используя функцию даты , или я должен взорвать свои строки и выполнить некоторые вычисления?

Большое спасибо

Для PHP> = 5.3

 $d1 = new DateTime("2009-09-01"); $d2 = new DateTime("2010-05-01"); var_dump($d1->diff($d2)->m); // int(4) var_dump($d1->diff($d2)->m + ($d1->diff($d2)->y*12)); // int(8) 

DateTime :: diff возвращает объект DateInterval

Если вы не работаете с PHP 5.3 или выше, я думаю, вам придется использовать временные метки unix:

 $d1 = "2009-09-01"; $d2 = "2010-05-01"; echo (int)abs((strtotime($d1) - strtotime($d2))/(60*60*24*30)); // 8 

Но это не очень точно (не всегда 30 дней в месяц).

Последнее: если эти даты поступают из вашей базы данных, то используйте эту СУБД для выполнения этой задачи, а не PHP.

Изменить: этот код должен быть более точным, если вы не можете использовать DateTime :: diff или вашу СУБД:

 $d1 = strtotime("2009-09-01"); $d2 = strtotime("2010-05-01"); $min_date = min($d1, $d2); $max_date = max($d1, $d2); $i = 0; while (($min_date = strtotime("+1 MONTH", $min_date)) <= $max_date) { $i++; } echo $i; // 8 

Или, если вы хотите процедурный стиль:

 $date1 = new DateTime("2009-09-01"); $date2 = new DateTime("2010-05-01"); $interval = date_diff($date1, $date2); echo $interval->m + ($interval->y * 12) . ' months'; 

UPDATE : добавлен бит кода для учета лет.

Или простой расчет даст:

 $numberOfMonths = abs((date('Y', $endDate) - date('Y', $startDate))*12 + (date('m', $endDate) - date('m', $startDate)))+1; 

Точная и работает во всех случаях.

После тестирования тонны решений, поставив все в единичном тесте, вот что я получаю:

 /** * 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); } 

Например, он вернется (тестовые примеры из модульного теста):

  • 01.11.2013 – 30.11.2013 – 1 месяц
  • 01.01.2013 – 31.12.2013 – 12 месяцев
  • 31.01.2011 – 28.02.2011 – 1 месяц
  • 01.09.2009 – 01.05.2010 – 8 месяцев
  • 01.01.2013 – 31.03.2013 – 3 месяца
  • 15.02.2013 – 15.04.2013 – 2 месяца
  • 01.02.1985 – 31.12.2013 – 347 месяцев

Примечание. Из-за округления, которое он делает с днями, даже половина месяца будет округлена, что может привести к возникновению проблемы, если вы используете его в некоторых случаях. Поэтому НЕ ИСПОЛЬЗУЙТЕ его для таких случаев, это вызовет проблемы.

Например:

  • 02.11.2013 – 31.12.2013 вернет 2, а не 1 (как и ожидалось).

Я использую это:

 $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); 

Это еще один способ получить количество месяцев между двумя датами:

 // Set dates $dateIni = '2014-07-01'; $dateFin = '2016-07-01'; // Get year and month of initial date (From) $yearIni = date("Y", strtotime($dateIni)); $monthIni = date("m", strtotime($dateIni)); // Get year an month of finish date (To) $yearFin = date("Y", strtotime($dateFin)); $monthFin = date("m", strtotime($dateFin)); // Checking if both dates are some year if ($yearIni == $yearFin) { $numberOfMonths = ($monthFin-$monthIni) + 1; } else { $numberOfMonths = ((($yearFin - $yearIni) * 12) - $monthIni) + 1 + $monthFin; } 

Используя DateTime, это даст вам более точное решение за любое количество месяцев:

 $d1 = new DateTime("2011-05-14"); $d2 = new DateTime(); $d3 = $d1->diff($d2); $d4 = ($d3->y*12)+$d3->m; echo $d4; 

Вам все равно нужно будет обрабатывать оставшиеся дни $d3->d если ваша проблема в реальном мире не такая простая и вырезанная и сухая, как исходный вопрос, где обе даты относятся к первому из месяца.

Это простой метод, который я написал в своем классе для подсчета количества месяцев, участвующих в двух заданных датах:

 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; } 

Я использовал это и работает во всех условиях

 $fiscal_year = mysql_fetch_row(mysql_query("SELECT begin,end,closed FROM fiscal_year WHERE id = '2'")); $date1 = $fiscal_year['begin']; $date2 = $fiscal_year['end']; $ts1 = strtotime($date1); $ts2 = strtotime($date2); $te=date('m',$ts2-$ts1); echo $te;