Вік від дати народження в пітоні


159

Як я можу знайти вік пітона на сьогоднішній день та дата народження? Дата народження - це датаДане поле в моделі Джанго.


4
Якщо стандартного datetimeмодуля недостатньо, ви можете спробувати: labix.org/python-dateutil
Tomasz Zieliński

1
Це майже напевно вирішено:dateutil.relativedelta.relativedelta
Вільямс

Відповіді:


288

Це можна зробити набагато простіше, враховуючи, що int (True) дорівнює 1, а 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
нітрик: date.today()повертає дату в місцевому часовому поясі, яка може відрізнятися від місця народження. Можливо, вам потрібно буде використовувати часові
зони

10
Це, мабуть, залежить від вашого визначення поняття "вік". Для всіх практичних цілей день народження, як правило, призначається як дата, а не час, який усвідомлює часовий пояс (тобто "народжені" відсутні дані). Більшість людей не народжуються різко опівночі (так зазвичай спостерігають передчасно :-)), і коли в іншому часовому поясі я вважаю, що більшість людей спостерігають за днем ​​народження за місцевим часом (саме так я і роблю, я живу на 10-12 год попереду часу мого народження). Якщо "народжений" був датою часу, відомий часовим поясом, ви могли б використовувати арифметику pytz і нормалізувати () - можливо, це цікавить програмне забезпечення для астрології?
Danny W. Adair

2
Я повністю погоджуюся в контексті людських віків, але ваша формула може бути використана в більш широкому контексті. Хоча я особисто ніколи не святкую свій день народження навіть на годину раніше через сімейну традицію і будучи програмістом, тривіально обчислювати час, де б я не був.
jfs

@pyd: дата народження / дата
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

Оновлення: Використовуйте рішення Дані , краще


2
Як правило, ваш exceptблок повинен охоплювати лише той конкретний виняток, який можна підняти.
Daenyth

1
@Daenyth: Добрий дзвінок ... Я думаю, що це ValueError. Оновлено.
mpen

Я навіть заходжу так далеко, щоб перевірити повідомлення про виняток, щоб переконатися в тому, що я очікую. Навіть з наведеним вище кодом існує ймовірність того, що ValueError кинуто, але це не ValueError, якого ви очікуєте.
Ренді Сірінг

+ за винятком, але чи є в мене проблема ? Я думаю, що це досить просто. def calculate_age(dob)
Grijesh Chauhan

1
@GrijeshChauhan: Так, ваш не працює. datetime.date(2014, 1, 1)дає -1, він повинен дати 0. Ви today > dobперевіряєте, чи був DOB в минулому, а не раніше цього ж року. datetime.date.today()включає інформацію про рік, тому я замінюю її на поточний рік у своєму рішенні.
квітня

18
from datetime import date

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

У Python 3 ви можете виконати поділ на datetime.timedelta:

from datetime import date, timedelta

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

2
кожен четвертий рік - високосний рік, за винятком того, що кожен сотий рік не є високосним, за винятком того, що кожен чотири сотий рік є високосним. спробуйте days_in_year = 365.2425
Dan

3
@Dan: різниця між 365.25календарним роком Юліана ( ) та григоріанського року ( 365.2425) менша за добу, якщо ви живете менше 130 років.
jfs

4
Це не працює на деякі дати: (date(2017, 3, 1) - date(2004, 3, 1)) / timedelta(days=365.2425)має повернутися 13, але повернеться 12. Без кольору, результат такий 12.999582469181433.
href_

13

Як запропонували @ [Tomasz Zielinski] та @Williams python-dateutil, це може зробити лише 5 рядків.

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

Найпростіший спосіб - це використання 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
Це не працює правильно, коли день народження - 29 лютого, а сьогоднішня дата - 28 лютого (це буде діяти так, ніби сьогодні 29 лютого).
Мисливець Трей

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

екземпляр дати або якийсь подібний об'єкт, docs.python.org/3/library/datetime.html#datetime.date, виправлено помилку.
gzerone

5

На жаль, ви не можете просто використовувати timedelata, оскільки найбільшою одиницею, яку він використовує, є день, а високосні роки зроблять ваші обчислення недійсними. Тому давайте знайдемо кількість років, а потім скоригуємо на одну, якщо останній рік не повний:

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

Оновлено:

Це рішення справді стає винятком, коли 29 лютого вступає в гру. Ось правильна перевірка:

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:

Називати декілька дзвінків на now()ударну виставу смішно, це не має значення у всіх, але надзвичайно особливих випадках. Справжня причина використання змінної - це ризик інкосистентності даних.


Дякую, я дізнався це, зробивши кілька тестів - і в кінцевому підсумку схожий код, знайдений за посиланням AndiDog.
tkalve

3
Страйк 1: Ви використовуєте datetime.datetime замість datetime.date. Страйк 2: Ваш код некрасивий і неефективний. Виклик datetime.now () ТРИ рази ?? Страйк 3: Дата народження 29 лютого 2004 року та сьогоднішня дата 28 лютого 2010 року повинна повернути вік до 6 років, а не померти криком "ValueError: день виходить за межі місяця". Ви вийшли!
Джон Махін

Вибачте, ваш "Оновлений" код є ще більш бароко і зламаним, ніж перша спроба - нічого спільного з 29 лютого; це не в багатьох випадках, таких як 2009-06-15 до 2010-07-02 ... людині, очевидно, трохи більше 1 року, але ви відраховуєте рік, оскільки тест на дні (2> = 15) не вдається. І очевидно, ви цього не перевірили - він містить помилку синтаксису.
Джон Махін

4

Класичний ґетча в цьому сценарії - що робити з людьми, народженими 29 лютого. Приклад: вам потрібно мати 18 років, щоб голосувати, керувати автомобілем, купувати алкоголь тощо ... якщо ви народилися 2004-02-29, який перший день, коли вам дозволяється робити такі речі: 2022-02 -28, або 2022-03-01? АФАЙКТ, переважно перший, але декілька вбивств можуть сказати останнє.

Ось код, який обслуговує 0,068% (приблизно) населення, народженого в цей день:

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

Ось результат:

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

І я нещодавно щойно дізнався про стрибок другий .
Бобборт

3

Якщо ви хочете роздрукувати це на сторінці за допомогою шаблонів джанго, то може бути достатньо наступного:

{{ birth_date|timesince }}

4
Не використовуйте Джанго |timesinceдля обчислення тиммеделі протягом кількох років, оскільки він не враховує високосні роки і, таким чином, дає неточні результати. Дивіться квиток на Django № 19210 для отримання додаткової інформації про це.
jnns

Не знав цього. Дякую.
Анойз

2

Ось рішення, як знайти вік людини як років, чи місяців, так і днів.

Скажімо, дата народження людини - 2012-01-17T00: 00: 00 Тому його вік на 2013-01-16T00: 00: 00 складе 11 місяців

або якщо він народився 2012-12-17T00: 00: 00 , його вік 2013-01-12T00: 00: 00 становитиме 26 днів

або якщо він народився 2000-02-29T00: 00: 00 , його вік на 2012-02-29T00: 00: 00 буде 12 років

Вам потрібно буде імпортувати дату .

Ось код:

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

Деякі додаткові функції, які використовуються у наведених вище кодах, є:

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

І

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

Тепер нам потрібно подати get_date_format () з такими рядками, як 2000-02-29T00: 00: 00

Він перетворить його в об'єкт типу дати, який повинен подаватися до get_person_age (date_birth, date_today) .

Функція get_person_age (date_birth, date_today) поверне вік у рядковому форматі.


2

Розгортаємося на рішення Денні , але всілякими способами повідомляти віки для молодих людей (зверніть увагу, сьогодні це 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'

Приклад коду:

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

Оскільки я не бачив правильної реалізації, я переробив цей спосіб таким чином ...

    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)

Припущення бути "18" 28 лютого, коли народився 29-го, просто неправильно. Поміняти межі можна без уваги ... для мого коду це лише особиста зручність :)


1

Додайте відповідь Danny W. Adair , щоб отримати також місяць

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

Сьогоднішня дата

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

Ваша дата народження

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

Твій вік

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

0

імпортувати дату

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

У вашому випадку:

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

Трохи модифіковане рішення Денні для легшого читання та розуміння

    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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.