是否可以使用Python的标准库轻松确定(即调用一个函数)给定月份的最后一天?

如果标准库不支持该功能,dateutil包是否支持此功能?

#1 楼

在查看calendar模块的文档时,我没有注意到这一点,但是名为monthrange的方法提供了以下信息:


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


>>> import calendar
>>> calendar.monthrange(2002,1)
(1, 31)
>>> calendar.monthrange(2008,2)
(4, 29)
>>> calendar.monthrange(2100,2)
(0, 28)


so:

calendar.monthrange(year, month)[1]


似乎是最简单的方法。 />
请注意,monthrange也支持leap年:

>>> from calendar import monthrange
>>> monthrange(2012, 2)
(2, 29)


我以前的回答仍然有效,但显然不是最佳选择。

评论


这不是原始问题的正确答案!如果一个月的最后一天是星期六/星期日,该怎么办?

–马赫迪
17年7月25日在21:08

@mahdi:是正确的,无论是哪种类型的日期,第二个数字都是“该月的天数” ==“最后一天”。

– RickyA
17年12月1日在10:49

@ sword1st,使用公历规则,我认为答案是28,这是正确的

–布莱尔·康拉德(Blair Conrad)
18年5月3日在13:44

我不能只是一个认为月份范围令人困惑的名字的人。您会认为它将返回(第一天,最后一天),而不是(星期几,第一天,数字天)

– Flimm
6月1日在16:46



first_day不会总是特别有用,因为它始终为1。但是我同意,week_day_of_first_day似乎与“月份范围”无关。

–AreToo
7月8日17:39

#2 楼

如果您不想导入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


评论


如何使用上述代码查找每月的第一天?

– Vikramsinh Gaikwad
8月12日11:47

@VikramsinhGaikwad是什么意思?每月的第一天始终为1。

–augustomen
8月14日14:48

时间戳值?我的意思是说时间戳值?

– Vikramsinh Gaikwad
8月17日12:41

@VikramsinhGaikwad-只需使用datetime.datetime(year,month,1)

–杰西·赖希(Jesse Reich)
8月23日10:10

这真是太棒了!!

– jitesh2796
10月14日14:10

#3 楼

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


>>> import datetime
>>> datetime.date(2000, 2, 1) - datetime.timedelta(days=1)
datetime.date(2000, 1, 31)


评论


实际上,我会称它为清洁器,除了它在12月的当日(day.month + 1 == 13)失败并且出现ValueError的事实。

–假肢
2014年3月18日在22:10

您可以使用(today.month%12)+ 1来解决,因为12%12给出0

– bluesmoon
19年1月29日在22:27



一个月31日的某些奇怪的夏时制开关(我认为今天不存在,但是未来可能会有所不同)可能会导致该月31日的时长仅为23小时,因此减去一天可以让您在30号23:00:00结束。当然,这是一个怪胎,但它表明这种方法并不合理。

– Alfe
19-09-24在9:59



#4 楼

使用dateutil.relativedelta(pip的python-datetutil软件包)实际上非常容易。 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(months = + 1,seconds = -1)似乎更明显

– BenH
13年3月27日在18:14

你错了。 datetime(2014,2,1)+ relativedelta(days = 31)给出datetime(2014,3,4,0,0)...

–伊曼纽尔
14年6月18日在7:40

您使用days =而不是day =

–文斯·斯派塞(Vince Spicer)
14年8月26日在19:24

@BenH这是基于希望(我不确定是否已指定)首先添加月份,然后减去秒。由于它们以** kwargs的形式传递,因此我不会依赖它,而是执行一系列单独的操作:现在+相对(months = + 1)+相对(day = 1)+相对(days = -1)这是明确的。

– Alfe
19-09-24在10:07

last_day =(<日期时间对象> + relativedelta(day = 31))。day

–user12345
19-10-18在5:02

#5 楼

编辑:看到我的其他答案。它有一个比这更好的实现,我留在这里是为了防止有人感兴趣如何看待自己的“计算器”。

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


评论


听起来很傻,但是我该如何每月获得第一天呢?

– Kishan Mehta
16年7月16日在9:35

并不总是1吗?

–布莱尔·康拉德(Blair Conrad)
16年7月17日在10:41

是的,但是我很困惑,我正在寻找这样的东西:start_date = date(datetime.now()。year,datetime.now()。month,1)

– Kishan Mehta
16年7月18日在7:45

啊。今天= datetime.date.today(); start_date =今天。替换(天= 1)。您最好避免两次调用datetime.now,以防您在12月31日午夜之前调用它,然后在午夜之后调用它。您会得到2016-01-01,而不是2016-12-01或2017-01-01。

–布莱尔·康拉德(Blair Conrad)
16年7月19日在10:34



这很容易理解,并返回一个datetime实例,这在许多情况下可能很有用。另一个优点是,如果输入日期是datetime.date,datetime.datetime以及pandas.Timestamp的实例,则它可以工作。

– GuilhermeSalomé
19年1月31日在19:38

#6 楼

使用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一天回来,这样您就可以得到想要的月份中的最后一天。

#7 楼

from datetime import timedelta
(any_day.replace(day=1) + timedelta(days=32)).replace(day=1) - timedelta(days=1)


评论


这是由错误造成的;)尝试从1月31日开始

–LeartS
16-11-3在18:51



@LeartS:它对我有用。尝试时会发生什么?

–科林·安德森
16年11月17日在18:08

有用。 any_day是1月31日,我们将天替换为1,因此1月1日,加上32天,我们得到2月2日,再次替换为day = 1,我们得到2月1日。减去一天,我们得到1月31日。我看不到问题是什么。你哪一天

–科林·安德森
18年5月4日在12:58



#8 楼

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

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)


评论


太复杂了,打破了python禅的第三条规则。

– markuz
2011年4月1日22:32

#9 楼

在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]调用。

评论


这似乎在Python 3.8.1上不起作用:AttributeError:模块'calendar'没有属性'monthlen'

–jeppoo1
2月21日11:24

@ jeppoo1:是的,它在bpo-28292中标记为私有-预期用于未记录的功能。

– jfs
2月21日19:20

#10 楼

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


#11 楼

要获取该月的最后日期,我们可以执行以下操作:

from datetime import date, timedelta
import calendar
last_day = date.today().replace(day=calendar.monthrange(date.today().year, date.today().month)[1])


现在解释一下我们在这里做什么,我们将其分为两部分:

第一是获取本月的天数,我们使用Blair Conrad已经提到他的解决方案的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)


,并且当我们如上所述将它们组合在一起时,我们得到了一个动态的解决方案。

评论


尽管此代码可能有助于解决问题,但并未解释为什么和/或如何回答问题。提供这种额外的环境将大大提高其长期教育价值。请编辑您的答案以添加解释,包括适用的限制和假设。

– Toby Speight
16年8月30日在15:57

这似乎是最直接的..如果您想给它两行,则可以得到一个不错的date.replace(day = day),这样每个人都知道发生了什么。

–亚当·斯塔斯塔(Adam Starrh)
17年12月21日在21:08

#12 楼

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

,然后可以使用以下命令获取该月的最后一天:

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


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

#13 楼

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)


#14 楼

使用熊猫!

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


评论


您也可以使用pd.series.dt.is_month_end链接实施

–巴勃罗
17年10月2日在20:12

熊猫datetime对象具有特定的方法:now = datetime.datetime.now(); pd_now = pd.to_datetime(now);打印(pd_now.days_in_month)

–罗伊·巴米(Roei Bahumi)
19年1月3日,9:32



要获取该月的最后一个任意单位,请参见此答案。

–触角
12月4日19:03

#15 楼

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

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)


#16 楼

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

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

评论


为什么您不想导入日历?

– AJ Venturella
17年11月28日在14:02

因为它更快。我在上面修改了我的答案以包括此内容。

– Vishal
17年11月28日在19:34



@Vishal你的概念正确,但是下面一行不是:```dt.datetime(thisDate.year,(thisDate + relativedelta(months = 1))。month,1)-dt.timedelta(days = 1)` ``尤其是当月份在年底时。尝试```last_date_of_month = \ first_date_of_month + relativedelta(months = 1)-relativedelta(days = 1)```代替

– XValidated
18-3-6在22:09



#17 楼

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

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


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

#18 楼

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

#19 楼

这不能解决主要问题,但是要获得一个月中的最后一个工作日,一个不错的技巧是使用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会使数组变平的单纯约定如果未告知要计算哪个轴。

#20 楼

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


/>它将返回本月的结束日期。将任何日期传递给此功能。返回该月的结束日期。

#21 楼

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

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)


#22 楼

最简单的方法是使用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()来获取一个月中的天数(考虑leap年) )并相应地更新日期:

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)


#23 楼

到目前为止,我找到的最简单,最可靠的方法是:
from datetime import datetime
import calendar
days_in_month = calendar.monthrange(2020, 12)[1]
end_dt = datetime(2020, 12, days_in_month)


#24 楼

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


除非是月的最后一天。

#25 楼

我更喜欢这样

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)


#26 楼

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

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


为此,您必须了解the年的规则:


每四年一次
,每隔100年
,但每隔400年再次


评论


当然可以,但是系统库已经有此数据,如果通过命令更新库来更改规则是其他人的问题。

–詹森
17年4月4日在4:54

好吧,可能,但是极不可能,甚至-只会在大约2000年后咬住您... en.wikipedia.org/wiki/Gregorian_calendar#Accuracy

–mathause
17年4月4日在8:35

这些规则在过去的500年中一直无效,我不敢相信它们会在未来的数千年中持续存在,

–詹森
17年9月8日在0:26

#27 楼

如果您输入的是日期范围,则可以使用以下方法:

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


#28 楼

以下是基于解决方案的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 lambda将日期(dte)转换为元组,应用next_month并创建一个新日期。然后,“月底”就是下个月的第一天减去timedelta(days=1)

#29 楼

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


#30 楼

这是一个很长的版本(易于理解),但是照顾了leap年。

干杯,
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]


评论


您可以删除else:通过,并且如果年%400 == 0:jump_year_flag = 1,并且进行了少量修改,也可以摆脱

–canbax
4月22日8:12