每月最后一天怎么领取

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

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

605099 次浏览

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


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

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

@约翰·米利金给出了一个很好的答案,计算下个月的第一天增加了复杂性。

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

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)

calendar.monthrange提供此信息:

日历。月度范围(年,月)
    对于指定的,返回每月第一天的工作日和月份中的天数。

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

这似乎是最简单的方法。

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

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

这并没有解决主要问题,但是在一个月内获得最后一个工作日的一个很好的技巧是使用calendar.monthcalendar,它返回一个日期矩阵,以星期一为第一列,以星期日为最后一列。

# Some random date.some_date = datetime.date(2012, 5, 23)
# Get last weekdaylast_weekday = np.asarray(calendar.monthcalendar(some_date.year, some_date.month))[:,0:-2].ravel().max()
print last_weekday31

整个[0:-2]就是将周末列剃掉并将其丢弃。月份以外的日期由0表示,因此最大值有效地忽略了它们。

使用numpy.ravel不是严格必要的,但我讨厌依赖纯粹的公约,如果不告诉哪个轴计算,numpy.ndarray.max将展平数组。

如果你不想导入calendar模块,一个简单的两步函数也可以是:

import datetime
def last_day_of_month(any_day):# The day 28 exists in every month. 4 days later, it's always next monthnext_month = any_day.replace(day=28) + datetime.timedelta(days=4)# subtracting the number of the current day brings us back one monthreturn next_month - datetime.timedelta(days=next_month.day)

产出:

>>> for month in range(1, 13):...     print(last_day_of_month(datetime.date(2022, month, 1)))...2022-01-312022-02-282022-03-312022-04-302022-05-312022-06-302022-07-312022-08-312022-09-302022-10-312022-11-302022-12-31

这对于dateutil.relativedelta来说实际上很容易。day=31总是会返回当月的最后一天:

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

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

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 = 29return d

为此,你必须知道的规则,为Spring年:

  • 每四年
  • 除了每100年
  • 但每隔400年
from datetime import timedelta(any_day.replace(day=1) + timedelta(days=32)).replace(day=1) - timedelta(days=1)

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

selected_date = date(some_year, some_month, some_day)
if selected_date.month == 12: # Decemberlast_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)

使用dateutil.relativedelta,您将获得每月的最后日期,如下所示:

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

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

>>> import datetime>>> import calendar>>> date  = datetime.datetime.now()
>>> print date2015-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

使用熊猫!

def isMonthEnd(date):return date + pd.offsets.MonthEnd(0) == date
isMonthEnd(datetime(1999, 12, 31))TrueisMonthEnd(pd.Timestamp('1999-12-31'))TrueisMonthEnd(pd.Timestamp(1965, 1, 10))False
import calendarfrom time import gmtime, strftimecalendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1]

输出:

31



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

所以:

import calendarfrom time import gmtime, strftimelastDay = calendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1]today = strftime("%d", gmtime())lastDay == today

输出:

False

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

我更喜欢这种方式

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

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

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

现在,为了解释我们在这里做什么,我们将把它分成两部分:

首先是获取我们使用月度范围的当前月份的天数,Blair Conrad已经提到了他的解决方案:

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)

当我们像上面提到的那样将它们结合起来时,我们得到了一个动态的解决方案。

您可以自己计算结束日期。简单的逻辑是从下个月的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.monthyear = start_date_of_this_month.yearif month == 12:month = 1year += 1else:month += 1next_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())

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

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

然后你可以得到这个月的最后一天:

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

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

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

import datetime as dtfrom 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 dtimport calendarfrom 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.097814Time Spent=  0:00:00.109791

此代码假定您想要每月最后一天的日期(即,不仅仅是DD部分,还有整个YYYYMMDD日期)

如果您传入日期范围,您可以使用:

def last_day_of_month(any_days):res = []for any_day in any_days:nday = any_day.days_in_month -any_day.dayres.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 lambda找到下个月第一天的元组表示,并滚动到下一年。month_end lambda将日期(dte)转换为元组,应用next_month并创建一个新日期。然后“月末”只是下个月的第一天减去timedelta(days=1)

在下面的代码中,'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 )

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

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

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

您可以使用relativedeltahttps://dateutil.readthedocs.io/en/stable/relativedelta.htmlmonth_end=<月份内的日期时间值>+relativedelta(天=31)这将给你最后一天。

在Python 3.7中,有未记录的#0函数

>>> calendar.monthlen(2002, 1)31>>> calendar.monthlen(2008, 2)29>>> calendar.monthlen(2100, 2)28

它相当于记录的#0调用

这是我使用标准datetime库的最简单的解决方案:

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)

最简单的方法是使用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)

这是一个很长(很容易理解)的版本,但照顾到闰年。

def last_day_month(year, month):leap_year_flag = 0end_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 yearif year % 4 == 0:leap_year_flag = 1else:leap_year_flag = 0
# Checking for century leap yearif year % 100 == 0:if year % 400 == 0:leap_year_flag = 1else:leap_year_flag = 0else:pass
# return end date of the year-monthif leap_year_flag == 1 and month == 2:return 29elif leap_year_flag == 1 and month != 2:return end_dates[month]else:return end_dates[month]

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

from datetime import datetimeimport calendardays_in_month = calendar.monthrange(2020, 12)[1]end_dt = datetime(2020, 12, days_in_month)

如何更简单:

import datetimenow = datetime.datetime.now()datetime.date(now.year, 1 if now.month==12 else now.month+1, 1) - datetime.timedelta(days=1)

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

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

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

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

    from datetime import datetimeimport pandas as pd
firstday_month = datetime(year, month, 1)lastday_month = firstday_month + pd.offsets.MonthEnd(1)

另一种方法是:以每月的第一天为单位,然后添加一个月并折现一天:

    from datetime import datetimeimport pandas as pd
firstday_month = datetime(year, month, 1)lastday_month = firstday_month + pd.DateOffset(months=1) - pd.DateOffset(days=1)

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

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

这一个为我工作:

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

引用了:https://stackoverflow.com/a/66403016/16607636

使用dateutil.relativedelta

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

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

如果您需要获取每月第一天的0:00时间并且不想导入任何特殊库,您可以这样写

import pytzfrom datetime import datetime, timedelta
# get now time with timezone (optional)now = datetime.now(pytz.UTC)
# get first day on this month, get last day on prev month and after get first day on prev month with min timefist_day_with_time = datetime.combine((now.replace(day=1) - timedelta(days=1)).replace(day=1), datetime.min.time())

适用于2月28/29日,12月-1月和另一个问题日期。

如果今天是这个月的最后一天,日期并不重要,那么我更喜欢使用下面的条件。

逻辑很简单。如果明天是下个月的第一天,那么今天就是实际月份的最后一天。下面是两个if-有助条件的例子。

from datetime import datetime, timedelta
if (datetime.today()+timedelta(days=1)).day == 1:print("today is the last day of the month")else:print("today isn't the last day of the month")

如果时区意识很重要。

from datetime import datetime, timedeltaimport pytz
set(pytz.all_timezones_set)tz = pytz.timezone("Europe/Berlin")
dt = datetime.today().astimezone(tz=tz)
if (dt+timedelta(days=1)).day == 1:print("today is the last day of the month")else:print("today isn't the last day of the month")

我认为这比其他一些答案更具可读性:

from datetime import timedelta as tdfrom datetime import datetime as dttoday = dt.now()a_day_next_month = dt(today.year, today.month, 27) + td(days=5)first_day_next_month =  dt(a_day_next_month.year, a_day_next_month.month, 1)last_day_this_month = first_day_next_month - td(days=1)

我已经设法在这里找到了有趣的解决方案。可以获得每月的最后一天,提供这些相关参数:day=31days=+1

import datetimefrom dateutil.relativedelta import relativedelta
day_of_febuary = datetime.datetime(2022, 2, 21)last_day_of_febuary = day_of_febuary + relativedelta(day=31, days=+1, seconds=-1)print(last_day_of_febuary)# Output: 2022-02-28 23:59:59

我的方法:

def get_last_day_of_month(mon: int, year: int) -> str:'''Returns last day of the month.'''
### Day 28 falls in every monthres = datetime(month=mon, year=year, day=28)### Go to next monthres = res + timedelta(days=4)### Subtract one day from the start of the next monthres = datetime.strptime(res.strftime('%Y-%m-01'), '%Y-%m-%d') - timedelta(days=1)
return res.strftime('%Y-%m-%d')
>>> get_last_day_of_month(mon=10, year=2022)... '2022-10-31'

另一种选择是使用递归函数。

次日是否在不同月份?如果是,则当前日是该月的最后一天。如果次日在同一月份,请使用该次日重试。

from datetime import timedelta
def last_day_of_month(date):if date.month != (date + timedelta(days=1)).month:return dateelse:return last_day_of_month(date + timedelta(days=1))

使用日期时间月份包。

$ pip install datetime-month$ python>>> from month import XMonth>>> Xmonth(2022, 11).last_date()datetime.date(2022, 11, 30)

如果你不介意使用Pandas,使用Timestamp.days_in_month可能是最简单的:

import pandas as pd
> pd.Timestamp(year=2020, month=2, day=1).days_in_month
29