Risposte:
Non l'avevo notato prima quando stavo guardando la documentazione per il calendar
modulo , ma un metodo chiamato monthrange
fornisce queste informazioni:
monthrange (anno, mese)
Restituisce il giorno della settimana del primo giorno del mese e il numero di giorni del 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 il modo più semplice di andare.
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.
28
come la risposta, che è corretta, usando le regole per il calendario gregoriano
monthrange
sia un nome confuso. Penseresti che sarebbe tornato (first_day, last_day)
, non(week_day_of_first_day, number_of_days)
Se non si desidera importare il calendar
modulo, una semplice funzione in due passaggi può essere anche:
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
EDIT: 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)
today.month + 1 == 13
e ottieni un ValueError
.
(today.month % 12) + 1
dato 12 % 12
da 0
Questo in realtà è abbastanza semplice con dateutil.relativedelta
(pacchetto python-datetutil per pip). day=31
restituirà 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)
datetime(2014, 2, 1) + relativedelta(days=31)
dà datetime(2014, 3, 4, 0, 0)
...
months
venga aggiunto, poi seconds
sottratto. Dal momento che sono passati perché **kwargs
non farei affidamento su di esso e fare una serie di operazioni separate: il now + relative(months=+1) + relative(day=1) + relative(days=-1)
che è inequivocabile.
last_day = (<datetime_object> + relativedelta(day=31)).day
EDIT: vedi la mia altra risposta . Ha un'implementazione migliore di questa, che lascio qui nel caso qualcuno fosse interessato a vedere come si potrebbe "rotolare il proprio" calcolatore.
@ John Millikin dà una buona risposta, con l'ulteriore complicazione del calcolo del primo giorno del mese successivo.
Quanto segue non è particolarmente elegante, ma per capire l'ultimo giorno del mese in cui vive una determinata 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)
today = datetime.date.today(); start_date = today.replace(day=1)
. Vorresti evitare di chiamare datetime.now due volte, nel caso in cui l'hai chiamato poco prima di mezzanotte del 31 dicembre e poi subito dopo mezzanotte. Otterrai 01-01-2016 anziché 01-12-2016 o 01-01-2017.
date
è un'istanza di datetime.date
, datetime.datetime
e anche pandas.Timestamp
.
Usandolo dateutil.relativedelta
otterrai la data dell'ultimo 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 utilizzare relativedelta
per andare avanti di 1 mese e 1 giorno indietro in modo da ottenere l'ultimo giorno del mese desiderato.
Un'altra soluzione sarebbe quella di 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)
se si desidera utilizzare una libreria esterna, consultare http://crsmithdev.com/arrow/
Puoi quindi ottenere l'ultimo giorno del mese con:
import arrow
arrow.utcnow().ceil('month').date()
Questo restituisce un oggetto data che puoi quindi fare la tua manipolazione.
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 suddivideremo in due parti:
in primo luogo sta ottenendo il numero di giorni del mese corrente per cui utilizziamo la gamma mensile che Blair Conrad ha già menzionato la sua soluzione:
calendar.monthrange(date.today().year, date.today().month)[1]
il secondo è ottenere l'ultima data stessa che facciamo con l'aiuto di sostituire ad es
>>> date.today()
datetime.date(2017, 1, 3)
>>> date.today().replace(day=31)
datetime.date(2017, 1, 31)
e quando li combiniamo come indicato in alto otteniamo una soluzione dinamica.
date.replace(day=day)
così tutti sanno cosa sta succedendo.
In Python 3.7 è presente la calendar.monthlen(year, month)
funzione non documentata :
>>> calendar.monthlen(2002, 1)
31
>>> calendar.monthlen(2008, 2)
29
>>> calendar.monthlen(2100, 2)
28
È equivalente alla chiamata documentatacalendar.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)
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
now=datetime.datetime.now(); pd_now = pd.to_datetime(now); print(pd_now.days_in_month)
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 quindi sottrarre un giorno da esso.
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 import calendar
all'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 si desideri la data dell'ultimo giorno del mese (ovvero, non solo la parte GG, ma l'intera data AAAAMMGG)
import calendar
?
Ecco un'altra risposta. Non sono richiesti pacchetti extra.
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.
Puoi calcolare tu stesso la data di fine. la semplice logica è sottrarre un giorno dalla data di inizio del mese prossimo. :)
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
Calling,
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.
puoi usare relativedelta
https://dateutil.readthedocs.io/en/stable/relativedelta.html
month_end = <your datetime value within the month> + relativedelta(day=31)
che ti darà l'ultimo giorno.
Questa è la soluzione più semplice per me utilizzando solo la libreria datetime standard:
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)
Questo non affronta la domanda principale, ma un bel trucco per ottenere l'ultimo giorno della settimana in un mese è usare calendar.monthcalendar
, che restituisce una matrice di date, organizzate con il lunedì come prima colonna fino alla 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]
è radere le colonne del fine settimana e buttarle via. Le date che cadono al di fuori del mese sono indicate da 0, quindi il massimo le ignora effettivamente.
L'uso di numpy.ravel
non è strettamente necessario, ma odio basarmi sulla semplice convenzione che numpy.ndarray.max
appiattirà l'array se non gli viene detto su quale asse calcolare.
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 è stato il 15 maggio 2016. Pertanto, l'output potrebbe essere diverso, tuttavia l'output sarà pari al numero di giorni del mese corrente. Ottimo se vuoi controllare l'ultimo giorno del mese eseguendo un cron job 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 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:
Nel codice qui sotto 'get_last_day_of_month (dt)' 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 )
Il modo più semplice è usare datetime
e un po 'di matematica per la data, ad esempio sottrarre un giorno dal primo giorno del mese successivo:
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)
)
In alternativa, è possibile utilizzare calendar.monthrange()
per ottenere il numero di giorni in un mese (tenendo conto degli anni bisestili) e aggiornare la data di conseguenza:
import calendar, datetime
def last_day_of_month(d: datetime.date) -> datetime.date:
return d.replace(day=calendar.monthrange(d.year, d.month)[1])
Un rapido benchmark mostra che la prima versione è notevolmente più veloce:
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)
Ecco una versione lunga (facile da capire) ma si occupa degli anni bisestili.
saluti, 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 puoi anche sbarazzarti if year % 400 == 0: leap_year_flag = 1
di piccole modifiche
Ecco una soluzione lambda in pitone:
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 next_month
lambda trova la rappresentazione tupla del primo giorno del mese successivo e passa al successivo anno. La month_end
lambda trasforma una data ( dte
) in una tupla, si applica next_month
e crea una nuova data. Quindi la "fine del mese" è solo il primo giorno meno del mese successivo timedelta(days=1)
.