【问题标题】:Add one month to a given date (rounded day after) with Python使用 Python 将一个月添加到给定日期(后四舍五入)
【发布时间】:2015-01-28 09:45:46
【问题描述】:

我想给给定日期增加一个月

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)

【问题讨论】:

  • 您是否想要添加例如 2 年 6 个月和 5 天?您能否更具体地回答您的问题?
  • 我只需要在日期时间中添加年份和月份(但我希望结果是“圆形”后天,而不是像 relativedelta 所做的前天)
  • 我在下面编辑了我的答案,以便它接受 relativedelta 参数。这将允许您使用任何您想要的时间。请注意,如果您包含天数、小时数等,它将不起作用。

标签: python pandas calendar timedelta python-dateutil


【解决方案1】:

您可以使用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

【讨论】:

    【解决方案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),我们必须再增加一个月并返回第一天。

    【讨论】:

      【解决方案3】:

      快速解决方案:

      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
      

      【讨论】:

        【解决方案4】:

        使用下面的方法:

        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)
        

        【讨论】:

          猜你喜欢
          • 2016-01-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-05-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多