Obtener el Último día del Mes en Python


¿Hay alguna manera de usar la biblioteca estándar de Python para determinar fácilmente (es decir, una llamada a una función) el último día de un mes determinado?

Si la biblioteca estándar no soporta eso, ¿el paquete dateutil soporta esto?

 453
Author: Cristian, 2008-09-04

24 answers

No me di cuenta de esto antes cuando estaba mirando la documentación para el módulo de calendario, pero un método llamado monthrange proporciona esta información:

monthrange (año, mes)
    Devuelve el día de la semana del primer día del mes y el número de días del mes, para el año y el mes especificados.

>>> import calendar
>>> calendar.monthrange(2002,1)
(1, 31)
>>> calendar.monthrange(2008,2)
(4, 29)
>>> calendar.monthrange(2100,2)
(0, 28)

Así que:

calendar.monthrange(year, month)[1]

Parece la forma más sencilla de hacerlo.

Solo para ser claros, monthrange soporta años bisiestos como bueno:

>>> from calendar import monthrange
>>> monthrange(2012, 2)
(2, 29)

Mi respuesta anterior todavía funciona, pero es claramente deficiente.

 824
Author: Blair Conrad,
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
2017-05-23 12:03:09

Si no desea importar el módulo calendar, una función simple de dos pasos también puede ser:

import datetime

def last_day_of_month(any_day):
    next_month = any_day.replace(day=28) + datetime.timedelta(days=4)  # this will never fail
    return next_month - datetime.timedelta(days=next_month.day)

Salidas:

>>> for month in range(1, 13):
...     print last_day_of_month(datetime.date(2012, month, 1))
...
2012-01-31
2012-02-29
2012-03-31
2012-04-30
2012-05-31
2012-06-30
2012-07-31
2012-08-31
2012-09-30
2012-10-31
2012-11-30
2012-12-31
 92
Author: Augusto Men,
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-26 12:48:40

EDITAR: Ver la respuesta de @Blair Conrad para una solución más limpia


>>> import datetime
>>> datetime.date (2000, 2, 1) - datetime.timedelta (days = 1)
datetime.date(2000, 1, 31)
>>> 
 59
Author: John Millikin,
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
2008-09-05 00:36:04

EDITAR: ver mi otra respuesta. Tiene una mejor implementación que esta, que dejo aquí por si alguien está interesado en ver cómo uno podría "rodar su propia" calculadora.

@John Millikin da una buena respuesta, con la complicación añadida de calcular el primer día del mes siguiente.

Lo siguiente no es particularmente elegante, pero para averiguar el último día del mes en el que vive una fecha determinada, puede probar:

def last_day_of_month(date):
    if date.month == 12:
        return date.replace(day=31)
    return date.replace(month=date.month+1, day=1) - datetime.timedelta(days=1)

>>> last_day_of_month(datetime.date(2002, 1, 17))
datetime.date(2002, 1, 31)
>>> last_day_of_month(datetime.date(2002, 12, 9))
datetime.date(2002, 12, 31)
>>> last_day_of_month(datetime.date(2008, 2, 14))
datetime.date(2008, 2, 29)
 37
Author: Blair Conrad,
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
2008-09-05 19:44:55

Esto es bastante fácil con dateutil.relativedelta (paquete python-datetutil para pip). day=31 siempre siempre devolverá el último día del mes.

Ejemplo:

from datetime import datetime
from dateutil.relativedelta import relativedelta

date_in_feb = datetime.datetime(2013, 2, 21)
print datetime.datetime(2013, 2, 21) + relativedelta(day=31)  # End-of-month
>>> datetime.datetime(2013, 2, 28, 0, 0)
 31
Author: Vince Spicer,
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-08-24 12:46:19

Usando relativedelta obtendrías la última fecha del mes así:

from dateutil.relativedelta import relativedelta
last_date_of_month = datetime(mydate.year,mydate.month,1)+relativedelta(months=1,days=-1)

La idea es obtener el primer día del mes y usar relativedelta para ir 1 mes por delante y 1 día atrás para que pueda obtener el último día del mes que quería.

 12
Author: Satish Reddy,
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-06-01 19:44:51

Otra solución sería hacer algo como esto:

from datetime import datetime

def last_day_of_month(year, month):
    """ Work out the last day of the month """
    last_days = [31, 30, 29, 28, 27]
    for i in last_days:
        try:
            end = datetime(year, month, i)
        except ValueError:
            continue
        else:
            return end.date()
    return None

Y usa la función así:

>>> 
>>> last_day_of_month(2008, 2)
datetime.date(2008, 2, 29)
>>> last_day_of_month(2009, 2)
datetime.date(2009, 2, 28)
>>> last_day_of_month(2008, 11)
datetime.date(2008, 11, 30)
>>> last_day_of_month(2008, 12)
datetime.date(2008, 12, 31)
 11
Author: Ulf Gjerdingen,
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-06-01 20:04:48
from datetime import timedelta
(any_day.replace(day=1) + timedelta(days=32)).replace(day=1) - timedelta(days=1)
 9
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-09-03 12:44:07
>>> import datetime
>>> import calendar
>>> date  = datetime.datetime.now()

>>> print date
2015-03-06 01:25:14.939574

>>> print date.replace(day = 1)
2015-03-01 01:25:14.939574

>>> print date.replace(day = calendar.monthrange(date.year, date.month)[1])
2015-03-31 01:25:14.939574
 8
Author: Vatsal,
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-03-05 20:02:18
import datetime

now = datetime.datetime.now()
start_month = datetime.datetime(now.year, now.month, 1)
date_on_next_month = start_month + datetime.timedelta(35)
start_next_month = datetime.datetime(date_on_next_month.year, date_on_next_month.month, 1)
last_day_month = start_next_month - datetime.timedelta(1)
 5
Author: Анатолий Панин,
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-06-16 16:57:32

Si está dispuesto a usar una biblioteca externa, consulte http://crsmithdev.com/arrow /

U puede obtener el último día del mes:

import arrow
arrow.utcnow().ceil('month').date()

Esto devuelve un objeto date que luego puede hacer su manipulación.

 5
Author: Blake,
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-11-27 10:01:06

Para obtener la última fecha del mes hacemos algo como esto:

from datetime import date, timedelta
import calendar
last_day = date.today().replace(day=calendar.monthrange(date.today().year, date.today().month)[1])

Ahora para explicar lo que estamos haciendo aquí lo dividiremos en dos partes:

Primero es obtener el número de días del mes actual para los que usamos monthrange que Blair Conrad ya ha mencionado su solución:

calendar.monthrange(date.today().year, date.today().month)[1]

Segundo es obtener la última fecha en sí, lo que hacemos con la ayuda de reemplazar por ejemplo

>>> date.today()
datetime.date(2017, 1, 3)
>>> date.today().replace(day=31)
datetime.date(2017, 1, 31)

Y cuando los combinamos como se menciona en la parte superior, obtenga una solución dinámica.

 4
Author: Siddharth K,
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
2017-01-03 09:54:54

Para mí es la forma más sencilla:

selected_date = date(some_year, some_month, some_day)

if selected_date.month == 12: # December
     last_day_selected_month = date(selected_date.year, selected_date.month, 31)
else:
     last_day_selected_month = date(selected_date.year, selected_date.month + 1, 1) - timedelta(days=1)
 3
Author: KravAn,
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-30 15:29:08

La forma más fácil (sin tener que importar calendario), es obtener el primer día del mes siguiente, y luego restar un día de él.

import datetime as dt
from dateutil.relativedelta import relativedelta

thisDate = dt.datetime(2017, 11, 17)

last_day_of_the_month = dt.datetime(thisDate.year, (thisDate + relativedelta(months=1)).month, 1) - dt.timedelta(days=1)
print last_day_of_the_month

Salida:

datetime.datetime(2017, 11, 30, 0, 0)

PS: Este código se ejecuta más rápido en comparación con el enfoque import calendar; ver a continuación:

import datetime as dt
import calendar
from dateutil.relativedelta import relativedelta

someDates = [dt.datetime.today() - dt.timedelta(days=x) for x in range(0, 10000)]

start1 = dt.datetime.now()
for thisDate in someDates:
    lastDay = dt.datetime(thisDate.year, (thisDate + relativedelta(months=1)).month, 1) - dt.timedelta(days=1)

print ('Time Spent= ', dt.datetime.now() - start1)


start2 = dt.datetime.now()
for thisDate in someDates:
    lastDay = dt.datetime(thisDate.year, 
                          thisDate.month, 
                          calendar.monthrange(thisDate.year, thisDate.month)[1])

print ('Time Spent= ', dt.datetime.now() - start2)

SALIDA:

Time Spent=  0:00:00.097814
Time Spent=  0:00:00.109791

Este código asume que desea la fecha del último día del mes (es decir, no solo la parte DD, sino toda la fecha AAAAMMDD)

 3
Author: Vishal,
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
2017-11-28 19:39:53

Puede calcular la fecha de finalización usted mismo. la lógica simple es restar un día de la start_date del próximo mes. :)

Así que escribe un método personalizado,

import datetime

def end_date_of_a_month(date):


    start_date_of_this_month = date.replace(day=1)

    month = start_date_of_this_month.month
    year = start_date_of_this_month.year
    if month == 12:
        month = 1
        year += 1
    else:
        month += 1
    next_month_start_date = start_date_of_this_month.replace(month=month, year=year)

    this_month_end_date = next_month_start_date - datetime.timedelta(days=1)
    return this_month_end_date

Llamando,

end_date_of_a_month(datetime.datetime.now().date())

Devolverá la fecha final de este mes. Pase cualquier fecha a esta función. devuelve la fecha final de ese mes.

 2
Author: Vipul Vishnu av,
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-09-02 08:47:11

Esto no aborda la pregunta principal, pero un buen truco para obtener el último día de la semana en un mes es usar calendar.monthcalendar, que devuelve una matriz de fechas, organizada con el lunes como primera columna hasta el domingo como última.

# Some random date.
some_date = datetime.date(2012, 5, 23)

# Get last weekday
last_weekday = np.asarray(calendar.monthcalendar(some_date.year, some_date.month))[:,0:-2].ravel().max()

print last_weekday
31

Todo lo [0:-2] es afeitar las columnas de fin de semana y tirarlas. Las fechas que caen fuera del mes se indican por 0, por lo que el máximo efectivamente las ignora.

El uso de numpy.ravel no es estrictamente necesario, pero odio confiar en la mera convención que numpy.ndarray.max aplanará la matriz si no se le dice sobre qué eje calcular.

 1
Author: ely,
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-14 20:06:26

¡Usa pandas!

def isMonthEnd(date):
    return date + pd.offsets.MonthEnd(0) == date

isMonthEnd(datetime(1999, 12, 31))
True
isMonthEnd(pd.Timestamp('1999-12-31'))
True
isMonthEnd(pd.Timestamp(1965, 1, 10))
False
 1
Author: Steve Schulist,
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-24 19:59:53

Prefiero así

import datetime
import calendar

date=datetime.datetime.now()
month_end_date=datetime.datetime(date.year,date.month,1) + datetime.timedelta(days=calendar.monthrange(date.year,date.month)[1] - 1)
 1
Author: MikA,
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-08-25 09:48:05
import calendar
from time import gmtime, strftime
calendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1]

Salida:

31



Esto imprimirá el último día de lo que sea que sea el mes actual. En este ejemplo fue el 15 de mayo de 2016. Por lo tanto, su salida puede ser diferente, sin embargo, la salida será tantos días como el mes actual es. Excelente si desea verificar el último día del mes ejecutando un trabajo cron diario.

So:

import calendar
from time import gmtime, strftime
lastDay = calendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1]
today = strftime("%d", gmtime())
lastDay == today

Salida:

False

A menos que SEA el último día del mes.

 0
Author: Audstanley,
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-05-16 04:51:56

Si quieres hacer tu propia función pequeña, este es un buen punto de partida:

def eomday(year, month):
    """returns the number of days in a given month"""
    days_per_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    d = days_per_month[month - 1]
    if month == 2 and (year % 4 == 0 and year % 100 != 0 or year % 400 == 0):
        d = 29
    return d

Para esto hay que conocer las reglas para los años bisiestos:

  • cada cuatro años
  • con la excepción de cada 100 años
  • pero de nuevo cada 400 años
 0
Author: mathause,
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
2017-09-04 08:26:45

Si pasas un intervalo de fechas, puedes usar esto:

def last_day_of_month(any_days):
    res = []
    for any_day in any_days:
        nday = any_day.days_in_month -any_day.day
        res.append(any_day + timedelta(days=nday))
    return res
 0
Author: JLord,
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-03-12 16:23:30

Aquí hay una solución basada en python lambdas:

next_month = lambda y, m, d: (y, m + 1, 1) if m + 1 < 13 else ( y+1 , 1, 1)
month_end  = lambda dte: date( *next_month( *dte.timetuple()[:3] ) ) - timedelta(days=1)

La lambda next_month encuentra la representación de la tupla del primer día del mes siguiente, y pasa al año siguiente. La lambda month_end transforma una fecha (dte) en una tupla, aplica next_month y crea una nueva fecha. Entonces el "fin de mes" es solo el primer día del mes siguiente menos timedelta(days=1).

 -1
Author: Johannes Blaschke,
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-04-07 22:52:09

Espero,Es útil para mucho..Pruébalo por aquí..necesitamos importar algún paquete

import time
from datetime import datetime, date
from datetime import timedelta
from dateutil import relativedelta

  start_date = fields.Date(
        string='Start Date', 
        required=True,
        ) 

    end_date = fields.Date(
        string='End Date', 
        required=True,
        )

    _defaults = {
        'start_date': lambda *a: time.strftime('%Y-%m-01'),
        'end_date': lambda *a: str(datetime.now() + relativedelta.relativedelta(months=+1, day=1, days=-1))[:10],
    }
 -2
Author: Vinoth Jo,
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
2017-09-27 12:23:03

Tengo una solución simple:

import datetime   
datetime.date(2012,2, 1).replace(day=1,month=datetime.date(2012,2,1).month+1)-timedelta(days=1)
datetime.date(2012, 2, 29)
 -7
Author: francois,
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-08-21 23:09:57