JavaScript Precise Age from a  Birthdate

Calculating an age in years, months and days from a user provided birthdate (MM/DD/YYYY)

Calculating a precise age in years from a birthdate with JavaScript is not as simple as it seems. To be truly precise you need to factor in time zone, daylight savings time variations and leap years. If you’ve been digging around the web then you’ve seen many approaches that will get you part of the way there, but it’s difficult to find a precise way to determine the difference between two exact dates with JavaScript. Many of the commons approaches fall short of what I’d consider to be ideal.

Leap Years & Incorrect Age From Date

If you’re working on a web application and you need to determine a persons precise age based on a birthdate input field you’ll need to factor for leap years. If the age is close but wrong by a few days then this likely the cause. Not all years are 365 days, and in fact 365 isn’t even precise. Ask NASA and you’ll learn that a year is actually 365 days, 5 hours, 48 minutes, 46 seconds. Leap years occur every 4 years and they’re 23 hours, 15 minutes, 4 seconds longer than a typical calendar year.

Leap years are the common cause of JavaScript age from birthdate calculations. When you calculate the age based on a birthdate and consider a year as 365 days it will be incorrect. The result is an age that off by a few days, and the older a person is the further away the different becomes. Most server-side languages have ways to handle this with date objects. For example, PHP’s DateTime::diff method handles leap year calculations automatically. JavaScript on the other hand does not, and it’s a common pitfall I’ve seen in many web applications.

Precise Age with JavaScript

The following vanilla JavaScript function will calculate the exact age in years, months and days for a birthdate.

JS exactAge() Function

/**
 * Exact Age
 *
 * Calculates the exact age for a given birthdate with JavaScript
 */
function exactAge(birthdate) {
  let startDate = new Date(new Date(birthdate).toISOString().substr(0, 10));
  const endingDate = new Date().toISOString().substr(0, 10); // YYYY-MM-DD
  let endDate = new Date(endingDate);
  if (startDate > endDate) {
    const swap = startDate;
    startDate = endDate;
    endDate = swap;
  }
  const startYear = startDate.getFullYear();

  // Leap years
  const february = (startYear % 4 === 0 && startYear % 100 !== 0) || startYear % 400 === 0 ? 29 : 28;
  const daysInMonth = [31, february, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

  let yearDiff = endDate.getFullYear() - startYear;
  let monthDiff = endDate.getMonth() - startDate.getMonth();
  let dayDiff = endDate.getDate() - startDate.getDate();

  if (monthDiff < 0) {
    yearDiff--;
    monthDiff += 12;
  }

  if (dayDiff < 0) {
    if (monthDiff > 0) {
      monthDiff--;
    } else {
      yearDiff--;
      monthDiff = 11;
    }
    dayDiff += daysInMonth[startDate.getMonth()];
  }

  return {
    years: yearDiff,
    months: monthDiff,
    days: dayDiff,
  };
}

Output

Output is returned as an object with the following simple structure. If you used the function today (01/05/2023) with exactAge("04/01/1986") the output provided back would look like this:

{
  years: 36,
  months: 9,
  days: 4
}

Which is 100% accurate down to the day in my machines time zone. This approach is what I myself use in a web application I’ve built for a life insurance company, where insurance age is determined based on certain specific rounding rules. It’s reliable, precise, and doesn’t depend on any third-party libraries like day.js or moment.js. It passes my smart and simple test: it works today, and it will very likely work for in 10+ years or more.

Long Road to Simplicity

This solution probably seems overly simple, that’s because it is. The path to get to it has been anything but simple. Over a span of years I’ve tried many other available solutions. Some using libraries, and others home grown with different approach. This function provides the most accurate age for a birthdate with minimal overhead and no third-party dependencies. It’s being used in a codebase that has been painstakingly tested by corporate QA teams using detailed leapwork tests and countless manual tests.

Related Articles

Meet the Author

Kevin Leary, WordPress Consultant

I'm a custom WordPress web developer and analytics consultant in Boston, MA with 17 years of experience building websites and applications. View a portfolio of my work or request an estimate for your next project.