【问题标题】:C# calculate accurate ageC#计算准确年龄
【发布时间】:2010-06-16 15:26:49
【问题描述】:

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

我在想这样的事情

string age = DateTime.Now.GetAccurateAge();

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

【问题讨论】:

  • 为什么要停在几天?它不应该直接到毫秒吗? ;)
  • @FrustratedWithFormsDesigner - 大声笑,但如果它是基于日期,你不知道实际出生的时间(包括毫秒) - 而且,那会根据头部或脚趾计算吗?跨度>
  • @Sohnee:基于脚趾,否则,您可能还没有完成;)
  • @FrustratedWithFormsDesigner - 不要忘记时区

标签: c#


【解决方案1】:
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");
    }
}

【讨论】:

  • 这是唯一好的答案。很遗憾它没有获得更高的票数。
  • 29-1-1967 到 1-3-1999 给出 32 年 1 个月零天。正确的是 32y 1m 和 1 天,因为将 32y+1m 添加到 29-1-1967 你将得到 28-2-1999
  • @Panos:谢谢,很好。这是一个已经存在很长时间的讨厌的错误。我现在已经修好了。
  • @Panos:再次修复。对此感到抱歉:(
  • @LukeH:谢谢。最新版本通过了我的所有测试!
【解决方案2】:

请参阅How do I calculate someone’s age in C#? 的答案以获取想法。

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 那肯定是不正确的。 DateTime.MinValue + ts` 的天数(闰年等)可能与我们处理的实际日期范围不同。
    • @Nelson:但不确定,如果您使用1970-06-16 作为 bd,它会准确返回 40 年,这是正确的,并且该日期范围跨越了相当多的闰年。
    • 但是1900-06-16 现在你有 +1 天了。非常接近,但并不准确。
    • 在这种特定情况下,一旦您超过 100 年,它似乎是不正确的。我不确定是否还有其他问题。这可能已经足够接近,不值得任何额外的努力。
    【解决方案4】:
    【解决方案5】:

    由于我无法将代码发布到评论中,这里是基于@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;
        }
    

    【讨论】:

      【解决方案6】:

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

      *更正确,因为正如您在分散的几次讨论和 cmets 中看到的,我们必须在闰年妥协一些先决条件 - 正常年份的 dob 是 3 月 1 日还是 2 月 28 日?

      我将 thisthis other 用作基准测试的网站,我的大脑也是如此;)

      我在这里实现了 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 我创建了一个内联数据生成器类:

         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 显然是错误的,产生了这个错误输出:

      2 月 29 日出生日期:

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

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

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

      希望展示测试设置对某人有所帮助。

      【讨论】:

        【解决方案7】:

        这是我使用的。它是selected answerthis answer 的组合。

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

        年数:1
        月数:1(2 月为 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;
        

        【讨论】:

          【解决方案8】:

          要获得年龄,我会使用生日的日期时间并找出它与当前系统时间之间的差异。 This link 展示了如何找出两个日期时间之间的差异。只需将 starttime 设为用户的生日,endtime 设为现在 (DateTime.Now;)

          【讨论】:

            【解决方案9】:

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

            【讨论】:

            • TimeSpan 不代表年或月字段的时间。最大的单位是天,将天转换为月或年很麻烦。
            【解决方案10】:

            这是我使用的:

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

            【讨论】:

              【解决方案11】:
              @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>
              
                                                      }
              

              【讨论】:

              • 岁月的年龄计算器。我正在视图页面中编写 MVC 的 Razor 视图引擎 C# 代码,非常适合我
              【解决方案12】:

              我觉得这个最准确。

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

              【讨论】:

              • 我有一个挥之不去的怀疑,你可能会在公元一世纪的日历结构上遇到有趣的问题。我建议谨慎使用这种方法。
              【解决方案13】:

              我注意到 LukeH 的回答和闰年的特殊性。最简单的例子可能是dob = 2/29/20162/28/20173/1/2017。在我看来,2016 年 2 月已经没有几天了,中间有 11 个完整的月(3 月至 1 月),而 2017 年 2 月到目前为止已经有 28 天,所以截至2/28/2017,我会打电话给这个 11 个月 28 天大的人。截至3/1/2017 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"));
              }
              

              【讨论】:

                【解决方案14】:

                如果您有一个名为 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);
                }
                
                

                希望对你有帮助:)

                【讨论】:

                  【解决方案15】:

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

                  这使用反射从 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 语句应该以相反的顺序排列,这样更容易理解。

                  【讨论】:

                    猜你喜欢
                    • 2023-03-06
                    • 2011-11-05
                    • 1970-01-01
                    • 2013-04-10
                    • 2012-03-26
                    • 1970-01-01
                    • 2020-12-28
                    • 2016-03-20
                    • 2011-04-16
                    相关资源
                    最近更新 更多