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


99

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

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

Answers:


62

试试这个:

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

如果我拥有正确的((date.month+delta-1) % 12)+1作品,并且意味着if not m: m = 12可以将其取下
查尔斯·

您可以详细说明@ dan-klasson吗?
让·弗朗索瓦·法布尔

@Jean-FrançoisFabre第一个并不简单。同样,可以使用datetime和timedelta轻松地做到这一点。
dan-klasson

是的,在编辑了OP之后,很明显,他们可以通过dt - timedelta(days=dt.day)按最初的措辞来完成所需的操作,尽管听起来像他们希望能够减去任意几个月的时间,但这有点困难。
邓肯

183

您可以使用第三方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

输出:

2013-02-28 00:00:00

我对参数monthmonthsdateutil 感到困惑。用参数减法month不起作用,但months有效 In [23]: created_datetime__lt - relativedelta(months=1) Out[23]: datetime.datetime(2016, 11, 29, 0, 0, tzinfo=<StaticTzInfo 'Etc/GMT-8'>) In [24]: created_datetime__lt - relativedelta(month=1) Out[24]: datetime.datetime(2016, 1, 29, 0, 0, tzinfo=<StaticTzInfo 'Etc/GMT-8'>)
Simin Jie

1
@SiminJie:我认为使用months将相差1个月。使用month会将月份设置为1(例如1月),而不考虑输入的月份。
Frank Meulenaar

70

将原始问题编辑为“上个月的任何日期时间对象”后,您可以轻松地做到这一点,只需从该月的第一天减去1天即可。

from datetime import datetime, timedelta

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

4
您可以使用dt.replace(day=1) - timedelta(1)
jfs 2014年

3
dt - timedelta(days=dt.day)
SergiyKolesnikov

23

邓肯答案的一种变体(我没有足够的声誉来发表评论),它使用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)

有关使用Python 获取本月最后一天的月份范围的信息



17

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

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

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


你是绝对正确的。只是在开玩笑说timedelta有争论。我想知道相同的逻辑是否适用于数小时或数分钟不带参数。从技术上讲,一天并非完全是24小时。
Bialecki 2010年

PHP 通过在2月28日之前增加2天来处理此问题,直到3月2日为止。有一些约定,让我快速找到它们
NullUserException 2010年

如果您要计算货币兑换的日期,它会变得更加有趣:例如,您可能需要在未来3或6个月找到一个日期,但是在两个国家/地区也都必须是一个工作日(在穆斯林国家中,不是星期五)而不是两个国家的银行假期。
邓肯2010年

@Bialecki:datetime模块假设一天正好是24小时(没有leap秒)
jfs

8

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

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

结果将是日期时间对象


2
那是最直观的解决方案
Kots

5

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

例如,从任何日期开始:

>>> 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月之后缺少的日期(并注意leap年),但这也很容易做到:

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

1

我将其用于第4季度从10月1日开始的政府财政年度。请注意,我将日期转换为季度,也将其撤消。

import pandas as pd

df['Date'] = '1/1/2020'
df['Date'] = pd.to_datetime(df['Date'])              #returns 2020-01-01
df['NewDate'] = df.Date - pd.DateOffset(months=3)    #returns 2019-10-01 <---- answer

# For fun, change it to FY Quarter '2019Q4'
df['NewDate'] = df['NewDate'].dt.year.astype(str) + 'Q' + df['NewDate'].dt.quarter.astype(str)

# Convert '2019Q4' back to 2019-10-01
df['NewDate'] = pd.to_datetime(df.NewDate)

0

这是一些执行此操作的代码。我自己还没有尝试过...

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

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个月。


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)

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)计算月份:此步骤会将开始日期定为2015年6月30日。
请注意,在“计算月份”步骤之后,您将获得所需月份的最后一天。

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

如果不满足此条件,则start_date保留上个月的最后日期。因此,如果您输入2015年12月31日为输入日期,并希望前6个月为输入日期,则会给您2015年6月30日


0

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

从日期时间导入日期开始

def next_month(给定日期,月份):
    yyyy = int((((给定日期。年* 12 +给定日期。月)+月)/ 12)
    mm = int((((给定日期。年* 12 +给定日期。月)+月)%12)

    如果mm == 0:
        yyyy-= 1
        毫米= 12

    返回给定的日期。替换(年= yyyy,月=毫米)


如果__name__ ==“ __main__”:
    今天= date.today()
    打印(今天)

    对于[-12,-1,0,1,2,12,20]中的mm:
        next_date = next_month(今天,毫米)
        打印(下一个日期)

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

0

一个班轮?

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


0

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

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)

0

前段时间,我遇到了以下算法,该算法在a date或上增加和减少月份时效果很好datetime

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)

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

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


2
日期大约是3月31日,将无法使用。
火播

1
或像1月1日
LeandroHumb

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.