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

时间:2010-08-06 14:48:50

标签: python

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

编辑:我对此并没有太多考虑,如下所述。真的我想要的是上个月的任何一天,因为最终我只会抓住一年和一个月。那么给定一个datetime对象,返回上个月的任何日期时间对象的最简单方法是什么?

22 个答案:

答案 0 :(得分:137)

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

import datetime
import dateutil.relativedelta

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

输出:

2013-02-28 00:00:00

答案 1 :(得分:42)

试试这个:

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)

修改已更正以处理当天。

修改另请参阅拼图中的答案,该答案指出d的更简单计算:

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

答案 2 :(得分:40)

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

from datetime import datetime, timedelta

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

答案 3 :(得分:21)

Duncan's answer的变体(我没有足够的评论声誉),它使用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)

来自Get Last Day of the Month in Python

的月份信息

答案 4 :(得分:14)

  

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

当您从3月30日的某个日期减去一个月时,您希望结果是什么?这是增加或减少月份的问题:月份有不同的长度!在某些应用程序中,异常在这种情况下是合适的,在其他情况下“上个月的最后一天”可以使用(但这是真正疯狂的算术,当减去一个月然后添加一个月总的来说,没有操作!),在其他情况下,除了日期之外你还要保留一些关于事实的迹象,例如,“我说的是2月28日,但如果它存在,我真的想要2月30日”,所以添加或减去另一个月可以再次设置正确的东西(后者显然需要一个自定义类来保存数据加上其他东西)。

对于所有应用程序都没有可以容忍的真正解决方案,并且您没有告诉我们您的特定应用程序对于这个可怜操作的语义的需求,因此我们在这里提供的帮助不多。

答案 5 :(得分:11)

我认为简单的方法是像这样使用Pandas的DateOffset:

import pandas as pd
date_1 = pd.to_datetime("2013-03-31", format="%Y-%m-%d") - pd.DateOffset(months=1)

结果将是日期时间对象

答案 6 :(得分:2)

那:

from datetime import date

current_date =date.today()
current_month = current_date.month
today_a_month_ago = date(current_date.year, current_month - 1, current_date.day)

对我来说,这似乎是最简单的

答案 7 :(得分:2)

返回上个月的最后一天:

>>> import datetime
>>> datetime.datetime.now() - datetime.timedelta(days=datetime.datetime.now().day)
datetime.datetime(2020, 9, 30, 14, 13, 15, 67582)

返回上个月的同一天:

>>> x = datetime.datetime.now() - datetime.timedelta(days=datetime.datetime.now().day)
>>> x.replace(day=datetime.datetime.now().day)
datetime.datetime(2020, 9, 7, 14, 22, 14, 362421)

答案 8 :(得分:2)

矢量化的熊猫解决方案非常简单:

df['date'] - pd.DateOffset(months=1)

答案 9 :(得分:2)

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

例如,从任何日期开始:

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

减去当前日期的日期:

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

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

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

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

当然,你需要注意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)

答案 10 :(得分:1)

一个班轮?

previous_month_date = (current_date - datetime.timedelta(days=current_date.day+1)).replace(day=current_date.day)

答案 11 :(得分:1)

我刚刚尝试过的最简单方法

from datetime import datetime
from django.utils import timezone





current = timezone.now()
if current.month == 1:
     month = 12
else:
     month = current.month - 1
current = datetime(current.year, month, current.day)

答案 12 :(得分:0)

您可以使用以下给定的功能获取X月之前/之后的日期。

from datetime import date

def next_month(given_date, month):
    yyyy = int(((given_date.year * 12 + given_date.month) + month)/12)
    mm = int(((given_date.year * 12 + given_date.month) + month)%12)

    if mm == 0:
        yyyy -= 1
        mm = 12

    return given_date.replace(year=yyyy, month=mm)


if __name__ == "__main__":
    today = date.today()
    print(today)

    for mm in [-12, -1, 0, 1, 2, 12, 20 ]:
        next_date = next_month(today, mm)
        print(next_date)

答案 13 :(得分:0)

我认为这个答案非常易读:

def month_delta(dt, delta):
    year_delta, month = divmod(dt.month + delta, 12)

    if month == 0:
        # convert a 0 to december
        month = 12
        if delta < 0:
            # if moving backwards, then it's december of last year
            year_delta -= 1

    year = dt.year + year_delta

    return dt.replace(month=month, year=year)

for delta in range(-20, 21):
    print(delta, "->", month_delta(datetime(2011, 1, 1), delta))

-20 -> 2009-05-01 00:00:00
-19 -> 2009-06-01 00:00:00
-18 -> 2009-07-01 00:00:00
-17 -> 2009-08-01 00:00:00
-16 -> 2009-09-01 00:00:00
-15 -> 2009-10-01 00:00:00
-14 -> 2009-11-01 00:00:00
-13 -> 2009-12-01 00:00:00
-12 -> 2010-01-01 00:00:00
-11 -> 2010-02-01 00:00:00
-10 -> 2010-03-01 00:00:00
-9 -> 2010-04-01 00:00:00
-8 -> 2010-05-01 00:00:00
-7 -> 2010-06-01 00:00:00
-6 -> 2010-07-01 00:00:00
-5 -> 2010-08-01 00:00:00
-4 -> 2010-09-01 00:00:00
-3 -> 2010-10-01 00:00:00
-2 -> 2010-11-01 00:00:00
-1 -> 2010-12-01 00:00:00
0 -> 2011-01-01 00:00:00
1 -> 2011-02-01 00:00:00
2 -> 2011-03-01 00:00:00
3 -> 2011-04-01 00:00:00
4 -> 2011-05-01 00:00:00
5 -> 2011-06-01 00:00:00
6 -> 2011-07-01 00:00:00
7 -> 2011-08-01 00:00:00
8 -> 2011-09-01 00:00:00
9 -> 2011-10-01 00:00:00
10 -> 2011-11-01 00:00:00
11 -> 2012-12-01 00:00:00
12 -> 2012-01-01 00:00:00
13 -> 2012-02-01 00:00:00
14 -> 2012-03-01 00:00:00
15 -> 2012-04-01 00:00:00
16 -> 2012-05-01 00:00:00
17 -> 2012-06-01 00:00:00
18 -> 2012-07-01 00:00:00
19 -> 2012-08-01 00:00:00
20 -> 2012-09-01 00:00:00

答案 14 :(得分:0)

我将其用于第四季度从10月1日开始的政府财政年度。注意,我将日期转换为四分之一,然后也将其撤消。

orders.within_last_week

答案 15 :(得分:0)

我使用以下代码返回特定日期的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

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

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

II)计算日: 条件 if(start_date.day&gt; your_date.day)检查input_date中的日期是否出现在所需的月份中。这处理输入日期为31(或30)且所需月份少于31(或在少数情况下为30天)的条件。它也处理闰年案(2月)。完成此步骤后,您将获得28/06/2015的结果

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

答案 16 :(得分:0)

您可以像这样在两行中完成此操作:

now = datetime.now()
last_month = datetime(now.year, now.month - 1, now.day)

记住进口

from datetime import datetime

答案 17 :(得分:0)

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)

答案 18 :(得分:0)

前段时间,我遇到了以下算法,该算法对于在datedatetime上增加和减少月份的效果很好。

CAVEAT :如果在新的月份day不可用,此操作将失败。我在总是day == 1的日期对象上使用它。

Python 3.x:

def increment_month(d, add=1):
    return date(d.year+(d.month+add-1)//12, (d.month+add-1) % 12+1, 1)

对于Python 2.7,由于隐含整数除法,请将//12更改为/12

最近,当脚本开始获取这些有用的全局变量时,我在默认文件中使用了此功能:

MONTH_THIS = datetime.date.today()
MONTH_THIS = datetime.date(MONTH_THIS.year, MONTH_THIS.month, 1)

MONTH_1AGO = datetime.date(MONTH_THIS.year+(MONTH_THIS.month-2)//12,
                           (MONTH_THIS.month-2) % 12+1, 1)

MONTH_2AGO = datetime.date(MONTH_THIS.year+(MONTH_THIS.month-3)//12,
                           (MONTH_THIS.month-3) % 12+1, 1)

答案 19 :(得分:0)

给定一个(年,月)元组,月份从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)

假设每年有12个月。

答案 20 :(得分:0)

这里有一些code。我自己没试过......

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

答案 21 :(得分:-2)

import datetime
date_str = '08/01/2018'
format_str = '%d/%m/%Y'
datetime_obj = datetime.datetime.strptime(date_str, format_str)   
datetime_obj.replace(month=datetime_obj.month-1)

简单的解决方案,不需要特殊的库。