C# calculate accurate age

前端 未结 14 1818
攒了一身酷
攒了一身酷 2020-12-15 11:01

anyone know how to get the age based on a date(birthdate)

im thinking of something like this

string age = DateTime.Now.GetAccurateAge();
14条回答
  •  渐次进展
    2020-12-15 11:41

    My answer is not exactly an answer; it is a way to find an answer in this and similar threads. The correct answer has already been provided by LukeH, and my 2 cents here is for anyone that want to know which is the more correct answer*.

    *more correct because, as you saw in several discussions and comments scattered around, we have to compromise with some preconditions in leap years - dob is 1-mar or 28-feb in normal years?

    I'm using as benchmark this and this other sites that do that, and my brain too ;)

    I implemented LukeH, @Panos Theof and @xr280xr answers here:

      public static class DateTimeExtensions
    {
        public static int HowOld(this DateTime initialDay, DateTime dayToCalculate, out int days, out int months)
        {
            //https://stackoverflow.com/a/3055445/2752308
            //LukeH: essentially right
            months = dayToCalculate.Month - initialDay.Month;
            int years = dayToCalculate.Year - initialDay.Year;
    
            if (dayToCalculate.Day < initialDay.Day)
            {
                months--;
            }
    
            if (months < 0)
            {
                years--;
                months += 12;
            }
    
            days = (dayToCalculate - initialDay.AddMonths((years * 12) + months)).Days;
            Console.WriteLine(
                $"{years} year{((years == 1) ? "" : "s")}, {months} month{((months == 1) ? "" : "s")} and {days} day{((days == 1) ? "" : "s")}");
            return years;
        }
        public static int HowOld2(this DateTime initialDay, DateTime dayToCalculate, out int days, out int months)
        {
            //@Panos Theof: wrong
    
            months = dayToCalculate.Month - initialDay.Month;
            int years = dayToCalculate.Year - initialDay.Year;
    
            if (dayToCalculate.Day < initialDay.Day)
            {
                dayToCalculate = dayToCalculate.AddMonths(-1);
            }
    
            if (months < 0)
            {
                dayToCalculate = dayToCalculate.AddYears(-1);
                months += 12;
            }
            years = dayToCalculate.Year - initialDay.Year;
            var offs = initialDay.AddMonths(years * 12 + months);
            days = (int)((dayToCalculate.Ticks - offs.Ticks) / TimeSpan.TicksPerDay);
            Console.WriteLine(
                $"{years} year{((years == 1) ? "" : "s")}, {months} month{((months == 1) ? "" : "s")} and {days} day{((days == 1) ? "" : "s")}");
            return years;
        }
        public static int HowOld3(this DateTime initialDay, DateTime dayToCalculate, out int days, out int months)
        {
            //@xr280xr: wrong
    
            //Get the relative difference between each date part
            days = dayToCalculate.Day - initialDay.Day;
            months = dayToCalculate.Month - initialDay.Month;
            int years = dayToCalculate.Year - initialDay.Year;
    
            if (days < 0)
            {
                days = DateTime.DaysInMonth(initialDay.Year, initialDay.Month) - initialDay.Day +    //Days left in month of birthday +
                       dayToCalculate.Day;                                                                   //Days passed in dayToCalculate's month
                months--;                                                                               //Subtract incomplete month that was already counted
            }
    
            if (months < 0)
            {
                months += 12;   //Subtract months from 12 to convert relative difference to # of months
                years--;        //Subtract incomplete year that was already counted
            }
    
            Console.WriteLine(string.Format("{0} year{1}, {2} month{3} and {4} day{5}",
                years, (years == 1) ? "" : "s",
                months, (months == 1) ? "" : "s",
                days, (days == 1) ? "" : "s"));
            return years;
        }
    }
    

    Using VS2019 and XUnit I create an Inlinedata generator Class:

       public class CalculatorTestData : IEnumerable
                public IEnumerator GetEnumerator()
                {
                    yield return new object[] { new DateTime(1966, 7, 27), new DateTime(2020, 7, 26), 53, 11, 29 };
                    yield return new object[] { new DateTime(1966, 7, 27), new DateTime(2020, 7, 27), 54, 0, 0 };
                    yield return new object[] { new DateTime(1966, 7, 27), new DateTime(2020, 7, 28), 54, 0, 1 };
                    yield return new object[] { new DateTime(1968, 2, 29), new DateTime(2020, 2, 28), 51, 11, 30 };
                    yield return new object[] { new DateTime(1968, 2, 29), new DateTime(2020, 2, 29), 52, 0, 0 };
                    yield return new object[] { new DateTime(1968, 2, 29), new DateTime(2020, 3, 01), 52, 0, 1 };
                    yield return new object[] { new DateTime(2016, 2, 29), new DateTime(2017, 2, 28), 0, 11, 30 };
                }
    
                IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
                IEnumerator IEnumerable.GetEnumerator()
                {
                    return GetEnumerator();
                }
            }
    

    And setup for the three methods:

        [Theory]
        [ClassData(typeof(CalculatorTestData))]
        public void TestHowOld(DateTime initialDay, DateTime dayToCalculate,int expectedYears, int expectedMonths, int expectedDays)//, out int days, out int months
        {
            //LukeH: essentially right
            int resultMonths, resultDays;
            int age = initialDay.HowOld(dayToCalculate,out resultDays,
                out resultMonths); //https://stackoverflow.com/questions/28970265/how-to-test-method-with-out-parameters
            Assert.Equal(age, expectedYears);
            Assert.Equal(resultMonths, expectedMonths);
            Assert.Equal(resultDays, expectedDays);
        }
        [Theory]
        [ClassData(typeof(CalculatorTestData))]
        public void TestHowOld2(DateTime initialDay, DateTime dayToCalculate, int expectedYears, int expectedMonths, int expectedDays)//, out int days, out int months
        {
            //@Panos Theof: wrong
            int resultMonths, resultDays;
            int age = initialDay.HowOld2(dayToCalculate, out resultDays,
                out resultMonths); //https://stackoverflow.com/questions/28970265/how-to-test-method-with-out-parameters
            Assert.Equal(age, expectedYears);
            Assert.Equal(resultMonths, expectedMonths);
            Assert.Equal(resultDays, expectedDays);
    
        }
        [Theory]
        [ClassData(typeof(CalculatorTestData))]
        public void TestHowOld3(DateTime initialDay, DateTime dayToCalculate, int expectedYears, int expectedMonths, int expectedDays)//, out int days, out int months
        {
            //@xr280xr: wrong
            int resultMonths, resultDays;
            int age = initialDay.HowOld3(dayToCalculate, out resultDays,
                out resultMonths); //https://stackoverflow.com/questions/28970265/how-to-test-method-with-out-parameters
            Assert.Equal(age, expectedYears);
            Assert.Equal(resultMonths, expectedMonths);
            Assert.Equal(resultDays, expectedDays);
    
        }
    

    The expected results are on the InlineData Class, of course.

    The output results are:

    So, LukeH has the correct answer. And, for my surprise, both sites disagree for leap DOB, and IMHO Calculator.net is correct, and timeanddate.com plainly wrong, producing this error outputs:

    For DOB Feb 29:

    • both 28/02 and 29/02 produce 52, 0, 0 (?!)

    • 01/03 yields 52, 0, 2 (!!!???)

    • Feb 29, 2016 => Feb 28, 2017 => 1 y, 0 m, 1 d (!!!???)

    Hope showing the test setup helps someone.

提交回复
热议问题