Età dalla data di nascita in pitone


159

Come posso trovare un'età in pitone dalla data odierna e una data di nascita delle persone? La data di nascita è di un DateField in un modello Django.


4
Quando il datetimemodulo standard non è abbastanza, puoi provare: labix.org/python-dateutil
Tomasz Zieliński

1
Ciò è stato quasi certamente risolto da:dateutil.relativedelta.relativedelta
Williams,

Risposte:


288

Questo può essere fatto molto più semplice considerando che 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))

4
a nitpick: date.today()restituisce la data nel fuso orario locale che potrebbe essere diversa dal luogo di nascita. Potrebbe essere necessario utilizzare esplicitamente i fusi orari
jfs

10
Ciò dipende probabilmente dalla tua definizione di "età". A tutti gli effetti pratici, di solito viene dato un compleanno come una data, non un datetime consapevole del fuso orario (ovvero, "nato" non contiene i dettagli). La maggior parte delle persone non è nata a mezzanotte in punto (quindi di solito osservo prematuramente :-)), e quando in un fuso orario diverso, presumo che la maggior parte delle persone osservi il loro compleanno nell'ora locale (è quello che faccio, vivo 10-12 ore in anticipo della mia ora del luogo di nascita). Se "born" fosse un datetime sensibile al fuso orario, potresti usare l'aritmetica di pytz e normalizzare () - forse di interesse per un software di astrologia?
Danny W. Adair,

2
Sono completamente d'accordo nel contesto delle età umane, ma la tua formula può essere utilizzata in un contesto più ampio. Anche se personalmente non festeggio mai il mio compleanno nemmeno un'ora prima a causa di una tradizione familiare ed essendo un programmatore, è banale calcolare il tempo ovunque io sia.
jfs,

@pyd: born is a date / 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

Aggiornamento: usa la soluzione di Danny , è meglio


2
Proprio come una questione di principio, il tuo exceptblocco dovrebbe catturare solo un'eccezione specifica che potrebbe essere sollevata.
Daenyth,

1
@Daenyth: buona chiamata ... penso che sia un ValueError. Aggiornato.
mpen

Vado persino al punto di testare il messaggio dell'eccezione per assicurarmi che sia quello che mi aspetto. Anche con il codice sopra, c'è la possibilità che venga lanciato un ValueError, ma non è il ValueError che ti aspetti.
Randy Syring,

+ per eccezione, ma c'è qualche problema nel mio ? Penso che sia abbastanza semplice. def calculate_age(dob)
Grijesh Chauhan,

1
@GrijeshChauhan: Sì, il tuo non funziona. datetime.date(2014, 1, 1)dà -1, dovrebbe dare 0. Stai today > dobverificando se il DOB è in passato, non all'inizio dello stesso anno. datetime.date.today()include informazioni sull'anno, motivo per cui le sostituisco con l'anno corrente nella mia soluzione.
Aprire il

18
from datetime import date

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

In Python 3, puoi eseguire la divisione su datetime.timedelta:

from datetime import date, timedelta

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

2
ogni quarto anno è un anno bisestile, tranne che ogni millesimo anno non è un anno bisestile, tranne che ogni quattrocentesimo anno è un anno bisestile. try days_in_year = 365.2425
Dan

3
@Dan: la differenza tra Julian ( 365.25) e l'anno civile gregoriano ( 365.2425) è inferiore a un giorno se vivi meno di 130 anni.
jfs

4
Questo non funziona per alcune date: (date(2017, 3, 1) - date(2004, 3, 1)) / timedelta(days=365.2425)dovrebbe tornare 13, ma ritorna 12. Senza pavimento, il risultato è 12.999582469181433.
href_

13

Come suggerito da @ [Tomasz Zielinski] e @Williams python-dateutil può fare solo 5 righe.

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

10

Il modo più semplice sta usando 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

7
Questo non funziona correttamente quando il compleanno è il 29 febbraio e la data di oggi è il 28 febbraio (funzionerà come se oggi fosse il 29 febbraio).
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

istanza data o qualche obj simile, docs.python.org/3/library/datetime.html#datetime.date, errore di battitura.
Gzerone,

5

Sfortunatamente, non puoi semplicemente usare timedelata poiché l'unità più grande che utilizza è il giorno e gli anni bisestili renderanno i calcoli non validi. Pertanto, troviamo il numero di anni, quindi aggiustiamo di uno se l'ultimo anno non è pieno:

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:

Questa soluzione provoca davvero un'eccezione quando entra in gioco il 29 febbraio. Ecco il controllo corretto:

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:

Chiamare più chiamate a now()un successo è ridicolo, non importa in tutti i casi, ma estremamente speciali. Il vero motivo per utilizzare una variabile è il rischio di incosistenza dei dati.


Grazie, l'ho scoperto facendo alcuni test e ho finito con un codice simile trovato dal link di AndiDog.
tkalve,

3
Colpo 1: stai usando datetime.datetime invece di datetime.date. Strike 2: il tuo codice è brutto e inefficiente. Chiamata datetime.now () TRE volte ?? Sciopero 3: data di nascita 29 febbraio 2004 e data odierna 28 febbraio 2010 dovrebbe tornare all'età di 6 anni, non morire urlando "ValueError: day is out of range for month". Sei fuori!
John Machin,

Siamo spiacenti, il tuo codice "Upd" è ancora più barocco e rotto rispetto al primo tentativo - niente a che fare con il 29 febbraio; fallisce in MOLTI casi semplici come dal 15-06-2009 al 02-07-2010 ... la persona ha ovviamente poco più di 1 anno di età ma tu deduci un anno perché il test nei giorni (2> = 15) fallisce. E ovviamente non l'hai ancora testato - contiene un errore di sintassi.
John Machin,

4

Il classico gotcha in questo scenario è cosa fare con le persone nate il 29 febbraio. Esempio: devi avere 18 anni per votare, guidare un'auto, comprare alcolici, ecc ... se sei nato il 29-02-2004, qual è il primo giorno in cui ti è permesso fare queste cose: 2022-02 -28 o 2022-03-01? AFAICT, principalmente il primo, ma alcuni assassini potrebbero dire il secondo.

Ecco il codice che si rivolge allo 0,068% (circa) della popolazione nata in quel giorno:

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

Ecco l'output:

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

E recentemente ho appena saputo del secondo salto .
Bobort,

3

Se stai cercando di stamparlo in una pagina usando i modelli di django, potrebbe essere sufficiente quanto segue:

{{ birth_date|timesince }}

4
Non usare Django |timesinceper calcolare una timedelta per diversi anni perché non tiene conto degli anni bisestili e quindi produce risultati imprecisi. Vedi il biglietto Django # 19210 per maggiori informazioni su questo.
gennaio

Non lo sapevo. Grazie.
Anoyz,

2

Ecco una soluzione per trovare l'età di una persona come anni o mesi o giorni.

Diciamo che la data di nascita di una persona è 2012-01-17T00: 00: 00 Pertanto, la sua età il 16-01-2013T00: 00: 00 sarà di 11 mesi

o se è nato il 17-12-2012T00: 00: 00 , la sua età il 12-01-2013T00: 00: 00 sarà di 26 giorni

o se è nato il 2000-02-29T00: 00: 00 , la sua età il 2012-02-29T00: 00: 00 sarà di 12 anni

Dovrai importare il datetime .

Ecco il codice:

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

Alcune funzioni extra utilizzate nei codici sopra sono:

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

Ora, dobbiamo alimentare get_date_format () con le stringhe come 2000-02-29T00: 00: 00

Lo convertirà nell'oggetto tipo data che deve essere inviato a get_person_age (date_birth, date_today) .

La funzione get_person_age (date_birth, date_today) restituirà l'età in formato stringa.


2

Espandendo la soluzione di Danny , ma con tutti i tipi di modi per segnalare l'età per i giovani (nota, oggi lo è 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'

Codice di esempio:

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

1

Poiché non ho visto la corretta implementazione, ho ricodificato il mio in questo modo ...

    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)

L'ipotesi di essere "18" il 28 febbraio quando è nato il 29 è semplicemente sbagliato. Scambiare i limiti può essere lasciato fuori ... è solo una comodità personale per il mio codice :)


1

Estendi alla risposta di Danny W. Adair , per ottenere anche il mese

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

1
import datetime

Data odierna

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

La tua data di nascita

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

Della tua età

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

0

importare datetime

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

Nel tuo 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

0

La soluzione di Danny leggermente modificata per facilitare la lettura e la comprensione

    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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.