Получить последний день месяца в 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)