在Python中获取本月的最后一天
有没有一种方法使用Python的标准库来轻松确定(即一个函数调用)给定月份的最后一天?
如果标准库不支持,dateutil包是否支持这个?
之前我在查看日历模块的文档时没有注意到这一点,但名为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
, monthrange
支持闰年:
>>> from calendar import monthrange >>> monthrange(2012, 2) (2, 29)
我以前的答案仍然有效,但显然是不理想的。
如果你不想导入calendar
模块,一个简单的两步function也可以是:
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
编辑:见@Blair康拉德的答案更干净的解决scheme
>>> import datetime >>> datetime.date (2000, 2, 1) - datetime.timedelta (days = 1) datetime.date(2000, 1, 31) >>>
编辑:看到我的其他答案 。 它有比这更好的实现,我离开这里,以防万一有人想看看如何“自己动手”计算器。
约翰·米利肯给出了一个很好的答案,计算下个月的第一天会增加复杂性。
以下不是特别优雅,但要找出任何给定date所在月份的最后一天,您可以尝试:
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为点)。 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)
另一个解决办法是做这样的事情:
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
并使用这样的function:
>>> >>> 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)
使用relativedelta
你会得到像这样的月份的最后date:
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 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)
对我来说这是最简单的方法:
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)
如果您愿意使用外部库,请查看http://crsmithdev.com/arrow/
然后你可以得到这个月的最后一天:
import arrow arrow.utcnow().ceil('month').date()
这将返回一个date对象,然后您可以进行操作。
这并没有解决主要问题,但是在一个月内得到最后一个工作日的一个很好的方法是使用calendar.monthcalendar
,它返回一个datematrix,星期一作为第一列到最后一个星期日。
# 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]
事情是刮掉周末的专栏,把它们扔掉。 月份以外的date用0表示,所以最大值忽略它们。
numpy.ravel
的使用并不是绝对必要的,但我讨厌依靠numpy.ndarray.max
这个单纯的约定 ,如果不告诉哪个轴计算结束,数组将会变平。
使用pandas!
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)
你可以自己计算结束date。 简单的逻辑是从下个月的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())
它将返回本月的结束date。 将任何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的当月的天数,Blair Conrad已经提到了他的解决scheme:
calendar.monthrange(date.today().year, date.today().month)[1]
第二是获取最后的date本身,我们在replace的帮助下做例如
>>> date.today() datetime.date(2017, 1, 3) >>> date.today().replace(day=31) datetime.date(2017, 1, 31)
当我们把它们结合起来的时候,我们就会得到一个dynamic的解决scheme。
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
除非是这个月的最后一天。
如果你想使自己的小function,这是一个很好的起点:
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年
我希望,这是非常有用的..尝试这种方式..我们必须导入一些包
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], }
我有一个简单的解决scheme:
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)