Réponses:
Vous avez besoin de plus d'un timedelta
pour dire 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 l' dateutil.relativedelta
objet , mais c'est un module tiers. Si vous voulez connaître la datetime
qui a été des n
années d' une certaine date (par défaut à l' heure actuelle), vous pouvez faire ce qui 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)
Si c'est 2/29, et il y a 18 ans il n'y avait pas 2/29, cette fonction renverra 2/28. Si vous préférez retourner 3/1, changez simplement la dernière return
instruction en read ::
return from_date.replace(month=3, day=1,
year=from_date.year-years)
Votre question indiquait à l'origine que vous vouliez savoir combien d'années cela faisait depuis une certaine date. En supposant que vous vouliez 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 yearsago
fonctions 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
datetime.now()
) est supérieure à la différence entre les années juliennes ( 365.25
) et grégoriennes ( 365.2425
) moyennes . . La bonne approche est suggérée dans la réponse de @Adam Rosenfield
Si vous essayez de vérifier si quelqu'un a 18 ans, l'utilisation timedelta
ne fonctionnera pas correctement dans certains cas extrêmes en raison des années bissextiles. Par exemple, une personne née le 1er janvier 2000 aura 18 ans exactement 6575 jours plus tard le 1er janvier 2018 (5 années bissextiles incluses), mais une personne née le 1er janvier 2001 aura 18 ans exactement 6574 jours plus tard le 1er janvier, 2019 (4 années bissextiles incluses). Ainsi, vous si quelqu'un a exactement 6574 jours, vous ne pouvez pas déterminer s'il a 17 ou 18 ans sans connaître un peu plus d'informations sur sa date de naissance.
La bonne façon de faire est de calculer l'âge directement à partir des dates, en soustrayant les deux années, puis en soustrayant un si le mois / jour actuel précède le mois / jour de naissance.
Tout d'abord, au niveau le plus détaillé, le problème ne peut pas être résolu exactement. Les années varient en longueur et il n'y a pas de «bon choix» clair pour la durée de l'année.
Cela dit, obtenez la différence entre les unités "naturelles" (probablement secondes) et divisez 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. Évitez les mois, car ils sont encore moins bien définis que les années.
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)))
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
Dans quelle mesure avez-vous besoin que ce soit exact? td.days / 365.25
vous rapprochera, si vous vous inquiétez des années bissextiles.
Une autre bibliothèque tierce non mentionnée ici est mxDateTime (prédécesseur de python datetime
et de tierstimeutil
) pourrait être utilisée pour cette tâche.
Ce qui précède yearsago
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 un DateTime
instance.
Pour convertir ordinaire datetime
en DateTime
vous pouvez utiliser ceci pour une précision d'une 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 exagération par rapport à l'utilisation de timeutil (suggéré par Rick Copeland).
En fin de compte, vous avez un problème de mathématiques. Si tous les 4 ans nous avons un jour supplémentaire, puis plonger le chronomètre en jours, pas par 365 mais 365 * 4 + 1, cela vous donnerait le montant de 4 ans. Puis divisez-le à nouveau par 4. timedelta / ((365 * 4) +1) / 4 = timedelta * 4 / (365 * 4 +1)
C'est la solution que j'ai élaboré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
Même si ce fil est déjà mort, puis-je suggérer une solution de travail pour ce même problème auquel je faisais face. Le voici (la date est une chaîne au format jj-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
cette fonction renvoie la différence en années entre deux dates (prise 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
Je suggérerai Pyfdate
Qu'est-ce que pyfdate?
Étant donné l'objectif de Python d'être un langage de script puissant et facile à utiliser, ses fonctionnalités pour travailler avec les dates et les heures ne sont pas aussi conviviales qu'elles devraient l'être. Le but de pyfdate est de remédier à cette situation en fournissant des fonctionnalités pour travailler avec des dates et des heures qui sont aussi puissantes et faciles à utiliser que le reste de Python.
le tutoriel
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()
C'est le code que l'opérateur du Carrousel exécutait dans le film Logan's Run;)
Je suis tombé sur cette question et j'ai trouvé la réponse d'Adams la plus utile https://stackoverflow.com/a/765862/2964689
Mais il n'y avait pas d'exemple python de sa méthode mais voici ce que j'ai fini par utiliser.
entrée: objet datetime
sortie: âge entier en années entières
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
J'ai aimé la solution de John Mee pour sa simplicité, et je ne suis pas très préoccupé par la façon dont, le 28 février ou le 1er mars, quand ce n'est pas une année bissextile, déterminer l'âge des personnes nées le 29 février. Mais voici un ajustement de son code qui, je pense, répond aux plaintes:
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