Foros del Web » Programando para Internet » Python »

Suma de meses a una fecha en python

Estas en el tema de Suma de meses a una fecha en python en el foro de Python en Foros del Web. ¿Como puedo sumarle meses a un objeto fecha en python? El código es algo similar a este. from datetime import date, timedelta pagos = 7 ...
  #1 (permalink)  
Antiguo 26/08/2011, 12:48
Avatar de wilmermorel  
Fecha de Ingreso: agosto-2011
Ubicación: Santo Domingo
Mensajes: 30
Antigüedad: 12 años, 8 meses
Puntos: 2
Pregunta Suma de meses a una fecha en python

¿Como puedo sumarle meses a un objeto fecha en python?

El código es algo similar a este.


from datetime import date, timedelta
pagos = 7
periodo = raw_input("Escriba la frecuencia de pagos")
hoy = date.today()

if periodo == "Semanal":
fecha = hoy
for a in range(pagos):
fecha = fecha + timedelta(days=7)

if periodo == "Quincenal":
fecha = hoy
for a in range(pagos):
fecha = fecha + timedelta(days=15)

if periodo == "Mensual":
fecha = hoy
for a in range(pagos):
fecha = fecha + timedelta(days=30)

print fecha

El problema cuando el periodo es "Mensual" es: que no todos los meses tienen 30 dias. Entonces ¿Habra alguna solución?
  #2 (permalink)  
Antiguo 26/08/2011, 14:47
AlvaroG
Invitado
 
Mensajes: n/a
Puntos:
Respuesta: Suma de meses a una fecha en python

* Existe un módulo dateutil que contiene operaciones que no están en datetime.
* el método timetuple() te puede ayudar a tomar los datos de una fecha, modificarlos y crear un objeto nuevo

Código Python:
Ver original
  1. t = list(mi_fecha.timetuple())
  2. t[2] += 1
  3. if t[1] > 12:
  4.     t[1] = t[1] % 12
  5.     t[0] += 1
  6. nueva_fecha = datetime.date(t[0], t[1], t[2]) # forma larga
  7. nueva_fecha = datetime.date(*t[:3]) # forma corta :-)

Pero ten en cuenta que si el día es > 28, va a haber al menos 1 mes al año en el que esto de un error (ya que no existe 29 de febrero a menos que sea bisiesto, y no existe nunca 30, 31 de febrero ni 31 de otros meses)


Saludos.
  #3 (permalink)  
Antiguo 30/08/2011, 18:25
Avatar de dual3nigma
Colaborador
 
Fecha de Ingreso: febrero-2010
Ubicación: Ciudad de México
Mensajes: 295
Antigüedad: 14 años, 1 mes
Puntos: 122
Respuesta: Suma de meses a una fecha en python

Hola wilmermorel

Tendrías que buscar una librería para trabajar con fechas, o hacer algo como esto ( el único problema es que solo funciona si los pagos no pasan de +1 año :/

Código Python:
Ver original
  1. from datetime import date
  2.  
  3. hoy = date.today()
  4.  
  5. pagos = 7
  6. nuevo_mes = hoy.month + pagos
  7. nuevo_year = hoy.year
  8. nuevo_dia = hoy.day
  9.  
  10. if nuevo_mes > 12:
  11.     nuevo_mes = nuevo_mes % 12
  12.     nuevo_year += 1
  13.  
  14. if nuevo_mes == 2 and hoy.day > 28: nuevo_dia = 28
  15. if hoy.day > 30 and nuevo_mes in (4, 6, 11): nuevo_dia = 30
  16.  
  17. hoy = hoy.replace(nuevo_year, nuevo_mes, nuevo_dia)
  18.  
  19. print hoy

Lo fácil que es hacer algo así en php:

Código PHP:
Ver original
  1. $pagos = 7;
  2.  
  3. $hoy = strtotime("now +{$pagos} months");
  4.  
  5. echo strftime('%d de %B del %Y', $hoy);

Saludos
  #4 (permalink)  
Antiguo 31/08/2011, 08:54
AlvaroG
Invitado
 
Mensajes: n/a
Puntos:
Respuesta: Suma de meses a una fecha en python

Hola dual3nigma,
Has recomendado "buscar una librería para trabajar con fechas", lo cual demuestra que no has leído mi mensaje anterior.

Tu código suma meses considerando cada vez el último día del mes. Si esa es la intención, se puede hacer mucho más sencillo de esta forma (que, dicho sea de paso, funciona casi en cualquier lenguaje):

Código Python:
Ver original
  1. meses = (31, 28, 31, 30....., 31) # tupla con cantidad de meses. Notar que no considera años bisiestos
  2. fecha = (2011, 3, 31) # una fecha inicial, nomás por ejemplo
  3. suma_meses = fecha[1] + cant_meses # suponiendo cant_meses = 12, esto da 15
  4. nueva_fecha = (fecha[0] + suma_meses / 12, suma_meses % 12, meses[(suma_meses % 12) -1]

La biblioteca estándar de Python no implementa una función para sumar meses debido a la dificultad para sumar correctamente en todos los casos (si hay algo que caracteriza a Python es que se "niega" a adivinar intenciones).

¿Qué resultado da "31 de enero + 1 mes"? Dependiendo de lo que necesites, podría ser:
- 28 de febrero (para último día)
- 29 de febrero (en años bisiestos)
- 2 de marzo (años bisiestos, suma 31 días, o años normales, suma 30 días)
- 1 de marzo (años bisiestos, suma 1 mes = 30 días)
- 3 de marzo (años no bisiestos, suma 31 días)

Por otro lado, el método de PHP es similar en concepto a trabajar con timetuple()


Saludos.
  #5 (permalink)  
Antiguo 31/08/2011, 10:04
Avatar de dual3nigma
Colaborador
 
Fecha de Ingreso: febrero-2010
Ubicación: Ciudad de México
Mensajes: 295
Antigüedad: 14 años, 1 mes
Puntos: 122
Respuesta: Suma de meses a una fecha en python

Hola AlvaroG

No es que no haya leído tu respuesta, es que no hace lo que wilmermorel quiere.

Mira el problema es el siguiente:

Este chico saco a credito una tele a 7 meses, empieza hoy (2011/08/31), ¿en que fecha será su último pago? La respuesta es (2012/03/31). Si fuera a 6 meses seria (2011/02/28). Ahora si lo sacó el (2011/08/10), a 7 meses su ultimo pago será el (2012/03/10).

Eso es lo que mi código hace si los meses no exceden +1 año, ósea el numero de pagos máximo que acepta es 12 :/

En tu código estas sacando el último día del mes.

La cosa esta en que este tipo de problemas son reales y python hace muy difícil el resolverlos mientras que en PHP "simplemente funciona", puedes usar "last day of next month", "first sat of July 2008", eso lo supera todo

Saludos.
  #6 (permalink)  
Antiguo 31/08/2011, 14:52
AlvaroG
Invitado
 
Mensajes: n/a
Puntos:
Respuesta: Suma de meses a una fecha en python

Quizás fue un problema de comprensión lectora, pero no vi nada en el mensaje original que mostrara que era ese el requisito.
El código que coloqué antes no solamente muestra el último día del mes, sino que devuelve el último día de (hoy + N meses), lo cual es lo que se buscaba.

De todas formas creo que no hay ninguna dificultad extra al usar objetos de tipo timedelta() ya que el cálculo de meses es lo único que específicamente se niega a hacer. Para solucionarlo, usas el módulo dateutil o el módulo calendar, con el que podés obtener el número de días de un mes y el primer día de semana.

Saludos.
  #7 (permalink)  
Antiguo 31/08/2011, 15:47
Avatar de razpeitia
Moderador
 
Fecha de Ingreso: marzo-2005
Ubicación: Monterrey, México
Mensajes: 7.321
Antigüedad: 19 años, 1 mes
Puntos: 1360
Respuesta: Suma de meses a una fecha en python

Cita:
Iniciado por dual3nigma Ver Mensaje
Este chico saco a credito una tele a 7 meses, empieza hoy (2011/08/31), ¿en que fecha será su último pago? La respuesta es (2012/03/31).
Si esta sería su ultima fecha de pago.

Cita:
Iniciado por dual3nigma Ver Mensaje
Si fuera a 6 meses seria (2011/02/28).
De hecho sería 2011/02/29 ya que 2012 es bisiesto.
Pero al correr tu programa en php con 6 meses, me dice 2012/03/02 (02 de Marzo del 2012).
Lo cual obviamente no es correcto.

Cita:
Iniciado por dual3nigma Ver Mensaje
Ahora si lo sacó el (2011/08/10), a 7 meses su ultimo pago será el (2012/03/10).
En esta también coincido.

Cita:
Iniciado por dual3nigma Ver Mensaje
Eso es lo que mi código hace si los meses no exceden +1 año, ósea el numero de pagos máximo que acepta es 12 :/
De hecho, no especificaron si tenían un limite de meses o si solo quería sumarle un mes.


Cita:
Iniciado por dual3nigma Ver Mensaje
La cosa esta en que este tipo de problemas son reales y python hace muy difícil el resolverlos mientras que en PHP "simplemente funciona", puedes usar "last day of next month", "first sat of July 2008", eso lo supera todo
Eso que "simplemente funciona" no te garantiza que funcione correctamente. (Como en tu código de arriba). Tampoco el hecho que tenga mas o menos código quiere decir que no funcione o que funcione mas lento. (Regularmente es cierto, pero no siempre es así).

Ahora con lo que respecta al problema es que el problema esta pobremente planteado y poco estructurado, un mes es sumamente arbitrario. Es por eso que deltatime no tiene months como parametro.
  #8 (permalink)  
Antiguo 23/11/2011, 15:13
Avatar de wilmermorel  
Fecha de Ingreso: agosto-2011
Ubicación: Santo Domingo
Mensajes: 30
Antigüedad: 12 años, 8 meses
Puntos: 2
De acuerdo Respuesta: Suma de meses a una fecha en python

Hola y gracias a todos: AlvaroG, dual3nigma y razpeitia. Mis disculpas por no volver a citar el tema, para ese entonces era nuevo aquí, me parece que este fue mi primer mensaje, y mi mala memoria me trajo aquí de nuevo.
  #9 (permalink)  
Antiguo 23/11/2011, 19:07
Avatar de razpeitia
Moderador
 
Fecha de Ingreso: marzo-2005
Ubicación: Monterrey, México
Mensajes: 7.321
Antigüedad: 19 años, 1 mes
Puntos: 1360
Respuesta: Suma de meses a una fecha en python

No he probado rigurosamente mi programa como para decir que funciona al 100% pero aqui te dejo una implementación hecha por mi para sumarle meses a una fecha.

Descripción:
Dado una fecha valida (en este caso un objeto datetime) añadir meses a la fecha de la siguiente manera:
  1. Calcula el nuevo año, después de todo si pasan mas de 12 meses es un año completo.
  2. Calcula cuantos meses se va a desplazar, esto siempre cae un rango de 0 a 11. Después de todo si nos movemos 12 meses llegamos al mismo mes, lo mismo pasa con 24, 48 y todos los múltiplos de 12.
  3. Se desplaza esa cantidad de meses y hace una corrección tanto en los meses como en los años.
  4. El día es el mismo.
  5. Si resulta ser una fecha no valida entonces el día se pasa al ultimo día valido del mes.

Código Python:
Ver original
  1. from datetime import datetime
  2.  
  3. def add_month(mDate, months):
  4.     nYear = mDate.year + months / 12
  5.     dMonth = months % 12
  6.     if dMonth + mDate.month > 12:
  7.         nYear += 1
  8.     nMonth = (mDate.month + dMonth) % 12 or 12
  9.     nDay = mDate.day
  10.  
  11.     try:
  12.         return datetime(nYear, nMonth, nDay)
  13.     except Exception, e:
  14.         pass
  15.     if nMonth == 2:
  16.         nDay = 28
  17.         if (nYear % 4 == 0 and  nYear % 100 != 0) or nYear % 400 == 0:
  18.             nDay = 29
  19.     else:
  20.         nDay = 30
  21.     return datetime(nYear, nMonth, nDay)
  22.  
  23. d = datetime(2000, 1, 31)
  24. for i in range(24):
  25.     print add_month(d, i)
NOTA: Solo funciona con fechas validas y meses positivos o cero.

Última edición por razpeitia; 24/11/2011 a las 23:30 Razón: Corrigiendo código.
  #10 (permalink)  
Antiguo 23/11/2011, 19:54
Avatar de wilmermorel  
Fecha de Ingreso: agosto-2011
Ubicación: Santo Domingo
Mensajes: 30
Antigüedad: 12 años, 8 meses
Puntos: 2
Respuesta: Suma de meses a una fecha en python

Eres un monstruo razpeitia! deberías hablar con Guido para que incluya tu código en los módulos de Python jejeje. Precisamente la incluire yo en un módulo mio.
Ya que estamos alimentando el tema, paso el código del modulo, para que me den su critica constructiva. De camino preciso saber como, en la funcion GetEdad (que obtengo un objeto timedelta con los dias) como puedo pasarlos a años?

Código Python:
Ver original
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. from Excepciones import * #excepciones predefinidas
  5. import webkit             #logra hacer que el formato de la fecha sea igual a la conf regional.???
  6. from datetime import *
  7.  
  8. class FechaHora():        
  9.     def GetHoy(self, texto=False):
  10.         """retorna el la fecha actual, si texto=True, la devuelve en modo texto.
  11.        GetHoy(texto=False)
  12.        """
  13.         if texto:
  14.             return self.SetFechaTexto()
  15.         else:
  16.             return datetime.today()
  17.    
  18.     def SetFecha(self, texto=False, *fecha):
  19.         """retorna la fecha indicada como segundo parametro, al formato ideal.
  20.        *fecha = (1988, 05, 07) >>> 07/05/1988, si el valor del parametro
  21.        *fecha es un string, se asume que la fecha esta en ese formato y se
  22.        quiere pasar a formato datetime o date (segun haiga una hora o no).
  23.        si el parametro texto = True retorna la fecha en modo texto
  24.        >>> sabado 07 de Mayo del 1988
  25.         """
  26.         if not fecha:
  27.             raise FechaHoraError("""Debe especificar una fecha en parametro 2
  28.                  SetFecha(texto=False, *fecha)""")
  29.                  
  30.         if len(fecha) == 1:
  31.             if isinstance(fecha[0], basestring):
  32.                 #si hay comas, se remplazan por espacios
  33.                 fecha = fecha[0].replace(",", " ")
  34.                 #verifica que tipo de divicion tiene (- / )
  35.                 buscarbarra = fecha.find("/")
  36.                 buscarguion = fecha.find("-")
  37.                
  38.                 #si la division es con barras----------------------------------
  39.                 if buscarbarra != -1:
  40.                     dia, mes, anoyhora = fecha.split("/")
  41.                    
  42.                     #se verifica si hay una hora indicada
  43.                     if anoyhora.find(":") != -1:
  44.                         #contar los espacios en que esta dividida
  45.                         espacios = anoyhora.count(" ")
  46.                         ano, hora = anoyhora.split(" " * espacios)
  47.                         horas, minutos, segundos = hora.split(":")
  48.                     else:
  49.                         ano = anoyhora
  50.                         horas, minutos, segundos = False, False, False
  51.                
  52.                 #si la divicion es con guion-----------------------------------
  53.                 elif buscarguion != -1:
  54.                     dia, mes, anoyhora = fecha.split("-")
  55.                    
  56.                     #se verifica si hay una hora indicada
  57.                     if anoyhora.find(":") != -1:
  58.                         #contar los espacios en q esta dividida
  59.                         espacios = anoyhora.count(" ")
  60.                         ano, hora = anoyhora.split(" " * espacios)
  61.                         horas, minutos, segundos = hora.split(":")
  62.                     else:
  63.                         ano = anoyhora
  64.                         horas, minutos, segundos = False, False, False
  65.                 else:
  66.                     return None
  67.                    
  68.                 #si el dia es mayor a 31, se asume que es el año---------------
  69.                 try:
  70.                     if int(dia) > 31:
  71.                         dia2 = ano
  72.                         ano = dia
  73.                         dia = dia2
  74.                 except ValueError:
  75.                     raise FechaHoraError(u"""La fecha indicada, no es valida,
  76.                          el dia, mes, año, hora, minutos, segundos que se
  77.                          especifiquen, deben ser caracteres numericos
  78.                          no """+dia+"/"+mes+"/"+ano)
  79.                 try:
  80.                     fecha = datetime(int(ano), int(mes), int(dia),
  81.                                    int(horas), int(minutos), int(segundos))
  82.                     if horas is False:
  83.                         fecha = date(int(ano), int(mes), int(dia))
  84.                 except ValueError as e:
  85.                     raise FechaHoraError(e)
  86.  
  87.                 if texto:
  88.                     #si no hay hora, no se indica en texto---------------------
  89.                     if horas is False:
  90.                         return self.SetFechaTexto(fecha, False)
  91.                     else:
  92.                         return self.SetFechaTexto(fecha, True)
  93.                 else:
  94.                     return fecha
  95.         #-------------------------------------------------------------------------------------------------------------
  96.         else:
  97.             if len(fecha) == 3:
  98.                 try:
  99.                     #si hay 3 elementos, se crear un objeto date
  100.                     fecha = date(*fecha)
  101.                 except ValueError:
  102.                     #si el dia no es valido, y es mayor a 31, se invierte la tupla convirtiendola en lista
  103.                     if fecha[2] > 31:
  104.                         fecha = list(fecha)
  105.                         fecha.reverse()
  106.                         try:
  107.                             fecha = date(*fecha)
  108.                         except ValueError as e:
  109.                             raise FechaHoraError(e)
  110.                     else:
  111.                         raise FechaHoraError("""Los datos introducidos para
  112.                        la creacion de la fecha, no son validos. """+str(fecha))
  113.             elif len(fecha) > 3:
  114.                 #si hay mas de 3 elementos, se crea un obj datetime
  115.                 try:
  116.                     fecha = datetime(*fecha)
  117.                 except ValueError:
  118.                     #si el dia no es valido, y es mayor a 31, se cambia este por el año
  119.                     if fecha[2] > 31:
  120.                         dia = fecha[0]
  121.                         ano = fecha[2]
  122.                         fecha = list(fecha)
  123.                         fecha[0] = ano
  124.                         fecha[2] = dia
  125.                         try:
  126.                             fecha = datetime(*fecha)
  127.                         except ValueError as e:
  128.                             raise FechaHoraError(e)
  129.                     else:
  130.                         raise FechaHoraError("""Los datos introducidos para
  131.                        la creacion de la fecha, no son validos. """+str(fecha))
  132.             try:
  133.                 #se formatea la fecha------------------------------------------
  134.                 if isinstance(fecha, datetime):
  135.                     fecha_str =datetime.strftime(fecha,"%d/%m/%Y %H:%M:%S")
  136.                     #si el retorno debe ser fecha en texto
  137.                     if texto:
  138.                         return self.SetFechaTexto(fecha, True)
  139.                     else:
  140.                         return fecha_str
  141.                
  142.                 elif isinstance(fecha, date):
  143.                     fecha_str =date.strftime(fecha,"%d/%m/%Y")
  144.                     #si el retorno debe ser fecha en texto
  145.                     if texto:
  146.                         return self.SetFechaTexto(fecha, False)
  147.                     else:
  148.                         return fecha_str
  149.             except BaseException as e:
  150.                 raise FechaHoraError(str(e))
  151.                
  152.    
  153.        
  154.     def GetHora(self):
  155.         """Devuelve la hora actual en formato string"""
  156.         hora_str = datetime.strftime(datetime.today(), "%H:%M:%S")
  157.         return hora_str
  158.    
  159.     def GetFecha(self, texto=False):
  160.         """Debuelve la fecha actual en formato string. si texto es True, devuelve
  161.        la fecha en formato texto
  162.        >>> GetFecha()      ->  07/05/1988
  163.        >>> GetFecha(True)  ->  Sábado 7 de mayo del 1988
  164.        """
  165.         fecha_str = datetime.strftime(datetime.today(), "%d/%m/%Y")
  166.         if texto:
  167.             return self.SetFechaTexto(fecha_str, False)
  168.         else:
  169.             return fecha_str
  170.    
  171.     def GetEdad(self, fecha):
  172.         """Devuelve un objeto 'datetime.timedelta' con el tiempo exacto desde la
  173.        fecha indicada hasta hoy. El parametro 1 'fecha', puede ser un objeto
  174.        date, datetime, o una cadena de texto, este ultimo debe ser en
  175.        formato 'dia/mes/año' o 'dia-mes-año'.
  176.        """
  177.         if isinstance(fecha, basestring):
  178.             try:
  179.                 fecha = self.SetFecha(False, fecha)
  180.             except FechaHoraError as e:
  181.                 raise FechaHoraError(e)
  182.         if isinstance(fecha, datetime):
  183.             print 1
  184.             return datetime.today() - fecha
  185.         elif isinstance(fecha, date):
  186.             print 2
  187.             return date.today() - fecha
  188.         else:
  189.             raise FechaHoraError("""Error calculando la fecha,
  190.                                 la fecha indicada no es correcta.""")
  191.                
  192.     def SetFechaTexto(self, fecha=None, hora=True):
  193.         """Formatea la fecha a formato texto.
  194.        hoy = datetime.datetime.today()   >> 1988-05-07 10:30:27.548789
  195.        fecha_texto = SetFechaTexto(hoy)  >> sábado 07 de mayo del 1988, 10:30:27
  196.        si el parametro hora = False, devolvera solo la fecha.
  197.        """
  198.         if fecha == None:
  199.             fecha = datetime.today()
  200.         if isinstance(fecha, basestring):
  201.             fecha = self.SetFecha(False, fecha)
  202.         try:
  203.             date_text = datetime.strftime(fecha, "%A %d de %B del %Y")
  204.             time_text = datetime.strftime(fecha, "%H:%M:%S")
  205.         except BaseException as e:
  206.             raise FechaHoraError(e)
  207.         else:
  208.             if hora == False:
  209.                 return date_text
  210.             else:
  211.                 return date_text+", "+time_text
  #11 (permalink)  
Antiguo 26/11/2011, 16:40
Avatar de razpeitia
Moderador
 
Fecha de Ingreso: marzo-2005
Ubicación: Monterrey, México
Mensajes: 7.321
Antigüedad: 19 años, 1 mes
Puntos: 1360
Respuesta: Suma de meses a una fecha en python

Mi código no es necesario acabo de encontrar una mejor solución.

Código Python:
Ver original
  1. from datetime import date
  2. from dateutil.relativedelta import relativedelta
  3. date.today() + relativedelta(months=1)

Etiquetas: datetime, fecha, meses
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 17:35.