繁体   English   中英

C#计算准确年龄

[英]C# calculate accurate age

任何人都知道如何根据日期(生日)获取年龄

我在想这样的事情

string age = DateTime.Now.GetAccurateAge();

输出将类似于 20Years 5Months 20Days

public static class DateTimeExtensions
{
    public static string ToAgeString(this DateTime dob)
    {
        DateTime today = DateTime.Today;

        int months = today.Month - dob.Month;
        int years = today.Year - dob.Year;

        if (today.Day < dob.Day)
        {
            months--;
        }

        if (months < 0)
        {
            years--;
            months += 12;
        }

        int days = (today - dob.AddMonths((years * 12) + months)).Days;

        return string.Format("{0} year{1}, {2} month{3} and {4} day{5}",
                             years, (years == 1) ? "" : "s",
                             months, (months == 1) ? "" : "s",
                             days, (days == 1) ? "" : "s");
    }
}

请参阅如何在 C# 中计算某人的年龄? 对于想法。

不确定它是否总是正确的(还没有考虑过是否存在闰年等可能使其失败的情况......),但这是一种摆脱年份和月份的简单方法:

DateTime bd = DateTime.Parse("2009-06-17");
TimeSpan ts = DateTime.Now.Subtract(bd);
DateTime age = DateTime.MinValue + ts;
string s = string.Format("{0} Years {1} months {2} days", age.Year -1 , age.Month - 1, age.Day - 1);

由于我无法将代码发布到评论中,这里是基于@LukeH 答案的代码,并修复了错误

public static int GetAge( DateTime dob, DateTime today, out int days, out int months ) {
        DateTime dt = today;
        if( dt.Day < dob.Day ) {
            dt = dt.AddMonths( -1 );
        }

        months = dt.Month - dob.Month;
        if( months < 0 ) {
            dt = dt.AddYears( -1 );
            months += 12;
        }

        int years = dt.Year - dob.Year;
        var offs = dob.AddMonths( years * 12 + months );
        days = (int)( ( today.Ticks - offs.Ticks ) / TimeSpan.TicksPerDay );
        return years;
    }

我的答案不完全是答案; 这是在此主题和类似主题中找到答案的一种方式。 LukeH已经提供了正确答案,我这里的 2 美分是给任何想知道哪个是更正确答案的人*。

*更正确,因为正如你在散布的一些讨论和评论中看到的那样,我们必须在闰年妥协一些先决条件 - 正常年份的 dob 是 1 月 1 日还是 2 月 28 日?

我正在使用这个其他这样做的网站作为基准,我的大脑也是 ;)

我在这里实现了 LukeH、@Panos Theof 和 @xr280xr 的答案:

  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;
    }
}

使用 VS2019 和 XUnit 我创建了一个 Inlinedata 生成器类:

   public class CalculatorTestData : IEnumerable<object[]>
            public IEnumerator<object[]> 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<object[]> IEnumerable<object[]>.GetEnumerator() => GetEnumerator();
            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }

并设置三种方法:

    [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);

    }

当然,预期的结果是在 InlineData 类上。

输出结果为:

在此处输入图片说明

所以,LukeH 有正确的答案。 而且,令我惊讶的是,两个站点都不同意跳跃 DOB,恕我直言 Calculator.net 是正确的,而 timeanddate.com 显然是错误的,产生了以下错误输出:

对于 DOB 2 月 29 日:

  • 28/02 和 29/02 都会产生 52, 0, 0 (?!)

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

  • 2016 年 2 月 29 日 => 2017 年 2 月 28 日 => 1 年、0 米、1 天(!!!???)

希望显示测试设置可以帮助某人。

这是我使用的:

    public static int GetAge(DateTime dateOfBirth)
    {
        int age = DateTime.Now.Year - dateOfBirth.Year;
        if (dateOfBirth.AddYears(age) > DateTime.Now)
        {
            age = age - 1;
        }

        return age;
    }

为了获得年龄,我将使用生日的日期时间并找到它与当前系统时间之间的差异。 此链接显示了如何查找两个日期时间之间的差异。 只需将开始时间设为用户的生日,将结束时间设为现在 (DateTime.Now;)

这听起来像是一个很好的练习,可以更好地了解TimeSpan 类

试试这个吧。 它在两个日期之间的所有日子循环,并在途中计算天,月和年。 这就是我需要的。

    public static class DateTimeExtension
    {
    /// <summary>
    /// Returns day, month, and year by subtracting date2 from date1
    /// </summary>
    /// <param name="date1">the date from which date2 will be subtracted</param>
    /// <param name="date2">this date will be subtracted from date1</param>
    /// <returns></returns>
    public static string GetAge(this DateTime date1, DateTime date2)
    {
        int y = 0, m = 0, d = 0;
        for (DateTime i = date2; i < date1; i = i.AddDays(1))
        {
            d++;
            if (d >= DateTime.DaysInMonth(i.Year, i.Month))
            {
                d = 0;
                m++;
            }
            if (m >= 12)
            {
                y++;
                m = 0;
            }
        }
        return "Time-span: " + y + " Year(s), " + m + " Month(s), " + d + " Day(s)";
    }
}

使用方法:

            Console.WriteLine(new DateTime(2018, 4, 24).GetAge(new DateTime(2018,1,3)));
            //Time-span: 0 Year(s), 3 Month(s), 20 Day(s)

            Console.WriteLine(new DateTime(2010, 4, 24).GetAge(new DateTime(2000, 7, 23)));
            //Time-span: 9 Year(s), 9 Month(s), 2 Day(s)
@if (Model.CF_DateOfBirth.HasValue)
                                        {
                                            var today = DateTime.Today;
                                            var months = DateTime.Today;
                                            var age = today.Year - Model.CF_DateOfBirth.Value.Year;
                                            var mons = months.Month - Model.CF_DateOfBirth.Val`enter code here`ue.Month;
                                            if (Model.CF_DateOfBirth > today.AddYears(-age) && Model.CF_DateOfBirth>months.AddMonths(-mons))
                                            {
                                                age--; mons--;
                                            }
                                            <i class="" style="color:cadetblue">&nbsp;&nbsp;Date of birth:&nbsp;&nbsp;</i><b style="color:teal">@Convert.ToDateTime(Model.CF_DateOfBirth).ToShortDateString().ToString()</b> <span>&nbsp;&nbsp;(&nbsp;<b>@age</b>Years &nbsp;<b>@mons</b> Months)</span>

                                        }

我觉得这个最准确。

            private int GetAge(DateTime birthDate)
            {
                TimeSpan ageTimeSpan = DateTime.UtcNow.Subtract(birthDate);
                int age = new DateTime(ageTimeSpan.Ticks).Year;
                return age;
            }

我注意到 LukeH 的回答和闰年的特殊性。 最简单的例子可能是一个dob = 2/29/2016作为2/28/20173/1/2017 在我看来,2016 年 2 月已经没有天数了,中间有 11 个完整的月份(3 月至 1 月),到目前为止,2017 年 2 月有 28 天,因此截至2/28/2017 ,我会称这个人为 11 个月 28 天。 截至3/1/2017 3 月 1 日 1 岁 1 天。 (虽然一些闰日婴儿确实会在平常年的 28 日庆祝……我很好奇法律上的考虑是什么。)

由于LukeH的方法使得使用DateTime.AddMonths ,它计算11个月30天,因为它发现之间的天差2/28/20171/29/2017 因此,它计算出2/28/20172/29/2016后 30 天后的 11 个月,以及3/1/2016后 27 天后的 11 个月。 生日相差 3 天,仅相隔 1 天。 如果像我“手工”那样得出 28 天,他们就会有一天的年龄差异。

我不太确定如何描述方法的根本区别,但这是我的尝试。 似乎总是有日期的问题,所以请仔细检查。

internal static void CalculateAge(DateTime dateOfBirth, DateTime asOfDate, out int years, out int months, out int days)
{
    Console.Write("As of " + asOfDate.ToShortDateString() + ": ");

    //Get the relative difference between each date part
    days = asOfDate.Day - dateOfBirth.Day;
    months = asOfDate.Month - dateOfBirth.Month;
    years = asOfDate.Year - dateOfBirth.Year;

    if (days < 0)
    {
        days = DateTime.DaysInMonth(dateOfBirth.Year, dateOfBirth.Month) - dateOfBirth.Day +    //Days left in month of birthday +
                asOfDate.Day;                                                                   //Days passed in asOfDate'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"));
}

如果您有一个名为 DateOfBirth 的实例变量/属性,则可以使用此方法。 否则,您可以将出生日期作为方法的参数传递。 我做了一些微积分,我证明这永远不会失败。

public int AgeCalc(){
      DateTime now = DateTime.Now;
      double age =  Math.Floor((DateTime.Now - DateOfBirth).TotalDays)/((DateTime.IsLeapYear(year: now.Year)? 366 : 365));
      return (age % 1) >= 0.951 ? Math.Round(age) : Math.Floor(age);
}

我希望这可以帮助你:)

这就是我使用的。 它是所选答案此答案的组合。

对于 2016 年 2 月 29 日出生的人,在 2017 年 3 月 1 日他们的年龄输出:

年数:1
月数:1(二月 28 天)
天数:0

var today = new DateTime(2020,11,4);
//var today = DateTime.Today;

// Calculate the age.
var years = today.Year - dateOfBirth.Year;

// Go back to the year in which the person was born in case of a leap year
if (dateOfBirth.Date > today.AddYears(-years))
{
    years--;
}

var months = today.Month - dateOfBirth.Month;
// Full month hasn't completed
if (today.Day < dateOfBirth.Day)
{
    months--;
}

if (months < 0)
{
    months += 12;
}

Years = years;
Months = months;
Days = (today - dateOfBirth.AddMonths((years * 12) + months)).Days;

我想把它放到第二个,因此想出了一些更精确的东西(也许有点过度设计)。

这使用反射从 DateTime 对象中提取字段,然后迭代提供的每个字段,计算用于相关单位的正确数量。

    /// <summary>  
    /// For calculating age  
    /// </summary>  
    /// <param name="dob">Date of birth</param>  
    /// <returns>Age in text format</returns>  
    public static string CalculateAge(DateTime dob)
    {
        // Initialise now and list of properties to retrieve for calculating age
        var now = DateTime.Now;
        var propertyNames = new List<string>()
        {
            "Second",
            "Minute",
            "Hour",
            "Day",
            "Month",
            "Year"
        };

        // Get the properties and order them in accordance to smallest unit
        // Important to do so as you perform standard subtraction starting with the smallest unit
        var properties = typeof(DateTime)
            .GetProperties()
            .Where(x => propertyNames.Contains(x.Name))
            .OrderBy(x => propertyNames.FindIndex(y => y == x.Name));

        // Create string builder to build age as a string
        var returnVal = new StringBuilder();
        foreach(var property in properties)
        {
            // Calculate the amount for the current property
            var amount = (int)property.GetValue(now) - (int)property.GetValue(dob);

            if (amount < 0)
                switch(property.Name)
                {
                    case "Month":
                        amount += 12;
                        dob = dob.AddYears(-1);
                        break;
                    case "Day":
                        amount += DateTime.DaysInMonth(dob.Year, dob.Month);
                        dob = dob.AddMonths(-1);
                        break;
                    case "Hour":
                        amount += 24;
                        dob = dob.AddDays(-1);
                        break;
                    case "Minute":
                        amount += 60;
                        dob = dob.AddHours(-1);
                        break;
                    case "Second":
                        amount += 60;
                        dob = dob.AddMinutes(-1);
                        break;
            }

            // Write to the stringbuilder
            returnVal.Insert(0, property.Name + "s: " + amount + ", ");
        }

        // Trim final ', ' at the end
        return returnVal.ToString().TrimEnd().TrimEnd(',');
    }

这使您可以达到您想要的任何精度(如果您希望年份在西方文化中计算年龄是正确的,那么您至少需要与当天一样精确)

此外,我刚刚在开始时手动初始化了列表,但是如果这是您的解决方案所需要的,您当然可以将其设置为可配置,但特别是列表必须从最低单位到最高单位排序(并且值必须特别指到 DateTime 的属性名称)。 如果您想在令人担忧的情况下验证这一点,配置值以错误的顺序设置为列表,您可以初始化一个包含所有 DateTime 字段的列表,然后拉出您的可配置列表,并在我获得时使用相同的 LINQ属性使用反射对传递的列表进行排序。

除了稍微复杂的代码之外,我喜欢我的解决方案的一点是它的运行方式很清晰,并且非常灵活。 也许唯一的问题是 switch 语句应该以相反的顺序排列,这样更容易理解。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM