Idade desde a data de nascimento em python

159

Como posso encontrar uma idade em python a partir da data de hoje e a data de nascimento de uma pessoa? A data de nascimento é a partir de um DateField em um modelo do Django.

tkalve
fonte
4
Quando o datetimemódulo padrão não é suficiente, você pode tentar: labix.org/python-dateutil
Tomasz Zieliński
1
Isso quase certamente foi resolvido por:dateutil.relativedelta.relativedelta
Williams

Respostas:

288

Isso pode ser muito mais simples, considerando que int (True) é 1 e int (False) é 0:

from datetime import date

def calculate_age(born):
    today = date.today()
    return today.year - born.year - ((today.month, today.day) < (born.month, born.day))
Danny W. Adair
fonte
4
um nitpick: date.today()retorna a data no fuso horário local que pode ser diferente do local de nascimento. Você pode precisar usar fusos horários explicitamente
jfs
10
Isso provavelmente depende da sua definição de "idade". Para todos os fins práticos, geralmente um aniversário é dado como uma data, e não no horário e no horário do fuso horário (ou seja, "nascido" está faltando os detalhes). A maioria das pessoas não nasce à meia-noite em ponto (geralmente observam prematuramente :-)) e, quando em um fuso horário diferente, eu suponho que a maioria das pessoas observa seu aniversário na hora local (é isso que eu faço, vivo 10 a 12 horas à frente) do meu local de nascimento). Se "nascido" fosse um datetime com reconhecimento de fuso horário, você poderia usar a aritmética de pytz e normalizar () - talvez de interesse para um software de astrologia?
Danny W. Adair
2
Concordo plenamente no contexto da era humana, mas sua fórmula pode ser usada em um contexto mais amplo. Embora eu pessoalmente nunca comemore meu aniversário nem uma hora antes, devido a uma tradição familiar e como programador, é trivial calcular o tempo onde quer que eu esteja.
JFS
@pyd: nascido é uma data / datetime
kjagiello
68
from datetime import date

def calculate_age(born):
    today = date.today()
    try: 
        birthday = born.replace(year=today.year)
    except ValueError: # raised when birth date is February 29 and the current year is not a leap year
        birthday = born.replace(year=today.year, month=born.month+1, day=1)
    if birthday > today:
        return today.year - born.year - 1
    else:
        return today.year - born.year

Atualização: use a solução de Danny , é melhor

mpen
fonte
2
Por uma questão de princípio, seu exceptbloco deve capturar apenas a única exceção específica que pode ser gerada.
Daenyth
1
@ Daenyth: Boa ligação ... Eu acho que é uma ValueError. Atualizada.
MPEN
Chego ao ponto de testar a mensagem da exceção para garantir que é o que estou esperando. Mesmo com o código acima, existe a possibilidade de um ValueError ser lançado, mas não é o ValueError que você está esperando.
Randy Syring
+ por exceção, mas, existe algum problema no meu ? Eu acho que é bem simples. def calculate_age(dob)
Grijesh Chauhan
1
@GrijeshChauhan: Sim, o seu não funciona. datetime.date(2014, 1, 1)dá -1, deve dar 0. Você today > dobestá verificando se o DOB ​​está no passado, não no início deste mesmo ano. datetime.date.today()inclui informações de ano, e é por isso que a substituo pelo ano atual em minha solução.
MPEN
18
from datetime import date

days_in_year = 365.2425    
age = int((date.today() - birth_date).days / days_in_year)

No Python 3, você pode executar a divisão em datetime.timedelta:

from datetime import date, timedelta

age = (date.today() - birth_date) // timedelta(days=365.2425)
Único
fonte
2
todo quarto ano é um ano bissexto, exceto oitocentos anos, não é um ano bissexto, exceto que todo quatrocentos anos é um ano bissexto. tente days_in_year = 365.2425
Dan
3
@ Dan: a diferença entre o 365.25ano civil juliano ( ) e gregoriano ( 365.2425) é inferior a um dia se você vive menos de 130 anos.
JFS
4
Isso não funciona em algumas datas: (date(2017, 3, 1) - date(2004, 3, 1)) / timedelta(days=365.2425)deve retornar 13, mas retorna 12. Sem piso, o resultado é 12.999582469181433.
href_
13

Conforme sugerido por @ [Tomasz Zielinski] e @Williams python-dateutil, é possível fazer apenas 5 linhas.

from dateutil.relativedelta import *
from datetime import date
today = date.today()
dob = date(1982, 7, 5)
age = relativedelta(today, dob)

>>relativedelta(years=+33, months=+11, days=+16)`
Arun Kumar Khattri
fonte
10

A maneira mais simples é usar python-dateutil

import datetime

import dateutil

def birthday(date):
    # Get the current date
    now = datetime.datetime.utcnow()
    now = now.date()

    # Get the difference between the current date and the birthday
    age = dateutil.relativedelta.relativedelta(now, date)
    age = age.years

    return age
user1569050
fonte
7
Isso não funciona corretamente quando o aniversário é 29 de fevereiro e a data de hoje é 28 de fevereiro (agirá como se hoje fosse 29 de fevereiro).
Trey Hunner
6
from datetime import date

def age(birth_date):
    today = date.today()
    y = today.year - birth_date.year
    if today.month < birth_date.month or today.month == birth_date.month and today.day < birth_date.day:
        y -= 1
    return y
Gzerone
fonte
date instance ou algum objeto como esse, docs.python.org/3/library/datetime.html#datetime.date, erro de digitação corrigido.
Gzerone 12/11
5

Infelizmente, você não pode simplesmente usar os dados de tempo, pois a maior unidade usada por dia e anos bissextos invalida seus cálculos. Portanto, vamos encontrar o número de anos e ajustar em um se o último ano não estiver cheio:

from datetime import date
birth_date = date(1980, 5, 26)
years = date.today().year - birth_date.year
if (datetime.now() - birth_date.replace(year=datetime.now().year)).days >= 0:
    age = years
else:
    age = years - 1

Upd:

Esta solução realmente causa uma exceção quando 29 de fevereiro entra em jogo. Aqui está a verificação correta:

from datetime import date
birth_date = date(1980, 5, 26)
today = date.today()
years = today.year - birth_date.year
if all((x >= y) for x,y in zip(today.timetuple(), birth_date.timetuple()):
   age = years
else:
   age = years - 1

Upd2:

Fazer várias chamadas para now()uma ocorrência de desempenho é ridículo, isso não importa em todos, exceto em casos extremamente especiais. O verdadeiro motivo para usar uma variável é o risco de incosistência dos dados.

Alexander Lebedev
fonte
Obrigado, eu descobri isso fazendo alguns testes - e acabei com um código semelhante encontrado no link do AndiDog.
precisa saber é o seguinte
3
Greve 1: você está usando datetime.datetime em vez de datetime.date. Greve 2: seu código é feio e ineficiente. Chamando datetime.now () TRÊS vezes ?? Greve 3: data de nascimento em 29 de fevereiro de 2004 e data de hoje em 28 de fevereiro de 2010 deve retornar a idade de 6 anos, não morrer gritando "ValueError: o dia está fora do intervalo por mês". Você está fora!
John Machin
Desculpe, seu código "Upd" é ainda mais barroco e quebrado do que a primeira tentativa - nada a ver com 29 de fevereiro; ele falha em MUITOS casos simples, como 15/06/2009 a 02/07/2010 ... obviamente a pessoa tem pouco mais de 1 ano, mas você deduz um ano porque o teste nos dias (2> = 15) falha. E, obviamente, você não o testou - ele contém um erro de sintaxe.
John Machin
4

A pegadinha clássica nesse cenário é o que fazer com as pessoas nascidas no dia 29 de fevereiro. Exemplo: você precisa ter 18 anos para votar, dirigir um carro, comprar álcool, etc ... se você nasceu em 29-02-2004, qual é o primeiro dia em que você pode fazer isso: 2022-02 -28 ou 2022-03-01? AFAICT, principalmente o primeiro, mas alguns killjoys podem dizer o último.

Aqui está o código que atende a 0,068% (aproximadamente) da população nascida naquele dia:

def age_in_years(from_date, to_date, leap_day_anniversary_Feb28=True):
    age = to_date.year - from_date.year
    try:
        anniversary = from_date.replace(year=to_date.year)
    except ValueError:
        assert from_date.day == 29 and from_date.month == 2
        if leap_day_anniversary_Feb28:
            anniversary = datetime.date(to_date.year, 2, 28)
        else:
            anniversary = datetime.date(to_date.year, 3, 1)
    if to_date < anniversary:
        age -= 1
    return age

if __name__ == "__main__":
    import datetime

    tests = """

    2004  2 28 2010  2 27  5 1
    2004  2 28 2010  2 28  6 1
    2004  2 28 2010  3  1  6 1

    2004  2 29 2010  2 27  5 1
    2004  2 29 2010  2 28  6 1
    2004  2 29 2010  3  1  6 1

    2004  2 29 2012  2 27  7 1
    2004  2 29 2012  2 28  7 1
    2004  2 29 2012  2 29  8 1
    2004  2 29 2012  3  1  8 1

    2004  2 28 2010  2 27  5 0
    2004  2 28 2010  2 28  6 0
    2004  2 28 2010  3  1  6 0

    2004  2 29 2010  2 27  5 0
    2004  2 29 2010  2 28  5 0
    2004  2 29 2010  3  1  6 0

    2004  2 29 2012  2 27  7 0
    2004  2 29 2012  2 28  7 0
    2004  2 29 2012  2 29  8 0
    2004  2 29 2012  3  1  8 0

    """

    for line in tests.splitlines():
        nums = [int(x) for x in line.split()]
        if not nums:
            print
            continue
        datea = datetime.date(*nums[0:3])
        dateb = datetime.date(*nums[3:6])
        expected, anniv = nums[6:8]
        age = age_in_years(datea, dateb, anniv)
        print datea, dateb, anniv, age, expected, age == expected

Aqui está a saída:

2004-02-28 2010-02-27 1 5 5 True
2004-02-28 2010-02-28 1 6 6 True
2004-02-28 2010-03-01 1 6 6 True

2004-02-29 2010-02-27 1 5 5 True
2004-02-29 2010-02-28 1 6 6 True
2004-02-29 2010-03-01 1 6 6 True

2004-02-29 2012-02-27 1 7 7 True
2004-02-29 2012-02-28 1 7 7 True
2004-02-29 2012-02-29 1 8 8 True
2004-02-29 2012-03-01 1 8 8 True

2004-02-28 2010-02-27 0 5 5 True
2004-02-28 2010-02-28 0 6 6 True
2004-02-28 2010-03-01 0 6 6 True

2004-02-29 2010-02-27 0 5 5 True
2004-02-29 2010-02-28 0 5 5 True
2004-02-29 2010-03-01 0 6 6 True

2004-02-29 2012-02-27 0 7 7 True
2004-02-29 2012-02-28 0 7 7 True
2004-02-29 2012-02-29 0 8 8 True
2004-02-29 2012-03-01 0 8 8 True
John Machin
fonte
E eu recentemente aprendi sobre o segundo salto .
Bobort
3

Se você deseja imprimir isso em uma página usando modelos de django, o seguinte pode ser suficiente:

{{ birth_date|timesince }}
Anoyz
fonte
4
Não use o Django |timesincepara calcular um intervalo de tempo ao longo de vários anos, porque ele não conta anos bissextos e, portanto, produz resultados imprecisos. Veja o ticket do Django nº 19210 para mais informações sobre isso.
jnns
Não sabia disso. Obrigado.
Anoyz
2

Aqui está uma solução para encontrar a idade de uma pessoa em anos, meses ou dias.

Digamos que a data de nascimento de uma pessoa seja 2012-01-17T00: 00: 00 Portanto, sua idade em 2013-01-16T00: 00: 00 será de 11 meses

ou se ele nasceu em 2012-12-17T00: 00: 00 , sua idade em 2013-01-12T00: 00: 00 será de 26 dias

ou se ele nasceu em 2000-02-29T00: 00: 00 , sua idade em 2012-02-29T00: 00: 00 será de 12 anos

Você precisará importar data e hora .

Aqui está o código:

def get_person_age(date_birth, date_today):

"""
At top level there are three possibilities : Age can be in days or months or years.
For age to be in years there are two cases: Year difference is one or Year difference is more than 1
For age to be in months there are two cases: Year difference is 0 or 1
For age to be in days there are 4 possibilities: Year difference is 1(20-dec-2012 - 2-jan-2013),
                                                 Year difference is 0, Months difference is 0 or 1
"""
years_diff = date_today.year - date_birth.year
months_diff = date_today.month - date_birth.month
days_diff = date_today.day - date_birth.day
age_in_days = (date_today - date_birth).days

age = years_diff
age_string = str(age) + " years"

# age can be in months or days.
if years_diff == 0:
    if months_diff == 0:
        age = age_in_days
        age_string = str(age) + " days"
    elif months_diff == 1:
        if days_diff < 0:
            age = age_in_days
            age_string = str(age) + " days"
        else:
            age = months_diff
            age_string = str(age) + " months"
    else:
        if days_diff < 0:
            age = months_diff - 1
        else:
            age = months_diff
        age_string = str(age) + " months"
# age can be in years, months or days.
elif years_diff == 1:
    if months_diff < 0:
        age = months_diff + 12
        age_string = str(age) + " months" 
        if age == 1:
            if days_diff < 0:
                age = age_in_days
                age_string = str(age) + " days" 
        elif days_diff < 0:
            age = age-1
            age_string = str(age) + " months"
    elif months_diff == 0:
        if days_diff < 0:
            age = 11
            age_string = str(age) + " months"
        else:
            age = 1
            age_string = str(age) + " years"
    else:
        age = 1
        age_string = str(age) + " years"
# The age is guaranteed to be in years.
else:
    if months_diff < 0:
        age = years_diff - 1
    elif months_diff == 0:
        if days_diff < 0:
            age = years_diff - 1
        else:
            age = years_diff
    else:
        age = years_diff
    age_string = str(age) + " years"

if age == 1:
    age_string = age_string.replace("years", "year").replace("months", "month").replace("days", "day")

return age_string

Algumas funções extras usadas nos códigos acima são:

def get_todays_date():
    """
    This function returns todays date in proper date object format
    """
    return datetime.now()

E

def get_date_format(str_date):
"""
This function converts string into date type object
"""
str_date = str_date.split("T")[0]
return datetime.strptime(str_date, "%Y-%m-%d")

Agora, temos que alimentar get_date_format () com as strings como 2000-02-29T00: 00: 00

Ele será convertido no objeto de tipo de data que será alimentado em get_person_age (date_birth, date_today) .

A função get_person_age (date_birth, date_today) retornará idade no formato de string.

Shubham L
fonte
2

Expandindo a solução de Danny , mas com todo tipo de formas de relatar idades para pessoas mais jovens (observe que hoje é datetime.date(2015,7,17)):

def calculate_age(born):
    '''
        Converts a date of birth (dob) datetime object to years, always rounding down.
        When the age is 80 years or more, just report that the age is 80 years or more.
        When the age is less than 12 years, rounds down to the nearest half year.
        When the age is less than 2 years, reports age in months, rounded down.
        When the age is less than 6 months, reports the age in weeks, rounded down.
        When the age is less than 2 weeks, reports the age in days.
    '''
    today = datetime.date.today()
    age_in_years = today.year - born.year - ((today.month, today.day) < (born.month, born.day))
    months = (today.month - born.month - (today.day < born.day)) %12
    age = today - born
    age_in_days = age.days
    if age_in_years >= 80:
        return 80, 'years or older'
    if age_in_years >= 12:
        return age_in_years, 'years'
    elif age_in_years >= 2:
        half = 'and a half ' if months > 6 else ''
        return age_in_years, '%syears'%half
    elif months >= 6:
        return months, 'months'
    elif age_in_days >= 14:
        return age_in_days/7, 'weeks'
    else:
        return age_in_days, 'days'

Código de amostra:

print '%d %s' %calculate_age(datetime.date(1933,6,12)) # >=80 years
print '%d %s' %calculate_age(datetime.date(1963,6,12)) # >=12 years
print '%d %s' %calculate_age(datetime.date(2010,6,19)) # >=2 years
print '%d %s' %calculate_age(datetime.date(2010,11,19)) # >=2 years with half
print '%d %s' %calculate_age(datetime.date(2014,11,19)) # >=6 months
print '%d %s' %calculate_age(datetime.date(2015,6,4)) # >=2 weeks
print '%d %s' %calculate_age(datetime.date(2015,7,11)) # days old

80 years or older
52 years
5 years
4 and a half years
7 months
6 weeks
7 days
Dannid
fonte
1

Como não vi a implementação correta, recodifiquei a minha dessa maneira ...

    def age_in_years(from_date, to_date=datetime.date.today()):
  if (DEBUG):
    logger.debug("def age_in_years(from_date='%s', to_date='%s')" % (from_date, to_date))

  if (from_date>to_date): # swap when the lower bound is not the lower bound
    logger.debug('Swapping dates ...')
    tmp = from_date
    from_date = to_date
    to_date = tmp

  age_delta = to_date.year - from_date.year
  month_delta = to_date.month - from_date.month
  day_delta = to_date.day - from_date.day

  if (DEBUG):
    logger.debug("Delta's are : %i  / %i / %i " % (age_delta, month_delta, day_delta))

  if (month_delta>0  or (month_delta==0 and day_delta>=0)): 
    return age_delta 

  return (age_delta-1)

A suposição de ter 18 anos no dia 28 de fevereiro, quando nasceu no dia 29, está errada. Trocar os limites pode ser deixado de fora ... é apenas uma conveniência pessoal para o meu código :)

Thomas
fonte
1

Estenda para Danny W. Adair Answer , para obter o mês também

def calculate_age(b):
    t = date.today()
    c = ((t.month, t.day) < (b.month, b.day))
    c2 = (t.day< b.day)
    return t.year - b.year - c,c*12+t.month-b.month-c2
SmartManoj
fonte
1
import datetime

Data de hoje

td=datetime.datetime.now().date() 

Sua data de nascimento

bd=datetime.date(1989,3,15)

Sua idade

age_years=int((td-bd).days /365.25)
Aseem
fonte
0

data e hora da importação

def age(date_of_birth):
    if date_of_birth > datetime.date.today().replace(year = date_of_birth.year):
        return datetime.date.today().year - date_of_birth.year - 1
    else:
        return datetime.date.today().year - date_of_birth.year

No seu caso:

import datetime

# your model
def age(self):
    if self.birthdate > datetime.date.today().replace(year = self.birthdate.year):
        return datetime.date.today().year - self.birthdate.year - 1
    else:
        return datetime.date.today().year - self.birthdate.year
doomatel
fonte
0

Solução Danny ligeiramente modificada para facilitar a leitura e a compreensão

    from datetime import date

    def calculate_age(birth_date):
        today = date.today()
        age = today.year - birth_date.year
        full_year_passed = (today.month, today.day) < (birth_date.month, birth_date.day)
        if not full_year_passed:
            age -= 1
        return age
praga de pecos
fonte