Respostas:
Eu não percebi isso antes quando estava examinando a documentação do calendar
módulo , mas um método chamado monthrange
fornece essas informações:
monthrange (ano, mês)
Retorna o dia da semana do primeiro dia do mês e o número de dias no mês, para o ano e mês especificados.
>>> import calendar
>>> calendar.monthrange(2002,1)
(1, 31)
>>> calendar.monthrange(2008,2)
(4, 29)
>>> calendar.monthrange(2100,2)
(0, 28)
tão:
calendar.monthrange(year, month)[1]
parece ser o caminho mais simples a seguir.
Só para esclarecer, também monthrange
suporta anos bissextos:
>>> from calendar import monthrange
>>> monthrange(2012, 2)
(2, 29)
Minha resposta anterior ainda funciona, mas é claramente abaixo do ideal.
28
como a resposta, o que é correto, usando as regras para o calendário gregoriano
monthrange
é um nome confuso. Você pensaria que retornaria (first_day, last_day)
, não(week_day_of_first_day, number_of_days)
Se você não deseja importar o calendar
módulo, uma função simples de duas etapas também pode 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)
Saídas:
>>> 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
EDIT: Veja a resposta de @Blair Conrad para uma solução mais limpa
>>> import datetime
>>> datetime.date(2000, 2, 1) - datetime.timedelta(days=1)
datetime.date(2000, 1, 31)
today.month + 1 == 13
e você recebe um ValueError
.
(today.month % 12) + 1
since since 12 % 12
0
Isso é realmente muito fácil com dateutil.relativedelta
(pacote python-datetutil for pip). day=31
sempre retornará sempre o último dia do mês.
Exemplo:
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)
datetime(2014, 2, 1) + relativedelta(days=31)
dá datetime(2014, 3, 4, 0, 0)
...
months
seja adicionado e depois seconds
subtraído. Uma vez que eles são aprovados, **kwargs
eu não confiaria nisso e faria uma série de operações separadas: o now + relative(months=+1) + relative(day=1) + relative(days=-1)
que é inequívoco.
last_day = (<datetime_object> + relativedelta(day=31)).day
EDIT: veja minha outra resposta . Ele tem uma implementação melhor do que esta, que deixo aqui para o caso de alguém estar interessado em ver como alguém pode "rolar sua própria" calculadora.
@ John Millikin dá uma boa resposta, com a complicação adicional de calcular o primeiro dia do próximo mês.
O seguinte não é particularmente elegante, mas para descobrir o último dia do mês em que uma determinada data reside, você pode tentar:
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)
today = datetime.date.today(); start_date = today.replace(day=1)
. Você gostaria de evitar ligar para datetime.now duas vezes, caso o tenha telefonado antes da meia-noite de 31 de dezembro e depois da meia-noite. Você receberá 01-01-2016 em vez de 01/12/2016 ou 01-01-2017.
date
for uma instância de datetime.date
, datetime.datetime
e também pandas.Timestamp
.
Se dateutil.relativedelta
você usar a última data do mês, será assim:
from dateutil.relativedelta import relativedelta
last_date_of_month = datetime(mydate.year, mydate.month, 1) + relativedelta(months=1, days=-1)
A idéia é obter o primeiro dia do mês e usar relativedelta
1 mês à frente e 1 dia de volta para obter o último dia do mês que você deseja.
Outra solução seria fazer algo assim:
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
E use a função assim:
>>>
>>> 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)
from datetime import timedelta
(any_day.replace(day=1) + timedelta(days=32)).replace(day=1) - timedelta(days=1)
se você estiver disposto a usar uma biblioteca externa, consulte http://crsmithdev.com/arrow/
Você pode obter o último dia do mês com:
import arrow
arrow.utcnow().ceil('month').date()
Isso retorna um objeto de data que você pode fazer sua manipulação.
Para obter a última data do mês, fazemos algo assim:
from datetime import date, timedelta
import calendar
last_day = date.today().replace(day=calendar.monthrange(date.today().year, date.today().month)[1])
Agora, para explicar o que estamos fazendo aqui, vamos dividi-lo em duas partes:
primeiro é obter o número de dias do mês atual para o qual usamos o intervalo do mês que Blair Conrad já mencionou sua solução:
calendar.monthrange(date.today().year, date.today().month)[1]
segundo é obter a última data em si, o que fazemos com a ajuda de substituir, por exemplo
>>> date.today()
datetime.date(2017, 1, 3)
>>> date.today().replace(day=31)
datetime.date(2017, 1, 31)
e quando os combinamos conforme mencionado na parte superior, obtemos uma solução dinâmica.
date.replace(day=day)
para que todos saibam o que está acontecendo.
No Python 3.7, há a calendar.monthlen(year, month)
função não documentada :
>>> calendar.monthlen(2002, 1)
31
>>> calendar.monthlen(2008, 2)
29
>>> calendar.monthlen(2100, 2)
28
É equivalente à chamada documentadacalendar.monthrange(year, month)[1]
.
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)
Use 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
now=datetime.datetime.now(); pd_now = pd.to_datetime(now); print(pd_now.days_in_month)
Para mim, é a maneira mais simples:
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)
A maneira mais fácil (sem precisar importar o calendário) é obter o primeiro dia do próximo mês e subtrair um dia dele.
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
Resultado:
datetime.datetime(2017, 11, 30, 0, 0)
PS: esse código roda mais rápido em comparação com a import calendar
abordagem; ver abaixo:
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)
RESULTADO:
Time Spent= 0:00:00.097814
Time Spent= 0:00:00.109791
Este código pressupõe que você deseja a data do último dia do mês (ou seja, não apenas a parte DD, mas a data AAAAMMDD inteira)
import calendar
?
Aqui está outra resposta. Não são necessários pacotes extras.
datetime.date(year + int(month/12), month%12+1, 1)-datetime.timedelta(days=1)
Obtenha o primeiro dia do próximo mês e subtraia um dia dele.
Você pode calcular a data final você mesmo. a lógica simples é subtrair um dia a partir da data de início do próximo mês. :)
Então escreva um 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
Chamando,
end_date_of_a_month(datetime.datetime.now().date())
Ele retornará a data final deste mês. Passe qualquer data para esta função. retorna a data final desse mês.
você pode usar o relativedelta
https://dateutil.readthedocs.io/en/stable/relativedelta.html
month_end = <your datetime value within the month> + relativedelta(day=31)
que fornecerá o último dia.
Esta é a solução mais simples para mim, usando apenas a biblioteca de data e hora padrão:
import datetime
def get_month_end(dt):
first_of_month = datetime.datetime(dt.year, dt.month, 1)
next_month_date = first_of_month + datetime.timedelta(days=32)
new_dt = datetime.datetime(next_month_date.year, next_month_date.month, 1)
return new_dt - datetime.timedelta(days=1)
Isso não aborda a questão principal, mas um bom truque para obter o último dia da semana em um mês é usar calendar.monthcalendar
, que retorna uma matriz de datas, organizada com segunda-feira como primeira coluna e domingo como último.
# 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
A [0:-2]
coisa toda é raspar as colunas do fim de semana e jogá-las fora. As datas que ficam fora do mês são indicadas por 0, então o máximo as ignora efetivamente.
O uso de numpy.ravel
não é estritamente necessário, mas eu odeio confiar na mera convenção que numpy.ndarray.max
achatará a matriz se não for informado sobre qual eixo calcular sobre.
import calendar
from time import gmtime, strftime
calendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1]
Resultado:
31
Isso imprimirá o último dia do mês atual. Neste exemplo, era 15 de maio de 2016. Portanto, sua produção pode ser diferente; no entanto, a produção será o número de dias que o mês atual for. Ótimo se você deseja verificar o último dia do mês executando um trabalho cron diário.
Assim:
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
Resultado:
False
A menos que seja o último dia do mês.
Se você deseja criar sua própria função pequena, este é um bom ponto 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 isso, você precisa conhecer as regras para os anos bissextos:
No código abaixo, 'get_last_day_of_month (dt)' fornecerá isso, com a data no formato de string como 'YYYY-MM-DD'.
import datetime
def DateTime( d ):
return datetime.datetime.strptime( d, '%Y-%m-%d').date()
def RelativeDate( start, num_days ):
d = DateTime( start )
return str( d + datetime.timedelta( days = num_days ) )
def get_first_day_of_month( dt ):
return dt[:-2] + '01'
def get_last_day_of_month( dt ):
fd = get_first_day_of_month( dt )
fd_next_month = get_first_day_of_month( RelativeDate( fd, 31 ) )
return RelativeDate( fd_next_month, -1 )
A maneira mais simples é usar datetime
e alguma matemática de datas, por exemplo, subtrair um dia do primeiro dia do próximo mês:
import datetime
def last_day_of_month(d: datetime.date) -> datetime.date:
return (
datetime.date(d.year + d.month//12, d.month % 12 + 1, 1) -
datetime.timedelta(days=1)
)
Como alternativa, você pode usar calendar.monthrange()
para obter o número de dias em um mês (considerando os anos bissextos) e atualizar a data de acordo:
import calendar, datetime
def last_day_of_month(d: datetime.date) -> datetime.date:
return d.replace(day=calendar.monthrange(d.year, d.month)[1])
Uma referência rápida mostra que a primeira versão é notavelmente mais rápida:
In [14]: today = datetime.date.today()
In [15]: %timeit last_day_of_month_dt(today)
918 ns ± 3.54 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [16]: %timeit last_day_of_month_calendar(today)
1.4 µs ± 17.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Aqui está uma versão longa (fácil de entender), mas cuida dos anos bissextos.
Saúde, JK
def last_day_month(year, month):
leap_year_flag = 0
end_dates = {
1: 31,
2: 28,
3: 31,
4: 30,
5: 31,
6: 30,
7: 31,
8: 31,
9: 30,
10: 31,
11: 30,
12: 31
}
# Checking for regular leap year
if year % 4 == 0:
leap_year_flag = 1
else:
leap_year_flag = 0
# Checking for century leap year
if year % 100 == 0:
if year % 400 == 0:
leap_year_flag = 1
else:
leap_year_flag = 0
else:
pass
# return end date of the year-month
if leap_year_flag == 1 and month == 2:
return 29
elif leap_year_flag == 1 and month != 2:
return end_dates[month]
else:
return end_dates[month]
else: pass
e também pode se livrar de if year % 400 == 0: leap_year_flag = 1
pequenas modificações
Aqui está uma solução baseada em 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)
O next_month
lambda encontra a representação da tupla do primeiro dia do próximo mês e passa para o próximo ano. O month_end
lambda transforma uma data ( dte
) em uma tupla, aplica next_month
e cria uma nova data. Então o "final do mês" é apenas o primeiro dia do mês seguinte timedelta(days=1)
.