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

6za6bjd0  于 2023-04-20  发布在  PostgreSQL
关注(0)|答案(6)|浏览(120)

Python日期计算,你在哪里?

我有一个Python应用程序,需要在几年内每三个月绘制一次日期。重要的是,日期每年正好出现4次,而且日期尽可能出现在每年的同一天,日期尽可能出现在每月的同一天,日期之间的间隔尽可能接近“3个月”(这是一个移动的目标,特别是在闰年)。不幸的是,datetime.timedelta不支持月份!
在python中有没有一个“标准”的方法来做这个计算???

SQL方式?

如果最坏的情况发生,我会让我的应用程序询问PostgreSQL,它确实有很好的内置日期计算支持,答案如下:

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

pn9klfpd1#

如果你正在寻找确切或“更精确”的日期,你最好去看看dateutil
简单的例子:

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

现在将3个月添加到TODAY,观察它是否与日期完全匹配(注意,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)
lrpiutwd

lrpiutwd2#

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

vatpfxk5

vatpfxk53#

使用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))
zkure5ic

zkure5ic4#

来自@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)
mctunoxg

mctunoxg5#

我写了这个函数,也许可以帮助你:

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
5tmbdcev

5tmbdcev6#

我没有足够的声誉来发表评论。所以,我只是要写一个解决方案,修复一个错误的解决方案,大卫Ragazzi张贴。
当您添加足够的月份以获得12月日期时发生错误。年份太多了1。
例如,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

相关问题