Ottieni l'ultimo giorno del mese in Python
Domanda
Esiste un modo per utilizzare la libreria standard di Python per determinare facilmente (ad es.una chiamata di funzione) l'ultimo giorno di un determinato mese?
Se la libreria standard non lo supporta, il pacchetto dateutil lo supporta?
Soluzione
Non l'avevo notato prima mentre stavo guardando il file documentazione per il modulo calendario, ma un metodo chiamato intervallo di mesi fornisce queste informazioni:
intervallo mese(anno, mese)
Restituisce il giorno della settimana del primo giorno del mese e il numero di giorni nel mese, per l'anno e il mese specificati.
>>> import calendar
>>> calendar.monthrange(2002,1)
(1, 31)
>>> calendar.monthrange(2008,2)
(4, 29)
>>> calendar.monthrange(2100,2)
(0, 28)
COSÌ:
calendar.monthrange(year, month)[1]
sembra la strada più semplice da percorrere.
Giusto per essere chiari, monthrange
supporta anche gli anni bisestili:
>>> from calendar import monthrange
>>> monthrange(2012, 2)
(2, 29)
La mia risposta precedente funziona ancora, ma è chiaramente non ottimale.
Altri suggerimenti
Se non vuoi importare il file calendar
module, una semplice funzione in due passaggi può anche essere:
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)
Uscite:
>>> 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
MODIFICARE:Vedi la risposta di @Blair Conrad per una soluzione più pulita
>>> import datetime
>>> datetime.date (2000, 2, 1) - datetime.timedelta (days = 1)
datetime.date(2000, 1, 31)
>>>
MODIFICARE:Vedere la mia altra risposta.Ha un'implementazione migliore di questa, che lascio qui nel caso in cui qualcuno sia interessato a vedere come si potrebbe "creare la propria" calcolatrice.
@John Millikin dà una buona risposta, con l'ulteriore complicazione di calcolare il primo giorno del mese successivo.
Quanto segue non è particolarmente elegante, ma per capire l'ultimo giorno del mese in cui si trova una data data, potresti provare:
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)
In realtà è abbastanza semplice dateutil.relativedelta
(pacchetto python-datetutil per pip). day=31
tornerà sempre sempre l'ultimo giorno del mese.
Esempio:
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)
Utilizzando relativedelta
otterresti l'ultima data del mese in questo modo:
from dateutil.relativedelta import relativedelta
last_date_of_month = datetime(mydate.year, mydate.month, 1) + relativedelta(months=1, days=-1)
L'idea è di ottenere il primo giorno del mese e utilizzarlo relativedelta
per andare 1 mese avanti e 1 giorno indietro in modo da ottenere l'ultimo giorno del mese desiderato.
Un'altra soluzione sarebbe fare qualcosa del genere:
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 usa la funzione in questo modo:
>>>
>>> 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)
>>> 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
se sei disposto a utilizzare una libreria esterna, dai un'occhiata http://crsmithdev.com/arrow/
Puoi quindi ottenere l'ultimo giorno del mese con:
import arrow
arrow.utcnow().ceil('month').date()
Ciò restituisce un oggetto data che puoi quindi manipolare.
Per ottenere l'ultima data del mese facciamo qualcosa del genere:
from datetime import date, timedelta
import calendar
last_day = date.today().replace(day=calendar.monthrange(date.today().year, date.today().month)[1])
Ora per spiegare cosa stiamo facendo qui lo divideremo in due parti:
il primo è ottenere il numero di giorni del mese corrente per i quali utilizziamo intervallo di mesi cui Blair Conrad ha già menzionato la sua soluzione:
calendar.monthrange(date.today().year, date.today().month)[1]
il secondo è ottenere l'ultimo appuntamento stesso, cosa che facciamo con l'aiuto di sostituire per esempio
>>> date.today()
datetime.date(2017, 1, 3)
>>> date.today().replace(day=31)
datetime.date(2017, 1, 31)
e quando li combiniamo come menzionato in alto otteniamo una soluzione dinamica.
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)
Per me è il modo più semplice:
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)
Il modo più semplice (senza dover importare il calendario) è ottenere il primo giorno del mese successivo e poi sottrarre da esso un giorno.
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
Produzione:
datetime.datetime(2017, 11, 30, 0, 0)
PS: Questo codice viene eseguito più velocemente rispetto a import calendar
approccio;vedi sotto:
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)
PRODUZIONE:
Time Spent= 0:00:00.097814
Time Spent= 0:00:00.109791
Questo codice presuppone che tu voglia la data dell'ultimo giorno del mese (ovvero, non solo la parte GG, ma l'intera data AAAAMMGG)
In Python 3.7 c'è i privi di documenti calendar.monthlen(year, month)
funzione:
>>> calendar.monthlen(2002, 1)
31
>>> calendar.monthlen(2008, 2)
29
>>> calendar.monthlen(2100, 2)
28
È equivalente a il documentato calendar.monthrange(year, month)[1]
chiamata.
Puoi calcolare tu stesso la data di fine.la logica semplice è sottrarre un giorno dalla start_date del mese successivo.:)
Quindi scrivi un metodo personalizzato,
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
chiamando,
end_date_of_a_month(datetime.datetime.now().date())
Restituirà la data di fine di questo mese.Passa qualsiasi data a questa funzione.ti restituisce la data di fine di quel mese.
Ecco un'altra risposta.Non sono richiesti pacchetti aggiuntivi.
datetime.date(year + int(month/12), month%12+1, 1)-datetime.timedelta(days=1)
Ottieni il primo giorno del mese successivo e sottrai un giorno da esso.
Questo non affronta la domanda principale, ma un bel trucco per ottenere l'ultima giorno feriale in un mese è da usare calendar.monthcalendar
, che restituisce una matrice di date, organizzata con lunedì come prima colonna e domenica come ultima.
# 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
Il tutto [0:-2]
la cosa è eliminare le colonne del fine settimana e buttarle via.Le date che non rientrano nel mese sono indicate da 0, quindi il massimo le ignora di fatto.
L'impiego di numpy.ravel
non è strettamente necessario, ma odio fare affidamento su mera convenzione Quello numpy.ndarray.max
appiattirà l'array se non viene detto su quale asse calcolare.
Usa i panda!
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
Preferisco così
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)
Se vuoi creare la tua piccola funzione, questo è un buon punto di partenza:
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
Per questo devi conoscere le regole per gli anni bisestili:
- ogni quattro anni
- ad eccezione di ogni 100 anni
- ma ancora una volta ogni 400 anni
puoi usare relativedeltahttps://dateutil.readthedocs.io/en/stable/relativedelta.html
month_end = <your datetime value within the month> + relativedelta(day=31)
che ti darà l'ultimo giorno.
import calendar
from time import gmtime, strftime
calendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1]
Produzione:
31
Questo stamperà l'ultimo giorno di qualunque sia il mese corrente.In questo esempio era il 15 maggio 2016.Quindi il tuo output potrebbe essere diverso, tuttavia l'output sarà pari a tanti giorni quanti sono i giorni del mese corrente.Ottimo se vuoi controllare l'ultimo giorno del mese eseguendo un lavoro cron giornaliero.
COSÌ:
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
Produzione:
False
A meno che non sia l'ultimo giorno del mese.
Se passi in un intervallo di date, puoi utilizzare questo:
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
Nel codice qui sotto 'get_ultimo_giorno_del_mese(gg)' ti darà questo, con la data in formato stringa come 'AAAA-MM-GG'.
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 )
Ecco una soluzione lambda basata su Python:
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)
IL next_month
lambda trova la rappresentazione della tupla del primo giorno del mese successivo e passa all'anno successivo.IL month_end
lambda trasforma una data (dte
) a una tupla, si applica next_month
e crea una nuova data.Quindi la "fine del mese" è semplicemente il primo giorno meno del mese successivo timedelta(days=1)
.
Spero che sia utile per molto... Provalo in questo modo... dobbiamo importare qualche pacchetto
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],
}
ho una soluzione semplice:
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)