web-dev-qa-db-fra.com

Python timedelta en années

Je dois vérifier si un certain nombre d'années se sont écoulées depuis une date donnée. Actuellement, j'ai le module timedelta du module datetime et je ne sais pas comment le convertir en années.

120
Migol

Vous avez besoin de plus que timedelta pour savoir combien d'années se sont écoulées; vous devez également connaître la date de début (ou de fin). (C'est une année bissextile.)

Votre meilleur pari est d'utiliser le dateutil.relativedeltaobjet , mais c'est un module tiers. Si vous voulez connaître le datetime qui était n ans à compter de quelque date (par défaut à maintenant), vous pouvez procéder comme suit:

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 vous préférez vous en tenir à la bibliothèque standard, la réponse est un peu plus complexe:

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)

S'il s'agit de 2/29 et qu'il y a 18 ans, il n'y en avait pas 2/29, cette fonction renverra 2/28. Si vous préférez retourner 3/1, changez simplement la dernière instruction return pour lire ::

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

Votre question disait à l'origine que vous vouliez savoir combien d'années cela fait depuis une date donnée. En supposant que vous souhaitiez un nombre entier d'années, vous pouvez deviner sur la base de 365,25 jours par an, puis vérifier en utilisant l'une des fonctions yearsago définies ci-dessus:

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
145
Rick Copeland

Si vous essayez de vérifier si une personne a 18 ans, utiliser timedelta ne fonctionnera pas correctement sur certains cas Edge en raison des années bissextiles. Par exemple, une personne née le 1 er janvier 2000 aura 18 ans exactement 6575 jours plus tard le 1 er janvier 2018 (5 années bissextiles incluses), mais une personne née le 1 er janvier 2001 aura 18 ans exactement 6574 jours plus tard le 1 er janvier. 2019 (4 années bissextiles incluses). Ainsi, si une personne a exactement 6574 jours, vous ne pouvez pas déterminer si elle a 17 ou 18 ans sans connaître un peu plus d'informations sur sa date de naissance.

Pour ce faire, calculez l’âge directement à partir des dates, en soustrayant les deux années, puis en soustrayant une si le mois/jour en cours précède le mois/jour de naissance.

45
Adam Rosenfield

Tout d’abord, au niveau le plus détaillé, le problème ne peut pas être résolu exactement. La durée des années varie et il n’existe pas de "bon choix" pour la durée d’une année.

Cela dit, obtenez la différence en unités "naturelles" (probablement en secondes) et divisez-les par le rapport entre cela et les années. Par exemple.

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

...ou peu importe. Restez à l'écart des mois, car ils sont encore moins bien définis que les années.

9
MarkusQ

Voici une fonction DOB mise à jour, qui calcule les anniversaires de la même manière que les humains:

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
antihero

Obtenez le nombre de jours, puis divisez par 365.2425 (année moyenne grégorienne) pour les années. Divisez par 30,436875 (le mois grégorien moyen) pendant des mois.

3
brianary

C’est la solution que j’ai trouvée, j’espère pouvoir vous aider ;-)

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
pvilas

Encore une autre bibliothèque tierce non mentionnée ici est mxDateTime (le prédécesseur de python datetime et de tierce partie timeutil pourrait être utilisé pour cette tâche.) Pourrait être utilisé pour cette tâche.

Le yearsago mentionné ci-dessus serait:

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)

Le premier paramètre devrait être une instance DateTime.

Pour convertir les datetime ordinaires en DateTime, vous pouvez utiliser ceci avec une précision de 1 seconde):

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

ou ceci pour une précision de 1 microseconde:

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)

Et oui, ajouter la dépendance pour cette seule tâche en question serait certainement une surexploitation par rapport à l'utilisation de timeutil (suggéré par Rick Copeland).

1
Antony Hatchkins

En fin de compte, vous avez un problème de calcul. Si tous les 4 ans, nous avons un jour supplémentaire, nous plongerons le timedelta en jours, pas par 365, mais par 365 * 4 + 1, le montant sera de 4 ans. Puis divisez à nouveau par 4. timedelta/((365 * 4) +1)/4 = timedelta * 4/(365 * 4 +1)

1
Norberto
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
John Mee

Je vais suggérer Pyfdate

Qu'est-ce que pyfdate?

L'objectif de Python étant de devenir un langage de script puissant et facile à utiliser, les fonctionnalités permettant de travailler avec les dates et les heures ne sont pas aussi conviviales qu'elles le devraient. Pyfdate a pour but de remédier à cette situation en fournissant des fonctionnalités permettant de travailler avec des dates et des heures aussi puissantes et faciles à utiliser que le reste de Python.

le tutoriel

0
twils

cette fonction renvoie la différence en années entre deux dates (prises sous forme de chaînes au format ISO, mais elle peut facilement être modifiée pour prendre n'importe quel format)

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
Mauro Bianchi

Quelle est l'exactitude dont vous avez besoin? td.days / 365.25 vous rapprochera si vous êtes inquiet pour les années bissextiles.

0
eduffy