In C#, how do I calculate someone's age based on a DateTime type birthday?

后端 未结 30 2163
名媛妹妹
名媛妹妹 2020-11-21 05:14

Given a DateTime representing a person\'s birthday, how do I calculate their age in years?

相关标签:
30条回答
  • 2020-11-21 05:30

    Do we need to consider people who is smaller than 1 year? as Chinese culture, we describe small babies' age as 2 months or 4 weeks.

    Below is my implementation, it is not as simple as what I imagined, especially to deal with date like 2/28.

    public static string HowOld(DateTime birthday, DateTime now)
    {
        if (now < birthday)
            throw new ArgumentOutOfRangeException("birthday must be less than now.");
    
        TimeSpan diff = now - birthday;
        int diffDays = (int)diff.TotalDays;
    
        if (diffDays > 7)//year, month and week
        {
            int age = now.Year - birthday.Year;
    
            if (birthday > now.AddYears(-age))
                age--;
    
            if (age > 0)
            {
                return age + (age > 1 ? " years" : " year");
            }
            else
            {// month and week
                DateTime d = birthday;
                int diffMonth = 1;
    
                while (d.AddMonths(diffMonth) <= now)
                {
                    diffMonth++;
                }
    
                age = diffMonth-1;
    
                if (age == 1 && d.Day > now.Day)
                    age--;
    
                if (age > 0)
                {
                    return age + (age > 1 ? " months" : " month");
                }
                else
                {
                    age = diffDays / 7;
                    return age + (age > 1 ? " weeks" : " week");
                }
            }
        }
        else if (diffDays > 0)
        {
            int age = diffDays;
            return age + (age > 1 ? " days" : " day");
        }
        else
        {
            int age = diffDays;
            return "just born";
        }
    }
    

    This implementation has passed below test cases.

    [TestMethod]
    public void TestAge()
    {
        string age = HowOld(new DateTime(2011, 1, 1), new DateTime(2012, 11, 30));
        Assert.AreEqual("1 year", age);
    
        age = HowOld(new DateTime(2011, 11, 30), new DateTime(2012, 11, 30));
        Assert.AreEqual("1 year", age);
    
        age = HowOld(new DateTime(2001, 1, 1), new DateTime(2012, 11, 30));
        Assert.AreEqual("11 years", age);
    
        age = HowOld(new DateTime(2012, 1, 1), new DateTime(2012, 11, 30));
        Assert.AreEqual("10 months", age);
    
        age = HowOld(new DateTime(2011, 12, 1), new DateTime(2012, 11, 30));
        Assert.AreEqual("11 months", age);
    
        age = HowOld(new DateTime(2012, 10, 1), new DateTime(2012, 11, 30));
        Assert.AreEqual("1 month", age);
    
        age = HowOld(new DateTime(2008, 2, 28), new DateTime(2009, 2, 28));
        Assert.AreEqual("1 year", age);
    
        age = HowOld(new DateTime(2008, 3, 28), new DateTime(2009, 2, 28));
        Assert.AreEqual("11 months", age);
    
        age = HowOld(new DateTime(2008, 3, 28), new DateTime(2009, 3, 28));
        Assert.AreEqual("1 year", age);
    
        age = HowOld(new DateTime(2009, 1, 28), new DateTime(2009, 2, 28));
        Assert.AreEqual("1 month", age);
    
        age = HowOld(new DateTime(2009, 2, 1), new DateTime(2009, 3, 1));
        Assert.AreEqual("1 month", age);
    
        // NOTE.
        // new DateTime(2008, 1, 31).AddMonths(1) == new DateTime(2009, 2, 28);
        // new DateTime(2008, 1, 28).AddMonths(1) == new DateTime(2009, 2, 28);
        age = HowOld(new DateTime(2009, 1, 31), new DateTime(2009, 2, 28));
        Assert.AreEqual("4 weeks", age);
    
        age = HowOld(new DateTime(2009, 2, 1), new DateTime(2009, 2, 28));
        Assert.AreEqual("3 weeks", age);
    
        age = HowOld(new DateTime(2009, 2, 1), new DateTime(2009, 3, 1));
        Assert.AreEqual("1 month", age);
    
        age = HowOld(new DateTime(2012, 11, 5), new DateTime(2012, 11, 30));
        Assert.AreEqual("3 weeks", age);
    
        age = HowOld(new DateTime(2012, 11, 1), new DateTime(2012, 11, 30));
        Assert.AreEqual("4 weeks", age);
    
        age = HowOld(new DateTime(2012, 11, 20), new DateTime(2012, 11, 30));
        Assert.AreEqual("1 week", age);
    
        age = HowOld(new DateTime(2012, 11, 25), new DateTime(2012, 11, 30));
        Assert.AreEqual("5 days", age);
    
        age = HowOld(new DateTime(2012, 11, 29), new DateTime(2012, 11, 30));
        Assert.AreEqual("1 day", age);
    
        age = HowOld(new DateTime(2012, 11, 30), new DateTime(2012, 11, 30));
        Assert.AreEqual("just born", age);
    
        age = HowOld(new DateTime(2000, 2, 29), new DateTime(2009, 2, 28));
        Assert.AreEqual("8 years", age);
    
        age = HowOld(new DateTime(2000, 2, 29), new DateTime(2009, 3, 1));
        Assert.AreEqual("9 years", age);
    
        Exception e = null;
    
        try
        {
            age = HowOld(new DateTime(2012, 12, 1), new DateTime(2012, 11, 30));
        }
        catch (ArgumentOutOfRangeException ex)
        {
            e = ex;
        }
    
        Assert.IsTrue(e != null);
    }
    

    Hope it's helpful.

    0 讨论(0)
  • 2020-11-21 05:33

    My suggestion

    int age = (int) ((DateTime.Now - bday).TotalDays/365.242199);
    

    That seems to have the year changing on the right date. (I spot tested up to age 107.)

    0 讨论(0)
  • 2020-11-21 05:34

    This classic question is deserving of a Noda Time solution.

    static int GetAge(LocalDate dateOfBirth)
    {
        Instant now = SystemClock.Instance.Now;
    
        // The target time zone is important.
        // It should align with the *current physical location* of the person
        // you are talking about.  When the whereabouts of that person are unknown,
        // then you use the time zone of the person who is *asking* for the age.
        // The time zone of birth is irrelevant!
    
        DateTimeZone zone = DateTimeZoneProviders.Tzdb["America/New_York"];
    
        LocalDate today = now.InZone(zone).Date;
    
        Period period = Period.Between(dateOfBirth, today, PeriodUnits.Years);
    
        return (int) period.Years;
    }
    

    Usage:

    LocalDate dateOfBirth = new LocalDate(1976, 8, 27);
    int age = GetAge(dateOfBirth);
    

    You might also be interested in the following improvements:

    • Passing in the clock as an IClock, instead of using SystemClock.Instance, would improve testability.

    • The target time zone will likely change, so you'd want a DateTimeZone parameter as well.

    See also my blog post on this subject: Handling Birthdays, and Other Anniversaries

    0 讨论(0)
  • 2020-11-21 05:39

    Here's yet another answer:

    public static int AgeInYears(DateTime birthday, DateTime today)
    {
        return ((today.Year - birthday.Year) * 372 + (today.Month - birthday.Month) * 31 + (today.Day - birthday.Day)) / 372;
    }
    

    This has been extensively unit-tested. It does look a bit "magic". The number 372 is the number of days there would be in a year if every month had 31 days.

    The explanation of why it works (lifted from here) is:

    Let's set Yn = DateTime.Now.Year, Yb = birthday.Year, Mn = DateTime.Now.Month, Mb = birthday.Month, Dn = DateTime.Now.Day, Db = birthday.Day

    age = Yn - Yb + (31*(Mn - Mb) + (Dn - Db)) / 372

    We know that what we need is either Yn-Yb if the date has already been reached, Yn-Yb-1 if it has not.

    a) If Mn<Mb, we have -341 <= 31*(Mn-Mb) <= -31 and -30 <= Dn-Db <= 30

    -371 <= 31*(Mn - Mb) + (Dn - Db) <= -1

    With integer division

    (31*(Mn - Mb) + (Dn - Db)) / 372 = -1

    b) If Mn=Mb and Dn<Db, we have 31*(Mn - Mb) = 0 and -30 <= Dn-Db <= -1

    With integer division, again

    (31*(Mn - Mb) + (Dn - Db)) / 372 = -1

    c) If Mn>Mb, we have 31 <= 31*(Mn-Mb) <= 341 and -30 <= Dn-Db <= 30

    1 <= 31*(Mn - Mb) + (Dn - Db) <= 371

    With integer division

    (31*(Mn - Mb) + (Dn - Db)) / 372 = 0

    d) If Mn=Mb and Dn>Db, we have 31*(Mn - Mb) = 0 and 1 <= Dn-Db <= 30

    With integer division, again

    (31*(Mn - Mb) + (Dn - Db)) / 372 = 0

    e) If Mn=Mb and Dn=Db, we have 31*(Mn - Mb) + Dn-Db = 0

    and therefore (31*(Mn - Mb) + (Dn - Db)) / 372 = 0

    0 讨论(0)
  • 2020-11-21 05:40

    I use this:

    public static class DateTimeExtensions
    {
        public static int Age(this DateTime birthDate)
        {
            return Age(birthDate, DateTime.Now);
        }
    
        public static int Age(this DateTime birthDate, DateTime offsetDate)
        {
            int result=0;
            result = offsetDate.Year - birthDate.Year;
    
            if (offsetDate.DayOfYear < birthDate.DayOfYear)
            {
                  result--;
            }
    
            return result;
        }
    }
    
    0 讨论(0)
  • 2020-11-21 05:40

    I have created a SQL Server User Defined Function to calculate someone's age, given their birthdate. This is useful when you need it as part of a query:

    using System;
    using System.Data;
    using System.Data.Sql;
    using System.Data.SqlClient;
    using System.Data.SqlTypes;
    using Microsoft.SqlServer.Server;
    
    public partial class UserDefinedFunctions
    {
        [SqlFunction(DataAccess = DataAccessKind.Read)]
        public static SqlInt32 CalculateAge(string strBirthDate)
        {
            DateTime dtBirthDate = new DateTime();
            dtBirthDate = Convert.ToDateTime(strBirthDate);
            DateTime dtToday = DateTime.Now;
    
            // get the difference in years
            int years = dtToday.Year - dtBirthDate.Year;
    
            // subtract another year if we're before the
            // birth day in the current year
            if (dtToday.Month < dtBirthDate.Month || (dtToday.Month == dtBirthDate.Month && dtToday.Day < dtBirthDate.Day))
                years=years-1;
    
            int intCustomerAge = years;
            return intCustomerAge;
        }
    };
    
    0 讨论(0)
提交回复
热议问题