tags:

views:

93

answers:

2

I have asked this question before and accepted the answer but now I found that the php version on our server is 5.2 and DateTime::diff() is not working on that.

I want to calculate person's age in months plus days using date of birth and a given date.

Date Format Input: Y-m-d (example: 1986-08-23)

Output:

5 months and 20 days old.
150 months and 4 days old.
285 months and 30 days old.

Thanks

A: 

Use your favorite date parsing function (strtotime, strptime, mktime) to get an UNIX timestamp out of the date, then the interval ($now - $then)... and then work out how many seconds there are in a month and use that to calculate how many months the person has lived (division and remainder are your friends).

This'll give you a mathematically precise value that should be close enough to real life too.

AKX
+3  A: 

Here's a solution that'll accurately determine the number of months and number of days, including leap years. It assumes that things like July 21 to August 21 is 1 month 0 days, not 1 month 1 day, and that March 21 to April 20 is 0 months 30 days, not 1 month 0 days. The latter in both cases is what occurs when you just do a straight divide by 30 to calculate months.

I'm sure there's a better way to optimize the function, but it gets the job done:

function diff_date($start_date, $end_date) {
  list($start_year, $start_month, $start_day) = explode('-', $start_date);
  list($end_year, $end_month, $end_day) = explode('-', $end_date);

  $month_diff = $end_month - $start_month;
  $day_diff   = $end_day - $start_day;

  $months = $month_diff + ($end_year - $start_year) * 12;
  $days = 0;

  if ($day_diff > 0) {
    $days = $day_diff;
  }
  else if ($day_diff < 0) {
    $days = $end_day;
    $months--;

    if ($month_diff > 0) {
      $days += 30 - $start_day;

      if (in_array($start_month, array(1, 3, 5, 7, 8, 10, 12))) {
        $days++;
      }
      else if ($start_month == 2) {
        if (($start_year % 4 == 0 && $start_year % 100 != 0) || $start_year % 400 == 0) {
          $days--;
        }
        else {
          $days -= 2;
        }
      }

      if (in_array($end_month - 1, array(1, 3, 5, 7, 8, 10, 12))) {
        $days++;
      }
      else if ($end_month - 1 == 2) {
        if (($end_year % 4 == 0 && $end_year % 100 != 0) || $end_year % 400 == 0) {
          $days--;
        }
        else {
          $days -= 2;
        }
      }
    }
  }

  return array($months, $days);
}

list($months, $days) = diff_date('1984-05-26', '2010-04-29');

print $months . ' months and ' . $days . ' days old.';

Output:

314 months and 3 days old.


Edit: I tried to get rid of redundancy in the code, and forgot to rename a variable. This function will now work correctly for diff_date('2010-06-29', '2011-07-01').


Edit: Now correctly works for end months occurring after months with 31 or 28/29 days.

Mark Trapp
I think there is something wrong. date_diff("2010-06-29","2011-07-01"); should output "12 Months 2 Days" but it gives "13 Months 2 Days"
NAVEED
Fixed. I tried to clean up the code and forgot a stray variable.
Mark Trapp
Thanks for your hard work.
NAVEED
same output for date_diff("2010-06-29","2011-09-01"); and date_diff("2010-06-29","2011-08-31"); **Output:** 14 Months 2 Days
NAVEED
Fixed. One quirk I noticed that I'm not sure how to handle: should date_diff("1984-01-29", "1984-03-01") produce 1 month, 1 day even though there is no February 29th? Should date_diff("2010-03-31", "2010-05-01") produce 1 month, 1 day even though there is no April 31st?
Mark Trapp
If someone born on **15 Jan, 2009** then that person will be exactly 1 year old on **15 Jan, 2010**. Therefore I thought that this person will be exactly one month old on **15 Feb, 2009** and 2 months old on **15 March, 2009**. Even there are different days in different months but I think that this rule is followed..
NAVEED
My function does that, and returns 1 month, 1 days in cases like January 31st to March 1st.
Mark Trapp
Great. I accepted your answer. In future, if I will find any other bug, I will report here. Thanks for your help.
NAVEED