sumar - restar fechas con python
Consigue el último día del mes en Python (25)
¡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
¿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 lo admite, ¿el paquete dateutil lo admite?
Aquí hay una solución basada en lambdas de pitón:
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)
El next_month
lambda encuentra la representación de la tupla del primer día del mes siguiente y se transfiere al año siguiente. El month_end
lambda transforma una fecha ( dte
) en una tupla, aplica next_month
y crea una nueva fecha. Luego, el "fin de mes" es solo el primer día del mes siguiente menos timedelta(days=1)
.
EDITAR: vea 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)
>>>
EDITAR: ver mi otra respuesta . Tiene una implementación mejor que esta, que dejo aquí solo en caso de que alguien esté interesado en ver cómo uno podría "rodar su propia calculadora".
@ John Millikin da una buena respuesta, con la complicación adicional de calcular el primer día del próximo mes.
Lo siguiente no es particularmente elegante, pero para averiguar el último día del mes en el que vive una fecha determinada, puede intentarlo:
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)
Espero que sea útil por mucho ... Inténtelo de esta manera ... 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],
}
Esto es realmente bastante fácil con dateutil.relativedelta
(paquete python-datetutil para pip). day=31
siempre volverá 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)
Esto no aborda la pregunta principal, pero un buen truco para obtener el último día laborable de un mes es usar calendar.monthcalendar
, que devuelve una matriz de fechas, organizada con el lunes desde la primera columna hasta el domingo como la ú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 el asunto [0:-2]
es rasurar las columnas del fin de semana y desecharlas. Las fechas que caen fuera del mes se indican con 0, por lo que el máximo las ignora de manera efectiva.
El uso de numpy.ravel
no es estrictamente necesario, pero odio confiar en la mera convención de que numpy.ndarray.max
la matriz si no se le numpy.ndarray.max
qué eje debe calcularse.
La forma más fácil (sin tener que importar el calendario), es obtener el primer día del mes siguiente y luego restarle un día.
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)
PD: este código se ejecuta más rápido en comparación con el enfoque del import calendar
; vea abajo:
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 de DD, sino la fecha completa de AAAAMMDD)
No noté esto antes cuando miraba la documentación del módulo de calendario , pero un método llamado monthrange proporciona esta información:
mes (año, mes)
Devuelve el día de la semana del primer día del mes y la cantidad de días en el mes, para el año y mes especificados.
>>> import calendar
>>> calendar.monthrange(2002,1)
(1, 31)
>>> calendar.monthrange(2008,2)
(4, 29)
>>> calendar.monthrange(2100,2)
(0, 28)
asi que:
calendar.monthrange(year, month)[1]
Parece la forma más sencilla de ir.
Solo para ser claros, monthrange
soporta años bisiestos:
>>> from calendar import monthrange
>>> monthrange(2012, 2)
(2, 29)
Mi respuesta anterior todavía funciona, pero es claramente subóptima.
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)
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)
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:
Lo primero es obtener la cantidad de días del mes en curso para los cuales utilizamos el monthrange que Blair Conrad ya mencionó 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 replace 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 obtenemos una solución dinámica.
Si está dispuesto a usar una biblioteca externa, visite http://crsmithdev.com/arrow/
U puede obtener el último día del mes con:
import arrow
arrow.utcnow().ceil(''month'').date()
Esto devuelve un objeto de fecha que luego puede hacer su manipulación.
Si no desea importar el módulo de 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
Si pasa en un rango de fechas, puede 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
Si quieres hacer tu propia pequeña función, 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 debes conocer las reglas de los años bisiestos:
- cada cuatro años
- con la excepción de cada 100 años
- pero de nuevo cada 400 años
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)
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 adelantar 1 mes y 1 día para que pueda obtener el último día del mes que desea.
Usted puede calcular la fecha de finalización usted mismo. la lógica simple es restar un día de la fecha de inicio 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
Vocación,
end_date_of_a_month(datetime.datetime.now().date())
Volverá la fecha de finalización de este mes. Pase cualquier fecha a esta función. Te devuelve la fecha de finalización de ese mes.
Yo prefiero de esta manera
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)
>>> 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
from datetime import timedelta
(any_day.replace(day=1) + timedelta(days=32)).replace(day=1) - timedelta(days=1)
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 del 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á la misma cantidad de días que el mes actual. Excelente si desea verificar el último día del mes ejecutando un trabajo cron diario.
Asi que:
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.
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)
import datetime
today = datetime.datetime.today() # currentr day for date example
last_date = today + relativedelta(day=31)
print "Date:", last_date.date(),"Day:", last_date.date().day
salida:
Date: 2018-10-31 Day: 31