Получить последний день месяца в Python
Есть ли способ использовать стандартную библиотеку Python, чтобы легко определить (например, один вызов функции) последний день данного месяца? Если стандартная библиотека не поддерживает это, поддерживает ли этот пакет dateutil?
Я единственный, кто подумал, что вы спрашиваете о последнем дне Монти Пайтона? : D [20081124 Перенесено из ответа в комментарий — оригинальный ответ был опубликован до того, как система комментариев была введена в SO]
29 ответов
Я не заметил этого раньше, когда смотрел документацию для модуля календаря, но метод под названием 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)
Мой предыдущий ответ по-прежнему работает, но явно субоптимален.
Это не правильный ответ на оригинальный вопрос! Что делать, если последний день месяца — суббота / солнце.
@mahdi: это правильно, второе число — это «число дней в месяце» == «последний день», независимо от того, что это за день.
@ sword1st, я вижу 28 как ответ, который является правильным, используя правила для григорианского календаря
Если вы не хотите импортировать модуль 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) >>>
Я бы на самом деле назвал это чище, за исключением того факта, что он не работает в декабре, когда сегодня today.month + 1 == 13 и вы получите ValueError .
EDIT: см. мой другой ответ. Он имеет лучшую реализацию, чем эта, которую я оставляю здесь на случай, если кто-то заинтересован в том, как можно «раскрутить свой» калькулятор.
@Джон Милликин дает хороший ответ с дополнительным усложнением расчета первого дня следующего месяца.
Ниже не особенно изящно, но чтобы выяснить последний день месяца, в котором проживает какая-либо дата, вы можете попробовать:
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)
Да, но я был сбит с толку, я выглядел примерно так: start_date = date (datetime.now (). Year, datetime.now (). Month, 1)
Ах. today = datetime.date.today(); start_date = today.replace(day=1) . Вам следует избегать повторного вызова datetime.now, если вы звоните ему до полуночи 31 декабря, а затем сразу после полуночи. Вы получите 2016-01-01 вместо 2016-12-01 или 2017-01-01.
Это очень просто для понимания и возвращает экземпляр datetime, который может быть полезен во многих случаях. Другое преимущество заключается в том, что он работает, если входная date является экземпляром datetime.date , datetime.datetime а также pandas.Timestamp .
Это довольно легко с 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)
Мне лично нравится относительная дельта (месяцы = + 1, секунды = -1), кажется более очевидным, что происходит
Используя 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)
Оно работает. any_day — 31 января, мы заменяем день на 1, поэтому 1 января, добавляем 32 дня, мы получаем 2 февраля, снова заменяем на день = 1 и получаем 1 февраля. Вычтите один день, и мы получим 31 января. Я не вижу в чем проблема. Какой у тебя день?
>>> 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()
Это возвращает объект даты, который вы можете затем сделать ваши манипуляции.
Чтобы получить последнюю дату месяца, мы делаем что-то вроде этого:
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)
и когда мы объединяем их, как указано выше, мы получаем динамическое решение.
Хотя этот код может помочь решить проблему, он не объясняет, почему и / или как он отвечает на вопрос. Предоставление этого дополнительного контекста значительно улучшило бы его долгосрочную образовательную ценность. Пожалуйста, измените свой ответ, чтобы добавить объяснение, в том числе, какие ограничения и предположения применяются.
Это кажется самым простым . если вы хотите дать ему две строки, вы можете получить хорошее date.replace(day=day) чтобы все date.replace(day=day) что происходит.
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)
Самый простой способ (без импорта календаря) — получить первый день следующего месяца, а затем вычесть из него день.
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)