Reputation: 6702
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.
Upvotes: 54
Views: 149165
Reputation: 104
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...
Upvotes: 0
Reputation: 19
$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');
Upvotes: 0
Reputation: 87
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
Upvotes: 0
Reputation: 1
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
Upvotes: 0
Reputation: 126
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;
}
Upvotes: 1
Reputation: 4010
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']);
}
Upvotes: 2
Reputation: 202
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
Upvotes: 5
Reputation: 16298
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;
}
Upvotes: 0
Reputation: 522
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);
Upvotes: 34
Reputation: 154
$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; }
Upvotes: 0
Reputation: 3772
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--;
}
}
}
Upvotes: 1
Reputation: 31
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;
}
}
Upvotes: 3
Reputation: 14747
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);
Upvotes: 1
Reputation: 5991
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.
Upvotes: 0
Reputation: 5921
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;
}
Upvotes: 27
Reputation: 1264
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
Upvotes: 4
Reputation: 522016
$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.
Upvotes: 124