Получить последний день месяца в Python
есть ли способ с помощью стандартной библиотеки Python легко определить (т. е. один вызов функции) последний день данного месяца?
Если стандартная библиотека не поддерживает это, поддерживает ли пакет dateutil это?
24 ответов
Я не заметил этого раньше, когда я смотрел на документация по календарному модулю, но метод, называемый monthrange предоставляет следующую информацию:
monthrange(год, месяц)
Возвращает день недели первого дня месяца и количество дней в месяце для заданного года и месяца.
>>> import calendar
>>> calendar.monthrange(2002,1)
(1, 31)
>>> calendar.monthrange(2008,2)
(4, 29)
>>> calendar.monthrange(2100,2)
(0, 28)
так:
calendar.monthrange(year, month)[1]
кажется самый простой способ идти.
просто для ясности,monthrange
поддерживает високосные годы, а также:
>>> from calendar import monthrange
>>> monthrange(2012, 2)
(2, 29)
мой предыдущий ответ все еще работает, но явно неоптимален.
Если вы не хотите импортировать calendar
модуль, простая двухступенчатая функция также может быть:
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)
выходы:
>>> 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: см. ответ @Blair Conrad для более чистого решения
>>> import datetime
>>> datetime.date (2000, 2, 1) - datetime.timedelta (days = 1)
datetime.date(2000, 1, 31)
>>>
редактировать: см.мой другой ответ. Он имеет лучшую реализацию, чем этот, который я оставляю здесь на случай, если кто-то заинтересован в том, как можно "свернуть свой собственный" калькулятор.
@John Millikin дает хороший ответ, с дополнительным усложнением вычисления первого дня следующего месяца.
следующее не особенно элегантно, но чтобы выяснить последний день месяца, в котором живет любая данная дата, вы можете попробуйте:
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)
Это на самом деле довольно легко с dateutil.relativedelta
(пакет python-datetutil для pip). day=31
всегда возвращает последний день месяца.
пример:
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)
используя relativedelta
вы получите последнюю дату месяца, как это:
from dateutil.relativedelta import relativedelta
last_date_of_month = datetime(mydate.year,mydate.month,1)+relativedelta(months=1,days=-1)
идея в том, чтобы получить первый день месяца и использовать relativedelta
идти 1 месяц вперед и 1 день назад, чтобы вы получили последний день месяца, который вы хотели.
другим решением было бы сделать что-то вроде этого:
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
и используйте функцию следующим образом:
>>>
>>> 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
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)
Если вы хотите использовать внешнюю библиотеку, проверьтеhttp://crsmithdev.com/arrow/
U может получить последний день месяца с помощью:
import arrow
arrow.utcnow().ceil('month').date()
Это возвращает объект date, который затем можно выполнить.
получить последнюю дату месяца мы делаем что-то вроде этого:
from datetime import date, timedelta
import calendar
last_day = date.today().replace(day=calendar.monthrange(date.today().year, date.today().month)[1])
Теперь, чтобы объяснить, что мы здесь делаем, мы разобьем его на две части:
сначала получает количество дней текущего месяца, для которого мы используем monthrange который Блэр Конрад уже упоминал о своем решении:
calendar.monthrange(date.today().year, date.today().month)[1]
второй получает последнюю дату, которую мы делаем с помощью заменить Эл.г
>>> date.today()
datetime.date(2017, 1, 3)
>>> date.today().replace(day=31)
datetime.date(2017, 1, 31)
и когда мы объединяем их, как указано сверху, мы получаем динамическое решение.
для меня это самый простой способ:
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)
самый простой способ (без необходимости импортировать календарь), чтобы получить первый день следующего месяца, а затем вычесть день из него.
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
выход:
datetime.datetime(2017, 11, 30, 0, 0)
PS: этот код работает быстрее по сравнению с import calendar
подход; см. ниже:
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)
выход:
Time Spent= 0:00:00.097814
Time Spent= 0:00:00.109791
этот код предполагает, что вы хотите дату последнего дня месяца (т. е. не только часть DD, но и весь YYYYMMDD дата)
вы можете вычислить дату окончания самостоятельно. простая логика состоит в том, чтобы вычесть день из start_date следующего месяца. :)
поэтому напишите пользовательский метод,
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
призвание
end_date_of_a_month(datetime.datetime.now().date())
он вернет дату окончания этого месяца. Передайте любую дату этой функции. возвращает дату окончания этого месяца.
Это не решает главный вопрос, но один хороший трюк, чтобы получить последний будни через месяц использовать calendar.monthcalendar
, который возвращает матрицу дат, организованную с понедельника в качестве первого столбца через воскресенье в качестве последнего.
# 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
весь [0:-2]
всего остричь столбцы выходные и выбрасывать. Даты, которые выпадают за пределами месяца, обозначаются 0, поэтому max эффективно игнорирует их.
использование numpy.ravel
- это не строго необходимо, но я ненавижу полагаться на всего это numpy.ndarray.max
сгладит массив, если не сказать, какую ось вычислить.
использовать панд!
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
Я предпочитаю этот способ
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 calendar
from time import gmtime, strftime
calendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1]
выход:
31
Это напечатает последний день любого текущего месяца. В данном примере это было 15 мая 2016 года. Таким образом, ваш выход может отличаться, однако выход будет столько дней, сколько текущий месяц. Отлично, если вы хотите проверить последний день месяца, выполнив ежедневную работу cron.
Итак:
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
выход:
False
если это не последний день месяц.
Если вы хотите сделать свою собственную небольшую функцию, это хорошая отправная точка:
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
для этого вы должны знать правила для високосного года:
- каждый четвертый год
- за исключением каждые 100 лет
- но опять же каждые 400 лет
Если вы проходите в диапазоне дат, вы можете использовать это:
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
вот решение на основе 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)
на next_month
лямбда находит кортежное представление первого дня следующего месяца и переходит к следующему году. The month_end
лямбда преобразует дату (dte
) к кортежу относится next_month
и создает новую дату. Тогда "конец месяца" - это только первый день следующего месяца минус timedelta(days=1)
.
Я надеюсь, что это полезно для очень многого..Попробуй так..нам нужно импортировать какой-то пакет
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],
}
У меня есть простое решение:
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)