Em C #, como calculo a idade de alguém com base no aniversário do tipo DateTime?

1868

Dada a DateTimerepresentação do aniversário de uma pessoa, como calculo sua idade em anos?

Shaik Raffi
fonte
147
o que todas as respostas até o momento não entenderam é que depende de onde a pessoa nasceu e de onde está agora.
Yaur
40
@Yaur: Basta converter a hora do agora + nascimento em GMT / UTC, a idade é apenas um valor relativo, portanto, os fusos horários são irrelevantes. Para determinar o fuso horário atual do usuário, você pode usar o GeoLocating.
Stefan Steiger
Por que não considerar [data juliana] [1]? [1]: stackoverflow.com/questions/7103064/…
Muhammad Hewedy
5
Se estivermos levando em consideração a sugestão de @Yaur de cálculos de fuso horário, o horário de verão deve afetar o cálculo de alguma maneira?
DDM
6
Voto negativo porque esta é claramente uma pergunta de lição de casa e nenhuma tentativa existente foi fornecida.
677 Marie

Respostas:

2122

Uma solução fácil de entender e simples.

// Save today's date.
var today = DateTime.Today;
// Calculate the age.
var age = today.Year - birthdate.Year;
// Go back to the year the person was born in case of a leap year
if (birthdate.Date > today.AddYears(-age)) age--;

No entanto, isso pressupõe que você esteja procurando a idéia ocidental de idade e não esteja usando o cálculo da Ásia Oriental .

Mike Polen
fonte
252
Só queria comentar sobre o desempenho do DateTime.Now. Se você não precisar de um valor preciso de fuso horário, use DateTime.UtcNow, é muito mais rápido.
JAG
104
Dado que estamos falando de aniversários, você pode simplesmente usar DateTime. Hoje, uma vez que a parte do tempo não tem relevância.
24511 Tristan Warner-Smith
78
Esta resposta não funciona com todos os locais e todas as idades. Vários países pularam datas após o nascimento das pessoas que vivem atualmente, incluindo Rússia (1918), Grécia (1924) e Turquia (1926).
Lars D
30
Na verdade, ainda não está totalmente correto. Esse código presume que 'bday' é a parte da data de um DateTime. É um caso extremo (acho que a maioria das pessoas passará datas e não datas e horários), mas se você passar o aniversário como uma data e hora em que a hora é maior que 00:00:00, então você ' Vou encontrar o bug que Danvil apontou. Definir bday = bday.Date corrige isso.
Øyvind
119
A última linha me fez pensar demais. Em vez disso, que tal: if (bday.AddYears (age)> now) age--; Esta parece ser uma expressão mais intuitiva.
Cdggins
1015

Essa é uma maneira estranha de fazê-lo, mas se você formatar a data yyyymmdde subtrair a data de nascimento da data atual, solte os últimos 4 dígitos da idade :)

Não sei c #, mas acredito que isso funcione em qualquer idioma.

20080814 - 19800703 = 280111 

Solte os últimos 4 dígitos 28.

Código C #:

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

Ou, alternativamente, sem toda a conversão de tipo na forma de um método de extensão. Verificação de erro omitida:

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;
}
ScArcher2
fonte
5
Na verdade, esta é excelente para utilização em MS-SQL com datetime-campos (total de dias desde 01-011900)
Patrik
5
@ numerek Por favor, poste suas modificações sugeridas como sua própria resposta. Pelo que vale, o ano atual em 10000 não chega nem perto de um estouro inteiro, em duas ordens de magnitude. 20150000 2147483648 vs
GalacticCowboy
7
@LongChalk 20180101 - 20171231 = 8870. Solte os últimos 4 dígitos e você terá (um implícito) 0para a idade. Como você conseguiu 1?
Rufus L
4
Sei que essa é uma resposta antiga, mas não usaria um método de extensão, não é o lugar certo para definir essas lógicas.
Lucca Ferri
1
Que tipo de bruxaria é essa?
Muleskinner
391

Aqui está um trecho de teste:

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

Aqui você tem os métodos:

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;
}
RMA
fonte
33
Enquanto esse código funciona, ele afirma que uma pessoa nascida em um dia bissexto atinge o próximo ano de idade em 1º de março em anos não bissextos, em vez de em 28 de fevereiro. Na realidade, qualquer uma das opções pode estar correta . A Wikipedia tem algo a dizer sobre isso . Portanto, embora seu código não esteja "errado", a solução aceita também não.
Matt Johnson-Pint
18
@ MattJohnson Eu acho que isso é realmente correto. Se meu aniversário era 29 de fevereiro, então 28 de fevereiro ainda não havia passado, e eu ainda deveria ter a mesma idade que 27 de fevereiro. No entanto, em 1º de março, passamos o meu aniversário e eu devo ter a próxima idade. Nos EUA, uma empresa que vende álcool terá uma placa que diz algo como "Se você nasceu depois deste dia em AAAA, não pode comprar álcool" (onde AAAA muda todos os anos). Isso significa que alguém nascido em 29 de fevereiro não podem comprar álcool em 28 de fevereiro no ano em que completar 21 (a maioria dos lugares), e dá suporte à idéia de que eles não são um ano mais velho até março 1.
jfren484
4
@ jfren484 - leia o artigo da Wikipedia. Varia consideravelmente entre jurisdições.
Matt Johnson-Pint
9
@ jfren484 Sua reivindicação não tem absolutamente nada a ver com filosofia; mas tudo a ver com o seu próprio sentimento pessoal . Quando uma pessoa nascida em 29 de fevereiro "envelhece" não tem importância, a menos que a idade constitua um 'limite de idade legal' (por exemplo, pode comprar álcool, votar, obter pensão, ingressar no exército, obter carteira de motorista). Considere a idade de beber nos EUA (21 anos): para a maioria das pessoas, isso corresponde a 7670 dias. São 7671 dias se nascermos antes de 29 de fevereiro no ano bissexto ou a partir de 1 de março antes do ano bissexto. Se nascido em 29 de fevereiro: 28 de fevereiro é 7670 dias e 1 de março é 7671 dias. A escolha é arbitrária , pode ser de qualquer maneira.
desiludido
4
@ CraigYoung Você não entende o que eu quis dizer com filosoficamente. Eu usei esse termo como um contraste legalmente. Se alguém está escrevendo um aplicativo que precisa saber a idade legal de uma pessoa, tudo o que eles precisam saber é como as jurisdições legais em que seu aplicativo é usado / tratam as pessoas nascidas em 29 de fevereiro. Se, no entanto, estamos falando sobre como isso deve ser tratado, então isso é por definição, filosofia. E sim, a opinião que eu dei é a minha própria opinião, mas como eu disse, eu acho que seria mais fácil para defender primeiro marco do que seria para fevereiro 28.
jfren484
110

A resposta simples para isso é aplicar AddYearscomo mostrado abaixo, porque este é o único método nativo para adicionar anos ao dia 29 de fevereiro dos anos bissextos e obter o resultado correto do dia 28 de fevereiro para os anos comuns.

Alguns acham que 1º de março é o aniversário dos pulos, mas nem o .Net nem nenhuma regra oficial apóiam isso, nem a lógica comum explica por que alguns nascidos em fevereiro devem ter 75% de seus aniversários em outro mês.

Além disso, um método Age se presta a ser adicionado como uma extensão para DateTime. Com isso, você pode obter a idade da maneira mais simples possível:

  1. Item da lista

int idade = 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;
    }
}

Agora, execute este teste:

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

O exemplo da data crítica é este:

Data de nascimento: 2000-02-29 Data posterior: 2011-02-28 Idade: 11

Resultado:

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

E para a data posterior 28/02/2012:

{
    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
}
camelCasus
fonte
4
Um comentário sobre o aniversário de 29 de fevereiro no dia 1º de março, tecnicamente, no dia 28 de fevereiro é muito cedo (um dia antes). No dia 1 é tarde demais. Mas como o aniversário termina, usar o 1º para calcular a idade em anos não bissextos faz mais sentido para mim, pois essa pessoa é realmente tão velha em 1º de março (e 2º e 3º) todos os anos, mas não em 28 de fevereiro.
24418 CyberClaw
1
Do ponto de vista do design de software, escrever isso como um método de extensão não faz muito sentido para mim. date.Age(other)?
Marsze # 12/18
90

Minha sugestão

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

Parece que o ano está mudando na data certa. (Eu testei até 107 anos de idade.)

James Curran
fonte
26
Eu não acho que Harry Patch teria apreciado a sua metodologia de testes local: latimes.com/news/obituaries/...
MusiGenesis
3
Google dizdays in a year = 365.242199
mpen 12/08
12
A duração média de um ano no calendário gregoriano é 365,2425 dias.
dan04
4
Eu diria que esta é uma das soluções mais simples e é boa o suficiente . Quem se importa se eu tenho meio dia antes do meu aniversário de X e o programa diz que eu tenho X anos? O programa está mais ou menos certo, embora não matematicamente. Eu realmente gosto desta solução.
Peter Perháč
13
^^ Porque às vezes é importante. Nos meus testes, isso falha no aniversário das pessoas, informa-as mais jovens do que são.
ChadT
76

Outra função, não por mim, mas encontrada na web e refinada um pouco:

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

Apenas duas coisas que me vêm à cabeça: E as pessoas de países que não usam o calendário gregoriano? DateTime.Now está na cultura específica do servidor, eu acho. Não tenho absolutamente nenhum conhecimento sobre como trabalhar com calendários asiáticos e não sei se há uma maneira fácil de converter datas entre calendários, mas, caso você esteja se perguntando sobre os chineses do ano 4660 :-)

Michael Stum
fonte
Isso parece lidar melhor com diferentes regiões (formatos de data).
precisa saber é o seguinte
53

2 Os principais problemas a serem resolvidos são:

1. Calcule a idade exata - em anos, meses, dias etc.

2. Calcular a idade geralmente percebida - as pessoas geralmente não se importam com a idade exata, apenas se importam com o aniversário do ano atual.


A solução para 1 é óbvia:

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

A solução para 2 é a que não é tão precisa na determinação da idade total, mas é percebida como precisa pelas pessoas. As pessoas também costumam usá-lo, quando calculam sua idade "manualmente":

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
}

Notas 2:

  • Esta é a minha solução preferida
  • Não podemos usar DateTime.DayOfYear ou TimeSpans, pois eles mudam o número de dias nos anos bissextos
  • Coloquei pouco mais linhas para facilitar a leitura

Apenas mais uma observação ... Eu criaria dois métodos estáticos sobrecarregados, um para uso universal e outro para facilidade de uso:

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

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

Aqui está uma frase:

int age = new DateTime(DateTime.Now.Subtract(birthday).Ticks).Year-1;
SillyMonkey
fonte
23
Isto está quebrado. Tornado testável: estático público int CalculateAge (DateTime dateOfBirth, DateTime dateToCalculateAge) {retorna novo DateTime (dateToCalculateAge.Subtract (dateOfBirth) .Ticks) .Year - 1; } ... Dá 14 anos quando insiro 1990-06-01 e calculo a idade no dia ANTES de seu 14º aniversário (31/05/1990).
precisa saber é o seguinte
43

Esta é a versão que usamos aqui. Funciona e é bastante simples. É a mesma ideia que a de Jeff, mas acho que é um pouco mais clara, porque separa a lógica de subtrair uma, por isso é um pouco mais fácil de entender.

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

Você pode expandir o operador ternário para torná-lo ainda mais claro, se achar que esse tipo de coisa não é clara.

Obviamente, isso é feito como um método de extensão DateTime, mas claramente você pode pegar aquela linha de código que faz o trabalho e colocá-la em qualquer lugar. Aqui temos outra sobrecarga do método Extension que passa DateTime.Now, apenas para ser completo.

David Wengier
fonte
6
Eu acho que isso pode ser desativado em um dia quando exatamente um dos dateOfBirth ou dateAsAt cair em um ano bissexto. Considere a idade de uma pessoa nascida em 1º de março de 2003 em 29 de fevereiro de 2004. Para corrigir isso, é necessário fazer uma comparação lexicográfica de pares (Month, DayOfMonth) e usá-la como condicional.
Doug McClean
1
também não mostrará a idade certa a partir do seu aniversário.
dotjoe
43

A melhor maneira que eu conheço por causa dos anos bissextos e tudo é:

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

Eu uso isso:

public static class DateTimeExtensions
{
    public static int Age(this DateTime birthDate)
    {
        return Age(birthDate, DateTime.Now);
    }

    public static int Age(this DateTime birthDate, DateTime offsetDate)
    {
        int result=0;
        result = offsetDate.Year - birthDate.Year;

        if (offsetDate.DayOfYear < birthDate.DayOfYear)
        {
              result--;
        }

        return result;
    }
}
Elmer
fonte
32

Isso dá "mais detalhes" a esta pergunta. Talvez seja isso que você está procurando

DateTime birth = new DateTime(1974, 8, 29);
DateTime today = DateTime.Now;
TimeSpan span = today - birth;
DateTime age = DateTime.MinValue + span;

// Make adjustment due to MinValue equalling 1/1/1
int years = age.Year - 1;
int months = age.Month - 1;
int days = age.Day - 1;

// Print out not only how many years old they are but give months and days as well
Console.Write("{0} years, {1} months, {2} days", years, months, days);
Jacqueline Loriault
fonte
1
Isso não funciona o tempo todo. Adicionar um Span ao DateTime.MinValue poderia funcionar, isso não leva em consideração os anos bissextos, etc. Data agora.
Athanasios Kataras
3
O próprio período de tempo leva automaticamente em consideração os anos bissextos entre duas datas, então não tenho certeza do que você está falando. Eu perguntei nos fóruns da Microsoft e a Microsoft confirmou que leva em consideração os anos bissextos entre duas datas.
Jacqueline Loriault 23/10
2
Considere os DOIS senarios a seguir. 1 DateTime.Now é 1/1/2001 e uma criança nasce em 1/1/2000. 2000 é um ano bissexto e o resultado será 1 ano, 0 mês e 1 dia. No segundo senarion, DateTime.Now é 1/1/2002 e a criança nasce em 1/1/2001. Nesse caso, o resultado será de 1 ano, 0 meses e 0 dias. Isso acontecerá porque você está adicionando o período em um ano sem salto. Se DateTime.MinValue fosse um ano bissexto, os resultados seriam 1 ano no primeiro e 0 anos 11 meses e 30 dias. (Experimente no seu código).
Athanasios Kataras
1
Voto a favor! Eu vim com uma solução praticamente idêntica (usei DateTime.MinValue.AddTicks (span.Ticks) em vez de +, mas o resultado é o mesmo e o seu possui alguns caracteres a menos de código).
Makotosan
4
Você está certo, não é. Mas se fosse esse seria o resultado. Por que isso Importa? Não faz. Em ambos os casos, pule ou não, há exemplos em que isso não funciona. Era isso que eu queria mostrar. O DIFF está correto. O período leva em consideração os anos bissextos. Mas ADICIONAR a uma data base não é. Experimente os exemplos no código e você verá que estou certo.
Athanasios Kataras /
28

Eu criei uma Função Definida pelo Usuário do SQL Server para calcular a idade de alguém, dada a data de nascimento. Isso é útil quando você precisa dele como parte de uma consulta:

using System;
using System.Data;
using System.Data.Sql;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public partial class UserDefinedFunctions
{
    [SqlFunction(DataAccess = DataAccessKind.Read)]
    public static SqlInt32 CalculateAge(string strBirthDate)
    {
        DateTime dtBirthDate = new DateTime();
        dtBirthDate = Convert.ToDateTime(strBirthDate);
        DateTime dtToday = DateTime.Now;

        // get the difference in years
        int years = dtToday.Year - dtBirthDate.Year;

        // subtract another year if we're before the
        // birth day in the current year
        if (dtToday.Month < dtBirthDate.Month || (dtToday.Month == dtBirthDate.Month && dtToday.Day < dtBirthDate.Day))
            years=years-1;

        int intCustomerAge = years;
        return intCustomerAge;
    }
};
user2601
fonte
28

Aqui está mais uma resposta:

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

Isso foi extensivamente testado por unidade. Parece um pouco "mágico". O número 372 é o número de dias que haveria em um ano se todos os meses tivessem 31 dias.

A explicação de por que funciona ( levantada daqui ) é:

Vamos definir Yn = DateTime.Now.Year, Yb = birthday.Year, Mn = DateTime.Now.Month, Mb = birthday.Month, Dn = DateTime.Now.Day, Db = birthday.Day

age = Yn - Yb + (31*(Mn - Mb) + (Dn - Db)) / 372

Sabemos que precisamos ou Yn-Ybse a data já foi alcançada, Yn-Yb-1se não tiver.

a) Se Mn<Mbtemos-341 <= 31*(Mn-Mb) <= -31 and -30 <= Dn-Db <= 30

-371 <= 31*(Mn - Mb) + (Dn - Db) <= -1

Com divisão inteira

(31*(Mn - Mb) + (Dn - Db)) / 372 = -1

b) Se Mn=Mbe Dn<Db, temos31*(Mn - Mb) = 0 and -30 <= Dn-Db <= -1

Com divisão inteira, novamente

(31*(Mn - Mb) + (Dn - Db)) / 372 = -1

c) Se Mn>Mbtivermos31 <= 31*(Mn-Mb) <= 341 and -30 <= Dn-Db <= 30

1 <= 31*(Mn - Mb) + (Dn - Db) <= 371

Com divisão inteira

(31*(Mn - Mb) + (Dn - Db)) / 372 = 0

d) Se Mn=Mbe Dn>Db, temos 31*(Mn - Mb) = 0 and 1 <= Dn-Db <= 30

Com divisão inteira, novamente

(31*(Mn - Mb) + (Dn - Db)) / 372 = 0

e) Se Mn=Mbe Dn=Db, temos31*(Mn - Mb) + Dn-Db = 0

e portanto (31*(Mn - Mb) + (Dn - Db)) / 372 = 0

Matthew Watson
fonte
3
Eu me deparei com essa discussão longa e irritante, e sua solução é uma abordagem muito boa e pequena. Obrigado por mantê-lo simples
nabuchodonossor
25

Passei algum tempo trabalhando nisso e criei isso para calcular a idade de alguém em anos, meses e dias. Eu testei contra o problema de 29 de fevereiro e os anos bissextos e parece funcionar, eu gostaria de receber comentários:

public void LoopAge(DateTime myDOB, DateTime FutureDate)
{
    int years = 0;
    int months = 0;
    int days = 0;

    DateTime tmpMyDOB = new DateTime(myDOB.Year, myDOB.Month, 1);

    DateTime tmpFutureDate = new DateTime(FutureDate.Year, FutureDate.Month, 1);

    while (tmpMyDOB.AddYears(years).AddMonths(months) < tmpFutureDate)
    {
        months++;

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

    if (FutureDate.Day >= myDOB.Day)
    {
        days = days + FutureDate.Day - myDOB.Day;
    }
    else
    {
        months--;

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

        days +=
            DateTime.DaysInMonth(
                FutureDate.AddMonths(-1).Year, FutureDate.AddMonths(-1).Month
            ) + FutureDate.Day - myDOB.Day;

    }

    //add an extra day if the dob is a leap day
    if (DateTime.IsLeapYear(myDOB.Year) && myDOB.Month == 2 && myDOB.Day == 29)
    {
        //but only if the future date is less than 1st March
        if (FutureDate >= new DateTime(FutureDate.Year, 3, 1))
            days++;
    }

}
Jon
fonte
21

Precisamos considerar pessoas com menos de 1 ano? como cultura chinesa, descrevemos a idade dos bebês pequenos como 2 meses ou 4 semanas.

Abaixo está minha implementação, não é tão simples quanto eu imaginava, especialmente para lidar com datas como 2/28.

public static string HowOld(DateTime birthday, DateTime now)
{
    if (now < birthday)
        throw new ArgumentOutOfRangeException("birthday must be less than now.");

    TimeSpan diff = now - birthday;
    int diffDays = (int)diff.TotalDays;

    if (diffDays > 7)//year, month and week
    {
        int age = now.Year - birthday.Year;

        if (birthday > now.AddYears(-age))
            age--;

        if (age > 0)
        {
            return age + (age > 1 ? " years" : " year");
        }
        else
        {// month and week
            DateTime d = birthday;
            int diffMonth = 1;

            while (d.AddMonths(diffMonth) <= now)
            {
                diffMonth++;
            }

            age = diffMonth-1;

            if (age == 1 && d.Day > now.Day)
                age--;

            if (age > 0)
            {
                return age + (age > 1 ? " months" : " month");
            }
            else
            {
                age = diffDays / 7;
                return age + (age > 1 ? " weeks" : " week");
            }
        }
    }
    else if (diffDays > 0)
    {
        int age = diffDays;
        return age + (age > 1 ? " days" : " day");
    }
    else
    {
        int age = diffDays;
        return "just born";
    }
}

Esta implementação passou abaixo dos casos de teste.

[TestMethod]
public void TestAge()
{
    string age = HowOld(new DateTime(2011, 1, 1), new DateTime(2012, 11, 30));
    Assert.AreEqual("1 year", age);

    age = HowOld(new DateTime(2011, 11, 30), new DateTime(2012, 11, 30));
    Assert.AreEqual("1 year", age);

    age = HowOld(new DateTime(2001, 1, 1), new DateTime(2012, 11, 30));
    Assert.AreEqual("11 years", age);

    age = HowOld(new DateTime(2012, 1, 1), new DateTime(2012, 11, 30));
    Assert.AreEqual("10 months", age);

    age = HowOld(new DateTime(2011, 12, 1), new DateTime(2012, 11, 30));
    Assert.AreEqual("11 months", age);

    age = HowOld(new DateTime(2012, 10, 1), new DateTime(2012, 11, 30));
    Assert.AreEqual("1 month", age);

    age = HowOld(new DateTime(2008, 2, 28), new DateTime(2009, 2, 28));
    Assert.AreEqual("1 year", age);

    age = HowOld(new DateTime(2008, 3, 28), new DateTime(2009, 2, 28));
    Assert.AreEqual("11 months", age);

    age = HowOld(new DateTime(2008, 3, 28), new DateTime(2009, 3, 28));
    Assert.AreEqual("1 year", age);

    age = HowOld(new DateTime(2009, 1, 28), new DateTime(2009, 2, 28));
    Assert.AreEqual("1 month", age);

    age = HowOld(new DateTime(2009, 2, 1), new DateTime(2009, 3, 1));
    Assert.AreEqual("1 month", age);

    // NOTE.
    // new DateTime(2008, 1, 31).AddMonths(1) == new DateTime(2009, 2, 28);
    // new DateTime(2008, 1, 28).AddMonths(1) == new DateTime(2009, 2, 28);
    age = HowOld(new DateTime(2009, 1, 31), new DateTime(2009, 2, 28));
    Assert.AreEqual("4 weeks", age);

    age = HowOld(new DateTime(2009, 2, 1), new DateTime(2009, 2, 28));
    Assert.AreEqual("3 weeks", age);

    age = HowOld(new DateTime(2009, 2, 1), new DateTime(2009, 3, 1));
    Assert.AreEqual("1 month", age);

    age = HowOld(new DateTime(2012, 11, 5), new DateTime(2012, 11, 30));
    Assert.AreEqual("3 weeks", age);

    age = HowOld(new DateTime(2012, 11, 1), new DateTime(2012, 11, 30));
    Assert.AreEqual("4 weeks", age);

    age = HowOld(new DateTime(2012, 11, 20), new DateTime(2012, 11, 30));
    Assert.AreEqual("1 week", age);

    age = HowOld(new DateTime(2012, 11, 25), new DateTime(2012, 11, 30));
    Assert.AreEqual("5 days", age);

    age = HowOld(new DateTime(2012, 11, 29), new DateTime(2012, 11, 30));
    Assert.AreEqual("1 day", age);

    age = HowOld(new DateTime(2012, 11, 30), new DateTime(2012, 11, 30));
    Assert.AreEqual("just born", age);

    age = HowOld(new DateTime(2000, 2, 29), new DateTime(2009, 2, 28));
    Assert.AreEqual("8 years", age);

    age = HowOld(new DateTime(2000, 2, 29), new DateTime(2009, 3, 1));
    Assert.AreEqual("9 years", age);

    Exception e = null;

    try
    {
        age = HowOld(new DateTime(2012, 12, 1), new DateTime(2012, 11, 30));
    }
    catch (ArgumentOutOfRangeException ex)
    {
        e = ex;
    }

    Assert.IsTrue(e != null);
}

Espero que seja útil.

rockXrock
fonte
20

Mantendo-o simples (e possivelmente estúpido :)).

DateTime birth = new DateTime(1975, 09, 27, 01, 00, 00, 00);
TimeSpan ts = DateTime.Now - birth;
Console.WriteLine("You are approximately " + ts.TotalSeconds.ToString() + " seconds old.");
user181261
fonte
O TimeSpan foi minha primeira escolha, mas descobriu que ele não oferece uma propriedade TotalYears. Você pode tentar (ts.TotalDays / 365) - mas isso não explica anos bissextos etc.
Lazlow
19

A maneira mais simples que eu já encontrei é essa. Funciona corretamente para os locais dos EUA e da Europa Ocidental. Não posso falar com outros locais, especialmente lugares como a China. 4 compara mais, no máximo, após o cálculo inicial da idade.

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

Eu estava analisando as respostas para isso e notei que ninguém fez referência às implicações regulatórias / legais dos nascimentos em dias bissextos. Por exemplo, por Wikipedia , se você nasceu em 29 de fevereiro em várias jurisdições, seu aniversário sem ano bissexto varia:

  • No Reino Unido e Hong Kong: é o dia ordinal do ano, então no dia seguinte, 1º de março é seu aniversário.
  • Na Nova Zelândia: é o dia anterior, 28 de fevereiro para fins de licença de motorista e 1º de março para outros fins.
  • Taiwan: é 28 de fevereiro.

E, tanto quanto eu posso dizer, nos EUA, os estatutos ficam em silêncio sobre o assunto, deixando o direito comum e a forma como vários órgãos reguladores definem as coisas em seus regulamentos.

Para esse fim, uma melhoria:

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

Note-se que este código assume:

  • Um acerto de contas ocidental (europeu) da idade e
  • Um calendário, como o calendário gregoriano que insere um único dia bissexto no final de um mês.
Nicholas Carey
fonte
19
TimeSpan diff = DateTime.Now - birthdayDateTime;
string age = String.Format("{0:%y} years, {0:%M} months, {0:%d}, days old", diff);

Não sei exatamente como você gostaria que ela retornasse para você, então acabei de criar uma sequência legível.

Dakotah Hicock
fonte
18

Esta não é uma resposta direta, mas mais um raciocínio filosófico sobre o problema em questão, de um ponto de vista quase científico.

Eu argumentaria que a pergunta não especifica a unidade nem a cultura na qual medir a idade; a maioria das respostas parece assumir uma representação anual inteira. A unidade SI para o tempo é second, portanto, a resposta genérica correta deve ser (naturalmente assumindo normalizada DateTimee não levando em consideração os efeitos relativísticos):

var lifeInSeconds = (DateTime.Now.Ticks - then.Ticks)/TickFactor;

Na maneira cristã de calcular a idade em anos:

var then = ... // Then, in this case the birthday
var now = DateTime.UtcNow;
int age = now.Year - then.Year;
if (now.AddYears(-age) < then) age--;

Nas finanças, existe um problema semelhante ao calcular algo conhecido como Fração de contagem de dias , que é aproximadamente um número de anos para um determinado período. E a questão da idade é realmente uma questão de medir o tempo.

Exemplo para a convenção real / real (contando todos os dias "corretamente"):

DateTime start, end = .... // Whatever, assume start is before end

double startYearContribution = 1 - (double) start.DayOfYear / (double) (DateTime.IsLeapYear(start.Year) ? 366 : 365);
double endYearContribution = (double)end.DayOfYear / (double)(DateTime.IsLeapYear(end.Year) ? 366 : 365);
double middleContribution = (double) (end.Year - start.Year - 1);

double DCF = startYearContribution + endYearContribution + middleContribution;

Outra maneira bastante comum de medir o tempo geralmente é "serializando" (o cara que nomeou essa convenção de data deve estar seriamente enganando):

DateTime start, end = .... // Whatever, assume start is before end
int days = (end - start).Days;

Eu me pergunto quanto tempo temos para que a idade relativística em segundos se torne mais útil do que a aproximação aproximada dos ciclos da Terra ao redor do sol durante a vida até agora :) Ou, em outras palavras, quando um período deve ter um local ou uma função que representa o movimento para ser válido :)

flindeberg
fonte
O que é o TickFactor ?
Protígua
@ Carrapatos protíguos por segundo, usados ​​para normalizar os ticks para segundos.
flindeberg
17

Aqui está uma solução.

DateTime dateOfBirth = new DateTime(2000, 4, 18);
DateTime currentDate = DateTime.Now;

int ageInYears = 0;
int ageInMonths = 0;
int ageInDays = 0;

ageInDays = currentDate.Day - dateOfBirth.Day;
ageInMonths = currentDate.Month - dateOfBirth.Month;
ageInYears = currentDate.Year - dateOfBirth.Year;

if (ageInDays < 0)
{
    ageInDays += DateTime.DaysInMonth(currentDate.Year, currentDate.Month);
    ageInMonths = ageInMonths--;

    if (ageInMonths < 0)
    {
        ageInMonths += 12;
        ageInYears--;
    }
}

if (ageInMonths < 0)
{
    ageInMonths += 12;
    ageInYears--;
}

Console.WriteLine("{0}, {1}, {2}", ageInYears, ageInMonths, ageInDays);
Rajeshwaran SP
fonte
Com concat corda, isso seria possível: 47 Yrs 11 Mo 7 dias
JoshYates1980
16

Esta é uma das respostas mais precisas capazes de resolver o aniversário de 29 de fevereiro em comparação com qualquer ano de 28 de fevereiro.

public int GetAge(DateTime birthDate)
{
    int age = DateTime.Now.Year - birthDate.Year;

    if (birthDate.DayOfYear > DateTime.Now.DayOfYear)
        age--;

    return age;
}



mjb
fonte
É hoje! (O próximo daqui a quatro anos.)
Peter Mortensen
15

Eu tenho um método personalizado para calcular a idade, além de uma mensagem de validação de bônus, caso isso ajude:

public void GetAge(DateTime dob, DateTime now, out int years, out int months, out int days)
{
    years = 0;
    months = 0;
    days = 0;

    DateTime tmpdob = new DateTime(dob.Year, dob.Month, 1);
    DateTime tmpnow = new DateTime(now.Year, now.Month, 1);

    while (tmpdob.AddYears(years).AddMonths(months) < tmpnow)
    {
        months++;
        if (months > 12)
        {
            years++;
            months = months - 12;
        }
    }

    if (now.Day >= dob.Day)
        days = days + now.Day - dob.Day;
    else
    {
        months--;
        if (months < 0)
        {
            years--;
            months = months + 12;
        }
        days += DateTime.DaysInMonth(now.AddMonths(-1).Year, now.AddMonths(-1).Month) + now.Day - dob.Day;
    }

    if (DateTime.IsLeapYear(dob.Year) && dob.Month == 2 && dob.Day == 29 && now >= new DateTime(now.Year, 3, 1))
        days++;

}   

private string ValidateDate(DateTime dob) //This method will validate the date
{
    int Years = 0; int Months = 0; int Days = 0;

    GetAge(dob, DateTime.Now, out Years, out Months, out Days);

    if (Years < 18)
        message =  Years + " is too young. Please try again on your 18th birthday.";
    else if (Years >= 65)
        message = Years + " is too old. Date of Birth must not be 65 or older.";
    else
        return null; //Denotes validation passed
}

Chame o método aqui e passe o valor de data e hora (MM / dd / aaaa se o servidor estiver definido como localidade dos EUA). Substitua por qualquer caixa de mensagens ou recipiente para exibir:

DateTime dob = DateTime.Parse("03/10/1982");  

string message = ValidateDate(dob);

lbldatemessage.Visible = !StringIsNullOrWhitespace(message);
lbldatemessage.Text = message ?? ""; //Ternary if message is null then default to empty string

Lembre-se de que você pode formatar a mensagem da maneira que desejar.

Nicker-Knickerless
fonte
14

Que tal esta solução?

static string CalcAge(DateTime birthDay)
{
    DateTime currentDate = DateTime.Now;         
    int approximateAge = currentDate.Year - birthDay.Year;
    int daysToNextBirthDay = (birthDay.Month * 30 + birthDay.Day) - 
        (currentDate.Month * 30 + currentDate.Day) ;

    if (approximateAge == 0 || approximateAge == 1)
    {                
        int month =  Math.Abs(daysToNextBirthDay / 30);
        int days = Math.Abs(daysToNextBirthDay % 30);

        if (month == 0)
            return "Your age is: " + daysToNextBirthDay + " days";

        return "Your age is: " + month + " months and " + days + " days"; ;
    }

    if (daysToNextBirthDay > 0)
        return "Your age is: " + --approximateAge + " Years";

    return "Your age is: " + approximateAge + " Years"; ;
}
Doron
fonte
12
private int GetAge(int _year, int _month, int _day
{
    DateTime yourBirthDate= new DateTime(_year, _month, _day);

    DateTime todaysDateTime = DateTime.Today;
    int noOfYears = todaysDateTime.Year - yourBirthDate.Year;

    if (DateTime.Now.Month < yourBirthDate.Month ||
        (DateTime.Now.Month == yourBirthDate.Month && DateTime.Now.Day < yourBirthDate.Day))
    {
        noOfYears--;
    }

    return  noOfYears;
}
AEMLoviji
fonte
10

A abordagem a seguir (extraída da biblioteca de período de tempo para a classe .NET DateDiff ) considera o calendário das informações da cultura:

// ----------------------------------------------------------------------
private static int YearDiff( DateTime date1, DateTime date2 )
{
  return YearDiff( date1, date2, DateTimeFormatInfo.CurrentInfo.Calendar );
} // YearDiff

// ----------------------------------------------------------------------
private static int YearDiff( DateTime date1, DateTime date2, Calendar calendar )
{
  if ( date1.Equals( date2 ) )
  {
    return 0;
  }

  int year1 = calendar.GetYear( date1 );
  int month1 = calendar.GetMonth( date1 );
  int year2 = calendar.GetYear( date2 );
  int month2 = calendar.GetMonth( date2 );

  // find the the day to compare
  int compareDay = date2.Day;
  int compareDaysPerMonth = calendar.GetDaysInMonth( year1, month1 );
  if ( compareDay > compareDaysPerMonth )
  {
    compareDay = compareDaysPerMonth;
  }

  // build the compare date
  DateTime compareDate = new DateTime( year1, month2, compareDay,
    date2.Hour, date2.Minute, date2.Second, date2.Millisecond );
  if ( date2 > date1 )
  {
    if ( compareDate < date1 )
    {
      compareDate = compareDate.AddYears( 1 );
    }
  }
  else
  {
    if ( compareDate > date1 )
    {
      compareDate = compareDate.AddYears( -1 );
    }
  }
  return year2 - calendar.GetYear( compareDate );
} // YearDiff

Uso:

// ----------------------------------------------------------------------
public void CalculateAgeSamples()
{
  PrintAge( new DateTime( 2000, 02, 29 ), new DateTime( 2009, 02, 28 ) );
  // > Birthdate=29.02.2000, Age at 28.02.2009 is 8 years
  PrintAge( new DateTime( 2000, 02, 29 ), new DateTime( 2012, 02, 28 ) );
  // > Birthdate=29.02.2000, Age at 28.02.2012 is 11 years
} // CalculateAgeSamples

// ----------------------------------------------------------------------
public void PrintAge( DateTime birthDate, DateTime moment )
{
  Console.WriteLine( "Birthdate={0:d}, Age at {1:d} is {2} years", birthDate, moment, YearDiff( birthDate, moment ) );
} // PrintAge
user687474
fonte
10

Essa pergunta clássica merece uma solução Noda Time .

static int GetAge(LocalDate dateOfBirth)
{
    Instant now = SystemClock.Instance.Now;

    // The target time zone is important.
    // It should align with the *current physical location* of the person
    // you are talking about.  When the whereabouts of that person are unknown,
    // then you use the time zone of the person who is *asking* for the age.
    // The time zone of birth is irrelevant!

    DateTimeZone zone = DateTimeZoneProviders.Tzdb["America/New_York"];

    LocalDate today = now.InZone(zone).Date;

    Period period = Period.Between(dateOfBirth, today, PeriodUnits.Years);

    return (int) period.Years;
}

Uso:

LocalDate dateOfBirth = new LocalDate(1976, 8, 27);
int age = GetAge(dateOfBirth);

Você também pode estar interessado nas seguintes melhorias:

  • Passar o relógio como IClock, em vez de usar SystemClock.Instance, melhoraria a testabilidade.

  • O fuso horário alvo provavelmente será alterado, então você também desejará um DateTimeZoneparâmetro.

Veja também meu post sobre este assunto: Manipulação de aniversários e outros aniversários

Matt Johnson
fonte
Você é afiliado à Noda Time?
Zimano 29/03/19
Fiz contribuições para ele, mas é principalmente de Jon Skeet.
Matt Johnson-Pint
9

Usei a solução do ScArcher2 para um cálculo preciso do ano da idade de uma pessoa, mas eu precisava ir além e calcular os meses e dias junto com os anos.

    public static Dictionary<string,int> CurrentAgeInYearsMonthsDays(DateTime? ndtBirthDate, DateTime? ndtReferralDate)
    {
        //----------------------------------------------------------------------
        // Can't determine age if we don't have a dates.
        //----------------------------------------------------------------------
        if (ndtBirthDate == null) return null;
        if (ndtReferralDate == null) return null;

        DateTime dtBirthDate = Convert.ToDateTime(ndtBirthDate);
        DateTime dtReferralDate = Convert.ToDateTime(ndtReferralDate);

        //----------------------------------------------------------------------
        // Create our Variables
        //----------------------------------------------------------------------
        Dictionary<string, int> dYMD = new Dictionary<string,int>();
        int iNowDate, iBirthDate, iYears, iMonths, iDays;
        string sDif = "";

        //----------------------------------------------------------------------
        // Store off current date/time and DOB into local variables
        //---------------------------------------------------------------------- 
        iNowDate = int.Parse(dtReferralDate.ToString("yyyyMMdd"));
        iBirthDate = int.Parse(dtBirthDate.ToString("yyyyMMdd"));

        //----------------------------------------------------------------------
        // Calculate Years
        //----------------------------------------------------------------------
        sDif = (iNowDate - iBirthDate).ToString();
        iYears = int.Parse(sDif.Substring(0, sDif.Length - 4));

        //----------------------------------------------------------------------
        // Store Years in Return Value
        //----------------------------------------------------------------------
        dYMD.Add("Years", iYears);

        //----------------------------------------------------------------------
        // Calculate Months
        //----------------------------------------------------------------------
        if (dtBirthDate.Month > dtReferralDate.Month)
            iMonths = 12 - dtBirthDate.Month + dtReferralDate.Month - 1;
        else
            iMonths = dtBirthDate.Month - dtReferralDate.Month;

        //----------------------------------------------------------------------
        // Store Months in Return Value
        //----------------------------------------------------------------------
        dYMD.Add("Months", iMonths);

        //----------------------------------------------------------------------
        // Calculate Remaining Days
        //----------------------------------------------------------------------
        if (dtBirthDate.Day > dtReferralDate.Day)
            //Logic: Figure out the days in month previous to the current month, or the admitted month.
            //       Subtract the birthday from the total days which will give us how many days the person has lived since their birthdate day the previous month.
            //       then take the referral date and simply add the number of days the person has lived this month.

            //If referral date is january, we need to go back to the following year's December to get the days in that month.
            if (dtReferralDate.Month == 1)
                iDays = DateTime.DaysInMonth(dtReferralDate.Year - 1, 12) - dtBirthDate.Day + dtReferralDate.Day;       
            else
                iDays = DateTime.DaysInMonth(dtReferralDate.Year, dtReferralDate.Month - 1) - dtBirthDate.Day + dtReferralDate.Day;       
        else
            iDays = dtReferralDate.Day - dtBirthDate.Day;             

        //----------------------------------------------------------------------
        // Store Days in Return Value
        //----------------------------------------------------------------------
        dYMD.Add("Days", iDays);

        return dYMD;
}
Dylan Hayes
fonte
9

Versão SQL:

declare @dd smalldatetime = '1980-04-01'
declare @age int = YEAR(GETDATE())-YEAR(@dd)
if (@dd> DATEADD(YYYY, -@age, GETDATE())) set @age = @age -1

print @age  
xenedia
fonte
8

Fiz uma pequena alteração na resposta de Mark Soen : reescrevi a terceira linha para que a expressão pudesse ser analisada um pouco mais facilmente.

public int AgeInYears(DateTime bday)
{
    DateTime now = DateTime.Today;
    int age = now.Year - bday.Year;            
    if (bday.AddYears(age) > now) 
        age--;
    return age;
}

Eu também fiz isso por uma questão de clareza.

cdiggins
fonte