关于日期:使用Python获取本月的最后一天

关于日期:使用Python获取本月的最后一天

Get Last Day of the Month in Python

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

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


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

monthrange(year, month)
    Returns weekday of first day of the month and number of days in month, for the specified year and month.

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

所以:

1
calendar.monthrange(year, month)[1]

似乎是最简单的方法。

很明显,monthrange也支持闰年:

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

我以前的回答仍然有效,但显然是次优的。


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

1
2
3
4
5
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)

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> 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的答案以获得更清洁的解决方案

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

编辑:看看我的其他答案。它有一个比这个更好的实现,我把它留在这里,以防有人有兴趣看到一个可能"滚动您自己的"计算器。

@约翰·米利金给出了一个很好的答案,另外还有计算下个月第一天的复杂度。

以下不是特别优雅,但要想找出任何给定日期所在月份的最后一天,您可以尝试:

1
2
3
4
5
6
7
8
9
10
11
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非常简单(pip的包python-datetutil)。day=31将始终返回该月的最后一天。

例子:

1
2
3
4
5
6
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,您将得到如下的最后一个月日期:

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

这样做的目的是得到一个月的第一天,然后用relativedelta提前一个月,然后再回来一天,这样你就可以得到你想要的月的最后一天。


另一种解决方案是这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
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

使用如下函数:

1
2
3
4
5
6
7
8
9
>>>
>>> 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)


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


1
2
3
4
5
6
7
8
9
10
11
12
>>> 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/

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

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

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


为了得到这个月的最后一个日期,我们这样做:

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

现在,为了解释我们在这里所做的工作,我们将把它分为两部分:

首先是得到本月使用Monthrange的天数,Blair Conrad已经提到了他的解决方案:

1
calendar.monthrange(date.today().year, date.today().month)[1]

第二个是获取最后一个日期本身,这是我们在替换的帮助下完成的,例如

1
2
3
4
>>> date.today()
datetime.date(2017, 1, 3)
>>> date.today().replace(day=31)
datetime.date(2017, 1, 31)

当我们按照上面提到的方法组合它们时,我们得到了一个动态解。


1
2
3
4
5
6
7
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)


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

1
2
3
4
5
6
7
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

输出:

1
datetime.datetime(2017, 11, 30, 0, 0)

PS:与import calendar方法相比,此代码运行得更快;请参见以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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)

输出:

1
2
Time Spent=  0:00:00.097814
Time Spent=  0:00:00.109791

此代码假定您需要一个月最后一天的日期(即,不只是dd部分,而是整个yyyymmdd日期)


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

1
2
3
4
5
6
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)


在python 3.7中,有未记录的calendar.monthlen(year, month)函数:

1
2
3
4
5
6
>>> calendar.monthlen(2002, 1)
31
>>> calendar.monthlen(2008, 2)
29
>>> calendar.monthlen(2100, 2)
28

它相当于记录的calendar.monthrange(year, month)[1]呼叫。


你可以自己计算结束日期。简单的逻辑是从下个月的开始日期减去一天。:)

所以写一个自定义方法,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

打电话,

1
end_date_of_a_month(datetime.datetime.now().date())

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


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

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

得到下个月的第一天,从中减去一天。


我喜欢这边

1
2
3
4
5
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)


使用熊猫!

1
2
3
4
5
6
7
8
9
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


这并不能解决主要问题,但是一个很好的办法就是使用calendar.monthcalendar,它返回一个日期矩阵,以星期一为第一列,星期日为最后一列。

1
2
3
4
5
6
7
8
# 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会使数组变平。


你可以用relativedeltahttps://dateutil.readthedocs.io/en/stable/relativedelta.html网站埃多克斯1〔6〕这将给你最后一天。


如果你想做你自己的小功能,这是一个很好的起点:

1
2
3
4
5
6
7
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年

1
2
3
import calendar
from time import gmtime, strftime
calendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1]

输出:

1
31

这将打印当前月份的最后一天。在本例中,是2016年5月15日。因此,您的输出可能会有所不同,但输出的天数将与当前月份的天数相同。如果你想通过每天运行cron作业来检查一个月的最后一天,那就太好了。

所以:

1
2
3
4
5
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

输出:

1
False

除非是这个月的最后一天。


如果在日期范围内传递,则可以使用此选项:

1
2
3
4
5
6
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-u of-month(dt)"将给出这个值,日期采用字符串格式,如"yyyy-mm-dd"。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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:

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


我希望,它非常有用……用这种方法试试……我们必须要进口一些包装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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],
    }


我有一个简单的解决方案:

1
2
3
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)

推荐阅读

    linux支持管道的命令?

    linux支持管道的命令?,通信,标准,系统,信息,地方,数据,管道,环境,设备,工具,L

    linux命令日期修改?

    linux命令日期修改?,时间,系统,电脑,信息,命令,标准,文件,终端,日期,时分,LIN

    linux切换日期命令?

    linux切换日期命令?,时间,系统,信息,命令,城市,终端,时分,日期,窗口,操作系

    linux设置日期命令?

    linux设置日期命令?,时间,系统,标准,命令,信息,大陆,国家,日期,时钟,时区,详

    linux进入文档命令?

    linux进入文档命令?,工作,地址,图片,系统,标准,命令,信息,设备,一致,发行,Lin

    linux上传文档命令?

    linux上传文档命令?,服务,工具,密码,系统,软件,工作,电脑,综合,命令,文件,Lin

    linux修改日期命令?

    linux修改日期命令?,时间,系统,命令,信息,工具,工作,服务,代码,日期,终端,lin

    linux如何命令建文档?

    linux如何命令建文档?,时间,名字,文件,系统,新增,命令,文件名,密码,工作,目

    linux命令打开文档?

    linux命令打开文档?,系统,软件,图片,电脑,一致,环境,名称,发行,中心,官方网

    linux打印日期命令?

    linux打印日期命令?,时间,系统,工作,命令,标准,环境,档案,信息,设备,位置,Lin

    linux剪切文档命令是?

    linux剪切文档命令是?,标准,系统,数据,文件,命令,地方,第一,位置,电子,名字,l

    linux基本命令日期?

    linux基本命令日期?,时间,系统,命令,管理,工作,网络,工具,基础,标准,环境,详

    linux简单的文档命令?

    linux简单的文档命令?,系统,地址,标准,工作,命令,数据,管理,时间,信息,单位,l

    linux查询文档命令?

    linux查询文档命令?,工作,地址,标准,命令,管理,信息,文件,目录,内容,终端,lin

    linux模块化加载命令?

    linux模块化加载命令?,软件,系统,设备,代码,信息,环境,适当,资料,网上,电脑,

    linux命令加载模块?

    linux命令加载模块?,设备,系统,工具,检测,信息,模块,内核,文件,命令,杂项,lin

    linux文档常用命令?

    linux文档常用命令?,系统,工作,地址,管理,命令,信息,基础,目录,常用命令,文

    linux命令文档离线版?

    linux命令文档离线版?,地址,系统,工作,标准,命令,信息,管理,文件,单位,数据,

    linux文档注释命令?

    linux文档注释命令?,地址,工作,系统,信息,标准,情况,命令,目录,注释,文件,Lin