如何根据DateTime类型的生日计算某人的年龄?

2237

给定一个代表一个人生日的DateTime,如何计算他们的年龄?


190
到目前为止,所有的回答都没有提到一个关键点,那就是这取决于这个人出生的地方和现在所在的地方。 - Yaur
54
@Yaur:将现在的时间加上出生时间转换成GMT/UTC时间,年龄只是一个相对值,因此时区是无关紧要的。要确定用户当前所在的时区,可以使用地理定位技术。 - Stefan Steiger
9
如果我们考虑 @Yaur 提出的跨时区计算建议,夏令时会以任何方式影响计算吗? - DDM
4
请注意,对于不足一岁的人,他们的年龄以天、周或月为单位表示。转换单位的过渡时间可能是特定于领域的。 - Andrew Morton
6
众所周知,年龄没有明确的定义。我遇到过很多女性都倾向于将自己的生命时间舍入到一个完整的年份,直到二十多岁,然后开始舍入到下一年。我出生在1月3日,所以只需从我的出生年份减去当前年份即可,无论今天是哪一天。有些人认为,如果你是在闰年的闰日出生的,你的年龄会按照四分之一的比例计算。那么如果你是在一个闰秒出生的呢?8个月大的婴儿算不算1岁?如果我往西飞行,我的年龄会变年轻吗?如果我的心脏停止了一分钟,我应该把这算在内吗? - Erdogan Kurtur
显示剩余6条评论
75个回答

2411

一种易于理解和简单的解决方案。

// Save today's date.
var today = DateTime.Today;

// Calculate the age.
var age = today.Year - birthdate.Year;

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

然而,这假设你正在寻找的是西方的年龄概念,并且不使用东亚计算法


97
这个答案并不适用于所有的语言环境和年龄段。有些国家在当前生活的人出生后跳过了某些日期,其中包括俄罗斯(1918年)、希腊(1924年)和土耳其(1926年)。 - Lars D
40
实际上,这还不完全正确。这段代码假定“bday”是DateTime的日期部分。这是一个边缘情况(我猜大多数人只会传递日期而不是日期时间),但如果您将生日作为日期和时间传递,并且时间大于00:00:00,则会遇到Danvil指出的错误。将bday = bday.Date进行设置可以修复此问题。 - Øyvind
8
这是12岁,但为什么你不只需减去出生日期 - 今天的日期,然后计算时间跨度就能得到答案,而不需要使用if语句。 - AbbathCL
4
时间段没有年份。 - Wouter
5
我这里有什么遗漏吗?我猜日期完全丢失了。当一个人的生日是在8月1日,而你在7月31日运行脚本时,你得到的结果与在8月1日运行脚本时一样,但此时这个人已经变老了一岁。 - M Stoerzel
显示剩余3条评论

1114

虽然这么做方式有点奇怪,但如果你将日期格式化为yyyymmdd,并从当前日期中减去出生日期,然后删除最后4位数字,就可以得到年龄 :)

我不知道C#,但我相信这在任何语言中都可以使用。

20080814 - 19800703 = 280111 

去掉最后4位数 = 28

C# 代码:

int now = int.Parse(DateTime.Now.ToString("yyyyMMdd"));
int dob = int.Parse(dateOfBirth.ToString("yyyyMMdd"));
int age = (now - dob) / 10000;

或者,使用扩展方法而不需要进行所有类型转换。省略错误检查:

public static Int32 GetAge(this DateTime dateOfBirth)
{
    var today = DateTime.Today;

    var a = (today.Year * 100 + today.Month) * 100 + today.Day;
    var b = (dateOfBirth.Year * 100 + dateOfBirth.Month) * 100 + dateOfBirth.Day;

    return (a - b) / 10000;
}

13
实际上,这对于在MS-SQL中使用日期时间字段(总天数自1900年01月01日起)非常有用。 - Patrik
9
好的,我会尽力以最准确、通俗易懂的方式翻译。以下是要翻译的内容:@numerek Please post your suggested modifications as their own answer. For what it's worth, the current year times 10000 is nowhere near an integer overflow, by two orders of magnitude. 20,150,000 vs 2,147,483,648。根据我的理解,这段话想表达的意思是:@numerek请将您建议的修改作为您自己的答案发布。就数值溢出而言,当前年份乘以10000远未接近整数溢出,相差两个数量级。20150000与2147483648之间的差距为两个数量级。以下是我的翻译: Please post your suggested modifications as their own answer. For what it's worth, the current year times 10000 is nowhere near an integer overflow, by two orders of magnitude. 20,150,000 vs 2,147,483,648。 就此而言,当前年份乘以10000不会引起整数溢出,两者相差两个数量级,20150000与2147483648的差距为两个数量级。 - GalacticCowboy
2
这个答案假设闰年宝宝在非闰年的生日是3月1日。 - Jamie Kitson
12
@LongChalk说的是:20180101减去20171231等于8870。去掉最后4位数字,就得到了一个隐含的年龄为0。你是怎么得到1这个数字的呢? - Rufus L
2
@RufusL,应该是0,不是1floor(8870 / 10000) == 0。你在"计算"万位时,在8870处是没有万位的。 - flindeberg
显示剩余5条评论

423
这是一个测试片段:

DateTime bDay = new DateTime(2000, 2, 29);
DateTime now = new DateTime(2009, 2, 28);
MessageBox.Show(string.Format("Test {0} {1} {2}",
                CalculateAgeWrong1(bDay, now),      // outputs 9
                CalculateAgeWrong2(bDay, now),      // outputs 9
                CalculateAgeCorrect(bDay, now),     // outputs 8
                CalculateAgeCorrect2(bDay, now)));  // outputs 8

以下是相关方法:

public int CalculateAgeWrong1(DateTime birthDate, DateTime now)
{
    return new DateTime(now.Subtract(birthDate).Ticks).Year - 1;
}

public int CalculateAgeWrong2(DateTime birthDate, DateTime now)
{
    int age = now.Year - birthDate.Year;

    if (now < birthDate.AddYears(age))
        age--;

    return age;
}

public int CalculateAgeCorrect(DateTime birthDate, DateTime now)
{
    int age = now.Year - birthDate.Year;

    if (now.Month < birthDate.Month || (now.Month == birthDate.Month && now.Day < birthDate.Day))
        age--;

    return age;
}

public int CalculateAgeCorrect2(DateTime birthDate, DateTime now)
{
    int age = now.Year - birthDate.Year;

    // For leap years we need this
    if (birthDate > now.AddYears(-age)) 
        age--;
    // Don't use:
    // if (birthDate.AddYears(age) > now) 
    //     age--;

    return age;
}

40
尽管此代码有效,但它断言在非闰年中,生于闰日的人在次年3月1日时年龄增加,而不是2月28日。实际上,“任何一种选择都可能是正确的”。维基百科对此有所说明。因此,尽管您的代码并没有“错误”,但也不是唯一可接受的解决方案。 - Matt Johnson-Pint
29
我认为 @MattJohnson 说得很正确。如果我的生日是2月29日,那么在2月28日我的生日还没有过去,我应该和2月27日时一样大。然而,在3月1日,我们已经过了我的生日,我应该算下一个年龄。在美国,售卖酒类的商店会放置一个牌子,上面写着“如果您出生在YYYY年这一天之后,您就不能购买酒类”(其中YYYY每年都会变化)。这意味着在他们21岁那年的2月28日,出生于2月29日的人不能购买酒精饮料(在大多数地方),这也支持了这个想法:在3月1日之前,他们还没有真正长了一岁。 - jfren484
7
请阅读维基百科文章,因为这一政策在不同司法管辖区存在着很大的差异。 - Matt Johnson-Pint
13
你的主张与哲学完全无关,但与你个人的感觉有关。一个在2月29日出生的人“变老”在大多数情况下并不重要,除非这个年龄形成了“法定年龄界限”(例如:能否购买酒精、投票、领养金、参军、获得驾照等)。考虑美国的饮酒年龄(21岁):对于大多数人来说,那是7670天。如果在闰年之前的3月1日或闰年前出生,则为7671天。如果在2月29日出生:2月28日是7670天,3月1日是7671天。选择是任意的,可以两种方式都行。 - Disillusioned
9
@CraigYoung,你没有理解我在哲学上的意思。我使用这个术语与法律相对应。如果一个人正在编写一个需要知道一个人的法定年龄的应用程序,那么他们只需要知道他们的应用程序所用于/适用于的法律管辖区如何对待2月29日出生的人。但是,如果我们谈论的是应该如何处理这个问题,那么这就是哲学定义中的问题。是的,我给出的意见是我自己的意见,但正如我所说的,我认为为3月1日辩护比为2月28日辩护更容易。 - jfren484
显示剩余2条评论

126
这个问题的简单答案是应用如下所示的AddYears,因为这是唯一的原生方法来添加年份到闰年的2月29日并获得正确的结果,即普通年份的2月28日。
有些人认为3月1日是闰年生日,但是.Net和任何官方规则都不支持这一点,通常逻辑也无法解释为什么在二月出生的人应该有75% 的生日在另一个月。
此外,Age 方法很适合作为DateTime的扩展添加。通过这种方式,您可以以最简单的方式获得年龄: int age = birthDate.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-02-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
}

6
有关于2月29日生日在3月1日的评论,从技术上讲,在28日过生日太早了(实际上早了1天)。在1日过生日则太晚了一天。但由于生日介于两者之间,在平年中使用1日来计算年龄对我来说更有意义,因为那个人每年3月1日、2日和3日确实已经那么大了,但在2月28日并不是。 - CyberClaw
3
从软件设计的角度来看,我认为将其编写为扩展方法并不是很合理。date.Age(other) - marsze
4
@marsze,如果你的变量命名得当,那么dob.Age(toDay)就会有很多意义。 - A.G.

98

我的建议

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

那似乎是在正确的日期更改年份。(我测试了107岁的情况。)


31
我认为哈利·帕奇不会赞同你的点测测试方法:http://www.latimes.com/news/obituaries/la-me-harry-patch26-2009jul26,0,7608030.story - MusiGenesis
4
谷歌说一年有365.242199天。 - mpen
13
公历一年的平均长度为365.2425天。 - dan04
7
我会说,这是其中一种最简单的解决方案,而且它足够好了。谁在乎我提前半天过X岁生日,而程序说我X岁了呢?虽然不是完全准确的数学结果,但该程序基本上是正确的。我真的很喜欢这个解决方案。 - Peter Perháč
21
有时候这很重要。在我的测试中,如果是某人的生日,它会报告他们比他们实际年龄更年轻。 - ChadT
显示剩余4条评论

94

这是我在网上找到的一个函数,不是我写的,但我稍微改进了一下:

public static int GetAge(DateTime birthDate)
{
    DateTime n = DateTime.Now; // To avoid a race condition around midnight
    int age = n.Year - birthDate.Year;

    if (n.Month < birthDate.Month || (n.Month == birthDate.Month && n.Day < birthDate.Day))
        age--;

    return age;
}

我能想到的只有两件事:那些不使用公历的国家的人怎么办呢?DateTime.Now 是服务器特定的文化,我认为。我对实际使用亚洲日历完全不了解,也不知道是否有一种简单的方法可以在不同日历之间转换日期,但如果你想知道那些4660年的中国人,就这样。


这似乎是最好处理不同地区(日期格式)的方法。 - webdad3

62

需要解决的两个主要问题是:

1.准确计算年龄-以年,月,日等为单位。

2.计算一般认为的年龄 - 人们通常不关心自己的确切年龄,只关心今年生日是哪一天。


第一个问题的解决方案很明显:

DateTime birth = DateTime.Parse("1.1.2000");
DateTime today = DateTime.Today;     //we usually don't care about birth time
TimeSpan age = today - birth;        //.NET FCL should guarantee this as precise
double ageInDays = age.TotalDays;    //total number of days ... also precise
double daysInYear = 365.2425;        //statistical value for 400 years
double ageInYears = ageInDays / daysInYear;  //can be shifted ... not so precise

解决方案2并不十分精确地确定总年龄,但在人们看来是比较精确的。当人们手动计算自己的年龄时,通常会使用这个方法:

DateTime birth = DateTime.Parse("1.1.2000");
DateTime today = DateTime.Today;
int age = today.Year - birth.Year;    //people perceive their age in years

if (today.Month < birth.Month ||
   ((today.Month == birth.Month) && (today.Day < birth.Day)))
{
  age--;  //birthday in current year not yet reached, we are 1 year younger ;)
          //+ no birthday for 29.2. guys ... sorry, just wrong date for birth
}

注释2:

  • 这是我首选的解决方案
  • 我们不能使用DateTime.DayOfYear或TimeSpans,因为它们在闰年中会改变天数
  • 我添加了更多的行来提高可读性

还有一个注意事项... 我会为此创建两个静态重载方法,一个用于通用使用,另一个用于友好使用:

public static int GetAge(DateTime bithDay, DateTime today) 
{ 
  //chosen solution method body
}

public static int GetAge(DateTime birthDay) 
{ 
  return GetAge(birthDay, DateTime.Now);
}

56

因为考虑到闰年等问题,我所知道的最好方法是:

DateTime birthDate = new DateTime(2000,3,1);
int age = (int)Math.Floor((DateTime.Now - birthDate).TotalDays / 365.25D);

2
有漏洞,因为它无法处理闰年/日。如果你在自己的生日运行它,它会一半时间计算出错误的年龄。 - lidqy

53

这是一个一行代码:

int age = new DateTime(DateTime.Now.Subtract(birthday).Ticks).Year-1;

27
这段代码存在问题。修改后如下:public static int CalculateAge(DateTime dateOfBirth, DateTime dateToCalculateAge) { TimeSpan span = dateToCalculateAge.Date - dateOfBirth.Date; int age = (int)(span.TotalDays / 365.25); return age; } ...当我输入1990-06-01并在他的14岁生日前一天(1990-05-31)计算年龄时,应该得到14岁的年龄结果。 - Kjensen
1
@Kjensen 这一天的偏移是由于在实时范围(dateOfBirth 到 dateToCalculateAge)内的 29th FEB 的计数与 DateTime.Substract 创建的时间范围不同,后者总是隐式地与 DateTime.Min(即 0001 年 1 月 1 日)进行比较。从 1990 年 5 月 31 日到 2005 年 6 月 1 日,您有四个这样的闰日,而从 0001 年 1 月 1 日到 0015 年 1 月 1 日,您只有三个 29th FEB。 - lidqy

46

这是我们这里使用的版本。 它有效,并且相当简单。 这与Jeff的想法相同,但我认为它更清晰一些,因为它分离了减一的逻辑,所以更容易理解。

public static int GetAge(this DateTime dateOfBirth, DateTime dateAsAt)
{
    return dateAsAt.Year - dateOfBirth.Year - (dateOfBirth.DayOfYear < dateAsAt.DayOfYear ? 0 : 1);
}

如果你认为三元运算符不够清晰,你可以扩展它以使其更加明确。

显然这是在DateTime上作为扩展方法完成的,但是你可以很清楚地获取那一行执行工作的代码并将其放在任何地方。在这里,我们有另一个重载的扩展方法,它传递了DateTime.Now,只是为了完整性。


11
当出生日期或参考日期中恰好有一个落在闰年时,我认为这可能会相差一天。考虑一个在2003年3月1日出生的人在2004年2月29日的年龄。要纠正这个问题,你需要对(月份,日期)进行字典序比较,并将其用于条件语句。 - Doug McClean
4
它也不会显示你生日时的正确年龄。 - dotjoe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接