web-dev-qa-db-fra.com

Comment incrémenter date-heure par mois personnalisé dans python sans utiliser la bibliothèque

J'ai besoin d'incrémenter le mois d'une valeur datetime

next_month = datetime.datetime(mydate.year, mydate.month+1, 1)

quand le mois est 12, il devient 13 et pose l'erreur "le mois doit être dans 1..12". (Je m'attendais à ce que l'année augmente)

Je voulais utiliser timedelta, mais cela ne prend pas mois argument. Il existe un package relativedelta python, mais je ne souhaite pas l'installer uniquement pour cela. Il existe également une solution utilisant strtotime .

time = strtotime(str(mydate));
next_month = date("Y-m-d", strtotime("+1 month", time));

Je ne veux pas convertir datetime en str puis en time, puis en datetime; donc, c'est toujours une bibliothèque aussi

Quelqu'un a-t-il une bonne et solution simple semblable à l'utilisation de timedelta?

181
jargalan

Edit - En fonction de votre commentaire, il faut arrondir les dates s'il y a moins de jours le mois prochain. Voici une solution:

import datetime
import calendar

def add_months(sourcedate, months):
    month = sourcedate.month - 1 + months
    year = sourcedate.year + month // 12
    month = month % 12 + 1
    day = min(sourcedate.day, calendar.monthrange(year,month)[1])
    return datetime.date(year, month, day)

Utilisé:

>>> somedate = datetime.date.today()
>>> somedate
datetime.date(2010, 11, 9)
>>> add_months(somedate,1)
datetime.date(2010, 12, 9)
>>> add_months(somedate,23)
datetime.date(2012, 10, 9)
>>> otherdate = datetime.date(2010,10,31)
>>> add_months(otherdate,1)
datetime.date(2010, 11, 30)

De plus, si vous n'êtes pas inquiet pour les heures, les minutes et les secondes, vous pouvez utiliser date plutôt que datetime. Si vous vous inquiétez des heures, des minutes et des secondes, vous devez modifier mon code pour utiliser datetime et copier les heures, les minutes et les secondes de la source au résultat.

119
Dave Webb

C'est une méthode courte et agréable pour ajouter un mois à une date en utilisant dateutil's relativedelta .

from datetime import datetime
from dateutil.relativedelta import relativedelta

date_after_month = datetime.today()+ relativedelta(months=1)
print 'Today: ',datetime.today().strftime('%d/%m/%Y')
print 'After Month:', date_after_month.strftime('%d/%m/%Y')

Sortie:

Aujourd'hui: 01/03/2013

Après le mois: 01/04/2013

Un mot d'avertissement : relativedelta(months=1) et relativedelta(month=1) ont significations différentes . Passer month=1 remplacera le mois de la date d'origine en janvier alors que passer months=1 ajoutera un mois à la date d'origine.

Remarque: cela nécessite python-dateutil. Pour l'installer, vous devez utiliser un terminal Linux.

Sudo apt-get update && Sudo apt-get install python-dateutil

Explication: Ajouter la valeur du mois en python

399
Atul Arvind

Voici mon sel:

current = datetime.datetime(mydate.year, mydate.month, 1)
next_month = datetime.datetime(mydate.year + (mydate.month / 12), ((mydate.month % 12) + 1), 1)

Rapide et facile :)

25
Cyril N.

puisque personne n'a suggéré de solution, voici comment j'ai résolu jusqu'à présent

year, month= divmod(mydate.month+1, 12)
if month == 0: 
      month = 12
      year = year -1
next_month = datetime.datetime(mydate.year + year, month, 1)
11
jargalan

Utilisez le package monthdelta , il fonctionne comme timedelta mais pour les mois calendaires plutôt que pour les jours/heures/etc.

Voici un exemple:

from monthdelta import MonthDelta

def prev_month(date):
    """Back one month and preserve day if possible"""
    return date + MonthDelta(-1)

Comparez cela à l'approche DIY:

def prev_month(date):
    """Back one month and preserve day if possible"""
   day_of_month = date.day
   if day_of_month != 1:
           date = date.replace(day=1)
   date -= datetime.timedelta(days=1)
   while True:
           try:
                   date = date.replace(day=day_of_month)
                   return date
           except ValueError:
                   day_of_month -= 1               
10
Eric Clack

Pour calculer le mois en cours, précédent et suivant:

import datetime
this_month = datetime.date.today().month
last_month = datetime.date.today().month - 1 or 12
next_month = (datetime.date.today().month + 1) % 12 or 12
6
Darren Weber
from datetime import timedelta
try:
    next = (x.replace(day=1) + timedelta(days=31)).replace(day=x.day)
except ValueError:  # January 31 will return last day of February.
    next = (x + timedelta(days=31)).replace(day=1) - timedelta(days=1)

Si vous voulez simplement le premier jour du mois prochain:

next = (x.replace(day=1) + timedelta(days=31)).replace(day=1)
4
Collin Anderson

Cette implémentation pourrait avoir une certaine valeur pour quelqu'un qui travaille avec la facturation.

Si vous travaillez avec la facturation, vous voudrez probablement avoir "la même date le mois prochain (si possible)" par opposition à "ajouter 1/12 d'un an".

Ce qui est tellement déroutant à ce sujet, c’est que vous devez réellement prendre en compte deux valeurs si vous le faites de manière continue. Sinon, pour les dates après le 27, vous perdrez quelques jours jusqu'à ce que vous finissiez au 27 après une année bissextile.

Les valeurs que vous devez prendre en compte:

  • La valeur que vous souhaitez ajouter un mois à
  • Le jour où tu as commencé avec

De cette façon, si vous êtes décalé du 31 au 30 quand vous ajoutez un mois, vous serez renvoyé au 31 pour le mois suivant.

Voici comment je l'ai fait:

def closest_date_next_month(year, month, day):
    month = month + 1
    if month == 13:
        month = 1
        year  = year + 1


    condition = True
    while condition:
        try:
            return datetime.datetime(year, month, day)
        except ValueError:
            day = day-1
        condition = day > 26

    raise Exception('Problem getting date next month')

paid_until = closest_date_next_month(
                 last_paid_until.year, 
                 last_paid_until.month, 
                 original_purchase_date.day)  # The trick is here, I'm using the original date, that I started adding from, not the last one
3
Chris Dutrow

Qu'en est-il de celui-ci? (ne nécessite aucune bibliothèque supplémentaire)

from datetime import date, timedelta
from calendar import monthrange

today = date.today()
month_later = date(today.year, today.month, monthrange(today.year, today.month)[1]) + timedelta(1)
3
Michał Tabor

Peut-être ajouter le nombre de jours du mois en cours en utilisant calendar.monthrange ()?

import calendar, datetime

def increment_month(when):
    days = calendar.monthrange(when.year, when.month)[1]
    return when + datetime.timedelta(days=days)

now = datetime.datetime.now()
print 'It is now %s' % now
print 'In a month, it will be %s' % increment_month(now)
3
uzi

La solution la plus simple est d'aller à la fin du mois (nous savons toujours que les mois ont au moins 28 jours) et d'ajouter suffisamment de jours pour passer au mois prochain:

>>> from datetime import datetime, timedelta
>>> today = datetime.today()
>>> today
datetime.datetime(2014, 4, 30, 11, 47, 27, 811253)
>>> (today.replace(day=28) + timedelta(days=10)).replace(day=today.day)
datetime.datetime(2014, 5, 30, 11, 47, 27, 811253)

Fonctionne également entre les années:

>>> dec31
datetime.datetime(2015, 12, 31, 11, 47, 27, 811253)
>>> today = dec31
>>> (today.replace(day=28) + timedelta(days=10)).replace(day=today.day)
datetime.datetime(2016, 1, 31, 11, 47, 27, 811253)

Gardez simplement à l'esprit qu'il n'est pas garanti que le mois suivant aura le même jour. Par exemple, si vous passez du 31 janvier au 31 février, cela échouera:

>>> today
datetime.datetime(2016, 1, 31, 11, 47, 27, 811253)
>>> (today.replace(day=28) + timedelta(days=10)).replace(day=today.day)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: day is out of range for month

Il s’agit donc d’une solution valable si vous devez passer au premier jour du mois suivant, car vous savez toujours que le mois suivant a un jour 1 (.replace(day=1)). Sinon, pour passer au dernier jour disponible, vous pouvez utiliser:

>>> today
datetime.datetime(2016, 1, 31, 11, 47, 27, 811253)
>>> next_month = (today.replace(day=28) + timedelta(days=10))
>>> import calendar
>>> next_month.replace(day=min(today.day, 
                               calendar.monthrange(next_month.year, next_month.month)[1]))
datetime.datetime(2016, 2, 29, 11, 47, 27, 811253)
2
amol

Semblable dans l’idéal à la solution de Dave Webb, mais sans toute cette arithmétique modulo délicate:

import datetime, calendar

def increment_month(date):
    # Go to first of this month, and add 32 days to get to the next month
    next_month = date.replace(day=1) + datetime.timedelta(32)
    # Get the day of month that corresponds
    day = min(date.day, calendar.monthrange(next_month.year, next_month.month)[1])
    return next_month.replace(day=day)
2
Matthew Schinckel

Je cherchais à résoudre le problème lié à la recherche de la date du premier jour du mois suivant, peu importe du jour de la date indiquée. Cela ne trouve pas le même jour 1 mois plus tard.

Donc, si tout ce que vous voulez, c'est mettre le 12 décembre 2014 (ou n'importe quel jour de décembre) et revenir le 1er janvier 2015, essayez ceci:

import datetime

def get_next_month(date):
    month = (date.month % 12) + 1
    year = date.year + (date.month + 1 > 12)
    return datetime.datetime(year, month, 1)
1
Ari Lacenski
def add_month(d,n=1): return type(d)(d.year+(d.month+n-1)/12, (d.month+n-1)%12+1, 1)
1
Neil C. Obremski

Bien avec quelques ajustements et l'utilisation de timedelta on y va:

from datetime import datetime, timedelta


def inc_date(Origin_date):
    day = Origin_date.day
    month = Origin_date.month
    year = Origin_date.year
    if Origin_date.month == 12:
        delta = datetime(year + 1, 1, day) - Origin_date
    else:
        delta = datetime(year, month + 1, day) - Origin_date
    return Origin_date + delta

final_date = inc_date(datetime.today())
print final_date.date()
1
dcolish

C'est ce que je suis venu avec

from calendar  import monthrange

def same_day_months_after(start_date, months=1):
    target_year = start_date.year + ((start_date.month + months) / 12)
    target_month = (start_date.month + months) % 12
    num_days_target_month = monthrange(target_year, target_month)[1]
    return start_date.replace(year=target_year, month=target_month, 
        day=min(start_date.day, num_days_target_month))
0
jaywhy13

Une solution sans calendrier:

def add_month_year(date, years=0, months=0):
    year, month = date.year + years, date.month + months + 1
    dyear, month = divmod(month - 1, 12)
    rdate = datetime.date(year + dyear, month + 1, 1) - datetime.timedelta(1)
    return rdate.replace(day = min(rdate.day, date.day))
0
Oliver Wienand
def month_sub(year, month, sub_month):
    result_month = 0
    result_year = 0
    if month > (sub_month % 12):
        result_month = month - (sub_month % 12)
        result_year = year - (sub_month / 12)
    else:
        result_month = 12 - (sub_month % 12) + month
        result_year = year - (sub_month / 12 + 1)
    return (result_year, result_month)

def month_add(year, month, add_month):
    return month_sub(year, month, -add_month)

>>> month_add(2015, 7, 1)                        
(2015, 8)
>>> month_add(2015, 7, 20)
(2017, 3)
>>> month_add(2015, 7, 12)
(2016, 7)
>>> month_add(2015, 7, 24)
(2017, 7)
>>> month_add(2015, 7, -2)
(2015, 5)
>>> month_add(2015, 7, -12)
(2014, 7)
>>> month_add(2015, 7, -13)
(2014, 6)
0
damn_c

Il suffit d'utiliser ceci:

import datetime
today = datetime.datetime.today()
nextMonthDatetime = today + datetime.timedelta(days=(today.max.day - today.day)+1)
0
ayged