在 Python 中从日期中减去一个月的最简单方法是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3424899/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 11:00:18  来源:igfitidea点击:

What's the simplest way to subtract a month from a date in Python?

python

提问by Bialecki

If only timedelta had a month argument in it's constructor. So what's the simplest way to do this?

如果只有 timedelta 在它的构造函数中有一个 month 参数。那么最简单的方法是什么?

EDIT:I wasn't thinking too hard about this as was pointed out below. Really what I wanted was any day in the last month because eventually I'm going to grab the year and month only. So given a datetime object, what's the simplest way to return any datetime object that falls in the previous month?

编辑:正如下面所指出的,我并没有想太多。我真正想要的是上个月的任何一天,因为最终我将只获取年份和月份。那么给定一个 datetime 对象,返回任何属于上个月的 datetime 对象的最简单方法是什么

采纳答案by Duncan

Try this:

尝试这个:

def monthdelta(date, delta):
    m, y = (date.month+delta) % 12, date.year + ((date.month)+delta-1) // 12
    if not m: m = 12
    d = min(date.day, [31,
        29 if y%4==0 and not y%400==0 else 28,31,30,31,30,31,31,30,31,30,31][m-1])
    return date.replace(day=d,month=m, year=y)

>>> for m in range(-12, 12):
    print(monthdelta(datetime.now(), m))


2009-08-06 16:12:27.823000
2009-09-06 16:12:27.855000
2009-10-06 16:12:27.870000
2009-11-06 16:12:27.870000
2009-12-06 16:12:27.870000
2010-01-06 16:12:27.870000
2010-02-06 16:12:27.870000
2010-03-06 16:12:27.886000
2010-04-06 16:12:27.886000
2010-05-06 16:12:27.886000
2010-06-06 16:12:27.886000
2010-07-06 16:12:27.886000
2010-08-06 16:12:27.901000
2010-09-06 16:12:27.901000
2010-10-06 16:12:27.901000
2010-11-06 16:12:27.901000
2010-12-06 16:12:27.901000
2011-01-06 16:12:27.917000
2011-02-06 16:12:27.917000
2011-03-06 16:12:27.917000
2011-04-06 16:12:27.917000
2011-05-06 16:12:27.917000
2011-06-06 16:12:27.933000
2011-07-06 16:12:27.933000
>>> monthdelta(datetime(2010,3,30), -1)
datetime.datetime(2010, 2, 28, 0, 0)
>>> monthdelta(datetime(2008,3,30), -1)
datetime.datetime(2008, 2, 29, 0, 0)

EditCorrected to handle the day as well.

编辑更正以处理这一天。

EditSee also the answer from puzzlement which points out a simpler calculation for d:

编辑另见困惑的答案,它指出了一个更简单的计算d

d = min(date.day, calendar.monthrange(y, m)[1])

回答by Alex Martelli

If only timedelta had a month argument in it's constructor. So what's the simplest way to do this?

如果只有 timedelta 在它的构造函数中有一个 month 参数。那么最简单的方法是什么?

What do you want the result to be when you subtract a month from, say, a date that is March 30? That is the problem with adding or subtracting months: months have different lengths! In some application an exception is appropriate in such cases, in others "the last day of the previous month" is OK to use (but that's truly crazy arithmetic, when subtracting a month then adding a month is notoverall a no-operation!), in others yet you'll want to keep in addition to the date some indication about the fact, e.g., "I'm saying Feb 28 but I really would want Feb 30 if it existed", so that adding or subtracting another month to that can set things right again (and the latter obviously requires a custom class holding a data plus s/thing else).

当您从 3 月 30 日的日期减去一个月时,您希望结果是什么?这就是加减月份的问题:月份有不同的长度!在一些应用程序中的例外是在这种情况下适当的,在别人“前一个月的最后一天”是OK的使用(但是这是真正疯狂的算术,减去一个月的时候再加入一个月是不是整体无操作!) ,在其他情况下,除了日期之外,您还想保留一些有关事实的指示,例如,“我说的是 2 月 28 日,但如果存在,我真的希望 2 月 30 日”,以便添加或减去另一个月份这可以再次设置正确(后者显然需要一个自定义类来保存数据和其他东西)。

There can be no real solution that is tolerable for all applications, and you have not told us what your specific app's needs are for the semantics of this wretched operation, so there's not much more help that we can provide here.

不可能有真正的解决方案可以适用于所有应用程序,并且您没有告诉我们您的特定应用程序对这种糟糕操作的语义有什么需求,因此我们在这里无法提供更多帮助。

回答by sajal

Here is some codeto do just that. Haven't tried it out myself...

这里有一些代码可以做到这一点。自己没试过...

def add_one_month(t):
    """Return a `datetime.date` or `datetime.datetime` (as given) that is
    one month earlier.

    Note that the resultant day of the month might change if the following
    month has fewer days:

        >>> add_one_month(datetime.date(2010, 1, 31))
        datetime.date(2010, 2, 28)
    """
    import datetime
    one_day = datetime.timedelta(days=1)
    one_month_later = t + one_day
    while one_month_later.month == t.month:  # advance to start of next month
        one_month_later += one_day
    target_month = one_month_later.month
    while one_month_later.day < t.day:  # advance to appropriate day
        one_month_later += one_day
        if one_month_later.month != target_month:  # gone too far
            one_month_later -= one_day
            break
    return one_month_later

def subtract_one_month(t):
    """Return a `datetime.date` or `datetime.datetime` (as given) that is
    one month later.

    Note that the resultant day of the month might change if the following
    month has fewer days:

        >>> subtract_one_month(datetime.date(2010, 3, 31))
        datetime.date(2010, 2, 28)
    """
    import datetime
    one_day = datetime.timedelta(days=1)
    one_month_earlier = t - one_day
    while one_month_earlier.month == t.month or one_month_earlier.day > t.day:
        one_month_earlier -= one_day
    return one_month_earlier

回答by PaulMcG

Given a (year,month) tuple, where month goes from 1-12, try this:

给定一个 (year,month) 元组,其中月份从 1 到 12,试试这个:

>>> from datetime import datetime
>>> today = datetime.today()
>>> today
datetime.datetime(2010, 8, 6, 10, 15, 21, 310000)
>>> thismonth = today.year, today.month
>>> thismonth
(2010, 8)
>>> lastmonth = lambda (yr,mo): [(y,m+1) for y,m in (divmod((yr*12+mo-2), 12),)][0]
>>> lastmonth(thismonth)
(2010, 7)
>>> lastmonth( (2010,1) )
(2009, 12)

Assumes there are 12 months in every year.

假设每年有 12 个月。

回答by Cory

After the original question's edit to "any datetime object in the previous month", you can do it pretty easily by subtracting 1 day from the first of the month.

在将原始问题编辑为“上个月的任何日期时间对象”之后,您可以通过从该月的第一天减去 1 天来轻松完成。

from datetime import datetime, timedelta

def a_day_in_previous_month(dt):
   return dt.replace(day=1) - timedelta(days=1)

回答by amoe

You can use the third party dateutilmodule (PyPI entry here).

您可以使用第三方dateutil模块(此处为PyPI 条目)。

import datetime
import dateutil.relativedelta

d = datetime.datetime.strptime("2013-03-31", "%Y-%m-%d")
d2 = d - dateutil.relativedelta.relativedelta(months=1)
print d2

output:

输出:

2013-02-28 00:00:00

回答by puzzlement

A variation on Duncan's answer(I don't have sufficient reputation to comment), which uses calendar.monthrange to dramatically simplify the computation of the last day of the month:

Duncan 答案的变体(我没有足够的声誉来评论),它使用 calendar.monthrange 显着简化了该月最后一天的计算:

import calendar
def monthdelta(date, delta):
    m, y = (date.month+delta) % 12, date.year + ((date.month)+delta-1) // 12
    if not m: m = 12
    d = min(date.day, calendar.monthrange(y, m)[1])
    return date.replace(day=d,month=m, year=y)

Info on monthrange from Get Last Day of the Month in Python

来自Get Last Day of the Month in Python 的关于 monthrange 的信息

回答by damn_c

def month_sub(year, month, sub_month):
    result_month = 0
    result_year = 0
    if month > (sub_month % 12):
        result_month = month - (sub_month % 12)
        result_year = year - (sub_month / 12)
    else:
        result_month = 12 - (sub_month % 12) + month
        result_year = year - (sub_month / 12 + 1)
    return (result_year, result_month)

>>> month_sub(2015, 7, 1)    
(2015, 6)
>>> month_sub(2015, 7, -1)
(2015, 8)
>>> month_sub(2015, 7, 13)
(2014, 6)
>>> month_sub(2015, 7, -14)
(2016, 9)

回答by ashay93k

I Used the following code to go back n Months from a specific Date:

我使用以下代码从特定日期返回 n 个月:

your_date =  datetime.strptime(input_date, "%Y-%m-%d")  #to convert date(2016-01-01) to timestamp
start_date=your_date    #start from current date

#Calculate Month
for i in range(0,n):    #n = number of months you need to go back
    start_date=start_date.replace(day=1)    #1st day of current month
    start_date=start_date-timedelta(days=1) #last day of previous month

#Calculate Day
if(start_date.day>your_date.day):   
    start_date=start_date.replace(day=your_date.day)            

print start_date

For eg: input date = 28/12/2015 Calculate 6 months previous date.

例如:输入日期 = 28/12/2015 计算 6 个月前的日期。

I) CALCULATE MONTH: This step will give you the start_date as 30/06/2015.
Notethat after the calculate month step you will get the last day of the required month.

I) 计算月份:此步骤将为您提供 start_date 为 30/06/2015。
请注意,在计算月份步骤之后,您将获得所需月份的最后一天。

II)CALCULATE DAY: Condition if(start_date.day>your_date.day)checks whether the day from input_date is present in the required month. This handles condition where input date is 31(or 30) and the required month has less than 31(or 30 in case of feb) days. It handles leap year case as well(For Feb). After this step you will get result as 28/06/2015

II)CALCULATE DAY:条件if(start_date.day>your_date.day)检查 input_date 中的日期是否存在于所需的月份中。这将处理输入日期为 31(或 30)并且所需月份少于 31(或 30,如果是二月)天的情况。它也处理闰年情况(二月)。完成此步骤后,您将获得 28/06/2015 的结果

If this condition is not satisfied, the start_date remains the last date of the previous month. So if you give 31/12/2015 as input date and want 6 months previous date, it will give you 30/06/2015

如果不满足此条件,则 start_date 仍为上个月的最后一个日期。因此,如果您将 31/12/2015 作为输入日期并想要 6 个月前的日期,它将为您提供 30/06/2015

回答by LeoRochael

If all you want is any day in the last month, the simplest thing you can do is subtract the number of days from the current date, which will give you the last day of the previous month.

如果您只想要上个月的任何一天,那么您可以做的最简单的事情是从当前日期中减去天数,这将为您提供上个月的最后一天。

For instance, starting with any date:

例如,从任何日期开始:

>>> import datetime                                                                                                                                                                 
>>> today = datetime.date.today()                                                                                                                                                   
>>> today
datetime.date(2016, 5, 24)

Subtracting the days of the current date we get:

减去当前日期的天数,我们得到:

>>> last_day_previous_month = today - datetime.timedelta(days=today.day)
>>> last_day_previous_month
datetime.date(2016, 4, 30)

This is enough for your simplified need of any day in the last month.

这足以满足您对上个月任何一天的简化需求。

But now that you have it, you can also get any day in the month, including the same day you started with (i.e. more or less the same as subtracting a month):

但是现在你有了它,你也可以得到一个月中的任何一天,包括你开始的同一天(即或多或少与减去一个月相同):

>>> same_day_last_month = last_day_previous_month.replace(day=today.day)
>>> same_day_last_month
datetime.date(2016, 4, 24)

Of course, you need to be careful with 31st on a 30 day month or the days missing from February (and take care of leap years), but that's also easy to do:

当然,您需要小心处理 30 天月份的 31 日或 2 月份缺少的日子(并注意闰年),但这也很容易做到:

>>> a_date = datetime.date(2016, 3, 31)                                                                                                                                             
>>> last_day_previous_month = a_date - datetime.timedelta(days=a_date.day)
>>> a_date_minus_month = (
...     last_day_previous_month.replace(day=a_date.day)
...     if a_date.day < last_day_previous_month.day
...     else last_day_previous_month
... )
>>> a_date_minus_month
datetime.date(2016, 2, 29)