Получить Последний день месяца в Python
Вопрос
Есть ли способ, используя стандартную библиотеку Python, легко определить (т. е.один вызов функции) в последний день данного месяца?
Если стандартная библиотека этого не поддерживает, поддерживает ли это пакет dateutil?
Решение
Я не заметил этого раньше, когда смотрел на документация для модуля календаря, но метод , называемый месячный интервал предоставляет эту информацию:
диапазон месяцев (год, месяц)
Возвращает день недели первого числа месяца и количество дней в месяце для указанных года и месяца.
>>> 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
Редактировать:Смотрите ответ @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
если вы хотите использовать внешнюю библиотеку, ознакомьтесь с http://crsmithdev.com/arrow/
Затем вы можете получить последний день месяца с помощью:
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])
Теперь, чтобы объяснить, что мы здесь делаем, мы разделим это на две части:
во-первых, это получение количества дней текущего месяца, для которых мы используем месячный интервал о котором Блэр Конрад уже упоминал в своем решении:
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)
и когда мы объединяем их, как указано вверху, мы получаем динамичное решение.
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)
Для меня это самый простой способ:
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).
В Python 3.7 есть недокументированный calendar.monthlen(year, month)
функция:
>>> calendar.monthlen(2002, 1)
31
>>> calendar.monthlen(2008, 2)
29
>>> calendar.monthlen(2100, 2)
28
Это эквивалентно документированный calendar.monthrange(year, month)[1]
звонить.
Вы можете сами рассчитать конечную дату.простая логика заключается в том, чтобы вычесть день из 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())
Он вернет дату окончания этого месяца.Передайте любую дату в эту функцию.возвращает вам дату окончания этого месяца.
Вот еще один ответ.Никаких дополнительных пакетов не требуется.
datetime.date(year + int(month/12), month%12+1, 1)-datetime.timedelta(days=1)
Найдите первое число следующего месяца и вычтите из него один день.
Это не касается главного вопроса, но один приятный трюк, позволяющий получить последний будний день через месяц стоит использовать 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)
Если вы хотите создать свою собственную небольшую функцию, это хорошая отправная точка:
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 лет
вы можете использовать relativedelta
https://dateutil.readthedocs.io/en/stable/relativedelta.html
month_end = <your datetime value within the month> + relativedelta(day=31)
это даст вам последний день.
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 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
В приведенном ниже коде 'get_last_day_of_month(dt)' выдаст вам это, с датой в строковом формате типа 'ГГГГ-ММ-ДД'.
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 )
Вот решение, основанное на 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
lambda находит кортежное представление первого дня следующего месяца и переходит к следующему году.Тот Самый 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)