Alter ab Geburtsdatum in Python


159

Wie kann ich ein Alter in Python ab dem heutigen Datum und ein Geburtsdatum einer Person finden? Das Geburtsdatum stammt aus einem DateField in einem Django-Modell.


4
Wenn das Standardmodul datetimenicht ausreicht, können Sie versuchen: labix.org/python-dateutil
Tomasz Zieliński

1
Dies wurde mit ziemlicher Sicherheit gelöst durch:dateutil.relativedelta.relativedelta
Williams

Antworten:


287

Dies kann viel einfacher gemacht werden, wenn man bedenkt, dass int (True) 1 und int (False) 0 ist:

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: Gibt date.today()das Datum in der lokalen Zeitzone zurück, das möglicherweise vom Geburtsort abweicht . Möglicherweise müssen Sie Zeitzonen explizit verwenden
jfs

10
Das hängt wahrscheinlich von Ihrer Definition von "Alter" ab. Für alle praktischen Zwecke wird ein Geburtstag normalerweise als Datum angegeben, nicht als zeitzonenabhängige Datums- / Uhrzeitangabe (dh "geboren" fehlen die Details). Die meisten Menschen werden nicht pünktlich um Mitternacht geboren (also normalerweise vorzeitig beobachten :-)), und wenn sie sich in einer anderen Zeitzone befinden, würde ich davon ausgehen, dass die meisten Menschen ihren Geburtstag in der Ortszeit feiern (das ist, was ich tue, ich lebe 10-12 Stunden im Voraus meiner Geburtsortzeit). Wenn "geboren" eine zeitzonenbezogene Datumszeit wäre, könnten Sie Pytz 'Arithmetik verwenden und normalisieren () - vielleicht von Interesse für eine Astrologie-Software?
Danny W. Adair

2
Ich stimme im Kontext des menschlichen Alters vollkommen zu, aber Ihre Formel kann in einem breiteren Kontext verwendet werden. Obwohl ich persönlich aus familiären Gründen und als Programmierer noch nie eine Stunde früher meinen Geburtstag feiere, ist es trivial, die Zeit zu berechnen, wo immer ich bin.
JFS

@pyd: geboren ist ein Datum / Datum / Uhrzeit
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

Update: Verwenden Sie Dannys Lösung , es ist besser


2
Grundsätzlich sollte Ihr exceptBlock nur die eine bestimmte Ausnahme abfangen , die ausgelöst werden könnte.
Daenyth

1
@Daenyth: Guter Anruf ... ich denke es ist ein ValueError. Aktualisiert.
Mpen

Ich gehe sogar so weit, die Meldung der Ausnahme zu testen, um sicherzustellen, dass sie den Erwartungen entspricht. Selbst mit dem obigen Code besteht die Möglichkeit, dass ein ValueError ausgelöst wird, aber es ist nicht der ValueError, den Sie erwarten.
Randy Syring

+ ausnahmsweise aber gibt es ein problem in meinem ? Ich denke es ist ganz einfach. def calculate_age(dob)
Grijesh Chauhan

1
@GrijeshChauhan: Ja, deine funktioniert nicht. datetime.date(2014, 1, 1)gibt -1, es sollte 0 geben. Sie today > dobprüfen, ob das Geburtsdatum in der Vergangenheit liegt, nicht früher im selben Jahr. datetime.date.today()Enthält Jahresinformationen, weshalb ich sie in meiner Lösung durch das aktuelle Jahr ersetze.
Mpen

18
from datetime import date

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

In Python 3 können Sie eine Division durchführen für datetime.timedelta:

from datetime import date, timedelta

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

2
Jedes vierte Jahr ist ein Schaltjahr, außer jedes hundertste Jahr ist kein Schaltjahr, außer jedes vierhundertste Jahr ist ein Schaltjahr. versuchen Sie Tage_in_Jahr = 365.2425
Dan

3
@ Dan: Der Unterschied zwischen Julian ( 365.25) und Gregorianischem Kalenderjahr ( 365.2425) beträgt weniger als einen Tag, wenn Sie weniger als 130 Jahre leben.
JFS

4
Dies funktioniert für einige Daten nicht: (date(2017, 3, 1) - date(2004, 3, 1)) / timedelta(days=365.2425)sollte zurückkehren 13, aber es kehrt zurück 12. Ohne Boden ist das Ergebnis 12.999582469181433.
href_

13

Wie von @ [Tomasz Zielinski] und @Williams vorgeschlagen, kann Python-Dateutil nur 5 Zeilen.

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

Der einfachste Weg ist die Verwendung 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
Dies funktioniert nicht richtig, wenn der Geburtstag am 29. Februar ist und das heutige Datum der 28. Februar ist (es wird so tun, als ob heute der 29. Februar ist).
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

Datumsinstanz oder ein ähnliches Objekt, docs.python.org/3/library/datetime.html#datetime.date, Tippfehler behoben.
Gzerone

5

Leider können Sie timedelata nicht einfach verwenden, da die größte verwendete Einheit der Tag ist und Schaltjahre Ihre Berechnungen ungültig machen. Lassen Sie uns daher die Anzahl der Jahre ermitteln und um eins anpassen, wenn das letzte Jahr nicht voll ist:

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

Aktualisieren:

Diese Lösung verursacht wirklich eine Ausnahme, wenn der 29. Februar ins Spiel kommt. Hier ist die richtige Prüfung:

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:

Das Anrufen mehrerer Anrufe zu now()einem Leistungstreffer ist lächerlich, es spielt keine Rolle, außer in ganz besonderen Fällen. Der wahre Grund für die Verwendung einer Variablen ist das Risiko von Dateninkonsistenzen.


Vielen Dank, ich habe dies durch einige Tests herausgefunden - und am Ende einen ähnlichen Code gefunden, der über den Link von AndiDog gefunden wurde.
tkalve

3
Strike 1: Sie verwenden datetime.datetime anstelle von datetime.date. Strike 2: Ihr Code ist hässlich und ineffizient. Der Aufruf datetime.now () DREI mal ?? Streik 3: Geburtsdatum 29. Februar 2004 und heutiges Datum 28. Februar 2010 sollten das 6. Lebensjahr zurückgeben, nicht das Kreischen "ValueError: Tag ist für Monat außerhalb des Bereichs". Du bist draußen!
John Machin

Entschuldigung, Ihr "Upd" -Code ist noch barocker und kaputter als der erste Versuch - nichts mit dem 29. Februar zu tun; es schlägt in vielen einfachen Fällen wie 2009-06-15 bis 2010-07-02 fehl ... die Person ist offensichtlich etwas mehr als 1 Jahr alt, aber Sie ziehen ein Jahr ab, weil der Test an den Tagen (2> = 15) fehlschlägt. Und offensichtlich haben Sie es nicht getestet - es enthält einen Syntaxfehler.
John Machin

4

Das klassische Problem in diesem Szenario ist, was mit Menschen zu tun ist, die am 29. Februar geboren wurden. Beispiel: Sie müssen 18 Jahre alt sein, um wählen zu können, ein Auto zu fahren, Alkohol zu kaufen usw. Wenn Sie am 29.02.2004 geboren sind, an welchem ​​ersten Tag dürfen Sie solche Dinge tun: 2022-02 -28 oder 2022-03-01? AFAICT, meistens der erste, aber ein paar Killjoys könnten letzteres sagen.

Hier ist der Code, der ungefähr (ungefähr) 0,068% der an diesem Tag geborenen Bevölkerung entspricht:

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

Hier ist die Ausgabe:

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

Und ich habe kürzlich gerade von der Schaltsekunde erfahren .
Bobort

3

Wenn Sie dies auf einer Seite mit Django-Vorlagen drucken möchten, reicht möglicherweise Folgendes aus:

{{ birth_date|timesince }}

4
Verwenden Sie Djangos nicht |timesincezur Berechnung eines Zeitdeltas über mehrere Jahre, da es keine Schaltjahre berücksichtigt und daher ungenaue Ergebnisse liefert. Weitere Informationen hierzu finden Sie im Django-Ticket Nr. 19210.
jnns

Wusste das nicht. Vielen Dank.
Anoyz

2

Hier ist eine Lösung, um das Alter einer Person als Jahre, Monate oder Tage zu ermitteln.

Lassen Sie uns sagen ein Geburtsdatum der Person ist 2012-01-17T00: 00: 00 Deshalb sein Alter auf 2013-01-16T00: 00: 00 wird 11 Monate

oder wenn er geboren wird 2012-12-17T00: 00: 00 , sein Alter auf 2013-01-12T00: 00: 00 wird 26 Tage

oder wenn er geboren wird 2000-02-29T00: 00: 00 , sein Alter auf 2012-02-29T00: 00: 00 wird 12 Jahre

Sie müssen datetime importieren .

Hier ist der Code:

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

Einige zusätzliche Funktionen, die in den obigen Codes verwendet werden, sind:

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

Und

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

Jetzt müssen wir get_date_format () mit Zeichenfolgen wie 2000-02-29T00: 00: 00 füttern

Es konvertiert es in das Datumstypobjekt, das get_person_age (date_birth, date_today) zugeführt werden soll .

Die Funktion get_person_age (date_birth, date_today) gibt das Alter im Zeichenfolgenformat zurück.


2

Erweiterung von Dannys Lösung , aber mit allen möglichen Möglichkeiten, Alter für jüngere Leute zu melden (Anmerkung, heute ist 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'

Beispielcode:

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

Da ich die korrekte Implementierung nicht gesehen habe, habe ich meine auf diese Weise neu codiert ...

    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)

Die Annahme, am 28. Februar "18" zu sein, wenn man am 29. geboren wird, ist einfach falsch. Das Vertauschen der Grenzen kann weggelassen werden ... es ist nur eine persönliche Annehmlichkeit für meinen Code :)


1

Erweitern Sie auf Danny W. Adair Antwort , um auch Monat zu bekommen

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

Heutiges Datum

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

Dein Geburtstag

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

Dein Alter

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

0

Datum / Uhrzeit importieren

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

In deinem Fall:

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

Leicht modifizierte Dannys Lösung zum leichteren Lesen und Verstehen

    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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.