给定代表某人生日的DateTime,我如何计算他们的年龄(以年为单位)?


当前回答

我找到的最简单的方法就是这样。它适用于美国和西欧地区。无法与其他地区通话,尤其是中国这样的地方。在最初计算年龄后,最多可额外进行4次比较。

public int AgeInYears(DateTime birthDate, DateTime referenceDate)
{
  Debug.Assert(referenceDate >= birthDate, 
               "birth date must be on or prior to the reference date");

  DateTime birth = birthDate.Date;
  DateTime reference = referenceDate.Date;
  int years = (reference.Year - birth.Year);

  //
  // an offset of -1 is applied if the birth date has 
  // not yet occurred in the current year.
  //
  if (reference.Month > birth.Month);
  else if (reference.Month < birth.Month) 
    --years;
  else // in birth month
  {
    if (reference.Day < birth.Day)
      --years;
  }

  return years ;
}

我仔细查看了答案,发现没有人提及闰日出生的监管/法律影响。例如,根据维基百科,如果你在2月29日出生在不同的司法管辖区,你的非闰年生日会有所不同:

在英国和香港:这是一年中的第几天,所以第二天,3月1日是你的生日。在新西兰:这是前一天,2月28日用于驾驶执照,3月1日用于其他目的。台湾:今天是2月28日。

据我所知,在美国,法规对此事保持沉默,这取决于普通法以及各个监管机构如何在其法规中定义事物。

为此,需要改进:

public enum LeapDayRule
{
  OrdinalDay     = 1 ,
  LastDayOfMonth = 2 ,
}

static int ComputeAgeInYears(DateTime birth, DateTime reference, LeapYearBirthdayRule ruleInEffect)
{
  bool isLeapYearBirthday = CultureInfo.CurrentCulture.Calendar.IsLeapDay(birth.Year, birth.Month, birth.Day);
  DateTime cutoff;

  if (isLeapYearBirthday && !DateTime.IsLeapYear(reference.Year))
  {
    switch (ruleInEffect)
    {
      case LeapDayRule.OrdinalDay:
        cutoff = new DateTime(reference.Year, 1, 1)
                             .AddDays(birth.DayOfYear - 1);
        break;

      case LeapDayRule.LastDayOfMonth:
        cutoff = new DateTime(reference.Year, birth.Month, 1)
                             .AddMonths(1)
                             .AddDays(-1);
        break;

      default:
        throw new InvalidOperationException();
    }
  }
  else
  {
    cutoff = new DateTime(reference.Year, birth.Month, birth.Day);
  }

  int age = (reference.Year - birth.Year) + (reference >= cutoff ? 0 : -1);
  return age < 0 ? 0 : age;
}

需要注意的是,该代码假设:

西方(欧洲)对年龄的推算,以及一种日历,如公历,在月底插入一个闰日。

其他回答

我经常用手指数。我需要看一下日历,以确定事情何时发生变化。这就是我在代码中要做的:

int AgeNow(DateTime birthday)
{
    return AgeAt(DateTime.Now, birthday);
}

int AgeAt(DateTime now, DateTime birthday)
{
    return AgeAt(now, birthday, CultureInfo.CurrentCulture.Calendar);
}

int AgeAt(DateTime now, DateTime birthday, Calendar calendar)
{
    // My age has increased on the morning of my
    // birthday even though I was born in the evening.
    now = now.Date;
    birthday = birthday.Date;

    var age = 0;
    if (now <= birthday) return age; // I am zero now if I am to be born tomorrow.

    while (calendar.AddYears(birthday, age + 1) <= now)
    {
        age++;
    }
    return age;
}

在LINQPad中运行此过程可获得以下结果:

PASSED: someone born on 28 February 1964 is age 4 on 28 February 1968
PASSED: someone born on 29 February 1964 is age 3 on 28 February 1968
PASSED: someone born on 31 December 2016 is age 0 on 01 January 2017

LINQPad中的代码在这里。

还有一个答案:

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

这已经过广泛的单元测试。它看起来确实有点“神奇”。数字372是如果每个月有31天,一年中会有多少天。

其工作原理的解释(此处省略)如下:

让我们设置Yn=DateTime.Now.Year,Yb=生日.Year,Mn=DateTime.Now.Month,Mb=生日.Month、Dn=DateTime.Now.Day,Db=生日.Day年龄=Yn-Yb+(31*(Mn-Mb)+(Dn-Db))/372我们知道,如果日期已经到达,我们需要的是Yn-Yb,如果日期尚未到达,则需要Yn-Yb-1。a) 如果Mn<Mb,我们有-341<=31*(Mn-Mb)<=-31和-30<=Dn-Db<=30-371<=31*(锰-Mb)+(Dn-Db)<=-1带整数除法(31*(Mn-Mb)+(Dn-Db))/372=-1b) 如果Mn=Mb和Dn<Db,则我们有31*(Mn-Mb)=0和-30<=Dn Db<=-1再次使用整数除法(31*(Mn-Mb)+(Dn-Db))/372=-1c) 如果Mn>Mb,我们有31<=31*(Mn-Mb)<=341和-30<=Dn-Db<=301<=31*(Mn-Mb)+(Dn-Db)<=371带整数除法(31*(Mn-Mb)+(Dn-Db))/372=0d) 如果Mn=Mb且Dn>Db,则我们有31*(Mn-Mb)=0且1<=Dn Db<=30再次使用整数除法(31*(Mn-Mb)+(Dn-Db))/372=0e) 如果Mn=Mb,Dn=Db,我们有31*(Mn-Mb)+Dn Db=0因此(31*(Mn-Mb)+(Dn-Db))/372=0

对此的简单答案是应用AddYears,如下所示,因为这是唯一一种将年份添加到闰年2月29日的本地方法,并获得普通年份2月28日的正确结果。

有些人认为3月1日是勒普林斯的生日,但.Net和任何官方规则都不支持这一点,也没有常见的逻辑解释为什么一些出生在2月的人应该在另一个月拥有75%的生日。

此外,Age方法可以作为DateTime的扩展添加。由此,您可以以最简单的方式获得年龄:

列表项目

int age=出生日期.age();

public static class DateTimeExtensions
{
    /// <summary>
    /// Calculates the age in years of the current System.DateTime object today.
    /// </summary>
    /// <param name="birthDate">The date of birth</param>
    /// <returns>Age in years today. 0 is returned for a future date of birth.</returns>
    public static int Age(this DateTime birthDate)
    {
        return Age(birthDate, DateTime.Today);
    }

    /// <summary>
    /// Calculates the age in years of the current System.DateTime object on a later date.
    /// </summary>
    /// <param name="birthDate">The date of birth</param>
    /// <param name="laterDate">The date on which to calculate the age.</param>
    /// <returns>Age in years on a later day. 0 is returned as minimum.</returns>
    public static int Age(this DateTime birthDate, DateTime laterDate)
    {
        int age;
        age = laterDate.Year - birthDate.Year;

        if (age > 0)
        {
            age -= Convert.ToInt32(laterDate.Date < birthDate.Date.AddYears(age));
        }
        else
        {
            age = 0;
        }

        return age;
    }
}

现在,运行此测试:

class Program
{
    static void Main(string[] args)
    {
        RunTest();
    }

    private static void RunTest()
    {
        DateTime birthDate = new DateTime(2000, 2, 28);
        DateTime laterDate = new DateTime(2011, 2, 27);
        string iso = "yyyy-MM-dd";

        for (int i = 0; i < 3; i++)
        {
            for (int j = 0; j < 3; j++)
            {
                Console.WriteLine("Birth date: " + birthDate.AddDays(i).ToString(iso) + "  Later date: " + laterDate.AddDays(j).ToString(iso) + "  Age: " + birthDate.AddDays(i).Age(laterDate.AddDays(j)).ToString());
            }
        }

        Console.ReadKey();
    }
}

关键日期示例如下:

出生日期:2000-02-29出生日期:2011-02-28年龄:11

输出:

{
    Birth date: 2000-02-28  Later date: 2011-02-27  Age: 10
    Birth date: 2000-02-28  Later date: 2011-02-28  Age: 11
    Birth date: 2000-02-28  Later date: 2011-03-01  Age: 11
    Birth date: 2000-02-29  Later date: 2011-02-27  Age: 10
    Birth date: 2000-02-29  Later date: 2011-02-28  Age: 11
    Birth date: 2000-02-29  Later date: 2011-03-01  Age: 11
    Birth date: 2000-03-01  Later date: 2011-02-27  Age: 10
    Birth date: 2000-03-01  Later date: 2011-02-28  Age: 10
    Birth date: 2000-03-01  Later date: 2011-03-01  Age: 11
}

2012年2月28日晚些时候:

{
    Birth date: 2000-02-28  Later date: 2012-02-28  Age: 12
    Birth date: 2000-02-28  Later date: 2012-02-29  Age: 12
    Birth date: 2000-02-28  Later date: 2012-03-01  Age: 12
    Birth date: 2000-02-29  Later date: 2012-02-28  Age: 11
    Birth date: 2000-02-29  Later date: 2012-02-29  Age: 12
    Birth date: 2000-02-29  Later date: 2012-03-01  Age: 12
    Birth date: 2000-03-01  Later date: 2012-02-28  Age: 11
    Birth date: 2000-03-01  Later date: 2012-02-29  Age: 11
    Birth date: 2000-03-01  Later date: 2012-03-01  Age: 12
}

这是用一行文字回答这个问题的最简单方法。

DateTime Dob = DateTime.Parse("1985-04-24");
 
int Age = DateTime.MinValue.AddDays(DateTime.Now.Subtract(Dob).TotalHours/24 - 1).Year - 1;

这也适用于闰年。

TimeSpan diff = DateTime.Now - birthdayDateTime;
string age = String.Format("{0:%y} years, {0:%M} months, {0:%d}, days old", diff);

我不知道你到底希望它返回给你多少,所以我只是做了一个可读的字符串。