返回上个月的 datetime 对象

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

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

191826 次浏览

如果 timedelta 有一个月的争论就好了 在它的构造函数中 最简单的方法吗?

当你从一个日期中减去一个月,比如说3月30日,你希望结果是什么?这就是月份加减的问题: 月份有不同的长度!在某些应用程序中,在这种情况下异常是合适的,在另一些应用程序中,“上个月的最后一天”是可以使用的(但这是真正疯狂的算术,当减去一个月然后加上一个月时,没有总体上是不可操作的!)在另外一些情况下,你会想要在日期之外保留一些关于这个事实的指示,例如,“我说的是2月28日,但是如果2月30日存在的话,我真的希望它存在”,这样再加上或者减去一个月就可以把事情重新设置好(后者显然需要一个自定义类来保存数据加上 s/thing else)。

对于所有应用程序来说,没有一个真正的解决方案是可以容忍的,而且您还没有告诉我们您的特定应用程序对于这个糟糕操作的语义需求是什么,所以我们在这里不能提供更多的帮助。

试试这个:

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%100==0 or 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])

这里有一些 密码就可以做到这一点。我自己还没有尝试过..。

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

如果给定一个(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)

假设每年有12个月。

在将原始问题编辑为“前一个月的任何日期时间对象”之后,您可以很容易地从每个月的第一天减去1天。

from datetime import datetime, timedelta


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

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

邓肯的回答的一个变体(我没有足够的声誉来评论) ,它使用 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 中获取每月的最后一天的关于 Monthrange 的信息

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)

我使用以下代码从一个特定的日期追溯到 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

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

I)计算月份: 这一步将给出 start _ date 作为30/06/2015。
注意 ,在执行计算月步骤之后,您将获得所需月份的最后一天。

II)计算日期: 条件 如果(start _ date. day > your _ date. day)检查 input _ date 的日期是否出现在所需的月份中。这将处理输入日期为31(或30)且所需月份的天数小于31(或在2月份为30)的情况。它也处理闰年案件(二月)。在此步骤之后,您将得到28/06/2015的结果

如果不满足此条件,start _ date 将保留上个月的最后一天。因此,如果你把2015年12月31日作为输入日期,并想要6个月前的日期,它会给你30/06/2015

如果你想要的只是上个月的某一天,最简单的方法就是从当前日期中减去天数,这样就得到了上个月的最后一天。

例如,从任何日期开始:

>>> 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天的月份或者2月份缺失的日子里,你需要注意31日(闰年) ,但是这也很容易做到:

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

您可以使用下面给定的函数来获取 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)

我认为这个答案很容易理解:

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

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

我使用这个为政府财政年度在第四季度开始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)

一个向量化的熊猫解决方案非常简单:

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

你可以像这样用两句话来表达:

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

别忘了进口货

from datetime import datetime

我认为最简单的方法就是像这样使用来自熊猫的 DateOffset:

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

结果将是一个 Timestamp 对象

一个班轮?

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

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

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)

一段时间以前,我偶然发现了下面的算法,它可以很好地在 datedatetime上递增和递减月份。

警告 : 如果 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)

返回上月最后一天:

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

对于大多数用例来说,关于

from datetime import date


current_date =date.today()
current_month = current_date.month
last_month = current_month - 1 if current_month != 1 else 12
today_a_month_ago = date(current_date.year, last_month, current_date.day)

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

注意: 我已经添加了倒数第二行,如果按照@Nick 的注释,当前月份是1月份,那么它就可以工作

注2: @OneCricketeer 指出,在大多数情况下,如果当前日期是给定月份的31日,那么结果将是无效日期,因为前一个月不会有31天(7月和8月除外)