使用Python在给定日期(舍入后一天)添加一个月

时间:2015-01-28 09:45:47

标签: python pandas calendar timedelta python-dateutil

我想在指定日期添加一个月

import datetime
dt = datetime.datetime(year=2014, month=5, day=2)

所以我应该

datetime.datetime(year=2014, month=6, day=2)

但是

dt = datetime.datetime(year=2015, month=1, day=31)

我应该

datetime.datetime(year=2015, month=3, day=1)

因为没有2015-02-31(我希望我的结果在之后一天完成)

有些月份有31天,有些月份有30天,有些是29天,有些是28天!

所以添加datetime.timedelta可能不是一种好的做法(因为我们不知道要添加的天数)

我注意到Pandas有一个有趣的概念DateOffset

http://pandas.pydata.org/pandas-docs/stable/timeseries.html#dateoffset-objects

但我找不到Month偏移量,只有MonthBeginMonthEnd

我也看到了这篇文章 How do I calculate the date six months from the current date using the datetime Python module?

所以我尝试了dateutil.relativedelta但是

from dateutil.relativedelta import relativedelta
datetime.datetime(year=2015, month=1, day=31)+relativedelta(months=1)

返回

datetime.datetime(2015, 2, 28, 0, 0)

所以结果在前一天四舍五入。

之后是否有(干净的)方式来回合

编辑: 我举了一个月的例子来添加,但我也希望能够添加例如:2年零6个月(使用relativedelta(years=2, months=6)

4 个答案:

答案 0 :(得分:4)

您可以使用dateutil.relativedelta.relativedelta并手动检查datetime.day属性,如果原始日期大于新的一天,则添加一天。

下面的函数接受datetime对象和relativedelta对象。请注意,下面的代码只能使用数年和数月,如果你使用低于(天,小时等)的任何东西,我认为它不会起作用。您可以轻松修改此函数以将yearsmonths作为参数,然后在函数本身内构造relativedelta

from datetime import datetime
from dateutil.relativedelta import relativedelta

def add_time(d, rd):
    day = relativedelta(days=+1)

    out = d + rd
    if d.day > out.day:
        out = out + day

    return out    

# Check that it "rolls over"
print(add_time(datetime(year=2015, month=1, day=29), relativedelta(years=+4, months=+1))) # 2019-03-01 00:00:00
print(add_time(datetime(year=2015, month=3, day=31), relativedelta(years=+0, months=+2))) # 2015-05-01 00:00:00

# Check that it handles "normal" scenarios
print(add_time(datetime(year=2015, month=6, day=19), relativedelta(months=+1))) # 2015-07-19 00:00:00
print(add_time(datetime(year=2015, month=6, day=30), relativedelta(years=+2, months=+1))) # 2017-07-30 00:00:00

# Check across years
print(add_time(datetime(year=2015, month=12, day=25), relativedelta(months=+1))) # 2016-01-25 00:00:00

# Check leap years
print(add_time(datetime(year=2016, month=1, day=29), relativedelta(years=+4, months=+1))) # 2020-02-29 00:00:00

答案 1 :(得分:2)

这似乎有效。它很干净,但不漂亮:

def add_month(now):
    try:
        then = (now + relativedelta(months=1)).replace(day=now.day)
    except ValueError:
        then = (now + relativedelta(months=2)).replace(day=1)
    return then

for now in [datetime(2015, 1, 20), datetime(2015, 1, 31), datetime(2015, 2, 28)]:
    print now, add_month(now)

打印:

2015-01-20 00:00:00 2015-02-20 00:00:00
2015-01-31 00:00:00 2015-03-01 00:00:00
2015-02-28 00:00:00 2015-03-28 00:00:00

它会增加一个月并尝试将当天替换为原始日期。如果成功,则不是特例。如果失败(ValueError),我们必须再添加一个月并转到第一天。

答案 2 :(得分:0)

快速解决方案:

import datetime
import calendar
dt = datetime.datetime(year=2014, month=5, day=2)
d = calendar.monthrange(dt.year,dt.month+1)[1] 
print dt+datetime.timedelta(days=d+1)

第一个输入(year=2014, month=5, day=2)的输出:

2014-06-02 00:00:00

第二个输入(year=2015, month=1, day=31)的输出:

2015-03-01 00:00:00

答案 3 :(得分:0)

使用下面的方法:

def datetime_offset_by_months(datetime_origin, n=1):
    """
    datetime offset by months
    :param datetime_origin: the original datetime
    :param n: count of months
    :return: after offset datetime
    """
    # create a shortcut object for one day
    one_day = datetime.timedelta(days=1)

    # first use div and mod to determine year cycle
    q, r = divmod(datetime_origin.month + n, 12)

    # create a datetime_offset
    # to be the last day of the target month
    datetime_offset = datetime.datetime(
        datetime_origin.year + q, r + 1, 1) - one_day

    '''
       if input date is the last day of this month
       then the output date should also be the last
       day of the target month, although the day
       www.iplaypy.com
       may be different.
       for example:
       datetime_origin = 8.31
       datetime_offset = 9.30
    '''

    if datetime_origin.month != (datetime_origin + one_day).month:
        return datetime_offset

    '''
        if datetime_origin day is bigger than last day of
        target month, then, use datetime_offset
        for example:
        datetime_origin = 10.31
        datetime_offset = 11.30
    '''

    if datetime_origin.day >= datetime_offset.day:
        return datetime_offset

    '''
     then, here, we just replace datetime_offset's day
     with the same of datetime_origin, that's ok.
    '''

    return datetime_offset.replace(day= datetime_origin.day)

使用它:

import datetime
now_date = datetime.datetime.now()

off_set_datetime = datetime_offset_by_months(now_date, -2)
print(off_set_datetime)