0

I'm trying to convert a personnel's date of birth to their actual age within a ViewModel inside of another viewmodel I plan on calling in the front end. I've managed to create public DateTime? PersonnelDOB { get; set; } and it's bringing back their Date of Birth I.E 6/12/1972 I need to convert this to their actual age so instead of 6/12/1972, it'll be "48 years old"

the issue to this current problem is that 'dob' is a DateTime and 'today.year' is an int. I can't subtract a DateTime from an int. I need to also make sure I account for leapyears and for it to actually accurately output their age. I also will want to check that dob isn't null. I dont have to do this within a viewmodel I created, it was just an avenue I was exploring. Thank you all for your help!

public DateTime? PersonnelDOB { get; set; }
    
    public PersonnelDOBViewModel()
    {
        var dob = PersonnelDOB;

        // Save today's date.
        var today = DateTime.Today;

        // Calculate the age.
        var age = today.Year - dob;

        // Go back to the year the person was born in case of a leap year
        if (dob > today.AddYears(-age)) age--;
        return age;
    }

** A coworker helped me out and for those of you interested in the right answer - here it is

public DateTime? PersonnelDOB { get; set; }

    public int? PersonnelAge
    {
        get
        {
            if (!PersonnelDOB.HasValue)
                return null;
            
            DateTime today = DateTime.Today;
            
            int years = today.Year - PersonnelDOB.Value.Year;

            years -= (today.Month < PersonnelDOB.Value.Month || (today.Month == PersonnelDOB.Value.Month && today.Day < PersonnelDOB.Value.Day)) ? 1 : 0;
            return years;
        }
    }
Ender91
  • 61
  • 1
  • 8
  • Why so complicated? Why not just `DateTime.Now - PersonnelDOB.Value`? – Legacy Code Jul 20 '20 at 17:50
  • 1
    `var age = today.Year - dob.Year`? Also you don't need to account for leap years, you have to account for if today is before or after the person's birthday. – juharr Jul 20 '20 at 17:50
  • @juharr that will initially be off by a year for almost all people, and less so as the current year progresses. – CodeCaster Jul 20 '20 at 17:52
  • 1
    @CodeCaster I'm just correcting the use of the code to get the initial age, the rest of it handles if it needs to subtract 1 from it if the person's birthday hasn't happened yet. The OP's entire problem seems to be with trying to subtract a `DateTime` from a `int`. – juharr Jul 20 '20 at 17:55
  • DateTime now = DateTime.Today; int age = now.Year - bday.Year; if (bday > now.AddYears(-age)) age--; – LDS Jul 20 '20 at 17:57

2 Answers2

2

If it were me, I'd lean on NodaTime for this since you can calculate the Period between the date-of-birth and "today".

Per NodaTime's documentation:

A Period is a number of years, months, weeks, days, hours and so on, which can be added to (or subtracted from) a LocalDateTime, LocalDate or LocalTime. The amount of elapsed time represented by a Period isn't fixed: a period of "one month" is effectively longer when added to January 1st than when added to February 1st, because February is always shorter than January.

There's even a "recipe" for what you're trying to do within the documentation.

Adapted to your view model, it could look something like this:

public class PersonnelDOBViewModel
{
    private readonly ZonedClock _clock;

    public PersonnelDOBViewModel()
    {
        // Depending on your goals, you may want to use a user-defined
        // time zone here
        var timezone = DateTimeZoneProviders.Tzdb.GetSystemDefault();

        _clock = SystemClock.Instance.InZone(timezone);
    }

    public DateTime? PersonnelDOB { get; set; }

    public int? Age
    {
        get
        {
            if (PersonnelDOB == null)
            {
                return null;
            }

            var dob = LocalDate.FromDateTime(PersonnelDOB.Value);
            var today = _clock.GetCurrentDate();

            return Period.Between(dob, today).Years;
        }
    }
}
1

Try using DateTime.Now (or DateTime.UtcNow) instead of DateTime.Today.Year.
Also keep in mind that age calculations get tricky when you take time zone into account.

var age = dob.HasValue ? GetAge(dob.Value, DateTime.Now) : (int?)null;

private int GetAge(DateTime dob, DateTime currentTime)
{
    var years = currentTime.Year - dob.Year;

    if (currentTime.Month < dob.Month || (currentTime.Month == dob.Month && currentTime.Day < dob.Day))
    {
        years--;
    }
    
    return years;
}
Merkle Groot
  • 846
  • 5
  • 9
  • 1
    That gives you a `TimeSpan` which would not tell you how many years old someone is, just how many days old they are. And since years are not a standard number of days you cannot calculate the age in years from that. – juharr Jul 20 '20 at 17:53
  • But since it is known how many days an average year has... It is easy to convert days in years. – Legacy Code Jul 20 '20 at 18:00
  • @LegacyCode Sure you can do that if you don't need to be accurate. If you're ok with saying someone is 40 up to ten days before their 40th birthday. With the window of error increasing by 1 day ever four years (in general since years ending in 00 that are not a multiple of 400 are not leap years) – juharr Jul 20 '20 at 18:04
  • @juharr Thanks for bringing that up. I've made corrections. – Merkle Groot Jul 20 '20 at 18:07
  • Note that you can just do `if(currentTime.AddYears(-years) < dob) years--;` as well. – juharr Jul 20 '20 at 18:08
  • @juharr or this :) private int GetAge(DateTime dob, DateTime currentTime) => currentTime.Year - dob.Year - (currentTime.Month < dob.Month || (currentTime.Month == dob.Month && currentTime.Day < dob.Day) ? 1 : 0); – Merkle Groot Jul 20 '20 at 18:11
  • Isn’t the subtraction of datetimes already taking leap years into account? – Legacy Code Jul 20 '20 at 18:12
  • @LegacyCode Yes, but the resulting `TimeSpan` will only give you the number of days, not years. So to convert to years you'd have to divide by something like 365, resulting in the issue with leap years. – juharr Jul 20 '20 at 18:15
  • Hmm I have to test that out. – Legacy Code Jul 20 '20 at 18:57