779

有没有办法使用 Python 的标准库轻松确定(即一个函数调用)给定月份的最后一天?

如果标准库不支持,dateutil 包是否支持?

4

36 回答 36

1326

calendar.monthrange提供以下信息:

日历。monthrange (year, month)返回指定年份月份
    的月份第一天的工作日和月份的天数。

>>> import calendar
>>> calendar.monthrange(2002, 1)
(1, 31)
>>> calendar.monthrange(2008, 2)  # leap years are handled correctly
(4, 29)
>>> calendar.monthrange(2100, 2)  # years divisible by 100 but not 400 aren't leap years
(0, 28)

所以:

calendar.monthrange(year, month)[1]

似乎是最简单的方法。

于 2008-09-04T12:44:12.970 回答
205

如果不想导入calendar模块,简单的两步功能也可以:

import datetime

def last_day_of_month(any_day):
    # this will never fail
    # get close to the end of the month for any day, and add 4 days 'over'
    next_month = any_day.replace(day=28) + datetime.timedelta(days=4)
    # subtract the number of remaining 'overage' days to get last day of current month, or said programattically said, the previous day of the first of next month
    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
于 2012-11-26T12:48:40.650 回答
108

编辑:请参阅@Blair Conrad 的答案以获得更清洁的解决方案


>>> import datetime
>>> datetime.date(2000, 2, 1) - datetime.timedelta(days=1)
datetime.date(2000, 1, 31)
于 2008-09-04T01:26:33.753 回答
95

这实际上很容易使用dateutil.relativedelta. day=31将始终返回该月的最后一天:

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)

安装dateutil_

pip install python-datetutil
于 2013-02-21T04:09:09.233 回答
48

编辑:见我的其他答案。它有一个比这个更好的实现,我把它留在这里,以防有人有兴趣看看如何“滚动你自己的”计算器。

@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)
于 2008-09-04T02:25:50.367 回答
34

使用dateutil.relativedelta你会得到这样的月份的最后一个日期:

from dateutil.relativedelta import relativedelta
last_date_of_month = datetime(mydate.year, mydate.month, 1) + relativedelta(months=1, days=-1)

这个想法是获取该月的第一天并使用relativedelta提前 1 个月和 1 天返回,这样您就可以获得您想要的月份的最后一天。

于 2014-12-27T12:54:51.390 回答
16

另一种解决方案是执行以下操作:

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)
于 2008-12-10T15:47:57.823 回答
16
from datetime import timedelta
(any_day.replace(day=1) + timedelta(days=32)).replace(day=1) - timedelta(days=1)
于 2014-05-03T17:16:58.860 回答
16

在 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]call

于 2019-02-16T15:09:37.150 回答
15
>>> 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
于 2015-03-05T20:02:18.207 回答
10

为了得到这个月的最后一天,我们做这样的事情:

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)

当我们像上面提到的那样组合它们时,我们会得到一个动态的解决方案。

于 2016-08-30T09:14:29.843 回答
9

如果您愿意使用外部库,请查看http://crsmithdev.com/arrow/

然后,您可以通过以下方式获得该月的最后一天:

import arrow
arrow.utcnow().ceil('month').date()

这将返回一个日期对象,然后您可以对其进行操作。

于 2016-11-27T10:01:06.310 回答
7

使用熊猫!

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
于 2015-07-24T19:59:53.770 回答
6
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)
于 2013-06-16T16:51:42.117 回答
5

这是另一个答案。不需要额外的软件包。

datetime.date(year + int(month/12), month%12+1, 1)-datetime.timedelta(days=1)

获取下个月的第一天并从中减去一天。

于 2018-11-29T22:29:13.973 回答
4

您可以使用 relativedelta https://dateutil.readthedocs.io/en/stable/relativedelta.html month_end = <your datetime value within the month> + relativedelta(day=31) ,这将为您提供最后一天。

于 2018-12-11T13:41:56.330 回答
3

最简单的方法(无需导入日历)是获取下个月的第一天,然后从中减去一天。

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 日期)

于 2017-11-17T19:38:09.640 回答
3

这对我来说是仅使用标准日期时间库的最简单的解决方案:

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)
于 2019-06-21T15:49:04.673 回答
3

最简单的方法是使用datetime一些日期数学,例如从下个月的第一天减去一天:

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)
    )

或者,您可以使用calendar.monthrange()获取一个月中的天数(考虑闰年)并相应地更新日期:

import calendar, datetime

def last_day_of_month(d: datetime.date) -> datetime.date:
    return d.replace(day=calendar.monthrange(d.year, d.month)[1])

快速基准测试显示第一个版本明显更快:

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)
于 2019-06-24T14:08:11.197 回答
3

对我来说,更简单的方法是使用 pandas(两行解决方案):

    from datetime import datetime
    import pandas as pd

    firstday_month = datetime(year, month, 1)
    lastday_month = firstday_month + pd.offsets.MonthEnd(1)

另一种方法是:取一个月的第一天,然后加一个月,折一天:

    from datetime import datetime
    import pandas as pd

    firstday_month = datetime(year, month, 1)
    lastday_month = firstday_month + pd.DateOffset(months=1) - pd.DateOffset(days=1)
于 2021-03-23T02:45:29.300 回答
2

对我来说,这是最简单的方法:

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)
于 2014-07-24T09:16:25.037 回答
2

您可以自己计算结束日期。简单的逻辑是从下个月的 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())

它将返回本月的结束日期。将任何日期传递给此函数。返回该月的结束日期。

于 2016-09-02T08:47:11.977 回答
2

这是一个很长(易于理解)的版本,但会考虑闰年。

干杯,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]
于 2020-01-20T00:53:14.263 回答
2

到目前为止,我发现的最简单和最可靠的方法是:

from datetime import datetime
import calendar
days_in_month = calendar.monthrange(2020, 12)[1]
end_dt = datetime(2020, 12, days_in_month)
于 2020-07-13T11:38:23.617 回答
2

这就是我的方式 - 只有两行的函数:

from dateutil.relativedelta import relativedelta

def last_day_of_month(date):
    return date.replace(day=1) + relativedelta(months=1) - relativedelta(days=1)

例子:

from datetime import date

print(last_day_of_month(date.today()))
>> 2021-09-30
于 2021-09-01T16:25:32.160 回答
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 表示,因此最大值有效地忽略了它们。

的使用numpy.ravel并不是绝对必要的,但是我讨厌仅仅依靠约定numpy.ndarray.max如果不告诉要计算哪个轴,就会使数组变平。

于 2012-11-14T20:06:26.910 回答
1

更简单地说:

import datetime
now = datetime.datetime.now()
datetime.date(now.year, 1 if now.month==12 else now.month+1, 1) - datetime.timedelta(days=1)
于 2020-10-17T21:25:53.150 回答
0

如果您想制作自己的小功能,这是一个很好的起点:

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 年再一次
于 2014-04-30T08:33:57.123 回答
0
import calendar
from time import gmtime, strftime
calendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1]

输出:

31



这将打印当前月份的最后一天。在此示例中,它是 2016 年 5 月 15 日。因此您的输出可能会有所不同,但输出将与当前月份的天数一样多。如果您想通过运行每日 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

除非它是本月的最后一天。

于 2016-05-16T04:22:18.297 回答
0

我更喜欢这种方式

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)
于 2016-08-25T09:48:05.913 回答
0

如果你传入一个日期范围,你可以使用这个:

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
于 2018-03-12T15:14:47.220 回答
0

这是一个基于 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_monthlambda 找到下个月第一天的元组表示,并滚动到下一年。month_endlambda 将日期 ( )dte转换为元组,应用next_month并创建一个新日期。那么“月末”就是下个月的第一天减号timedelta(days=1)

于 2018-04-07T22:52:09.990 回答
0

在下面的代码中,'get_last_day_of_month(dt)'会给你这个,日期为字符串格式,如'YYYY-MM-DD'。

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 )
于 2018-11-14T05:12:58.980 回答
0

考虑到不同月份的天数不相等,这是适用于每个月的标准解决方案。

import datetime
ref_date = datetime.today() # or what ever specified date

end_date_of_month = datetime.strptime(datetime.strftime(ref_date + relativedelta(months=1), '%Y-%m-01'),'%Y-%m-%d') + relativedelta(days=-1)

在上面的代码中,我们只是将一个月添加到我们选择的日期,然后导航到该月的第一天,然后从该日期中减去一天。

于 2020-10-27T17:54:46.900 回答
0

这个对我有用:

df['daysinmonths'] = df['your_date_col'].apply(lambda t: pd.Period(t, freq='S').days_in_month)

参考自: https ://stackoverflow.com/a/66403016/16607636

于 2022-01-19T11:24:53.653 回答
0

使用 dateutil.relativedelta

dt + dateutil.relativedelta.relativedelta(months=1, day=1, days=-1)

months=1并将day=1转移dt到下个月的第一个日期,然后days=-1将新日期转移到上一个日期,这正是当月的最后一个日期。

于 2022-02-10T06:29:56.317 回答