我有一个python应用程序需要每隔三个月绘制几年的日期。重要的是,日期每年恰好发生4次,并且日期尽可能在每年的同一天发生,并且日期尽可能在每月的同一天发生,并且日期为尽可能接近“3个月”(这是一个移动的目标,特别是在闰年)。不幸的是,datetime.timedelta
不支持数月!
在python中有没有“标准”的方法来进行这个计算?
如果最糟糕的情况发生,我会试着让我的应用程序询问PostgreSQL,它对日期计算有很好的内置支持,可以得到这样的答案:
# select ('2010-11-29'::date + interval '3 months')::date;
date
------------
2011-02-28
(1 row)
答案 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)