如何使用datetime Python模块计算从当前日期起六个月的日期?


391

我正在使用datetime Python模块。我想计算从当前日期起6个月的日期。有人可以给我一点帮助吗?

我要从当前日期起6个月生成日期的原因是要产生一个审阅日期。如果用户将数据输入到系统中,则其输入日期为6个月。


2
您将需要更具体:从3月31日起六个月几点?从8月30日开始?
kmkaplan,2009年

3
是的,编辑有帮助:这意味着您可以在6个月到183天左右的时间里保持不适。因此,今天增加183天即可解决问题。
kmkaplan 2009年

15
以上评论使我感到愚蠢。“增加六个月”的概念非常明确-将月份组成部分并添加6到其中,1如果我们超过12月,则支持在一年中滚动(并将月份循环回)。这恰好relativedelta是实际上所做的,实际上是每种支持这种概念的编程语言所要做的。
Kirk Woll

5
@KirkWoll:我敢肯定这很清楚。但是无论说话的人还是不同的。Python:date(2015, 3, 31) + relativedelta(months = 6)给出datetime.date(2015, 9, 30)。Perl:DateTime->new(year=>2000, month=>3, day=>31)->add(months=>6)给出2000-10-01T00:00:00。Php:date_create('2000-03-31', new DateTimeZone('UTC'))->add(new DateInterval('P6M'))给出2000-10-01。选择你的毒药。
kmkaplan 2015年

2
...添加182对于生成审核日期似乎更为实用:它使星期几保持完整。

Answers:


1047

我发现此解决方案很好。(这使用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)

2
+6表示它可能是-6,同样的情况也适用于日和年:)
securecurve

5
@sliders_alpha您需要安装python-dateutil软件包(pip install python-dateutil)
poiuytrez 2013年

18
这是正确的解决方案。如果OP要求的是6年而不是6个月,那么批准的答案将惨遭失败。因此,请记住,答案越概括,它们就越有价值。
丹尼尔F

3
请注意,该relativedelta函数还接受了month一个参数,该参数基本上替换/设置/修正了通过日期中的月份,这与添加更多月份非常不同。为乡亲想知道如果功能被打破,因为他们忘了额外的只是一个预警小号几个月
shad0w_wa1k3r

1
我很好奇:有人知道为什么默认情况下不包括吗?datetime默认情况下,Timedelta似乎打包了。我实际上以为我可以将“月份”传递给timedelta。
dTanMan

51

好吧,这取决于您从当前日期算起的6个月的意思。

  1. 使用自然月份:

    (day, month, year) = (day, (month + 5) % 12 + 1, year + (month + 5)/12)
  2. 使用银行家的定义6 * 30:

    date += datetime.timedelta(6 * 30)

3
您能同时输入半年定义(183天)和26周定义吗?将它们全部集中在一个位置会有所帮助。
S.Lott

11
简要说明一下:我认为,对于月份,公式将改为(月份+ 5)%12 +1月的1 b / c,您的公式给出0,而预期结果为12 ...尽管有这个小错误,我的想法是,您的答案是最能回答这个问题的人
PierrOz 2010-3-15

3
和年份相同:应该是year +(month + 5)/ 12
PierrOz 2010年

7
如果日期是31,而六个月后的月份不能有31天(大多数情况下是31天的月份就是这种情况)怎么办?
2011年

4
拒绝投票,因为第一个解决方案(day, month, year) = (day, (month+6)%12, year+(month+6)/12)可能会出错,因为它会生成无效的日期,例如(31, 8, 2015)->(31, 2, 2016)
ohw 2016年

37

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

pip install ...
内森

我建议from dateutil.relativedelta import relativedelta。使用import *不是明确的。
Sam Morgan

19

对于月初的计算:

from datetime import timedelta
from dateutil.relativedelta import relativedelta

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

18
这不是上面评论中提到的relativedelta解决方案。持续滚动600多个解决方案。
Nostalg.io

15

“ 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的更多信息


14

因此,这是一个示例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)

14

此解决方案在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-您增加一个月意味着什么?一种解决方案是回溯到有效的一天,因为大多数人会假设jan的最后一天加上一个月等于2月的最后一天。这也适用于负数个月。证明:

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

3
解决了“ 1月31日+ 1个月”的问题:days_in_month = calendar.monthrange(newyear,newmonth)[1]; newday = min(d.day,days_in_month); (如果一天过大,则会使一天下跌,例如2月31日到2月28/29日)
Curtis Yallop 2015年

这仅对我有用,只需将newyear转换为整数数据类型def AddMonths(d,x):newmonth =(((d.month-1)+ x)%12)+ 1 newyear = d.year + int(( (d.month-1)+ x)/ 12)返回datetime.date(newyear,newmonth,d.day)
Manmeet Singh,

12

我知道这是6个月,但是如果您要添加一个月,答案就会在Google中显示为“在python中添加月份”:

import calendar

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

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

这将计算当月的天数并将其添加到当前日期,使用365/12表示一年的1/12会导致短/长月份的问题(如果您对该日期进行迭代)。



10

只需使用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个月等极端情况。:)


3
您的代码中的错误:new_month % 12应该为(new_month % 12) or 12。否则,如果您在11月尝试此操作,则会收到错误消息。:)
Jordan Reiter

这实际上比接受的解决方案干净得多。这并不需要任何新的进口,只是基本的数学
西蒙PA

return datetime.date(year +(new_month / / 12),(new_month%12)或12,day)
paytam

9

Dateutil软件包具有此类功能的实现。但是请注意,正如其他人已经指出的那样,这将是幼稚的


dateutil很棒。也可以使用easy_install进行安装。
苏联2009年

优秀的。感谢您的建议。那似乎是天赐之物。
ayaz

9

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

这有一个错误:如果目标月份是12月,它将返回比需要晚一年的日期。示例:add_months(datetime.date(2018, 11, 30), 1)回报datetime.date(2019, 12, 30)(年份应为2018,而不是2019)。因此,最好为此使用经过专门测试的专用库!如果您真的只需要使用标准库模块,请参阅我对类似问题的回答
taleinat

5

我有一个更好的方法来解决“ 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)

我认为它也可以使用负数(减去数月),但是我还没有对此进行太多测试。


1
此答案的优点是仅使用内置Python库正确解决问题。在stackoverflow.com/a/4131114/302264上找到等效但更简洁的答案。
Eduardo Dobay

3

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)

3

这个怎么样?不使用其他库(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

我希望你觉得这有帮助。


1
day + datetime.timedelta(6*365/12)不会总是工作,因为有些年份有365天,而另一些则有366天。
3kstc

3

这不能回答特定的问题(datetime仅使用),但是,鉴于其他人建议使用不同的模块,此处提供了一个解决方案using 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

在leap年中可以正常工作

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

2019-02-28 00:00:00

2

修改了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)

这似乎没有考虑leap年(2月29天)。
处理

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

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

1

在1new_month = 121的情况下修改了Johannes Wei的答案。这对我来说非常有效。月份可以是正数或负数。

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)

2
当开始日期中的某天大于目标月份中的天数时,它就不会“完美地工作”。例如:2001-01-31加上一个月试图创建一个日期2001-02-31。
约翰·马钦

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

1

这个答案,看到parsedatetime。代码示例如下。更多详细信息:单元测试包含许多自然语言-> YYYY-MM-DD转换示例,以及明显的parsedatetime转换挑战/错误

#!/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
$ 

1

这是一个示例,它使用户可以决定如何返回日期大于月份中天数的日期。

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)

1

假设您的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)

1

获取x个月之后/之前的下一个日期的常规功能。

从日期时间导入日期

def after_month(给定日期,月份):
    yyyy = int((((给定日期。年* 12 +给定日期。月)+月)/ 12)
    mm = int((((给定日期。年* 12 +给定日期。月)+月)%12)

    如果mm == 0:
        yyyy-= 1
        毫米= 12
    返回给定的date.replace(年= yyyy,月= mm)


如果__name__ ==“ __main__”:
    今天= date.today()
    打印(今天)

    对于[-12,-1,0,1,2,12,20]中的mm:
        next_date = after_month(今天,毫米)
        打印(下一个日期)

这显然是最好的答案。简单有效,没有四舍五入的问题。
jprobichaud


0

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

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

当然,你将要解决什么约翰内斯Weiß--提出的问题你6个月意味着什么呢?


1
timedelta不支持数月,因此回避了“ 6个月中有多少天?”这一问题的可能答案。Eef的代码将设置一个审核日期,因此我建议您可以考虑使用天(6 * 30)设置时间增量。如果该期限代表客户访问产品/服务,则可能需要/首选业务定义。
卡尔,

0

这就是我想出的。它可以移动正确的月数和年数,但会忽略天数(这是我当时所需要的)。

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)

0

我使用此功能更改年份和月份,但保留日期:

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)

0

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

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.