Necesita más que un timedeltapara saber cuántos años han pasado; También necesita saber la fecha de inicio (o finalización). (Es un año bisiesto).
Su mejor opción es usar el dateutil.relativedeltaobjeto , pero ese es un módulo de terceros. Si desea saber datetimeque pasaron naños desde alguna fecha (por defecto en este momento), puede hacer lo siguiente:
Si prefiere seguir con la biblioteca estándar, la respuesta es un poco más compleja:
from datetime import datetime
def yearsago(years, from_date=None):if from_date isNone:
from_date = datetime.now()try:return from_date.replace(year=from_date.year - years)exceptValueError:# Must be 2/29!assert from_date.month ==2and from_date.day ==29# can be removedreturn from_date.replace(month=2, day=28,
year=from_date.year-years)
Si es 2/29, y hace 18 años no había 2/29, esta función devolverá 2/28. Si prefiere devolver 3/1, simplemente cambie la última returninstrucción para leer ::
Su pregunta originalmente decía que quería saber cuántos años han pasado desde alguna fecha. Suponiendo que desea un número entero de años, puede adivinar en base a 365.25 días por año y luego verificar usando cualquiera de las yearsagofunciones definidas anteriormente ::
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
vea también esto y esto Ambos son excelentes listas de cosas que no son ciertas sobre el tiempo.
gvoysey
49
Si está tratando de verificar si alguien tiene 18 años de edad, el uso timedeltano funcionará correctamente en algunos casos extremos debido a los años bisiestos. Por ejemplo, alguien nacido el 1 de enero de 2000 cumplirá 18 años exactamente 6575 días después el 1 de enero de 2018 (5 años bisiestos incluidos), pero alguien nacido el 1 de enero de 2001 cumplirá 18 años exactamente 6574 días después el 1 de enero, 2019 (4 años bisiestos incluidos). Por lo tanto, si alguien tiene exactamente 6574 días, no puede determinar si tiene 17 o 18 años sin conocer un poco más de información sobre su fecha de nacimiento.
La forma correcta de hacer esto es calcular la edad directamente de las fechas, restando los dos años, y luego restando uno si el mes / día actual precede al mes / día de nacimiento.
En primer lugar, en el nivel más detallado, el problema no puede resolverse exactamente. Los años varían en duración, y no hay una clara "elección correcta" para la duración del año.
Dicho esto, obtenga la diferencia en cualquier unidad que sea "natural" (probablemente segundos) y divida por la relación entre eso y años. P.ej
Eso NO es lo que nadie quiere decir o usar cuando se trata de cuántos años de servicio o una persona ha alcanzado una edad en particular.
John Machin el
3
Su 365.25 debe ser 365.2425 para tener en cuenta la excepción de 400 años del calendario gregoriano.
brianary
1
Bueno, el problema se puede resolver correctamente: puede saber con anticipación qué años tienen días bisiestos y segundos bisiestos y todo eso. Es solo que no hay una forma extremadamente elegante de hacerlo sin restar los años, luego los meses, luego los días, etc. en las dos fechas formateadas
Litherum,
7
Aquí hay una función DOB actualizada, que calcula los cumpleaños de la misma manera que los 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]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.year
try:
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 e
if today < birthday:
years -=1return years
print(age(datetime.date(1988,2,29)))
Una persona nacida el 29 de febrero será tratada como haber cumplido 1 año el siguiente 28 de febrero.
John Machin el
Okay. Corregido para acomodar al 0.08% de la población nacida el 29 invirtiendo la prueba de "es cumpleaños después de hoy" a "es cumpleaños antes de hoy". ¿Eso lo resuelve?
John Mee el
¡Funciona correctamente para tu ejemplo! Si establezco "hoy" al 28 de febrero de 2009, y la fecha de nacimiento al 29 de febrero de 2008, devuelve Cero, al menos para mí; no 1 como sugiere (Python 2.5.2). No hay necesidad de grosero Sr. Machin. ¿Exactamente con qué dos fechas tienes problemas?
John Mee
Lo intentaré nuevamente: una persona nacida el 29 de febrero será tratada por la mayoría de las personas para la mayoría de los propósitos legales como haber cumplido 1 año el siguiente 28 de febrero. Su código produce 0, tanto antes como después de su "corrección". De hecho, las dos versiones de su código producen EXACTAMENTE la misma salida para TODAS las 9 posibilidades de entrada (mes <==> X día <==>). Por cierto, 100.0 / (4 * 365 + 1) produce 0.068, no 0.08.
John Machin
2
Suspiro. (0) Abordar los problemas del 29 de febrero es esencial en cualquier fecha aritmética; Simplemente lo ignoraste. (1) Su código no fue mejor la primera vez; ¿Qué no entiendes en "producir EXACTAMENTE la misma salida"? (2) Tres resultados atómicos posibles (<, ==,>) comparando today.month y dob.month; tres posibles resultados atómicos que comparan today.day y dob.day; 3 * 3 == 9 (3) stackoverflow.com/questions/2217488/…
John Machin
1
¿Qué tan exacto necesitas que sea? td.days / 365.25te acercará bastante, si te preocupan los años bisiestos.
Estoy realmente preocupado por los años bisiestos. Debe verificar si la persona es mayor de 18 años.
Migol
Entonces no hay una frase fácil, tendrás que analizar las dos fechas y averiguar si la persona ha cumplido 18 años o no.
eduffy
1
Sin embargo, otra biblioteca de terceros que no se menciona aquí es mxDateTime (predecesora de python datetimey de terceros timeutil) podría usarse para esta tarea.
Y sí, agregar la dependencia para esta tarea en cuestión definitivamente sería una exageración en comparación incluso con el uso de timeutil (sugerido por Rick Copeland).
Al final, lo que tienes es un problema de matemáticas. Si cada 4 años tenemos un día extra, dejemos el tiempo delta en días, no en 365 sino 365 * 4 + 1, eso le daría la cantidad de 4 años. Luego divídalo nuevamente por 4. timedelta / ((365 * 4) +1) / 4 = timedelta * 4 / (365 * 4 +1)
Lo del año bisiesto no se aplica cuando los años son divisibles por 100, excepto cuando son divisibles por 400. Entonces, para el año 2000: - es divisible por cuatro, por lo que debería ser bisiesto pero ... - también es divisible por cien, por lo que no debería ser bisiesto pero ... - es divisible por 400, por lo que en realidad fue un año bisiesto. Para 1900: - es divisible por 4, por lo que debería ser un salto. - es divisible por 100, por lo que no debe ser un salto. - NO es divisible por 400, por lo que esta regla no se aplica. 1900 no fue un año bisiesto.
Jblasco
1
Esta es la solución que resolví, espero que pueda ayudar ;-)
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
Aunque este hilo ya está muerto, ¿puedo sugerir una solución de trabajo para este mismo problema que estaba enfrentando? Aquí está (la fecha es una cadena en el 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
Esta función devuelve la diferencia en años entre dos fechas (tomadas como cadenas en formato ISO, pero puede modificarse fácilmente para tomar en cualquier 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 el objetivo de Python de ser un lenguaje de secuencias de comandos potente y fácil de usar, sus funciones para trabajar con fechas y horas no son tan fáciles de usar como deberían ser. El propósito de pyfdate es remediar esa situación al proporcionar funciones para trabajar con fechas y horas que son tan poderosas y fáciles de usar como el resto de Python.
Me gustó la solución de John Mee por su simplicidad, y no me preocupa cómo, el 28 de febrero o el 1 de marzo, cuando no es un año bisiesto, determinar la edad de las personas nacidas el 29 de febrero. Pero aquí hay un ajuste de su código que creo aborda las quejas:
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
Respuestas:
Necesita más que un
timedelta
para saber cuántos años han pasado; También necesita saber la fecha de inicio (o finalización). (Es un año bisiesto).Su mejor opción es usar el
dateutil.relativedelta
objeto , pero ese es un módulo de terceros. Si desea saberdatetime
que pasaronn
años desde alguna fecha (por defecto en este momento), puede hacer lo siguiente:Si prefiere seguir con la biblioteca estándar, la respuesta es un poco más compleja:
Si es 2/29, y hace 18 años no había 2/29, esta función devolverá 2/28. Si prefiere devolver 3/1, simplemente cambie la última
return
instrucción para leer ::Su pregunta originalmente decía que quería saber cuántos años han pasado desde alguna fecha. Suponiendo que desea un número entero de años, puede adivinar en base a 365.25 días por año y luego verificar usando cualquiera de las
yearsago
funciones definidas anteriormente ::fuente
datetime.now()
) es mayor que la diferencia entre los años promedio de Julian (365.25
) y Gregorian (365.2425
). . El enfoque correcto se sugiere en la respuesta de @Adam RosenfieldSi está tratando de verificar si alguien tiene 18 años de edad, el uso
timedelta
no funcionará correctamente en algunos casos extremos debido a los años bisiestos. Por ejemplo, alguien nacido el 1 de enero de 2000 cumplirá 18 años exactamente 6575 días después el 1 de enero de 2018 (5 años bisiestos incluidos), pero alguien nacido el 1 de enero de 2001 cumplirá 18 años exactamente 6574 días después el 1 de enero, 2019 (4 años bisiestos incluidos). Por lo tanto, si alguien tiene exactamente 6574 días, no puede determinar si tiene 17 o 18 años sin conocer un poco más de información sobre su fecha de nacimiento.La forma correcta de hacer esto es calcular la edad directamente de las fechas, restando los dos años, y luego restando uno si el mes / día actual precede al mes / día de nacimiento.
fuente
En primer lugar, en el nivel más detallado, el problema no puede resolverse exactamente. Los años varían en duración, y no hay una clara "elección correcta" para la duración del año.
Dicho esto, obtenga la diferencia en cualquier unidad que sea "natural" (probablemente segundos) y divida por la relación entre eso y años. P.ej
...o lo que sea. Manténgase alejado de los meses, ya que están incluso menos definidos que años.
fuente
Aquí hay una función DOB actualizada, que calcula los cumpleaños de la misma manera que los humanos:
fuente
Obtenga el número de días, luego divida por 365.2425 (el año gregoriano medio) por años. Dividir por 30.436875 (el mes gregoriano medio) por meses.
fuente
fuente
¿Qué tan exacto necesitas que sea?
td.days / 365.25
te acercará bastante, si te preocupan los años bisiestos.fuente
Sin embargo, otra biblioteca de terceros que no se menciona aquí es mxDateTime (predecesora de python
datetime
y de tercerostimeutil
) podría usarse para esta tarea.Lo anterior
yearsago
sería:Se espera que el primer parámetro sea una
DateTime
instancia.Para convertir ordinario
datetime
aDateTime
podría usar esto para precisión de 1 segundo):o esto para 1 microsegundo de precisión:
Y sí, agregar la dependencia para esta tarea en cuestión definitivamente sería una exageración en comparación incluso con el uso de timeutil (sugerido por Rick Copeland).
fuente
Al final, lo que tienes es un problema de matemáticas. Si cada 4 años tenemos un día extra, dejemos el tiempo delta en días, no en 365 sino 365 * 4 + 1, eso le daría la cantidad de 4 años. Luego divídalo nuevamente por 4. timedelta / ((365 * 4) +1) / 4 = timedelta * 4 / (365 * 4 +1)
fuente
Esta es la solución que resolví, espero que pueda ayudar ;-)
fuente
Aunque este hilo ya está muerto, ¿puedo sugerir una solución de trabajo para este mismo problema que estaba enfrentando? Aquí está (la fecha es una cadena en el formato dd-mm-aaaa):
fuente
Esta función devuelve la diferencia en años entre dos fechas (tomadas como cadenas en formato ISO, pero puede modificarse fácilmente para tomar en cualquier formato)
fuente
Voy a sugerir Pyfdate
el tutorial
fuente
Este es el código que el operador Carrousel estaba ejecutando en la película Logan's Run;)
https://en.wikipedia.org/wiki/Logan%27s_Run_(film)
fuente
Me encontré con esta pregunta y encontré que Adams responde la más útil https://stackoverflow.com/a/765862/2964689
Pero no hubo un ejemplo de su método en Python, pero esto es lo que terminé usando.
entrada: objeto de fecha y hora
salida: edad entera en años enteros
fuente
Me gustó la solución de John Mee por su simplicidad, y no me preocupa cómo, el 28 de febrero o el 1 de marzo, cuando no es un año bisiesto, determinar la edad de las personas nacidas el 29 de febrero. Pero aquí hay un ajuste de su código que creo aborda las quejas:
fuente