Você precisa de mais do que um timedeltapara 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.relativedeltaobjeto , mas esse é um módulo de terceiros. Se você quiser saber o datetimeque se passou há nanos a partir de alguma data (o padrão é agora), você pode fazer o seguinte:
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 yearsagofunções definidas acima:
def num_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
Você pode ser totalmente preciso com 365.2425 (em vez de 365.25), que leva em consideração a exceção de 400 anos para o calendário gregoriano.
brianary 24/12/2009
3
Sua função é legalmente interrompida para países como o Reino Unido e Hong Kong, porque eles "são arredondados" para o dia 1º de março nos anos bissextos.
veja também isto e isto Ambos são excelentes listas de coisas que não são verdadeiras sobre o tempo.
precisa saber é
49
Se você estiver tentando verificar se alguém tem 18 anos, o uso timedeltanã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
NÃO é isso que alguém quer dizer ou usa quando se trata de quantos anos de serviço ou uma pessoa atingiu uma idade específica.
John Machin
3
Seu 365.25 deve ser 365.2425 para levar em consideração a exceção de 400 anos do calendário gregoriano.
brianary 24/12/2009
1
Bem, o problema pode ser resolvido corretamente - você pode dizer com antecedência quais anos têm dias bissextos e segundos bissextos e tudo mais. É que não há nenhuma maneira extremamente elegante de fazê-lo sem subtrair dos anos, então os meses, então os dias, etc ... nas duas datas formatadas
Litherum
7
Aqui está uma função DOB atualizada, que calcula os aniversários da mesma maneira que os humanos:
import datetimeimport 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]exceptIndexError:raiseException('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:raiseException('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.yeartry:
birthday = datetime.date(today.year, dob.month, dob.day)exceptValueErroras e:if dob.month ==2and dob.day ==29:
birthday = get_leap_birthday(today.year)else:raise eif today < birthday:
years -=1return yearsprint(age(datetime.date(1988,2,29)))
Uma pessoa nascida em 29 de fevereiro será tratada como tendo atingido a idade 1 no dia 28 de fevereiro seguinte.
John Machin
Está bem. Corrigido para acomodar 0,08% da população nascida no dia 29, invertendo o teste de "é aniversário depois de hoje" para "é aniversário antes de hoje". Isso resolve?
John Mee
Funciona corretamente para o seu exemplo!?! Se eu definir "hoje" para 28 de fevereiro de 2009 e a data de nascimento para 29 de fevereiro de 2008, ele retornará zero - pelo menos para mim; não 1 como você sugere (Python 2.5.2). Não há necessidade de rude Sr. Machin. Exatamente com quais datas você está tendo problemas?
John Mee
Tentarei novamente: uma pessoa nascida em 29 de fevereiro será tratada pela maioria das pessoas para fins mais legais como tendo atingido a idade de 1 no dia 28 de fevereiro seguinte. Seu código produz 0, antes e depois da sua "correção". De fato, as duas versões do seu código produzem exatamente a mesma saída para TODAS as 9 possibilidades de entrada (mês <==> X dia <==>). BTW, 100,0 / (4 * 365 + 1) produz 0,068, não 0,08.
John Machin
2
Suspiro. (0) Abordar questões de 29 de fevereiro é essencial em qualquer data aritmética; você apenas ignorou. (1) Seu código não foi melhor na primeira vez; o que você não entende em "produz EXATAMENTE a mesma saída"? (2) Três resultados atômicos possíveis (<, ==,>) comparando today.month e dob.month; três possíveis resultados atômicos comparando today.day e dob.day; 3 * 3 == 9 (3) stackoverflow.com/questions/2217488/…
John Machin
1
Quão exato você precisa que seja? td.days / 365.25vai te aproximar bastante, se você estiver preocupado com anos bissextos.
Estou realmente preocupado com anos bissextos. Deve verificar se a pessoa tem mais de 18 anos de idade.
Migol
Então não há uma linha fácil, você terá que analisar as duas datas e descobrir se a pessoa passou ou não pelo seu aniversário de 18 anos.
eduffy
1
Ainda outra biblioteca de terceiros não mencionada aqui é o mxDateTime (predecessor do python datetimee de terceiros timeutil) que pode ser usado para esta tarefa.
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)
A coisa do ano bissexto não se aplica quando os anos são divisíveis por 100, exceto quando são divisíveis por 400. Então, para o ano 2000: - é divisível por quatro, portanto deve ser bissexto, mas ... - também é divisível por cem, então não deve ser um salto, mas ... - é divisível por 400, então foi realmente um ano bissexto. Para 1900: - é divisível por 4, portanto deve ser um salto. - é divisível por 100, portanto não deve ser um salto. - NÃO é divisível por 400, portanto, esta regra não se aplica. 1900 não foi um ano bissexto.
Jblasco #
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:returnTrueelse:returnFalseexceptException, ex_edad:
logging.error('Error menor de edad: %s'% ex_edad)returnTrue
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)==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
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 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
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.
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 ==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
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 odatetime
que se passou hán
anos a partir de alguma data (o padrão é agora), você pode fazer o seguinte:Se você prefere ficar com a biblioteca padrão, a resposta é um pouco mais complexa:
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: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:fonte
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 deSe 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.
fonte
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
...como queiras. Fique longe de meses, pois eles são ainda menos bem definidos que os anos.
fonte
Aqui está uma função DOB atualizada, que calcula os aniversários da mesma maneira que os humanos:
fonte
Obtenha o número de dias e divida por 365,2425 (o ano gregoriano médio) por anos. Divida por 30,436875 (o mês gregoriano médio) por meses.
fonte
fonte
Quão exato você precisa que seja?
td.days / 365.25
vai te aproximar bastante, se você estiver preocupado com anos bissextos.fonte
Ainda outra biblioteca de terceiros não mencionada aqui é o mxDateTime (predecessor do python
datetime
e de terceirostimeutil
) que pode ser usado para esta tarefa.O mencionado acima
yearsago
seria:O primeiro parâmetro deve ser uma
DateTime
instância.Para converter comum
datetime
emDateTime
você, você pode usar isso para 1 segundo de precisão):ou isso para precisão de 1 microssegundo:
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).
fonte
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)
fonte
Esta é a solução que resolvi, espero que possa ajudar ;-)
fonte
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):
fonte
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)
fonte
Vou sugerir Pyfdate
o tutorial
fonte
Este é o código que o operador Carrousel estava executando no filme Run de Logan;)
https://en.wikipedia.org/wiki/Logan%27s_Run_(film)
fonte
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
fonte
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:
fonte