Preciso verificar se há alguns anos desde uma data. Atualmente eu tenho timedelta
do datetime
módulo e não sei como convertê-lo em anos.
Preciso verificar se há alguns anos desde uma data. Atualmente eu tenho timedelta
do datetime
módulo e não sei como convertê-lo em anos.
Respostas:
Você precisa de mais do que um timedelta
para dizer quantos anos se passaram; você também precisa saber a data de início (ou final). (É uma coisa de ano bissexto.)
Sua melhor aposta é usar o dateutil.relativedelta
objeto , mas esse é um módulo de terceiros. Se você quiser saber o datetime
que se passou há n
anos a partir de alguma data (o padrão é agora), você pode fazer o seguinte:
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)
Se você prefere ficar com a biblioteca padrão, a resposta é um pouco mais complexa:
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)
Se for 2/29 e há 18 anos não havia 2/29, essa função retornará 2/28. Se você preferir retornar 3/1, basta alterar a última return
instrução para ler:
return from_date.replace(month=3, day=1,
year=from_date.year-years)
Sua pergunta originalmente dizia que você queria saber quantos anos se passaram desde uma data. Supondo que você queira um número inteiro de anos, você pode adivinhar com base em 365,25 dias por ano e depois verificar usando uma das yearsago
funções definidas acima:
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()
) é maior que a diferença entre os anos médios de Julian ( 365.25
) e Gregorian ( 365.2425
). . A abordagem correta é sugerida na resposta de
Se você estiver tentando verificar se alguém tem 18 anos, o uso timedelta
não funcionará corretamente em alguns casos extremos por causa dos anos bissextos. Por exemplo, alguém nascido em 1 de janeiro de 2000 completará 18 exatamente 6575 dias depois em 1 de janeiro de 2018 (incluindo 5 anos bissextos), mas alguém nascido em 1 de janeiro de 2001 completará 18 exatamente 6574 dias depois em 1 de janeiro, 2019 (4 anos bissextos incluídos). Assim, se alguém tiver exatamente 6574 dias, não será possível determinar se eles têm 17 ou 18 anos sem conhecer um pouco mais de informações sobre a data de nascimento.
A maneira correta de fazer isso é calcular a idade diretamente das datas, subtraindo os dois anos e subtraindo um se o mês / dia atual preceder o mês / dia do nascimento.
Primeiro, no nível mais detalhado, o problema não pode ser resolvido exatamente. Os anos variam em duração e não há uma "escolha certa" clara para a duração do ano.
Dito isto, obtenha a diferença em quaisquer unidades que sejam "naturais" (provavelmente segundos) e divida pela razão entre isso e anos. Por exemplo
delta_in_days / (365.25)
delta_in_seconds / (365.25*24*60*60)
...como queiras. Fique longe de meses, pois eles são ainda menos bem definidos que os anos.
Aqui está uma função DOB atualizada, que calcula os aniversários da mesma maneira que os humanos:
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)))
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
Quão exato você precisa que seja? td.days / 365.25
vai te aproximar bastante, se você estiver preocupado com anos bissextos.
Ainda outra biblioteca de terceiros não mencionada aqui é o mxDateTime (predecessor do python datetime
e de terceiros timeutil
) que pode ser usado para esta tarefa.
O mencionado acima yearsago
seria:
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)
O primeiro parâmetro deve ser uma DateTime
instância.
Para converter comum datetime
em DateTime
você, você pode usar isso para 1 segundo de precisão):
def DT_from_dt_s(t):
return DT.DateTimeFromTicks(time.mktime(t.timetuple()))
ou isso para precisão de 1 microssegundo:
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)
E sim, adicionar a dependência para esta única tarefa em questão seria definitivamente um exagero comparado ao uso do timeutil (sugerido por Rick Copeland).
No final, o que você tem é uma questão de matemática. Se a cada 4 anos, tivermos um dia extra, vamos mergulhar o timedelta em dias, não em 365, mas em 365 * 4 + 1, o que daria a quantidade de 4 anos. Em seguida, divida-o novamente por 4. timedelta / ((365 * 4) +1) / 4 = timedelta * 4 / (365 * 4 +1)
Esta é a solução que resolvi, espero que possa ajudar ;-)
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
Embora esse segmento já esteja morto, posso sugerir uma solução para esse mesmo problema que eu estava enfrentando. Aqui está (data é uma string no formato dd-mm-aaaa):
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
essa função retorna a diferença de anos entre duas datas (interpretada como strings no formato ISO, mas pode ser facilmente modificada para incluir em qualquer formato)
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
Vou sugerir Pyfdate
O que é pyfdate?
Dado o objetivo do Python de ser uma linguagem de script poderosa e fácil de usar, seus recursos para trabalhar com datas e horas não são tão amigáveis quanto deveriam ser. O objetivo do pyfdate é remediar essa situação, fornecendo recursos para trabalhar com datas e horários tão poderosos e fáceis de usar quanto o restante do Python.
o tutorial
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()
Este é o código que o operador Carrousel estava executando no filme Run de Logan;)
Me deparei com esta pergunta e encontrei Adams responder a mais útil https://stackoverflow.com/a/765862/2964689
Mas não havia nenhum exemplo python de seu método, mas eis o que acabei usando.
input: objeto datetime
saída: idade inteira em anos inteiros
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
Gostei da solução de John Mee por sua simplicidade, e não me preocupo com o modo como, em 28 de fevereiro ou 1º de março, quando não é um ano bissexto, determinar a idade das pessoas nascidas em 29 de fevereiro. que eu acho que aborda as reclamações:
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