Calculate Age Between Two Dates

Recently, in one of my projects I needed to calculate the age of a person and based on that the program would decide the next step. At first, I thought this is a straightforward process, all I needed to do is to subtract the person’s dob year from present year and voila ๐Ÿ˜Š. Little did I know,  it is more than that. Soon I realized that not only  do I need to take into consideration the months and days but also the leap years. The reason we need to handle the leap years is because in a leap year there are 366 days vs to 365 days for a normal year. For example, if someone is born on Feb 29, his birthday would be on Feb 28 in a normal year (non leap year). This is because in a normal year February has 28 days not 29.
 
So, because I am generally a lazy person and I did not want to reinvent the wheel I decided on googling to find an implementation online. Unfortunatly, soon I understood that none of the implementations I found actually were addressing the leapers (born on Feb 29).
 
After the unsuccessful finds, I decided to implement it myself and add it to my GitHub repository collection for future reference.
 
Here I present my implementation below, but first let me list the steps this code should accomplish.
  • Be an extension to DateTime class.
  • Calculate and return years between two dates.
  • Calculate and return remaining months.
  • Calculate and return remaining days.
  • Handle Feb 29 and leap years.
To calculate the years component, we have to know two things. First, we need to find out if there is an extra day between start (dob) and present date. Second, we need to know whether dob has occurred in the present year. Once we know those two things, we can than calculate the years component by subtracting dob year from present year and an additional year if dob has not yet occur in the present year.
 
Once we implement the two things mentioned above and find the years component, the rest becomes pretty simple. By using the two flags and the years, we can now advance to prior or present year and calculate the remaining months and days. The code is simple and self-explanatory based on the information I provided so I will leave the explanation of each line of code for you to understand.
  1. using System;  
  2.   
  3. namespace age.calculation.example  
  4. {  
  5.     [DebuggerDisplay(  
  6.     nameof(Years) + " = {" + nameof(Years) + "}, " +  
  7.     nameof(Months) + " = {" + nameof(Months) + "}, " +  
  8.     nameof(Days) + " = {" + nameof(Days) + "}")]  
  9.     public class Age  
  10.     {  
  11.         public ushort Years { getset; }  
  12.         public byte Months { getset; }  
  13.         public byte Days { getset; }  
  14.   
  15.         public override string ToString()  
  16.         {  
  17.             return $"{Years}yr., {Months}mos., {Days}d";  
  18.         }  
  19.     }  
  20.   
  21.     public static class DateTimeExtensions  
  22.     {  
  23.         /// <summary>  
  24.         /// Indicates whether this date is in leap year.  
  25.         /// </summary>  
  26.         /// <param name="value">This <see cref="DateTime"/> instance.</param>  
  27.         /// <returns>A boolean value indicating whether this date instance is in leap your.</returns>  
  28.         public static bool IsInLeapYear(this DateTime value)  
  29.         {  
  30.             return DateTime.IsLeapYear(value.Year);  
  31.         }  
  32.   
  33.         /// <summary>  
  34.         /// Calculate the age of a person based on the date of birth.  
  35.         /// </summary>  
  36.         /// <param name="dob">The date of birth of the person.</param>  
  37.         /// <param name="presentDate">The present date. Defaults to <see cref="DateTime.Now"/>, if not specified.</param>  
  38.         /// <remarks>This function supports leaper DOBs (Feb 29).</remarks>  
  39.         /// <returns>An <see cref="Age"/> object containing years, months and days information.</returns>  
  40.         public static Age CalculateAge(  
  41.             this DateTime dob,  
  42.             DateTime? presentDate = null)  
  43.         {  
  44.             presentDate ??= DateTime.Now.ToPacificDate();  
  45.             if (dob > presentDate) throw new ArgumentOutOfRangeException(  
  46.                 nameof(dob),  
  47.                 "DOB must be less or equal from present date.");  
  48.   
  49.             const byte maxMonths = 12;  
  50.             const byte feb28 = 59;  
  51.   
  52.             // indicates whether dob year has extra day compare to present date.  
  53.             // Note, leap years have 366 days vs to 365 for regular years.  
  54.             var extraDay = dob.IsInLeapYear() &&  
  55.                            !presentDate.Value.IsInLeapYear() &&  
  56.                            dob.DayOfYear > feb28 ? 1 : 0;  
  57.   
  58.             // indicates whether dob has been celebrated in the present year.  
  59.             var hasDobOccur = presentDate.Value.DayOfYear >= dob.DayOfYear - extraDay;  
  60.   
  61.             // calculate the years of age  
  62.             var age = new Age  
  63.             {  
  64.                 Years = (ushort)(presentDate.Value.Year - dob.Year - (hasDobOccur ? 0 : 1))  
  65.             };  
  66.   
  67.             // calculate the months of age  
  68.             if (hasDobOccur)  
  69.             {  
  70.                 age.Months = (byte)(presentDate.Value.Month - dob.Month);  
  71.                 if (age.Months > 0 & presentDate.Value.Day < dob.Day)  
  72.                 {  
  73.                     age.Months -= 1;  
  74.                 }  
  75.             }  
  76.             else  
  77.             {  
  78.                 age.Months = (byte)(maxMonths - 1 - Math.Abs(presentDate.Value.Month - dob.Month));  
  79.             }  
  80.   
  81.             // calculate the days of age  
  82.             var currentMonth = dob.Month + age.Months;  
  83.             if (currentMonth > maxMonths)  
  84.             {  
  85.                 currentMonth = Math.Abs(currentMonth - maxMonths);  
  86.             }  
  87.   
  88.             if (currentMonth == presentDate.Value.Month)  
  89.             {  
  90.                 age.Days = (byte)(presentDate.Value.Day - (dob.Day - extraDay));  
  91.             }  
  92.             else  
  93.             {  
  94.                 age.Days = (byte)(  
  95.                     DateTime.DaysInMonth(dob.Year + age.Years, currentMonth) -  
  96.                     (dob.Day - (hasDobOccur ? extraDay : 0)) +  
  97.                     presentDate.Value.Day);  
  98.             }  
  99.   
  100.             return age;  
  101.         }  
  102.     }  
  103. }  
Source - https://github.com/arman-g/CalculateAge
 
Output
'L' - Leap Year, 'N' - Normal Year
-------- General Cases ---------
01/01/2000:L - 01/01/2000:L Age: 0yr., 0mos., 0d
01/01/2000:L - 01/02/2000:L Age: 0yr., 0mos., 1d
01/01/2000:L - 02/01/2000:L Age: 0yr., 1mos., 0d
01/01/2000:L - 12/31/2000:L Age: 0yr., 11mos., 30d
01/01/2000:L - 01/01/2001:N Age: 1yr., 0mos., 0d
04/22/2001:N - 10/27/2019:N Age: 18yr., 6mos., 5d
04/22/2001:N - 01/01/2020:L Age: 18yr., 8mos., 10d
04/22/2001:N - 02/17/2020:L Age: 18yr., 9mos., 26d
04/22/2001:N - 04/22/2020:L Age: 19yr., 0mos., 0d
04/22/2001:N - 07/07/2020:L Age: 19yr., 2mos., 15d
 
-------- Special Cases (Feb 29) ---------
02/29/1960:L - 02/28/2020:L Age: 59yr., 11mos., 30d
02/29/1960:L - 02/29/2020:L Age: 60yr., 0mos., 0d
02/29/1960:L - 03/01/2020:L Age: 60yr., 0mos., 1d
02/29/1960:L - 02/27/2021:N Age: 60yr., 11mos., 29d
02/29/1960:L - 02/28/2021:N Age: 61yr., 0mos., 0d
02/29/1960:L - 03/01/2021:N Age: 61yr., 0mos., 1d


Similar Articles