Мне нужно проверить, прошло ли какое-то количество лет с какой-то даты. В настоящее время у меня есть timedelta
из модуля datetime
, и я не знаю, как преобразовать его в годы.
Преобразовать timedelta в годы?
Ответы (20)
Вам нужно больше, чем 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,2425 дней в году, а затем проверить, используя любую из функций yearsago
, определенных выше:
def num_years(begin, end=None):
if end is None:
end = datetime.now()
num_years = int((end - begin).days / 365.2425)
if begin > yearsago(num_years, end):
return num_years - 1
else:
return num_years
datetime.now()
), больше, чем разница между средний юлианский (365.25
) и григорианский (365.2425
) годы.. Правильный подход предлагается в ответе @Adam Rosenfield
- person jfs; 11.12.2015
Если вы пытаетесь проверить, достиг ли кто-то 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)))
Получите количество дней, затем разделите на 365,2425 (средний год по григорианскому календарю) за годы. Разделите месяцы на 30,436875 (средний месяц по григорианскому календарю).
Насколько точным он вам нужен? td.days / 365.25
поможет вам довольно близко, если вы беспокоитесь о високосных годах.
Еще одна сторонняя библиотека, не упомянутая здесь, - это 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
`Простое решение!
import datetime as dt
from dateutil.relativedelta import relativedelta
dt1 = dt.datetime(1990,2,1)
dt2 = dt.datetime(2021,5,16)
out = relativedelta(dt2, dt1)
print(f'Complete: {out}')
print(f'years:{out.years}, months:{out.months}, days:{out.days}') `
Завершено: relativedelta (годы = + 31, месяцы = + 3, дни = + 15)
лет: 31, месяцев: 3, дней: 15
Несмотря на то, что этот поток уже мертв, могу ли я предложить рабочее решение той же самой проблемы, с которой я столкнулся. Вот она (дата - строка в формате дд-мм-гггг):
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://en.wikipedia.org/wiki/Logan%27s_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
Вместо этого я бы использовал тип данных datetime.date
, так как это проще, когда дело доходит до проверки того, сколько лет, месяцев и дней прошло:
now = date.today()
birthday = date(1993, 4, 4)
print("you are", now.year - birthday.year, "years,", now.month - birthday.month, "months and",
now.day - birthday.day, "days old")
Выход:
you are 27 years, 7 months and 11 days old
Я использую timedelta
, когда мне нужно выполнить арифметические действия в определенный день:
age = now - birthday
print("addition of days to a date: ", birthday + timedelta(days=age.days))
Выход:
addition of days to a date: 2020-11-15
Поздно на вечеринку, но это даст вам возраст (в годах) точно и легко:
b = birthday
today = datetime.datetime.today()
age = today.year - b.year + (today.month - b.month > 0 or
(today.month == b.month > 0 and
today.day - b.day > 0))
Для моих целей, поскольку вычитание двух объектов datetime
(по крайней мере, в Python 3.8) преобразуется в timedelta
только с атрибутом days
:
>>> admit = datetime.strptime('20130530', '%Y%m%d')
>>> birth = datetime.strptime('20010621', '%Y%m%d')
>>> age = (admit - birth).days/365.2425
>>> age
11.940012457476882