如何在python中向datetime.date对象添加“3个月”?

时间:2012-03-07 00:54:58

标签: python sql postgresql

Python日期计算,你在哪里?

我有一个python应用程序需要每隔三个月绘制几年的日期。重要的是,日期每年恰好发生4次,并且日期尽可能在每年的同一天发生,并且日期尽可能在每月的同一天发生,并且日期为尽可能接近“3个月”(这是一个移动的目标,特别是在闰年)。不幸的是,datetime.timedelta不支持数月!

在python中有没有“标准”的方法来进行这个计算?

SQL方式?

如果最糟糕的情况发生,我会试着让我的应用程序询问PostgreSQL,它对日期计算有很好的内置支持,可以得到这样的答案:

# select ('2010-11-29'::date + interval '3 months')::date;
    date    
------------
 2011-02-28
(1 row)

7 个答案:

答案 0 :(得分:55)

如果您正在寻找确切的或更准确的"约会,你最好退房dateutil

快速举例:

>>> from dateutil.relativedelta import relativedelta
>>> import datetime
>>> TODAY = datetime.date.today()
>>> TODAY
datetime.date(2012, 3, 6)

现在向TODAY添加3个月,请注意它与当天完全匹配(请注意relativedelta(months=3)relativedelta(month=3)有不同的行为。请务必使用months实例!)。

>>> three_mon_rel = relativedelta(months=3)
>>> TODAY + three_mon_rel
datetime.date(2012, 6, 6)

它在整个一年中保持一致。当天每三个月一次(必须继续添加,因为某些原因乘以relativedelta并将其添加到datetime.date对象会引发TypeError):

>>> TODAY + three_mon_rel + three_mon_rel
datetime.date(2012, 9, 6)
>>> TODAY + three_mon_rel + three_mon_rel + three_mon_rel
datetime.date(2012, 12, 6)
>>> TODAY + three_mon_rel + three_mon_rel + three_mon_rel + three_mon_rel
datetime.date(2013, 3, 6)

mVChr建议的解决方案,虽然肯定"足够好",随着时间的推移稍微漂移:

>>> three_mon_timedelta = datetime.timedelta(days=3 * 365/12)
>>> TODAY + three_mon_timedelta
datetime.date(2012, 6, 5)

在一年的过程中,月份的日子不断下滑:

>>> TODAY + three_mon_timedelta * 2
datetime.date(2012, 9, 4)
>>> TODAY + three_mon_timedelta * 3
datetime.date(2012, 12, 4)
>>> TODAY + three_mon_timedelta * 4
datetime.date(2013, 3, 5)

答案 1 :(得分:7)

import datetime

some_date = datetime.date.today()
three_months = datetime.timedelta(3*365/12)
print (some_date + three_months).isoformat()
# => '2012-06-01'

然后将每个新年“正常化”到原始日期(除非2月29日)

答案 2 :(得分:1)

这是一个很好的解决方案 http://www.voidspace.org.uk/python/weblog/arch_d7_2012_02_25.shtml#e1235

编辑 啊标准方式对不起......

答案 3 :(得分:1)

使用Python标准库,即没有dateutil或其他人,并解决“2月31日”问题。问题:

import datetime
import calendar

def add_months(date, months):
    months_count = date.month + months

    # Calculate the year
    year = date.year + int(months_count / 12)

    # Calculate the month
    month = (months_count % 12)
    if month == 0:
        month = 12

    # Calculate the day
    day = date.day
    last_day_of_month = calendar.monthrange(year, month)[1]
    if day > last_day_of_month:
        day = last_day_of_month

    new_date = datetime.date(year, month, day)
    return new_date

测试:

>>>date = datetime.date(2018, 11, 30)

>>>print(date, add_months(date, 3))
(datetime.date(2018, 11, 30), datetime.date(2019, 2, 28))

>>>print(date, add_months(date, 14))
(datetime.date(2018, 12, 31), datetime.date(2020, 2, 29))

答案 4 :(得分:1)

我写的这个函数或许可以帮到你:

import datetime
import calendar

def add_months(date, months):

    # Determine the month and year of the new date
    month, year = (date + relativedelta(months=months)).month, (date + relativedelta(months=months)).year

    # Determine the day of the new date
    # If the day of the current date is at the end of the month,
    # the day of the new date should also be at the end of the month
    if(date.day == calendar.monthrange(date.year, date.month)[1]):
        day = calendar.monthrange(year, month)[1]
    else:
        day = date.day

    new_date = datetime.datetime(year, month, day)
    return new_date

它还支持添加负月份(即减去月份)。

以下是一些示例用法,说明如何根据您的规范获取 2021 年和 2022 年日期:

import datetime

a = datetime.datetime(2020, 1, 1)

# Initialse a list to hold the dates
dates = [0]*8

# Obtain the dates
for i in range(0, len(dates)):
    dates[i] = add_months(a, 3*i)

dates

答案 5 :(得分:0)

我没有足够的声誉来发表评论。因此,我将写一个解决方案来解决David Ragazzi发布的解决方案中的错误。

当您添加足够的月份以达到12月的日期时,就会发生错误。年份太多了。

例如,add_months(date.fromisoformat('2020-01-29'), 11)返回2021,而不是2020。我通过更改以year =开头的行来解决了这个问题。

    import datetime
    import calendar
    def add_months(dateInput, months):
        months_count = dateInput.month + months

        # Calculate the year
        year = dateInput.year + int((months_count-1) / 12)

        # Calculate the month
        month = (months_count % 12)
        if month == 0:
            month = 12

        # Calculate the day
        day = dateInput.day
        last_day_of_month = calendar.monthrange(year, month)[1]
        if day > last_day_of_month:
            day = last_day_of_month

        new_date = date(year, month, day)
        return new_date

答案 6 :(得分:0)

@ david-ragazzi和@ mikedugas77的答案在months为正值的情况下效果很好,但如果months <= -date.month为正值,则效果很好。这是一个修改,即使在负的月份偏移量下也应该起作用:

import calendar
import datetime

def add_months(date, months):
    months_count = date.year * 12 + date.month + months - 1

    # Calculate the year
    year = months_count // 12

    # Calculate the month
    month = months_count % 12 + 1

    # Calculate the day
    day = date.day
    last_day_of_month = calendar.monthrange(year, month)[1]
    if day > last_day_of_month:
        day = last_day_of_month

    new_date = datetime.date(year, month, day)
    return new_date

使用今天的日期进行一些测试:

date = datetime.date(2020, 7, 30)
assert add_months(date, -12) == datetime.date(2019, 7, 30)
assert add_months(date, -11) == datetime.date(2019, 8, 30)
assert add_months(date, -10) == datetime.date(2019, 9, 30)
assert add_months(date, -9) == datetime.date(2019, 10, 30)
assert add_months(date, -8) == datetime.date(2019, 11, 30)
assert add_months(date, -7) == datetime.date(2019, 12, 30)
assert add_months(date, -6) == datetime.date(2020, 1, 30)
assert add_months(date, -5) == datetime.date(2020, 2, 29)
assert add_months(date, -4) == datetime.date(2020, 3, 30)
assert add_months(date, -3) == datetime.date(2020, 4, 30)
assert add_months(date, -2) == datetime.date(2020, 5, 30)
assert add_months(date, -1) == datetime.date(2020, 6, 30)
assert add_months(date, 0) == datetime.date(2020, 7, 30)
assert add_months(date, 1) == datetime.date(2020, 8, 30)
assert add_months(date, 2) == datetime.date(2020, 9, 30)
assert add_months(date, 3) == datetime.date(2020, 10, 30)
assert add_months(date, 4) == datetime.date(2020, 11, 30)
assert add_months(date, 5) == datetime.date(2020, 12, 30)
assert add_months(date, 6) == datetime.date(2021, 1, 30)
assert add_months(date, 7) == datetime.date(2021, 2, 28)
assert add_months(date, 8) == datetime.date(2021, 3, 30)
assert add_months(date, 9) == datetime.date(2021, 4, 30)
assert add_months(date, 10) == datetime.date(2021, 5, 30)
assert add_months(date, 11) == datetime.date(2021, 6, 30)
assert add_months(date, 12) == datetime.date(2021, 7, 30)