14 ответов:
вам нужно больше, чем
timedeltaчтобы сказать, сколько лет прошло; вы также должны знать дату начала (или окончания). (Это високосный год.)лучше всего использовать
dateutil.relativedeltaобъект, но это модуль третьей стороны. Если вы хотите знатьdatetimeэто былоnлет с какой даты (по умолчанию сейчас), вы можете сделать следующее:from dateutil.relativedelta import relativedelta def yearsago(years, from_date=None): if from_date is None: from_date = datetime.now() return from_date - relativedelta(years=years)если вы предпочитаете придерживаться стандартной библиотеки, ответ немного сложнее::
from datetime import datetime def yearsago(years, from_date=None): if from_date is None: from_date = datetime.now() try: return from_date.replace(year=from_date.year - years) except ValueError: # Must be 2/29! assert from_date.month == 2 and from_date.day == 29 # can be removed return from_date.replace(month=2, day=28, year=from_date.year-years)если это 2/29, а 18 лет назад не было 2/29, эта функция вернет 2/28. Если вы предпочитаете вернуть 3/1, просто измените последнее
returnзаявление читать::return from_date.replace(month=3, day=1, year=from_date.year-years)ваш вопрос первоначально сказал, что вы хотели знать, сколько лет прошло с какой-то даты. Если вы хотите целое число лет, можно догадаться, основано на 365.25 дней в году, а затем проверить, используя
yearsagoфункции, определенные выше::def num_years(begin, end=None): if end is None: end = datetime.now() num_years = int((end - begin).days / 365.25) if begin > yearsago(num_years, end): return num_years - 1 else: return num_years
Если вы пытаетесь проверить, если кто-то 18 лет, используя
timedeltaне будет работать правильно на некоторых случаях края из-за високосных лет. Например, кому-то, родившемуся 1 января 2000 года, исполнится 18 ровно 6575 дней спустя 1 января 2018 года (5 високосных лет включительно), а кому-то, родившемуся 1 января 2001 года, исполнится 18 ровно 6574 дня позже 1 января 2019 года (4 високосных года включительно). Таким образом, вы, если кому-то ровно 6574 дня, вы не можете определить, являются ли они 17 или 18, не зная немного больше информации о дате их рождения.правильный способ сделать это-вычислить возраст непосредственно из дат, вычитая два года, а затем вычитая один, если текущий месяц/день предшествует месяцу/дню рождения.
во-первых, на самом детальном уровне проблема не может быть решена точно. Годы различаются по длине, и нет четкого "правильного выбора" для длины года.
тем не менее, получите разницу в любых единицах "естественного" (вероятно, секунд) и разделите на соотношение между этим и годами. Е. Г.
delta_in_days / (365.25) delta_in_seconds / (365.25*24*60*60)...или еще что-нибудь. Держитесь подальше от месяцев, так как они еще менее четко определены, чем годы.
вот обновленная функция DOB, которая вычисляет дни рождения так же, как и люди:
import datetime import locale # Source: https://en.wikipedia.org/wiki/February_29 PRE = [ 'US', 'TW', ] POST = [ 'GB', 'HK', ] def get_country(): code, _ = locale.getlocale() try: return code.split('_')[1] except IndexError: raise Exception('Country cannot be ascertained from locale.') def get_leap_birthday(year): country = get_country() if country in PRE: return datetime.date(year, 2, 28) elif country in POST: return datetime.date(year, 3, 1) else: raise Exception('It is unknown whether your country treats leap year ' + 'birthdays as being on the 28th of February or ' + 'the 1st of March. Please consult your country\'s ' + 'legal code for in order to ascertain an answer.') def age(dob): today = datetime.date.today() years = today.year - dob.year try: birthday = datetime.date(today.year, dob.month, dob.day) except ValueError as e: if dob.month == 2 and dob.day == 29: birthday = get_leap_birthday(today.year) else: raise e if today < birthday: years -= 1 return years print(age(datetime.date(1988, 2, 29)))
получить количество дней, затем разделить на 365.2425 (средний Григорианский год) в течение многих лет. Разделите на 30.436875 (средний Григорианский месяц) в течение нескольких месяцев.
def age(dob): import datetime today = datetime.date.today() if today.month < dob.month or \ (today.month == dob.month and today.day < dob.day): return today.year - dob.year - 1 else: return today.year - dob.year >>> import datetime >>> datetime.date.today() datetime.date(2009, 12, 1) >>> age(datetime.date(2008, 11, 30)) 1 >>> age(datetime.date(2008, 12, 1)) 1 >>> age(datetime.date(2008, 12, 2)) 0
еще одна сторонняя lib, не упомянутая здесь, - mxDateTime (предшественник обоих python
datetimeи 3-й партииtimeutil) может быть использован для этой задачи.вышеуказанного
yearsagoбудет:from mx.DateTime import now, RelativeDateTime def years_ago(years, from_date=None): if from_date == None: from_date = now() return from_date-RelativeDateTime(years=years)первый параметр должен быть
DateTimeэкземпляра.для преобразования обычных
datetimeдоDateTimeвы можете использовать это в течение 1 секунды):def DT_from_dt_s(t): return DT.DateTimeFromTicks(time.mktime(t.timetuple()))или это для точности 1 микросекунды:
def DT_from_dt_u(t): return DT.DateTime(t.year, t.month, t.day, t.hour, t.minute, t.second + t.microsecond * 1e-6)и да, добавление зависимости для этой единственной задачи, о которой идет речь, определенно было бы излишним по сравнению даже с использованием timeutil (предложенного Риком Коуплендом).
В конце концов, что у вас есть-это вопрос математики. Если каждые 4 года у нас есть дополнительный день позволяет затем нырнул timedelta в днях, а не на 365, но 365*4 + 1 это даст вам сумму в 4 года. Затем разделить еще на 4. timedelta / ((365*4) +1) / 4 = timedelta * 4 / (365*4 +1)
Это решение я разработал, я надеюсь, может помочь ;-)
def menor_edad_legal(birthday): """ returns true if aged<18 in days """ try: today = time.localtime() fa_divuit_anys=date(year=today.tm_year-18, month=today.tm_mon, day=today.tm_mday) if birthday>fa_divuit_anys: return True else: return False except Exception, ex_edad: logging.error('Error menor de edad: %s' % ex_edad) return True
насколько точно вам это нужно?
td.days / 365.25будет вам довольно близко, если вы беспокоитесь о високосных годах.
хотя этот поток уже мертв, могу ли я предложить рабочее решение для этой самой проблемы, с которой я столкнулся. Вот она (дата-строка в формате ДД-ММ-гггг):
def validatedate(date): parts = date.strip().split('-') if len(parts) == 3 and False not in [x.isdigit() for x in parts]: birth = datetime.date(int(parts[2]), int(parts[1]), int(parts[0])) today = datetime.date.today() b = (birth.year * 10000) + (birth.month * 100) + (birth.day) t = (today.year * 10000) + (today.month * 100) + (today.day) if (t - 18 * 10000) >= b: return True return False
эта функция возвращает разницу в годах между двумя датами (взятыми в виде строк в формате ISO, но ее можно легко изменить, чтобы взять в любом формате)
import time def years(earlydateiso, laterdateiso): """difference in years between two dates in ISO format""" ed = time.strptime(earlydateiso, "%Y-%m-%d") ld = time.strptime(laterdateiso, "%Y-%m-%d") #switch dates if needed if ld < ed: ld, ed = ed, ld res = ld[0] - ed [0] if res > 0: if ld[1]< ed[1]: res -= 1 elif ld[1] == ed[1]: if ld[2]< ed[2]: res -= 1 return res
Я предлагаю Pyfdate
что такое pyfdate?
учитывая цель Python, чтобы быть мощным и простым в использовании сценариев язык, его особенности для работы с датами и временем все не так удобный, как они должны быть. Этот цель pyfdate состоит в том, чтобы исправить это ситуации, предоставляя возможности для работа с датами и временем, которые как мощный и легкий в использовании как остальное Питон.
the учебник
ну, вопрос кажется довольно легко. Вам нужно проверить количество "полных" лет, и только если оно равно 18, вам нужно беспокоиться о месяцах и днях. Крайний случай:
endDate.year - startDate.year == 18и он разбивается на два случая:startDate.month != endDate.monthиstartDate.month == endDate.month, когда вы просто должны проверить дней:def isOfAge(birthDate, age=18): endDate = date.today() years = endDate.year - birthDate.year if years == age: return (birthDate.month < endDate.month or (birthDate.month == endDate.month and birthDate.day < endDate.day)) return years > ageЭто все еще больше, чем один лайнер-лямбда, но он все еще довольно короткий и кажется быстрым в исполнении.
Comments