Python timedelta en años


Tengo que comprobar si algún número de años han sido desde alguna fecha. Actualmente tengo timedelta del módulo datetime y no se como convertirlo a años.

Author: Migol, 2009-04-19

14 answers

Necesitas más que un timedelta para saber cuántos años han pasado; también necesitas saber la fecha de inicio (o finalización). (Es una cosa de año bisiesto.)

Su mejor apuesta es utilizar el dateutil.relativedelta object, pero ese es un módulo de terceros. Si quieres saber el datetime que fue n años a partir de alguna fecha (por defecto hasta ahora), puedes hacer lo siguiente::

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)

Si prefieres seguir con la biblioteca estándar, la respuesta es un poco más complex::

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)

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 instrucción return para que diga::

    return from_date.replace(month=3, day=1,
                             year=from_date.year-years)

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 funciones yearsago definidas anteriormente::

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
 121
Author: Rick Copeland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-11 14:26:41

Si está tratando de verificar si alguien tiene 18 años de edad, usar timedelta no funcionará correctamente en algunos casos extremos debido a los años bisiestos. Por ejemplo, una persona nacida 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 una persona nacida el 1 de enero de 2001 cumplirá 18 exactamente 6574 días después el 1 de enero de 2019 (4 años bisiestos incluidos). Por lo tanto, si alguien tiene exactamente 6574 días de edad, no puede determinar si tiene 17 o 18 sin conocer un poco más información sobre su fecha de nacimiento.

La forma correcta de hacer esto es calcular la edad directamente a partir 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.

 44
Author: Adam Rosenfield,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-19 18:36:03

En primer lugar, en el nivel más detallado, el problema no se puede resolver exactamente. Los años varían en longitud, 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. Por ejemplo,

delta_in_days / (365.25)
delta_in_seconds / (365.25*24*60*60)

...o lo que sea. Manténgase alejado de los meses, ya que están aún menos bien definidos que los años.

 9
Author: MarkusQ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-19 18:08:08

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]
    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)))
 6
Author: antihero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-04 14:08:36

Obtenga el número de días, luego divida por 365.2425 (el año gregoriano medio) durante años. Divida por 30.436875 (el mes gregoriano medio) por meses.

 3
Author: brianary,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-24 21:13:42
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
 1
Author: John Mee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-01 04:33:05

Otra 3rd party lib no mencionada aquí es mxDateTime (predecesor de python datetime y 3rd party timeutil) podría ser usada para esta tarea.

Lo antes mencionado yearsago sería:

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)

Se espera que el primer parámetro sea una instancia DateTime.

Para convertir ordinario datetime a DateTime usted podría utilizar esto para la precisión de 1 segundo):

def DT_from_dt_s(t):
    return DT.DateTimeFromTicks(time.mktime(t.timetuple()))

O esto para una precisión de 1 microsegundo:

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)

Y sí, añadiendo la dependencia para esta única tarea en cuestión definitivamente sería un exceso en comparación incluso con el uso de timeutil (sugerido por Rick Copeland).

 1
Author: Antony Hatchkins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-24 20:52:12

Al final lo que tienes es un problema de matemáticas. Si cada 4 años tenemos un día extra permite entonces bucear el timedelta en días, no por 365 pero 365*4 + 1, eso te daría la cantidad de 4 años. Luego divídelo de nuevo por 4. timedelta / ((365*4) +1) / 4 = timedelta * 4 / (365*4 +1)

 1
Author: Norberto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-15 23:20:49

Esta es la solución que encontré, 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:
            return True
        else:
            return False            

    except Exception, ex_edad:
        logging.error('Error menor de edad: %s' % ex_edad)
        return True
 1
Author: pvilas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-10 14:29:20

¿Qué tan exacto necesita que sea? td.days / 365.25 te acercará bastante, si te preocupan los años bisiestos.

 0
Author: eduffy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-19 18:04:03

A pesar de que este hilo ya está muerto, podría sugerir una solución de trabajo para este mismo problema que estaba enfrentando. Aquí está (date es una cadena en el 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
 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-03 11:05:00

Esta función devuelve la diferencia en años entre dos fechas (tomada 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 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
 0
Author: Mauro Bianchi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-01 09:34:41

Sugeriré Pyfdate

¿Qué es pyfdate?

Dado el objetivo de Python de ser un scripting potente y fácil de usar lenguaje, sus características para trabajar con fechas y horas no son tan fácil de usar como deben ser. El el propósito de pyfdate es remediar que situación mediante el suministro de características para trabajar con fechas y horas que son potente y fácil de usar como el resto de Python.

El tutorial

 0
Author: twils,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-01 04:48:56

Bueno, la pregunta parece bastante fácil. Debe verificar el número de años' completos', y solo si es igual a 18 debe molestarse con meses y días. El caso edge es: endDate.year - startDate.year == 18 y se divide en dos casos: startDate.month != endDate.month y startDate.month == endDate.month, cuando solo tienes que comprobar días:

 def isOfAge(birthDate, age=18):
     endDate = date.today()
     years = endDate.year - birthDate.year
     if years == age:
         return (birthDate.month < endDate.month or 
                  (birthDate.month == endDate.month and birthDate.day < endDate.day))         
     return years > age

Todavía es más de un-liner-lambda, pero todavía es bastante corto, y parece rápido en la ejecución.

 -2
Author: Abgan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-19 13:33:56