Мне нужно проверить, прошло ли какое-то количество лет с какой-то даты. В настоящее время я получил timedelta
от datetime
модуля , и я не знаю , как преобразовать его в годы.
Мне нужно проверить, прошло ли какое-то количество лет с какой-то даты. В настоящее время я получил timedelta
от datetime
модуля , и я не знаю , как преобразовать его в годы.
Ответы:
Вам нужно больше, чем a, 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
datetime.now()
365.25
365.2425
вносимая местным часовым поясом ( ), больше, чем разница между средними юлианскими ( ) и григорианскими ( ) годами. . Правильный подход предлагается в ответе @Adam Rosenfield
Если вы пытаетесь проверить, достиг ли кто-то 18 лет, timedelta
в некоторых крайних случаях использование не будет работать правильно из-за високосных лет. Например, человеку, родившемуся 1 января 2000 года, 1 января 2018 года исполнится 18 ровно на 6575 дней (включая 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)))
Насколько точным он вам нужен? td.days / 365.25
поможет вам довольно близко, если вы беспокоитесь о високосных годах.
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
Еще одна сторонняя библиотека, не упомянутая здесь, - это mxDateTime (предшественник как python, так datetime
и стороннего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
Несмотря на то, что этот поток уже мертв, могу ли я предложить рабочее решение той же самой проблемы, с которой я столкнулся. Вот она (дата - строка в формате дд-мм-гггг):
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 - исправить эту ситуацию, предоставив функции для работы с датой и временем, которые являются такими же мощными и простыми в использовании, как и остальная часть Python.
import datetime
def check_if_old_enough(years_needed, old_date):
limit_date = datetime.date(old_date.year + years_needed, old_date.month, old_date.day)
today = datetime.datetime.now().date()
old_enough = False
if limit_date <= today:
old_enough = True
return old_enough
def test_ages():
years_needed = 30
born_date_Logan = datetime.datetime(1988, 3, 5)
if check_if_old_enough(years_needed, born_date_Logan):
print("Logan is old enough")
else:
print("Logan is not old enough")
born_date_Jessica = datetime.datetime(1997, 3, 6)
if check_if_old_enough(years_needed, born_date_Jessica):
print("Jessica is old enough")
else:
print("Jessica is not old enough")
test_ages()
Это код, который оператор Carrousel запускал в фильме Logan's Run;)
Я наткнулся на этот вопрос и нашел, что ответ Адамса самый полезный https://stackoverflow.com/a/765862/2964689
Но не было примера его метода на Python, но вот что я в итоге использовал.
ввод: объект datetime
вывод: целочисленный возраст в целых годах
def age(birthday):
birthday = birthday.date()
today = date.today()
years = today.year - birthday.year
if (today.month < birthday.month or
(today.month == birthday.month and today.day < birthday.day)):
years = years - 1
return years
Мне понравилось решение Джона Ми из-за его простоты, и меня не особо беспокоит, как 28 февраля или 1 марта, когда это не високосный год, определять возраст людей, родившихся 29 февраля. Но вот поправка его кода который, как мне кажется, касается жалоб:
def age(dob):
import datetime
today = datetime.date.today()
age = today.year - dob.year
if ( today.month == dob.month == 2 and
today.day == 28 and dob.day == 29 ):
pass
elif today.month < dob.month or \
(today.month == dob.month and today.day < dob.day):
age -= 1
return age