Cómo incrementar datetime por meses personalizados en python sin usar library [duplicate]


Esta pregunta ya tiene una respuesta aquí:

Necesito incrementar el mes de un valor datetime

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

Cuando el mes es 12, se convierte en 13 y aumenta el error "mes debe estar en 1..12". (Yo esperaba que el año incremento)

Quería usar timedelta, pero no toma argumento mes. Existe relativedelta paquete python, pero no quiero instalarlo solo para esto. También hay una solución usando strtotime.

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

No quiero convertir de datetime a str luego a time, y luego a datetime; por lo tanto, sigue siendo una biblioteca también

¿Alguien tiene alguna solución buena y simple como usar timedelta?

Author: jargalan, 2010-11-09

21 answers

Editar - basado en su comentario de que las fechas deben redondearse hacia abajo si hay menos días en el próximo mes, aquí hay una solución:

>>> 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)
...
>>> 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)

Además, si no te preocupan las horas, los minutos y los segundos, podrías usar date en lugar de datetime. Si estás preocupado por horas, minutos y segundos necesitas modificar mi código para usar datetime y copiar horas, minutos y segundos del origen al resultado.

 104
Author: Dave Webb,
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
2018-01-11 16:21:45

Este es un método corto y dulce para agregar un mes a una fecha usando 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')

Salida:

Hoy: 01/03/2013

Después del mes: 01/04/2013

Una palabra de advertencia: relativedelta(months=1) y relativedelta(month=1) tiene diferentes significados. Passing month=1 will replace the month in original date to January whereas passing months=1 will add one month to original date.

Nota: esto requiere python-dateutil. Instalar es necesario ejecutar en el terminal Linux.

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

Explicación: Agregar valor mensual en python

 350
Author: Atul Arvind,
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
2018-09-27 20:47:08

Aquí está mi sal:

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

Rápido y fácil :)

 25
Author: Cyril N.,
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
2016-10-06 13:19:47

Ya que nadie sugirió ninguna solución, aquí es cómo he resuelto hasta ahora

year, month= divmod(mydate.month+1, 12)
if month == 0: 
      month = 12
      year = year -1
next_month = datetime.datetime(mydate.year + year, month, 1)
 10
Author: jargalan,
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-11-09 08:43:15

Use el paquete monthdelta, funciona igual que timedelta pero para meses calendario en lugar de días/horas/etc.

Aquí hay un ejemplo:

from monthdelta import MonthDelta

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

Compare eso con el enfoque de bricolaje:

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
Author: Eric Clack,
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
2012-11-25 01:47:11

Para calcular el mes actual, anterior y siguiente:

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
 5
Author: Darren Weber,
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
2014-05-07 00:26:59
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 simplemente quieres el primer día del mes siguiente:

next = (x.replace(day=1) + timedelta(days=31)).replace(day=1)
 4
Author: Collin Anderson,
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
2014-05-03 17:46:30

Tal vez añadir el número de días en el mes actual utilizando calendario.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
Author: uzi,
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-11-09 06:31:55

Esta implementación podría tener algún valor para alguien que esté trabajando con facturación.

Si está trabajando con facturación, probablemente desee obtener "la misma fecha el próximo mes (si es posible)" en lugar de "agregar 1/12 de un año".

Lo que es tan confuso sobre esto es que realmente necesita tener en cuenta dos valores si está haciendo esto continuamente. De lo contrario, para cualquier fecha posterior al 27, seguirá perdiendo unos días hasta que termine en el 27 después del año bisiesto.

El valores que debe tener en cuenta:

  • El valor que desea agregar un mes a
  • El día que empezaste con

De esta manera, si te topas del 31 al 30 cuando añades un mes, te toparás de nuevo con el 31 para el próximo mes que tenga ese día.

Así es como lo hice:

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
Author: Chris Dutrow,
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
2014-07-22 22:02:57

Similar en ideal a la solución de Dave Webb, pero sin todo ese complicado módulo aritmético:

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
Author: Matthew Schinckel,
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-06-23 01:03:03

¿Qué hay de este? (no requiere ninguna biblioteca adicional)

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)
 2
Author: Michał Tabor,
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-12-31 01:01:29

Solo Usa Esto:

import datetime
today = datetime.datetime.today()
nextMonthDatetime = today + datetime.timedelta(days=(today.max.day - today.day)+1)
 2
Author: ayged,
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
2014-05-31 05:56:25

Bueno, con algunos ajustes y el uso de timedelta aquí vamos:

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
Author: dcolish,
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-11-09 06:25:08

Estaba buscando resolver el problema relacionado de encontrar la fecha para el primero del mes siguiente, independientemente del día en la fecha dada. Esto no encuentra el mismo día 1 mes después.

Así que, si todo lo que quieres es poner en diciembre 12, 2014 (o cualquier día de diciembre) y volver Enero 1, 2015, prueba esto:

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
Author: Ari Lacenski,
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
2014-03-06 21:36:59
def add_month(d,n=1): return type(d)(d.year+(d.month+n-1)/12, (d.month+n-1)%12+1, 1)
 1
Author: Neil C. Obremski,
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
2014-03-07 09:24:50

La solución más simple es ir al final del mes (siempre sabemos que los meses tienen al menos 28 días) y agregar días suficientes para pasar a la siguiente polilla:

>>> 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)

También funciona entre años:

>>> 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)

Solo tenga en cuenta que no está garantizado que el próximo mes tendrá el mismo día, por ejemplo, al pasar del 31 de enero al 31 de febrero fallará:

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

Así que esta es una solución válida si necesita pasar al primer día del mes siguiente, ya que siempre sabe que la el próximo mes tiene día 1 (.replace(day=1)). De lo contrario, para pasar al último día disponible, es posible que desee utilizar:

>>> 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)
 1
Author: amol,
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
2014-04-30 10:03:41

Una solución sin el uso de calendario:

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
Author: Oliver Wienand,
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
2012-05-16 14:16:55

Esto es lo que se me ocurrió

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
Author: jaywhy13,
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-07-16 01:34:47
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
Author: damn_c,
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-07-29 02:34:20

Ejemplo usando el objeto time:

start_time = time.gmtime(time.time())    # start now

#increment one month
start_time = time.gmtime(time.mktime([start_time.tm_year, start_time.tm_mon+1, start_time.tm_mday, start_time.tm_hour, start_time.tm_min, start_time.tm_sec, 0, 0, 0]))
 -3
Author: karl,
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-11-03 14:25:30

Mi solución muy simple, que no requiere ningún módulo adicional:

def addmonth(date):
    if date.day < 20:
        date2 = date+timedelta(32)
    else :
        date2 = date+timedelta(25)
    date2.replace(date2.year, date2.month, day)
    return date2
 -3
Author: pilou,
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
2012-11-28 13:59:46