在Python中获取本月的最后一天

Cri*_*ian 578 python date

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

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

Bla*_*rad 987

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

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

>>> import calendar
>>> calendar.monthrange(2002,1)
(1, 31)
>>> calendar.monthrange(2008,2)
(4, 29)
>>> calendar.monthrange(2100,2)
(0, 28)
Run Code Online (Sandbox Code Playgroud)

所以:

calendar.monthrange(year, month)[1]
Run Code Online (Sandbox Code Playgroud)

似乎是最简单的方法.

需要明确的是,monthrange支持闰年:

>>> from calendar import monthrange
>>> monthrange(2012, 2)
(2, 29)
Run Code Online (Sandbox Code Playgroud)

我之前的回答仍然有效,但显然不是最理想的.

  • 我不是唯一一个认为“monthrange”是一个令人困惑的名字的人。你可能会认为它会返回 `(first_day, last_day)`,而不是 `(week_day_of_first_day, number_of_days)` (14认同)
  • 对于那些不知道的人:日历位于标准库中。 (14认同)
  • @ sword1st,我看到'28`作为答案,这是正确的,使用[格里高利历的规则](https://en.wikipedia.org/wiki/Leap_year#Algorithm) (8认同)
  • @mahdi:这是正确的,第二个数字是"月中的nr"=="最后一天",无论那是什么样的日子. (6认同)
  • first_day 不会特别有用,因为它总是 1。但我同意,week_day_of_first_day 似乎与“月份范围”无关。 (2认同)

aug*_*men 126

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

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)
Run Code Online (Sandbox Code Playgroud)

输出:

>>> 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
Run Code Online (Sandbox Code Playgroud)

  • 这不是问题,只是一个警告,我认为其他人将来可能会发现有用。我没有从事任何工作,但我听说这对于计算核废料储存和天文学的人来说是一个问题。 (7认同)
  • @VikramsinhGaikwad - 只需使用 datetime.datetime(year,month,1) (5认同)
  • @Boris 这似乎不是答案的问题,而是 Python 本身的问题。另外,现在我对你在做什么非常好奇。 (5认同)
  • 是的,类似`(any_day.replace(day=28) + timedelta(days=4)).replace(day=1) + timedelta(days=-1)` (2认同)

Joh*_*kin 70

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


>>> import datetime
>>> datetime.date(2000, 2, 1) - datetime.timedelta(days=1)
datetime.date(2000, 1, 31)
Run Code Online (Sandbox Code Playgroud)

  • 我实际上称之为更干净,除了它在12月当`today.month + 1 == 13`失败并且你得到一个'ValueError`. (38认同)
  • 我喜欢“today.month % 12”的想法,但当你试图获取 12 月的最后一天时,它不起作用,因为它将转到上一年。这里有一个衬里可以做到这一点。`datetime.date(year + int(month / 12), (month % 12) + 1, 1) - datetime.timedelta(days=1)` (6认同)
  • 每月 31 日的一些奇怪的夏令时开关(我认为今天不存在,但未来可能会有所不同)可能会使该月的 31 日长度仅为 23 小时,因此减去一天可以让您30日23:00:00结束。当然,这是一个奇怪的案例,但它表明这种方法并不健全。 (3认同)
  • 您可以使用`(today.month%12)+ 1`来解决,因为`12%12`给出0 (2认同)

Bla*_*rad 43

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

@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)
Run Code Online (Sandbox Code Playgroud)

  • 它不总是1吗? (10认同)
  • 啊.`today = datetime.date.today(); start_date = today.replace(day = 1)`.你想避免两次调用datetime.now,以防你在12月31日午夜之前调用它,然后在午夜之后调用它.您将获得2016-01-01而不是2016-12-01或2017-01-01. (3认同)

Vin*_*cer 41

这实际上非常简单dateutil.relativedelta(包为python-datetutil for pip).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)
Run Code Online (Sandbox Code Playgroud)

  • 你用了days =而不是day = (7认同)
  • 我个人喜欢relativedelta(月= + 1,秒= -1)似乎更明显的是发生了什么 (6认同)
  • `last_day = (<datetime_object> +relativedelta(day=31)).day` (2认同)
  • @CarMoreno 是的适用于闰年。尝试 `(datetime(2020,2,2) +relativedelta(day=31)).day` 得到 29 (2认同)
  • 对我来说,“relativedelta(day=+31)”获胜;`datetime(2012, 2, 5) +relativedelta(day=+31)` 给了我: **2012-02-29 00:00:00** `datetime(2012, 2, 5) +relativedelta(months=+ 1,秒=-1)`给了我:**2012-03-04 23:59:59**​ (2认同)

Sat*_*ddy 22

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

from dateutil.relativedelta import relativedelta
last_date_of_month = datetime(mydate.year, mydate.month, 1) + relativedelta(months=1, days=-1)
Run Code Online (Sandbox Code Playgroud)

这个想法是为了获得一个月的第一天,并relativedelta提前1个月和1天回来,这样你就可以得到你想要的月份的最后一天.


小智 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
Run Code Online (Sandbox Code Playgroud)

并使用这样的功能:

>>> 
>>> 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)
Run Code Online (Sandbox Code Playgroud)

  • 太复杂了,打破了蟒蛇禅的第三条规则. (4认同)

Pie*_*ero 12

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

    from datetime import datetime
    import pandas as pd

    firstday_month = datetime(year, month, 1)
    lastday_month = firstday_month + pd.offsets.MonthEnd(1)
Run Code Online (Sandbox Code Playgroud)

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

    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)
Run Code Online (Sandbox Code Playgroud)


Col*_*son 11

from datetime import timedelta
(any_day.replace(day=1) + timedelta(days=32)).replace(day=1) - timedelta(days=1)
Run Code Online (Sandbox Code Playgroud)

  • 有用。any_day 是 Jan 31,我们将 day 替换为 1,因此 Jan 1,添加 32 天,我们得到 2 月 2 日,再次替换为 day=1,我们得到 Feb 1。减去一天,我们得到 Jan 31。我不明白问题是什么。你能得到哪一天? (3认同)
  • 这就是 bug 的组成部分;)尝试 1 月 31 日 (2认同)

Bla*_*ake 9

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

然后你可以通过以下方式获得本月的最后一天:

import arrow
arrow.utcnow().ceil('month').date()
Run Code Online (Sandbox Code Playgroud)

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


Jos*_*roz 9

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

from dateutil.relativedelta import relativedelta

def last_day_of_month(date):
    return date.replace(day=1) + relativedelta(months=1) - relativedelta(days=1)
Run Code Online (Sandbox Code Playgroud)

例子:

from datetime import date

print(last_day_of_month(date.today()))
>> 2021-09-30
Run Code Online (Sandbox Code Playgroud)


Vat*_*sal 8

>>> 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
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案!拉取开始和结束日期 start_date = datetime.today().replace(day=1).date() ; last_date = date.replace(day = calendar.monthrange(date.year, date.month)[1]).date() (2认同)

小智 8

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

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

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

首先是获得当月的天,我们使用的数量monthrange这布莱尔康拉德已经提到了他的解决方案:

calendar.monthrange(date.today().year, date.today().month)[1]
Run Code Online (Sandbox Code Playgroud)

二是让我们的帮助下做的最后日期本身代替

>>> date.today()
datetime.date(2017, 1, 3)
>>> date.today().replace(day=31)
datetime.date(2017, 1, 31)
Run Code Online (Sandbox Code Playgroud)

当我们将它们组合在一起时,我们得到了一个动态的解决方案.


jfs*_*jfs 7

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

>>> calendar.monthlen(2002, 1)
31
>>> calendar.monthlen(2008, 2)
29
>>> calendar.monthlen(2100, 2)
28
Run Code Online (Sandbox Code Playgroud)

它等效于记录的calendar.monthrange(year, month)[1]呼叫

  • 这似乎不适用于 Python 3.8.1: *AttributeError: module 'calendar' has no attribute 'monthlen'* (2认同)
  • @jeppoo1:是的,它在 [bpo-28292](https://github.com/python/cpython/commit/0c16f6b307f7607e29b98b8fbb99cbca28f91a48) 中被标记为私有——预计是一个未记录的函数。 (2认同)
  • 从 Python 3.8 开始,它是“calendar._monthlen”。 (2认同)

小智 7

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

from datetime import datetime
import calendar
days_in_month = calendar.monthrange(2020, 12)[1]
end_dt = datetime(2020, 12, days_in_month)
Run Code Online (Sandbox Code Playgroud)


Ste*_*ist 6

使用熊猫!

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
Run Code Online (Sandbox Code Playgroud)

  • 您还可以使用“pd.series.dt.is_month_end”[链接](https://pandas.pydata.org/pandas-docs/stable/ generated/pandas.Series.dt.is_month_end.html)来实现 (3认同)
  • Pandas datetime 对象有一个特定的方法:`now=datetime.datetime.now(); pd_now = pd.to_datetime(now); 打印(pd_now.days_in_month)` (2认同)

Ана*_*нин 5

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)
Run Code Online (Sandbox Code Playgroud)


kev*_*erg 5

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

datetime.date(year + int(month/12), month%12+1, 1)-datetime.timedelta(days=1)
Run Code Online (Sandbox Code Playgroud)

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


Jak*_*den 5

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


Tob*_*tty 5

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

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)
Run Code Online (Sandbox Code Playgroud)


Eug*_*ash 5

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

\n\n
import datetime\n\ndef last_day_of_month(d: datetime.date) -> datetime.date:\n    return (\n        datetime.date(d.year + d.month//12, d.month % 12 + 1, 1) -\n        datetime.timedelta(days=1)\n    )\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
import calendar, datetime\n\ndef last_day_of_month(d: datetime.date) -> datetime.date:\n    return d.replace(day=calendar.monthrange(d.year, d.month)[1])\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
In [14]: today = datetime.date.today()\n\nIn [15]: %timeit last_day_of_month_dt(today)\n918 ns \xc2\xb1 3.54 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 1000000 loops each)\n\nIn [16]: %timeit last_day_of_month_calendar(today)\n1.4 \xc2\xb5s \xc2\xb1 17.3 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 1000000 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n


小智 5

使用 dateutil.relativedelta

dt + dateutil.relativedelta.relativedelta(months=1, day=1, days=-1)
Run Code Online (Sandbox Code Playgroud)

months=1并将day=1移至dt下个月的第一个日期,然后days=-1将新日期移至上一个日期,即当月的最后一个日期。