Umur dari tanggal lahir dengan python


159

Bagaimana saya bisa menemukan umur dalam python dari tanggal hari ini dan tanggal lahir seseorang? Tanggal lahir adalah dari DateField dalam model Django.


4
Ketika datetimemodul standar tidak cukup, Anda dapat mencoba: labix.org/python-dateutil
Tomasz Zieliński

1
Ini hampir pasti telah diselesaikan oleh:dateutil.relativedelta.relativedelta
Williams

Jawaban:


288

Itu bisa dilakukan lebih sederhana mengingat bahwa int (Benar) adalah 1 dan int (Salah) adalah 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()mengembalikan tanggal di zona waktu lokal yang mungkin berbeda dari tempat lahir. Anda mungkin perlu menggunakan zona waktu secara eksplisit
jfs

10
Itu mungkin tergantung pada definisi Anda tentang "usia". Untuk semua tujuan praktis, ulang tahun biasanya diberikan sebagai tanggal, bukan waktu-sadar zona waktu (yaitu, "lahir" tidak ada detailnya). Kebanyakan orang tidak dilahirkan pada tengah malam tajam (jadi biasanya mengamati sebelum waktunya :-)), dan ketika di zona waktu yang berbeda, saya akan berasumsi bahwa sebagian besar orang merayakan ulang tahun mereka di waktu setempat (itulah yang saya lakukan, saya hidup 10-12 jam ke depan waktu kelahiran saya). Jika "lahir" adalah waktu-sadar zona waktu, Anda bisa menggunakan aritmatika dan menormalkan pytz () - mungkin menarik untuk perangkat lunak astrologi?
Danny W. Adair

2
Saya setuju sepenuhnya dalam konteks usia manusia tetapi formula Anda dapat digunakan dalam konteks yang lebih luas. Meskipun saya pribadi tidak pernah merayakan ulang tahun saya bahkan satu jam sebelumnya karena tradisi keluarga dan menjadi seorang programmer, itu sepele untuk menghitung waktu di mana pun saya berada.
jfs

@ pyd: born adalah tanggal / 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

Pembaruan: Gunakan solusi Danny , lebih baik


2
Sama seperti prinsipnya, exceptblok Anda harus menangkap hanya satu pengecualian khusus yang dapat dinaikkan.
Daenyth

1
@ Ketidaksamaan: Panggilan bagus ... Saya pikir ini adalah ValueError. Diperbarui.
mpen

Saya bahkan melangkah lebih jauh dengan menguji pesan pengecualian untuk memastikan apa yang saya harapkan. Bahkan dengan kode di atas, ada kemungkinan ValueError dilemparkan, tetapi bukan ValueError yang Anda harapkan.
Randy Syring

+ untuk pengecualian tetapi, Apakah ada masalah di saya ? Saya pikir ini cukup sederhana. def calculate_age(dob)
Grijesh Chauhan

1
@GrijeshChauhan: Ya, milik Anda tidak berfungsi. datetime.date(2014, 1, 1)memberi -1, itu harus memberi 0. Anda today > dobsedang memeriksa apakah DOB di masa lalu, bukan awal tahun yang sama ini. datetime.date.today()termasuk informasi tahun, itulah sebabnya saya menggantinya dengan tahun berjalan dalam solusi saya.
mpen

18
from datetime import date

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

Di Python 3, Anda bisa melakukan pembagian pada datetime.timedelta:

from datetime import date, timedelta

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

2
setiap tahun keempat adalah tahun kabisat kecuali setiap tahun keseratus bukan tahun kabisat kecuali setiap tahun keempat ratus adalah tahun kabisat. coba days_in_year = 365.2425
Dan

3
@Dan: perbedaan antara 365.25tahun kalender Julian ( ) dan Gregorian ( 365.2425) kurang dari sehari jika Anda hidup kurang dari 130 tahun.
jfs

4
Ini tidak berfungsi untuk beberapa tanggal: (date(2017, 3, 1) - date(2004, 3, 1)) / timedelta(days=365.2425)harus kembali 13, tetapi kembali 12. Unfloored, hasilnya adalah 12.999582469181433.
href_

13

Seperti yang disarankan oleh @ [Tomasz Zielinski] dan @Williams python-dateutil dapat melakukannya hanya 5 baris.

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

Cara paling sederhana adalah menggunakan 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
Ini tidak berfungsi dengan benar ketika ulang tahun pada 29 Februari dan tanggal hari ini adalah 28 Februari (akan bertindak seolah-olah hari ini adalah 29 Februari).
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

tanggal instance atau beberapa keberatan seperti itu, docs.python.org/3/library/datetime.html#datetime.date, kesalahan ketik diperbaiki.
gzerone

5

Sayangnya, Anda tidak bisa hanya menggunakan timedelata karena unit terbesar yang digunakannya adalah hari dan tahun kabisat akan membuat perhitungan Anda tidak valid. Karena itu, mari kita cari jumlah tahun kemudian sesuaikan satu jika tahun lalu tidak penuh:

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

Pembaruan:

Solusi ini benar-benar menyebabkan pengecualian ketika Feb, 29 berperan. Ini cek yang benar:

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:

Memanggil beberapa panggilan ke now()hit kinerja adalah konyol, itu tidak masalah dalam semua kasus tetapi sangat istimewa. Alasan sebenarnya untuk menggunakan variabel adalah risiko inkosistensi data.


Terima kasih, saya mengetahuinya dengan melakukan beberapa tes - dan akhirnya menemukan kode serupa yang ditemukan dari tautan AndiDog.
tkalve

3
Strike 1: Anda menggunakan datetime.datetime alih-alih datetime.date. Strike 2: Kode Anda jelek dan tidak efisien. Memanggil datetime.now () TIGA kali ?? Mogok 3: Tanggal Lahir 29 Feb 2004 dan tanggal hari ini 28 Feb 2010 harus mengembalikan usia 6, tidak berteriak "ValueError: hari di luar jangkauan untuk bulan". Kamu keluar!
John Machin

Maaf, kode "Pembaruan" Anda bahkan lebih barok dan rusak daripada upaya pertama - tidak ada hubungannya dengan 29 Februari; gagal dalam BANYAK kasus sederhana seperti 2009-06-15 hingga 2010-07-02 ... orang tersebut jelas berusia di atas 1 tahun tetapi Anda mengurangi setahun karena pengujian pada hari-hari (2> = 15) gagal. Dan jelas Anda belum mengujinya - ini berisi kesalahan sintaksis.
John Machin

4

Gotcha klasik dalam skenario ini adalah apa yang harus dilakukan dengan orang yang lahir pada tanggal 29 Februari. Contoh: Anda harus berusia 18 tahun untuk memilih, mengendarai mobil, membeli alkohol, dll ... jika Anda dilahirkan pada 2004-02-29, apa hari pertama Anda diizinkan melakukan hal-hal seperti itu: 2022-02 -28, atau 2022-03-01? AFAICT, kebanyakan yang pertama, tetapi beberapa killjoys mungkin mengatakan yang terakhir.

Berikut kode yang sesuai untuk 0,068% (kurang-lebih) dari populasi yang lahir pada hari itu:

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

Inilah hasilnya:

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

Dan saya baru saja belajar tentang lompatan kedua .
Bobort

3

Jika Anda ingin mencetak ini di halaman menggunakan templat Django, maka berikut ini mungkin cukup:

{{ birth_date|timesince }}

4
Jangan gunakan Django |timesinceuntuk menghitung timedelta selama beberapa tahun karena itu tidak memperhitungkan tahun kabisat dan karenanya menghasilkan hasil yang tidak akurat. Lihat tiket Django # 19210 untuk info lebih lanjut tentang ini.
jnns

Tidak tahu itu. Terima kasih.
Anoyz

2

Berikut adalah solusi untuk menemukan usia seseorang sebagai tahun atau bulan atau hari.

Katakanlah tanggal lahir seseorang adalah 2012-01-17T00: 00: 00 Karena itu, usianya pada 2013-01-16T00: 00: 00 adalah 11 bulan

atau jika ia dilahirkan pada 2012-12-17T00: 00: 00 , usianya pada 2013-01-12T00: 00: 00 akan menjadi 26 hari

atau jika dia dilahirkan pada 2000-02-29T00: 00: 00 , usianya pada 2012-02-29T00: 00: 00 akan menjadi 12 tahun

Anda perlu mengimpor datetime .

Ini kodenya:

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

Beberapa fungsi tambahan yang digunakan dalam kode di atas adalah:

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

Dan

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

Sekarang, kita harus memberi makan get_date_format () dengan string seperti 2000-02-29T00: 00: 00

Ini akan mengubahnya menjadi objek tipe tanggal yang akan diumpankan ke get_person_age (date_birth, date_today) .

Fungsi get_person_age (date_birth, date_today) akan mengembalikan umur dalam format string.


2

Memperluas Solusi Danny , tetapi dengan segala macam cara untuk melaporkan usia bagi kaum muda (catatan, hari ini 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'

Kode sampel:

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

Karena saya tidak melihat implementasi yang benar, saya recoded saya dengan cara ini ...

    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)

Anggapan sebagai "18" pada tanggal 28 Februari ketika lahir pada tanggal 29 adalah salah. Menukar batas dapat diabaikan ... itu hanya kenyamanan pribadi untuk kode saya :)


1

Perpanjang ke Danny W. Adair Answer , untuk mendapatkan bulan juga

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

Tanggal hari ini

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

Tanggal lahirmu

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

Usia kamu

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

0

mengimpor 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

Dalam kasus Anda:

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

Sedikit modifikasi solusi Danny untuk memudahkan membaca dan memahami

    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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.