我正在使用datetime Python模块。我希望从当前日期计算6个月的日期。有人能帮我一下吗?

我想从当前日期生成一个6个月后的日期的原因是为了生成一个回顾日期。如果用户在系统中输入数据,系统将有从输入数据之日起6个月的审查日期。


使用python datetime模块为datetime.today()添加6个月的时间增量。

http://docs.python.org/library/datetime.html

你当然要解决Johannes weß提出的问题——你说的6个月是什么意思?


你说6个月是什么意思?

2009-02-13 + 6个月== 2009-08-13?还是2009-02-13 + 6*30天?

import mx.DateTime as dt

#6 Months
dt.now()+dt.RelativeDateTime(months=6)
#result is '2009-08-13 16:28:00.84'

#6*30 days
dt.now()+dt.RelativeDateTime(days=30*6)
#result is '2009-08-12 16:30:03.35'

更多关于mx的信息。DateTime


只需使用timetuple方法提取月份,添加您的月份并构建一个新的dateobject。如果有一个已经存在的方法,我不知道它。

import datetime

def in_the_future(months=1):
    year, month, day = datetime.date.today().timetuple()[:3]
    new_month = month + months
    return datetime.date(year + (new_month / 12), (new_month % 12) or 12, day)

这个API有点笨拙,但可以作为示例使用。这显然也不适用于像2008-01-31 + 1个月这样的极端情况。:)


Dateutil包实现了这样的功能。但要知道,这将是天真的,因为其他人已经指出。


Python的datetime没有直接的方法来做到这一点。

在python-dateutil中查看relativedelta类型。它允许您指定以月为单位的时间增量。


嗯,这取决于你说的6个月后的日期。

使用自然月份: Inc = 6 年=年+(月+ inc - 1) // 12 月份=(月份+ inc - 1) % 12 + 1 用银行家的定义,6*30: 日期+=日期时间。Timedelta (6 * 30)


PyQt4的QDate类有一个addmonths函数。

>>>from PyQt4.QtCore import QDate  
>>>dt = QDate(2009,12,31)  
>>>required = dt.addMonths(6) 

>>>required
PyQt4.QtCore.QDate(2010, 6, 30)

>>>required.toPyDate()
datetime.date(2010, 6, 30)

修改了Johannes Wei在1new_month = 121情况下的答案。这对我来说非常合适。月份可以是正的,也可以是负的。

def addMonth(d,months=1):
    year, month, day = d.timetuple()[:3]
    new_month = month + months
    return datetime.date(year + ((new_month-1) / 12), (new_month-1) % 12 +1, day)

这是我想到的。它移动了正确的月份和年份,但忽略了天数(这是我的情况所需要的)。

import datetime

month_dt = 4
today = datetime.date.today()
y,m = today.year, today.month
m += month_dt-1
year_dt = m//12
new_month = m%12
new_date = datetime.date(y+year_dt, new_month+1, 1)

我使用这个函数来更改年和月,但保留日:

def replace_month_year(date1, year2, month2):
    try:
        date2 = date1.replace(month = month2, year = year2)
    except:
        date2 = datetime.date(year2, month2 + 1, 1) - datetime.timedelta(days=1)
    return date2

你应该这样写:

new_year = my_date.year + (my_date.month + 6) / 12
new_month = (my_date.month + 6) % 12
new_date = replace_month_year(my_date, new_year, new_month)

这里有一个dateutil的例子。relativedelta,我发现它对于迭代过去一年很有用,每次跳过一个月到现在的日期:

>>> import datetime
>>> from dateutil.relativedelta import relativedelta
>>> today = datetime.datetime.today()
>>> month_count = 0
>>> while month_count < 12:
...  day = today - relativedelta(months=month_count)
...  print day
...  month_count += 1
... 
2010-07-07 10:51:45.187968
2010-06-07 10:51:45.187968
2010-05-07 10:51:45.187968
2010-04-07 10:51:45.187968
2010-03-07 10:51:45.187968
2010-02-07 10:51:45.187968
2010-01-07 10:51:45.187968
2009-12-07 10:51:45.187968
2009-11-07 10:51:45.187968
2009-10-07 10:51:45.187968
2009-09-07 10:51:45.187968
2009-08-07 10:51:45.187968

和其他答案一样,你必须弄清楚你说的“6个月后”到底是什么意思。如果你的意思是“六年后某个月的今天”,那么这个是:

datetime.datetime.now() + relativedelta(months=6)

这个解决方案适用于12月,而本页上的大多数答案都不适用。 在使用模量(%)或整数除法(//)之前,您需要首先将月份从基于1的索引(例如Jan = 1)转移到基于0的索引(例如Jan = 0),否则11月(11)加上1个月得到12,当找到余数(12% 12)时,得到0。

(不要建议“(月% 12)+ 1”或10月+ 1 = 12月!)

def AddMonths(d,x):
    newmonth = ((( d.month - 1) + x ) % 12 ) + 1
    newyear  = int(d.year + ((( d.month - 1) + x ) / 12 ))
    return datetime.date( newyear, newmonth, d.day)

然而……这并不能解释1月31日+一个月的问题。所以我们回到OP,你说增加一个月是什么意思?一种解决方案是回溯,直到找到一个有效的日期,因为大多数人会假设一月的最后一天加一个月等于二月的最后一天。 这也适用于负数的月份。 证明:

>>> import datetime
>>> AddMonths(datetime.datetime(2010,8,25),1)
datetime.date(2010, 9, 25)
>>> AddMonths(datetime.datetime(2010,8,25),4)
datetime.date(2010, 12, 25)
>>> AddMonths(datetime.datetime(2010,8,25),5)
datetime.date(2011, 1, 25)
>>> AddMonths(datetime.datetime(2010,8,25),13)
datetime.date(2011, 9, 25)
>>> AddMonths(datetime.datetime(2010,8,25),24)
datetime.date(2012, 8, 25)
>>> AddMonths(datetime.datetime(2010,8,25),-1)
datetime.date(2010, 7, 25)
>>> AddMonths(datetime.datetime(2010,8,25),0)
datetime.date(2010, 8, 25)
>>> AddMonths(datetime.datetime(2010,8,25),-12)
datetime.date(2009, 8, 25)
>>> AddMonths(datetime.datetime(2010,8,25),-8)
datetime.date(2009, 12, 25)
>>> AddMonths(datetime.datetime(2010,8,25),-7)
datetime.date(2010, 1, 25)>>> 

我发现这个解决方法很好。(使用python-dateutil扩展名)

from datetime import date
from dateutil.relativedelta import relativedelta

six_months = date.today() + relativedelta(months=+6)

这种方法的优势在于,它可以处理28天、30天、31天的问题。这在处理业务规则和场景(比如发票生成等)时非常有用。

$ date(2010,12,31)+relativedelta(months=+1)
  datetime.date(2011, 1, 31)

$ date(2010,12,31)+relativedelta(months=+2)
  datetime.date(2011, 2, 28)

我认为这样做会比手动添加天数更安全:

import datetime
today = datetime.date.today()

def addMonths(dt, months = 0):
    new_month = months + dt.month
    year_inc = 0
    if new_month>12:
        year_inc +=1
        new_month -=12
    return dt.replace(month = new_month, year = dt.year+year_inc)

newdate = addMonths(today, 6)

我是这样解决这个问题的:

import calendar
from datetime import datetime
moths2add = 6
now = datetime.now()
current_year = now.year
current_month = now.month
#count days in months you want to add using calendar module
days = sum(
  [calendar.monthrange(current_year, elem)[1] for elem in range(current_month, current_month + moths)]
    )
print now + days

import datetime


'''
Created on 2011-03-09

@author: tonydiep
'''

def add_business_months(start_date, months_to_add):
    """
    Add months in the way business people think of months. 
    Jan 31, 2011 + 1 month = Feb 28, 2011 to business people
    Method: Add the number of months, roll back the date until it becomes a valid date
    """
    # determine year
    years_change = months_to_add / 12

    # determine if there is carryover from adding months
    if (start_date.month + (months_to_add % 12) > 12 ):
        years_change = years_change + 1

    new_year = start_date.year + years_change

    # determine month
    work = months_to_add % 12
    if 0 == work:
        new_month = start_date.month
    else:
        new_month = (start_date.month + (work % 12)) % 12

    if 0 == new_month:
        new_month = 12 

    # determine day of the month
    new_day = start_date.day
    if(new_day in [31, 30, 29, 28]):
        #user means end of the month
        new_day = 31


    new_date = None
    while (None == new_date and 27 < new_day):
        try:
            new_date = start_date.replace(year=new_year, month=new_month, day=new_day)
        except:
            new_day = new_day - 1   #wind down until we get to a valid date

    return new_date


if __name__ == '__main__':
    #tests
    dates = [datetime.date(2011, 1, 31),
             datetime.date(2011, 2, 28),
             datetime.date(2011, 3, 28),
             datetime.date(2011, 4, 28),
             datetime.date(2011, 5, 28),
             datetime.date(2011, 6, 28),
             datetime.date(2011, 7, 28),
             datetime.date(2011, 8, 28),
             datetime.date(2011, 9, 28),
             datetime.date(2011, 10, 28),
             datetime.date(2011, 11, 28),
             datetime.date(2011, 12, 28),
             ]
    months = range(1, 24)
    for start_date in dates:
        for m in months:
            end_date = add_business_months(start_date, m)
            print("%s\t%s\t%s" %(start_date, end_date, m))

我知道这是6个月,但是如果你要添加一个月,答案在谷歌中显示为“在python中添加月”:

import calendar

date = datetime.date.today()    //Or your date

datetime.timedelta(days=calendar.monthrange(date.year,date.month)[1])

这将计算当前月份的天数,并将它们添加到当前日期,如果您在日期上迭代,则使用365/12将AD年的1/12会导致短/长月份出现问题。


我对Tony Diep的答案的修改,可能稍微更优雅(当然,Python 2,匹配问题和原始答案的日期,对于Python 3,必要时修改,至少包括/ to //):

def add_months(date, months):
    month = date.month + months - 1
    year = date.year + (month / 12)
    month = (month % 12) + 1
    day = date.day
    while (day > 0):
        try:
            new_date = date.replace(year=year, month=month, day=day)
            break
        except:
            day = day - 1    
    return new_date

根据“业务需求”解释添加月份,即日期映射到月底之后,应该映射到月底,而不是下一个月。


我有一个更好的办法来解决“2月31日”的问题:

def add_months(start_date, months):
    import calendar

    year = start_date.year + (months / 12)
    month = start_date.month + (months % 12)
    day = start_date.day

    if month > 12:
        month = month % 12
        year = year + 1

    days_next = calendar.monthrange(year, month)[1]
    if day > days_next:
        day = days_next

    return start_date.replace(year, month, day)

我认为它也适用于负数(减去月份),但我还没有对此进行过多测试。


修改了AddMonths()在Zope中使用和处理无效的天数:

def AddMonths(d,x):
    days_of_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    newmonth = ((( d.month() - 1) + x ) % 12 ) + 1
    newyear  = d.year() + ((( d.month() - 1) + x ) // 12 ) 
    if d.day() > days_of_month[newmonth-1]:
      newday = days_of_month[newmonth-1]
    else:
      newday = d.day() 
    return DateTime( newyear, newmonth, newday)

import time

def add_month(start_time, months):  

        ret = time.strptime(start_time, '%Y-%m-%d')
        t = list(ret)

        t[1] += months

        if t[1] > 12:
            t[0] += 1 + int(months / 12)

            t[1] %= 12

        return int(time.mktime(tuple(t)))

user417751之前回答的重做。也许不是那么python的方式,但它照顾到不同的月份长度和闰年。在本例中,2012年1月31日+ 1个月= 2012年2月29日。

import datetime
import calendar

def add_mths(d, x):
    newday = d.day
    newmonth = (((d.month - 1) + x) % 12) + 1
    newyear  = d.year + (((d.month - 1) + x) // 12)
    if newday > calendar.mdays[newmonth]:
        newday = calendar.mdays[newmonth]
        if newyear % 4 == 0 and newmonth == 2:
            newday += 1
    return datetime.date(newyear, newmonth, newday)

在这个函数中,n可以是正数,也可以是负数。

def addmonth(d, n):
    n += 1
    dd = datetime.date(d.year + n/12, d.month + n%12, 1)-datetime.timedelta(1)
    return datetime.date(dd.year, dd.month, min(d.day, dd.day))

我们可能应该使用dateutil。relativedelta

然而,出于学术兴趣,我只想补充一点,在我发现它之前,我打算用这个:

亿: 今天代表。年度+ (K .今日month + 6) / 12 / (K . today . 12 month + 5%) + 1, K .今日day)。 except: 今天代表。年+ (K.today.month+6)//12 (K.today.month+6)%12+1, 1) -时间轴(天= 1)

它看起来很简单,但仍然可以捕捉到所有的问题,如29、30、31

它也适用于- 6 MTHS通过执行-timedelta

别被k弄糊涂了,今天它只是我程序中的一个变量


    def addDay(date, number):
        for i in range(number)
            #try to add a day
            try:
                date = date.replace(day = date.day + 1)
            #in case it's impossible ex:january 32nd add a month and restart at day 1
            except:
                #add month part
                try:
                    date = date.replace(month = date.month +1, day = 1)
                except:
                    date = date.replace(year = date.year +1, month = 1, day = 1)

写给还在读这篇文章的人。我认为这段代码更加清晰,特别是与使用模(%)的代码相比。

很抱歉有语法错误,英语不是我的主要语言


还有另一个解决方案,希望有人会喜欢:

def add_months(d, months):
    return d.replace(year=d.year+months//12).replace(month=(d.month+months)%12)

这个解决方案并不适用于所有情况下的第29、30、31天,所以需要更健壮的解决方案(这已经不是那么好了:)):

def add_months(d, months):
    for i in range(4):
        day = d.day - i
        try:
            return d.replace(day=day).replace(year=d.year+int(months)//12).replace(month=(d.month+int(months))%12)
        except:
            pass
    raise Exception("should not happen")

从这个答案中,请参见parsedatetime。下面是代码示例。更多细节:使用许多自然语言-> YYYY-MM-DD转换示例进行单元测试,以及明显的parsedatetime转换挑战/bug。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time, calendar
from datetime import date

# from https://github.com/bear/parsedatetime
import parsedatetime as pdt

def print_todays_date():
    todays_day_of_week = calendar.day_name[date.today().weekday()]
    print "today's date = " + todays_day_of_week + ', ' + \
                              time.strftime('%Y-%m-%d')

def convert_date(natural_language_date):
    cal = pdt.Calendar()
    (struct_time_date, success) = cal.parse(natural_language_date)
    if success:
        formal_date = time.strftime('%Y-%m-%d', struct_time_date)
    else:
        formal_date = '(conversion failed)'
    print '{0:12s} -> {1:10s}'.format(natural_language_date, formal_date)

print_todays_date()
convert_date('6 months')

上面的代码从MacOSX机器生成以下内容:

$ ./parsedatetime_simple.py 
today's date = Wednesday, 2015-05-13
6 months     -> 2015-11-13
$ 

这个怎么样?没有使用其他库(dateutil)或timedelta? 基于vartec的回答,我这样做了,我相信它是有效的:

import datetime

today = datetime.date.today()
six_months_from_today = datetime.date(today.year + (today.month + 6)/12, (today.month + 6) % 12, today.day)

我尝试使用timedelta,但因为它是计算天数的,365/2或6*356/12并不总是转换为6个月,而是182天。如。

day = datetime.date(2015, 3, 10)
print day
>>> 2015-03-10

print (day + datetime.timedelta(6*365/12))
>>> 2015-09-08

我相信我们通常会假设某一天的6个月将在6个月后的同一天登陆(即2015-03-10—> 2015-09-10,而不是2015-09-08)

我希望这对你有帮助。


当我需要添加几个月或几年的时间,并且不想导入更多的库时,我就会这样做。

import datetime
__author__ = 'Daniel Margarido'


# Check if the int given year is a leap year
# return true if leap year or false otherwise
def is_leap_year(year):
    if (year % 4) == 0:
        if (year % 100) == 0:
            if (year % 400) == 0:
                return True
            else:
                return False
        else:
            return True
    else:
        return False


THIRTY_DAYS_MONTHS = [4, 6, 9, 11]
THIRTYONE_DAYS_MONTHS = [1, 3, 5, 7, 8, 10, 12]

# Inputs -> month, year Booth integers
# Return the number of days of the given month
def get_month_days(month, year):
    if month in THIRTY_DAYS_MONTHS:   # April, June, September, November
        return 30
    elif month in THIRTYONE_DAYS_MONTHS:   # January, March, May, July, August, October, December
        return 31
    else:   # February
        if is_leap_year(year):
            return 29
        else:
            return 28

# Checks the month of the given date
# Selects the number of days it needs to add one month
# return the date with one month added
def add_month(date):
    current_month_days = get_month_days(date.month, date.year)
    next_month_days = get_month_days(date.month + 1, date.year)

    delta = datetime.timedelta(days=current_month_days)
    if date.day > next_month_days:
        delta = delta - datetime.timedelta(days=(date.day - next_month_days) - 1)

    return date + delta


def add_year(date):
    if is_leap_year(date.year):
        delta = datetime.timedelta(days=366)
    else:
        delta = datetime.timedelta(days=365)

    return date + delta


# Validates if the expected_value is equal to the given value
def test_equal(expected_value, value):
    if expected_value == value:
        print "Test Passed"
        return True

    print "Test Failed : " + str(expected_value) + " is not equal to " str(value)
    return False

# Test leap year
print "---------- Test leap year ----------"
test_equal(True, is_leap_year(2012))
test_equal(True, is_leap_year(2000))
test_equal(False, is_leap_year(1900))
test_equal(False, is_leap_year(2002))
test_equal(False, is_leap_year(2100))
test_equal(True, is_leap_year(2400))
test_equal(True, is_leap_year(2016))

# Test add month
print "---------- Test add month ----------"
test_equal(datetime.date(2016, 2, 1), add_month(datetime.date(2016, 1, 1)))
test_equal(datetime.date(2016, 6, 16), add_month(datetime.date(2016, 5, 16)))
test_equal(datetime.date(2016, 3, 15), add_month(datetime.date(2016, 2, 15)))
test_equal(datetime.date(2017, 1, 12), add_month(datetime.date(2016, 12, 12)))
test_equal(datetime.date(2016, 3, 1), add_month(datetime.date(2016, 1, 31)))
test_equal(datetime.date(2015, 3, 1), add_month(datetime.date(2015, 1, 31)))
test_equal(datetime.date(2016, 3, 1), add_month(datetime.date(2016, 1, 30)))
test_equal(datetime.date(2016, 4, 30), add_month(datetime.date(2016, 3, 30)))
test_equal(datetime.date(2016, 5, 1), add_month(datetime.date(2016, 3, 31)))

# Test add year
print "---------- Test add year ----------"
test_equal(datetime.date(2016, 2, 2), add_year(datetime.date(2015, 2, 2)))
test_equal(datetime.date(2001, 2, 2), add_year(datetime.date(2000, 2, 2)))
test_equal(datetime.date(2100, 2, 2), add_year(datetime.date(2099, 2, 2)))
test_equal(datetime.date(2101, 2, 2), add_year(datetime.date(2100, 2, 2)))
test_equal(datetime.date(2401, 2, 2), add_year(datetime.date(2400, 2, 2)))

只需创建一个datetime.date()对象,调用add_month(date)来添加一个月,调用add_year(date)来添加一个年。


下面是一个示例,它允许用户决定如何返回一个日期,其中一天大于一个月中的天数。

def add_months(date, months, endOfMonthBehaviour='RoundUp'):
    assert endOfMonthBehaviour in ['RoundDown', 'RoundIn', 'RoundOut', 'RoundUp'], \
        'Unknown end of month behaviour'
    year = date.year + (date.month + months - 1) / 12
    month = (date.month + months - 1) % 12 + 1
    day = date.day
    last = monthrange(year, month)[1]
    if day > last:
        if endOfMonthBehaviour == 'RoundDown' or \
            endOfMonthBehaviour == 'RoundOut' and months < 0 or \
            endOfMonthBehaviour == 'RoundIn' and months > 0:
            day = last
        elif endOfMonthBehaviour == 'RoundUp' or \
            endOfMonthBehaviour == 'RoundOut' and months > 0 or \
            endOfMonthBehaviour == 'RoundIn' and months < 0:
            # we don't need to worry about incrementing the year
            # because there will never be a day in December > 31
            month += 1
            day = 1
    return datetime.date(year, month, day)


>>> from calendar import monthrange
>>> import datetime
>>> add_months(datetime.datetime(2016, 1, 31), 1)
datetime.date(2016, 3, 1)
>>> add_months(datetime.datetime(2016, 1, 31), -2)
datetime.date(2015, 12, 1)
>>> add_months(datetime.datetime(2016, 1, 31), -2, 'RoundDown')
datetime.date(2015, 11, 30)

另一种解决方案:计算下个月的天数总和,并将结果添加到当前日期。

import calendar
import datetime

def date_from_now(months):
    today = datetime.datetime.today()

    month = today.month
    year = today.year
    sum_days = 0

    for i in range(int(months)):
        month += 1

        if month == 13:
            month = 1
            year += 1

        sum_days += calendar.monthrange(year, month)[1]

    return datetime.date.today() + datetime.timedelta(sum_days)

print(date_from_now(12)) # if to day is 2017-01-01, output: 2019-01-01 

按月开始计算:

from datetime import timedelta
from dateutil.relativedelta import relativedelta

end_date = start_date + relativedelta(months=delta_period) + timedelta(days=-delta_period)

假设你的datetime变量叫做date:

date=datetime.datetime(year=date.year+int((date.month+6)/12),
                       month=(date.month+6)%13 + (1 if (date.month + 
                       months>12) else 0), day=date.day)

获得x个月之后或之前的下一个日期的一般函数。

from datetime import date

def after_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 = after_month(today, mm)
        print(next_date)

使用下面给出的函数,你可以得到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)

我找不到这个问题的确切解决方案,所以我将发布我的解决方案,以防使用标准日历和datetime库可能有任何帮助。这适用于添加和减去月份,并考虑月末滚动和最后一个月比第一个月天数少的情况。如果你正在寻找更复杂的操作,我还有一个更通用的解决方案,它添加了定期间隔(天,月,年,季度,学期等),如:“1m”,“-9m”,“-1.5y”,“-3q”,“1s”等。

from datetime import datetime
from calendar import monthrange
def date_bump_months(start_date, months):
    """
    bumps months back and forth. 
    --> if initial date is end-of-month, i will move to corresponding month-end
    --> ir inital date.day is greater than end of month of final date, it casts it to momth-end
    """
    signbit = -1 if months < 0 else 1
    d_year, d_month = divmod(abs(months),12)    
    end_year = start_date.year + d_year*signbit 
    end_month = 0
    if signbit ==-1:            
        if d_month < start_date.month:
            end_month = start_date.month - d_month
        else:
            end_year -=1
            end_month = 12 - (d_month - start_date.month)
    else:
        end_month +=start_date.month
        if end_month  > 12:
            end_year +=1
            end_month -=12
    # check if we are running end-of-month dates
    eom_run = monthrange(start_date.year, start_date.month)[1]==start_date.day
    eom_month = monthrange((end_year), (end_month))[1]
    if eom_run:
        end_day = eom_month 
    else:
        end_day = min(start_date.day, eom_month )    
    return date(end_year, end_month, end_day)

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

我使用replace()方法并编写了这个递归函数。dt是一个日期时间。datetime对象:

def month_timedelta(dt, m):
    y = m // 12
    dm = m % 12
    if y == 0:
        if dt.month + m <= 12:
            return dt.replace(month = dt.month + m)
        else:
            dy = (dt.month + m) // 12
            ndt = dt.replace(year=dt.year + dy)
            return ndt.replace(month=(ndt.month + m) % 12)
    else:
        return month_timedelta(dt.replace(year=dt.year + y),dm)

使用Python 3。X你可以这样做:

from datetime import datetime, timedelta
from dateutil.relativedelta import *

date = datetime.now()
print(date)
# 2018-09-24 13:24:04.007620

date = date + relativedelta(months=+6)
print(date)
# 2019-03-24 13:24:04.007620

但是你需要安装python-dateutil模块:

pip install python-dateutil

我插话晚了,但是

查看肯雷茨玛雅模块,

https://github.com/kennethreitz/maya

像这样的方法可能会对你有帮助,只需将小时=1改为天=1或年=1

>>> from maya import MayaInterval

# Create an event that is one hour long, starting now.
>>> event_start = maya.now()
>>> event_end = event_start.add(hours=1)

>>> event = MayaInterval(start=event_start, end=event_end)

一个简单的建议是《绿箭侠》

PIP安装箭头

>>> import arrow

>>> arrow.now().date()
datetime.date(2019, 6, 28)
>>> arrow.now().shift(months=6).date()
datetime.date(2019, 12, 28)

这并没有回答具体的问题(仅使用datetime),但是,鉴于其他人建议使用不同的模块,这里有一个使用pandas的解决方案。

import datetime as dt
import pandas as pd

date = dt.date.today() - \
       pd.offsets.DateOffset(months=6)

print(date)

2019-05-04 00:00:00

在闰年哪种方式会正常工作

date = dt.datetime(2019,8,29) - \
       pd.offsets.DateOffset(months=6)
print(date)

2019-02-28 00:00:00

“Python -dateutil”(外部扩展)是一个很好的解决方案,但你可以使用内置的Python模块(datetime和datetime)来实现它。

我做了一个简短的代码,来解决它(处理年,月和日)

(运行:Python 3.8.2)

from datetime import datetime
from calendar import monthrange

# Time to increase (in months)
inc = 12

# Returns mod of the division for 12 (months)
month = ((datetime.now().month + inc) % 12) or 1

# Increase the division by 12 (months), if necessary (+ 12 months increase)
year = datetime.now().year + int((month + inc) / 12)

# (IF YOU DON'T NEED DAYS,CAN REMOVE THE BELOW CODE)
# Returns the same day in new month, or the maximum day of new month
day = min(datetime.now().day,monthrange(year, month)[1])

print("Year: {}, Month: {}, Day: {}".format(year, month, day))

我经常需要一个月的最后一天来保持上个月的最后一天。为了解决这个问题,我在计算前加一天,然后在返回前再减去它。

from datetime import date, timedelta

# it's a lot faster with a constant day
DAY = timedelta(1)

def add_month(a_date, months):
    "Add months to date and retain last day in month."
    next_day = a_date + DAY
    # calculate new year and month
    m_sum = next_day.month + months - 1
    y = next_day.year + m_sum // 12
    m = m_sum % 12 + 1
    try:
        return date(y, m, next_day.day) - DAY
    except ValueError:
        # on fail return last day in month
        # can't fail on december so I don't bother changing the year
        return date(y, m + 1, 1) - DAY

我的实现基于taleinat的答案:

import datetime
import calendar

def add_months(orig_date, month_count = 1):
    while month_count > 12:
        month_count -= 12
        orig_date = add_months(orig_date, 12)
    new_year = orig_date.year
    new_month = orig_date.month + month_count
    # note: in datetime.date, months go from 1 to 12
    if new_month > 12:
        new_year += 1
        new_month -= 12

    last_day_of_month = calendar.monthrange(new_year, new_month)[1]
    new_day = min(orig_date.day, last_day_of_month)

    return orig_date.replace(year=new_year, month=new_month, day=new_day)

有了这个功能,你可以添加任意多的月份。

from datetime import date
dt = date(2021, 1, 31)

print(add_months(dt, 49))

返回 2025-02-28


Python可以使用datautil包,请参阅下面的示例

它不仅限于此,你还可以同时通过日、月和年的组合。

import datetime
from dateutil.relativedelta import relativedelta

# subtract months
proc_dt = datetime.date(2021,8,31)
proc_dt_minus_3_months = proc_dt + relativedelta(months=-3)
print(proc_dt_minus_3_months)

# add months
proc_dt = datetime.date(2021,8,31)
proc_dt_plus_3_months = proc_dt + relativedelta(months=+3)
print(proc_dt_plus_3_months)

# subtract days:
proc_dt = datetime.date(2021,8,31)
proc_dt_minus_3_days = proc_dt + relativedelta(days=-3)
print(proc_dt_minus_3_days)

# add days days:
proc_dt = datetime.date(2021,8,31)
proc_dt_plus_3_days = proc_dt + relativedelta(days=+3)
print(proc_dt_plus_3_days)

# subtract years:
proc_dt = datetime.date(2021,8,31)
proc_dt_minus_3_years = proc_dt + relativedelta(years=-3)
print(proc_dt_minus_3_years)

# add years:
proc_dt = datetime.date(2021,8,31)
proc_dt_plus_3_years = proc_dt + relativedelta(years=+3)
print(proc_dt_plus_3_years)

结果:

2021-05-31

2021-11-30

2021-08-28

2021-09-03

2018-08-31

2024-08-31


我知道这个问题已经有很多答案,但是使用collections.deque和rotate()方法,可以创建一个函数,该函数接受一个datetime对象作为输入,然后输出一个比当前对象晚一个“业务月”的新datetime对象。如果该月的某一天在下个月不存在,则减去1,直到它到达该月的有效日期,然后返回该对象。

import collections
import datetime

def next_month(dt: datetime.datetime):
    month_list = list(range(1, 12 + 1))
    months = collections.deque(month_list)
    while True:
        this_month = list(months)[0]
        if dt.month == this_month:
            break
        months.rotate(-1)
    months.rotate(-1)
    month_plus = list(months)[0]
    for i in range(4):
        try:
            return dt.replace(month=month_plus, day=dt.day - i)
        except ValueError:
            continue

使用itertools.cycle也可以得到相同的结果。

import datetime
import itertools

def next_month(dt: datetime.datetime):
    month_list = list(range(1, 12 + 1))
    month = itertools.cycle(month_list)
    while True:
        if next(month) == dt.month:
            break
    month_plus = next(month)
    for i in range(4):
        try:
            return dt.replace(month=month_plus, day=dt.day - i)
        except ValueError:
            continue