web-dev-qa-db-fra.com

Comment ajouter "3 mois" à un objet datetime.date en python?

Calculs de date en python, où es-tu?

J'ai une application python qui doit tracer des dates tous les trois mois pendant plusieurs années. Il est important que les dates soient exactement 4 fois par an, le plus souvent possible le même jour chaque année, et le plus souvent possible le même jour du mois, et que ces dates soient aussi proches que possible de "3 mois" (cible mobile, en particulier pour les années bissextiles). Malheureusement, datetime.timedelta ne prend pas en charge les mois!

Existe-t-il une méthode "standard" pour effectuer ce calcul en python ???

La manière SQL?

Si le pire venait à se dégrader, je demanderai à mon application de demander à PostgreSQL, qui dispose du support intégré de Nice pour les calculs de date, de répondre comme ceci:

# select ('2010-11-29'::date + interval '3 months')::date;
    date    
------------
 2011-02-28
(1 row)
22
Nathan

Si vous recherchez des dates exactes ou "plus précises", vous feriez probablement mieux de consulter dateutil .

Exemple rapide:

>>> from dateutil.relativedelta import relativedelta
>>> import datetime
>>> TODAY = datetime.date.today()
>>> TODAY
datetime.date(2012, 3, 6)

Ajoutez maintenant 3 mois à TODAY, observez qu’il correspond exactement au jour (notez que relativedelta(months=3) et relativedelta(month=3) ont des comportements différents. Veillez à utiliser months pour ces exemples!).

>>> three_mon_rel = relativedelta(months=3)
>>> TODAY + three_mon_rel
datetime.date(2012, 6, 6)

Et cela reste constant tout au long de l'année. Littéralement tous les trois mois, le jour (il fallait continuer à ajouter car, pour une raison quelconque, multiplier une relativedelta et l'ajouter à un objet datetime.date génère une TypeError):

>>> TODAY + three_mon_rel + three_mon_rel
datetime.date(2012, 9, 6)
>>> TODAY + three_mon_rel + three_mon_rel + three_mon_rel
datetime.date(2012, 12, 6)
>>> TODAY + three_mon_rel + three_mon_rel + three_mon_rel + three_mon_rel
datetime.date(2013, 3, 6)

Alors que la solution suggérée par les mVChr , bien qu’elle soit "assez bonne", dérive légèrement dans le temps:

>>> three_mon_timedelta = datetime.timedelta(days=3 * 365/12)
>>> TODAY + three_mon_timedelta
datetime.date(2012, 6, 5)

Et au cours d'une année, le jour du mois continue de glisser:

>>> TODAY + three_mon_timedelta * 2
datetime.date(2012, 9, 4)
>>> TODAY + three_mon_timedelta * 3
datetime.date(2012, 12, 4)
>>> TODAY + three_mon_timedelta * 4
datetime.date(2013, 3, 5)
53
jathanism
import datetime

some_date = datetime.date.today()
three_months = datetime.timedelta(3*365/12)
print (some_date + three_months).isoformat()
# => '2012-06-01'

Puis "normaliser" chaque nouvelle année à la date de la date originale (sauf le 29 février)

6
mVChr

voici une bonne solution http://www.voidspace.org.uk/python/weblog/Arch_d7_2012_02_25.shtml#e1235

modifier ah moyen standard désolé ...

1
heinzderaugust

En utilisant les bibliothèques standard Python, c'est-à-dire sans dateutil ou autres, et en résolvant le problème du «31 février»:

import datetime
import calendar

def add_months(date, months):
    months_count = date.month + months

    # Calculate the year
    year = date.year + int(months_count / 12)

    # Calculate the month
    month = (months_count % 12)
    if month == 0:
        month = 12

    # Calculate the day
    day = date.day
    last_day_of_month = calendar.monthrange(year, month)[1]
    if day > last_day_of_month:
        day = last_day_of_month

    new_date = datetime.date(year, month, day)
    return new_date

Essai:

>>>date = datetime.date(2018, 11, 30)

>>>print(date, add_months(date, 3))
(datetime.date(2018, 11, 30), datetime.date(2019, 2, 28))

>>>print(date, add_months(date, 14))
(datetime.date(2018, 12, 31), datetime.date(2020, 2, 29))
1
David Ragazzi