Мне нужно проверить, прошло ли какое-то количество лет с какой-то даты. В настоящее время я получил timedeltaот datetimeмодуля , и я не знаю , как преобразовать его в годы.
Вам нужно больше, чем a, timedeltaчтобы сказать, сколько лет прошло; вам также необходимо знать дату начала (или окончания). (Это високосный год.)
Лучше всего использовать dateutil.relativedeltaобъект , но это сторонний модуль. Если вы хотите знать , datetimeчто было nлет с некоторого момента (неплатежеспособного к прямо сейчас), вы можете сделать следующее ::
Первоначально в вашем вопросе говорилось, что вы хотите знать, сколько лет прошло с какой-то даты. Предполагая, что вам нужно целое число лет, вы можете угадать, исходя из 365,25 дней в году, а затем проверить, используя любую из yearsagoфункций, определенных выше:
defnum_years(begin, end=None):if end isNone:
end = datetime.now()
num_years = int((end - begin).days / 365.25)
if begin > yearsago(num_years, end):
return num_years - 1else:
return num_years
см. также это и это Оба являются отличными списками того, что не соответствует действительности относительно времени.
gvoysey
49
Если вы пытаетесь проверить, достиг ли кто-то 18 лет, timedeltaв некоторых крайних случаях использование не будет работать правильно из-за високосных лет. Например, человеку, родившемуся 1 января 2000 года, 1 января 2018 года исполнится 18 ровно на 6575 дней (включая 5 високосных лет), а человеку, родившемуся 1 января 2001 года, исполнится 18 ровно на 6574 дня позже, 1 января. 2019 г. (включая 4 високосных года). Таким образом, если кому-то ровно 6574 дня, вы не можете определить, 17 или 18 лет ему, не зная немного больше информации о дате его рождения.
Правильный способ сделать это - вычислить возраст непосредственно из дат, вычтя два года, а затем вычтя один, если текущий месяц / день предшествует месяцу / дню рождения.
Во-первых, на самом детальном уровне проблема не может быть решена точно. Годы различаются по длине, и нет четкого «правильного выбора» в отношении продолжительности года.
Тем не менее, получите разницу в любых «естественных» единицах (возможно, в секундах) и разделите на соотношение между ними и годами. Например
Это НЕ то, что кто-либо имеет в виду или использует, когда речь идет о том, сколько лет службы или достиг ли человек определенного возраста.
Джон Мачин,
3
Ваш 365,25 должен быть 365,2425, чтобы принять во внимание 400-летнее исключение григорианского календаря.
brianary
1
Что ж, проблему можно решить правильно - заранее можно сказать, в каких годах есть високосные дни, високосные секунды и все такое. Просто нет чрезвычайно элегантного способа сделать это без вычитания лет, затем месяцев, затем дней и т. Д. В двух форматированных датах
Litherum
8
Вот обновленная функция DOB, которая вычисляет дни рождения так же, как и люди:
import datetime
import locale
# Source: https://en.wikipedia.org/wiki/February_29
PRE = [
'US',
'TW',
]
POST = [
'GB',
'HK',
]
defget_country():
code, _ = locale.getlocale()
try:
return code.split('_')[1]
except IndexError:
raise Exception('Country cannot be ascertained from locale.')
defget_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.')
defage(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 == 2and dob.day == 29:
birthday = get_leap_birthday(today.year)
else:
raise e
if today < birthday:
years -= 1return years
print(age(datetime.date(1988, 2, 29)))
Это прерывается, когда dob 29 февраля, а текущий год не является високосным.
Trey Hunner
4
Получите количество дней, затем разделите на 365,2425 (средний год по григорианскому календарю) за годы. Разделите на 30,436875 (среднее значение месяца по григорианскому календарю) по месяцам.
Человек, родившийся 29 февраля, будет считаться достигшим 1 года 28 февраля следующего года.
John Machin
ОК. Исправлено с учетом 0,08% населения, родившегося 29-го числа, путем инвертирования теста с «день рождения после сегодняшнего дня» на «день рождения до сегодняшнего дня». Это решает проблему?
Джон Ми,
Это правильно работает для вашего примера!?! Если я установлю «сегодня» на 28 февраля 2009 г., а дату рождения на 29 февраля 2008 г., он вернет ноль - по крайней мере для меня; а не 1, как вы предлагаете (Python 2.5.2). Не нужно грубить мистеру Мачину. С какими именно свиданиями у вас проблемы?
Джон Ми
Я попробую еще раз: большинство людей будут рассматривать человека, родившегося 29 февраля, как человека, достигшего 1 года 28 февраля следующего года. Ваш код производит 0 как до, так и после вашего «исправления». Фактически, две версии вашего кода производят ТОЧНО одинаковый вывод для ВСЕХ 9 вариантов ввода (месяц <==> X день <==>). Кстати, 100.0 / (4 * 365 + 1) дает 0,068, а не 0,08.
Джон Мачин
2
Вздох. (0) Решение проблем, связанных с 29 февраля, необходимо в арифметике любых дат; ты просто проигнорировал это. (1) Ваш код не стал лучше с первого раза; что вы не понимаете в слове «производить ТОЧНО такой же результат»? (2) Три возможных атомарных результата (<, ==,>) при сравнении today.month и dob.month; сравнение трех возможных атомарных результатов today.day и dob.day; 3 * 3 == 9 (3) stackoverflow.com/questions/2217488/…
Джон Мачин,
1
Еще одна сторонняя библиотека, не упомянутая здесь, - это mxDateTime (предшественник как python, так datetimeи стороннегоtimeutil ), может использоваться для этой задачи.
И да, добавление зависимости для этой единственной рассматриваемой задачи определенно было бы излишним по сравнению даже с использованием timeutil (предложено Риком Коуплендом).
В конце концов, у вас возникла проблема с математикой. Если каждые 4 года у нас будет дополнительный день, а затем понизить timedelta в днях, не на 365, а на 365 * 4 + 1, то получится 4 года. Затем разделите его еще раз на 4. timedelta / ((365 * 4) +1) / 4 = timedelta * 4 / (365 * 4 +1)
Високосный год не применяется, когда годы делятся на 100, за исключением случаев, когда они делятся на 400. Итак, для 2000 года: - он делится на четыре, поэтому он должен быть високосным, но ... - он также делится на сотню, так что это не должно быть високосным, но ... - оно делится на 400, так что на самом деле это был високосный год. Для 1900: - оно делится на 4, поэтому должно быть скачком. - он делится на 100, поэтому не должен быть скачком. - он НЕ делится на 400, поэтому это правило не применяется. 1900 год не был високосным.
Jblasco
1
Это решение, которое я разработал, надеюсь, может помочь ;-)
defmenor_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:
returnTrueelse:
returnFalseexcept Exception, ex_edad:
logging.error('Error menor de edad: %s' % ex_edad)
returnTrue
Несмотря на то, что этот поток уже мертв, могу ли я предложить рабочее решение той же самой проблемы, с которой я столкнулся. Вот она (дата - строка в формате дд-мм-гггг):
defvalidatedate(date):
parts = date.strip().split('-')
if len(parts) == 3andFalsenotin [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:
returnTruereturnFalse
эта функция возвращает разницу в годах между двумя датами (взятые как строки в формате ISO, но ее можно легко изменить, чтобы принять в любом формате)
import time
defyears(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 neededif ld < ed:
ld, ed = ed, ld
res = ld[0] - ed [0]
if res > 0:
if ld[1]< ed[1]:
res -= 1elif ld[1] == ed[1]:
if ld[2]< ed[2]:
res -= 1return res
Учитывая цель Python - быть мощным и простым в использовании языком сценариев, его функции для работы с датой и временем не так удобны для пользователя, как должны быть. Цель pyfdate - исправить эту ситуацию, предоставив функции для работы с датой и временем, которые являются такими же мощными и простыми в использовании, как и остальная часть Python.
Но не было примера его метода на Python, но вот что я в итоге использовал.
ввод: объект datetime
вывод: целочисленный возраст в целых годах
defage(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 - 1return years
Мне понравилось решение Джона Ми из-за его простоты, и меня не особо беспокоит, как 28 февраля или 1 марта, когда это не високосный год, определять возраст людей, родившихся 29 февраля. Но вот поправка его кода который, как мне кажется, касается жалоб:
defage(dob):import datetime
today = datetime.date.today()
age = today.year - dob.year
if ( today.month == dob.month == 2and
today.day == 28and dob.day == 29 ):
passelif today.month < dob.month or \
(today.month == dob.month and today.day < dob.day):
age -= 1return age
Ответы:
Вам нужно больше, чем 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)))
источник
Получите количество дней, затем разделите на 365,2425 (средний год по григорианскому календарю) за годы. Разделите на 30,436875 (среднее значение месяца по григорианскому календарю) по месяцам.
источник
Насколько точным он вам нужен?
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
учебник
источник
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_(film)
источник
Я наткнулся на этот вопрос и нашел, что ответ Адамса самый полезный 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
источник