关于python:查找两个日期之间的月份的最佳方法

Best way to find the months between two dates

我需要能够在python中准确地找到两个日期之间的月份。我有一个有效的解决方案,但它不是很好(如在优雅)或很快。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
dateRange = [datetime.strptime(dateRanges[0],"%Y-%m-%d"), datetime.strptime(dateRanges[1],"%Y-%m-%d")]
months = []

tmpTime = dateRange[0]
oneWeek = timedelta(weeks=1)
tmpTime = tmpTime.replace(day=1)
dateRange[0] = tmpTime
dateRange[1] = dateRange[1].replace(day=1)
lastMonth = tmpTime.month
months.append(tmpTime)
while tmpTime < dateRange[1]:
    if lastMonth != 12:
        while tmpTime.month <= lastMonth:
            tmpTime += oneWeek
        tmpTime = tmpTime.replace(day=1)
        months.append(tmpTime)
        lastMonth = tmpTime.month

    else:
        while tmpTime.month >= lastMonth:
            tmpTime += oneWeek
        tmpTime = tmpTime.replace(day=1)
        months.append(tmpTime)
        lastMonth = tmpTime.month

所以,为了解释一下,我在这里要做的是获取这两个日期,并将它们从ISO格式转换为python-datetime对象。然后,我循环向start datetime对象添加一周,并检查月份的数值是否较大(除非月份是12月,否则它检查日期是否较小),如果该值较大,我将其附加到月份列表中,并一直循环直到到达结束日期。

它工作得很好,只是看起来不是一个很好的方法…


首先定义一些测试用例,然后您将看到函数非常简单,不需要循环。

1
2
3
4
5
6
7
8
9
from datetime import datetime

def diff_month(d1, d2):
    return (d1.year - d2.year) * 12 + d1.month - d2.month

assert diff_month(datetime(2010,10,1), datetime(2010,9,1)) == 1
assert diff_month(datetime(2010,10,1), datetime(2009,10,1)) == 12
assert diff_month(datetime(2010,10,1), datetime(2009,11,1)) == 11
assert diff_month(datetime(2010,10,1), datetime(2009,8,1)) == 14

你应该在你的问题中添加一些测试用例,因为有很多潜在的角落用例需要覆盖——有不止一种方法可以定义两个日期之间的月数。


一行代码,用于查找两个日期之间按月递增的日期时间列表。

1
2
3
4
5
6
7
import datetime
from dateutil.rrule import rrule, MONTHLY

strt_dt = datetime.date(2001,1,1)
end_dt = datetime.date(2005,6,1)

dates = [dt for dt in rrule(MONTHLY, dtstart=strt_dt, until=end_dt)]


这对我有用-

1
2
3
4
5
6
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime('2011-08-15 12:00:00', '%Y-%m-%d %H:%M:%S')
date2 = datetime.strptime('2012-02-15', '%Y-%m-%d')
r = relativedelta.relativedelta(date2, date1)
r.months * (r.years+1)


获取结束月份(相对于开始月份的年份和月份,例如:2011年1月=13,如果开始日期从2010年10月开始),然后生成开始月份和结束月份的日期时间,如下所示:

1
2
3
4
5
6
dt1, dt2 = dateRange
start_month=dt1.month
end_months=(dt2.year-dt1.year)*12 + dt2.month+1
dates=[datetime.datetime(year=yr, month=mn, day=1) for (yr, mn) in (
          ((m - 1) / 12 + dt1.year, (m - 1) % 12 + 1) for m in range(start_month, end_months)
      )]

如果两个日期都在同一年,也可以简单地写为:

1
dates=[datetime.datetime(year=dt1.year, month=mn, day=1) for mn in range(dt1.month, dt2.month + 1)]


您可以使用dateutil模块中的rrule轻松计算:

1
2
3
4
from dateutil import rrule
from datetime import date

print(list(rrule.rrule(rrule.MONTHLY, dtstart=date(2013, 11, 1), until=date(2014, 2, 1))))

会给你:

1
2
3
4
 [datetime.datetime(2013, 11, 1, 0, 0),
 datetime.datetime(2013, 12, 1, 0, 0),
 datetime.datetime(2014, 1, 1, 0, 0),
 datetime.datetime(2014, 2, 1, 0, 0)]

这篇文章钉住了!使用dateutil.relativedelta

1
2
3
4
5
6
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime(str('2011-08-15 12:00:00'), '%Y-%m-%d %H:%M:%S')
date2 = datetime.strptime(str('2012-02-15'), '%Y-%m-%d')
r = relativedelta.relativedelta(date2, date1)
r.months


有点像@vin-g提出的一个有点美化的解决方案。

1
2
3
4
5
6
7
8
import datetime

def monthrange(start, finish):
  months = (finish.year - start.year) * 12 + finish.month + 1
  for i in xrange(start.month, months):
    year  = (i - 1) / 12 + start.year
    month = (i - 1) % 12 + 1
    yield datetime.date(year, month, 1)

您也可以使用箭头库。这是一个简单的例子:

1
2
3
4
5
6
7
8
from datetime import datetime
import arrow

start = datetime(2014, 1, 17)
end = datetime(2014, 6, 20)

for d in arrow.Arrow.range('month', start, end):
    print d.month, d.format('MMMM')

这将打印:

1
2
3
4
5
6
1 January
2 February
3 March
4 April
5 May
6 June

希望这有帮助!


有一个基于360天年的简单解决方案,所有月份都有30天。它适用于大多数用例,在给定两个日期的情况下,您需要计算完整月份数加上剩余天数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from datetime import datetime, timedelta

def months_between(start_date, end_date):
    #Add 1 day to end date to solve different last days of month
    s1, e1 = start_date , end_date  + timedelta(days=1)
    #Convert to 360 days
    s360 = (s1.year * 12 + s1.month) * 30 + s1.day
    e360 = (e1.year * 12 + e1.month) * 30 + e1.day
    #Count days between the two 360 dates and return tuple (months, days)
    return divmod(e360 - s360, 30)

print"Counting full and half months"
print months_between( datetime(2012, 01, 1), datetime(2012, 03, 31)) #3m
print months_between( datetime(2012, 01, 1), datetime(2012, 03, 15)) #2m 15d
print months_between( datetime(2012, 01, 16), datetime(2012, 03, 31)) #2m 15d
print months_between( datetime(2012, 01, 16), datetime(2012, 03, 15)) #2m
print"Adding +1d and -1d to 31 day month"
print months_between( datetime(2011, 12, 01), datetime(2011, 12, 31)) #1m 0d
print months_between( datetime(2011, 12, 02), datetime(2011, 12, 31)) #-1d => 29d
print months_between( datetime(2011, 12, 01), datetime(2011, 12, 30)) #30d => 1m
print"Adding +1d and -1d to 29 day month"
print months_between( datetime(2012, 02, 01), datetime(2012, 02, 29)) #1m 0d
print months_between( datetime(2012, 02, 02), datetime(2012, 02, 29)) #-1d => 29d
print months_between( datetime(2012, 02, 01), datetime(2012, 02, 28)) #28d
print"Every month has 30 days - 26/M to 5/M+1 always counts 10 days"
print months_between( datetime(2011, 02, 26), datetime(2011, 03, 05))
print months_between( datetime(2012, 02, 26), datetime(2012, 03, 05))
print months_between( datetime(2012, 03, 26), datetime(2012, 04, 05))


您可以使用python-dateutil。参见python:两个日期时间的月差异


试试这个。如果两个日期恰好在同一个月,则当前包括该月。

1
2
3
4
5
6
7
8
9
10
11
12
from datetime import datetime,timedelta

def months_between(start,end):
    months = []
    cursor = start

    while cursor <= end:
        if cursor.month not in months:
            months.append(cursor.month)
        cursor += timedelta(weeks=1)

    return months

输出如下:

1
2
3
4
>>> start = datetime.now() - timedelta(days=120)
>>> end = datetime.now()
>>> months_between(start,end)
[6, 7, 8, 9, 10]


我的简单解决方案:

1
2
3
4
5
6
7
8
9
import datetime

def months(d1, d2):
    return d1.month - d2.month + 12*(d1.year - d2.year)

d1 = datetime.datetime(2009, 9, 26)  
d2 = datetime.datetime(2019, 9, 26)

print(months(d1, d2))

将"月"定义为1/12year,然后执行以下操作:

1
2
3
4
5
6
def month_diff(d1, d2):
   """Return the number of months between d1 and d2,
    such that d2 + month_diff(d1, d2) == d1
   """

    diff = (12 * d1.year + d1.month) - (12 * d2.year + d2.month)
    return diff

您可以尝试将一个月定义为"29天、28天、30天或31天(取决于年份)"。但是你这样做,你还有一个额外的问题要解决。

虽然通常很清楚6月15日+1个月应该是7月15日,但通常不清楚1月30日+1个月是在2月还是3月。在后一种情况下,您可能不得不将日期计算为2月30日,然后"更正"到3月2日。但当你这样做的时候,你会发现3月2日-1月显然是2月2日。因此,还原和荒谬(这个操作没有很好的定义)。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#This definition gives an array of months between two dates.
import datetime
def MonthsBetweenDates(BeginDate, EndDate):
    firstyearmonths = [mn for mn in range(BeginDate.month, 13)]<p>

    lastyearmonths = [mn for mn in range(1, EndDate.month+1)]<p>

    months = [mn for mn in range(1, 13)]<p>

    numberofyearsbetween = EndDate.year - BeginDate.year - 1<p>

    return firstyearmonths + months * numberofyearsbetween + lastyearmonths<p>


#example
BD = datetime.datetime.strptime("2000-35", '%Y-%j')
ED = datetime.datetime.strptime("2004-200", '%Y-%j')
MonthsBetweenDates(BD, ED)


它可以使用datetime.timedelta完成,其中跳转到下个月的天数可以通过calendar.monthrange获得。Monthrange返回给定年份和月份的工作日(0-6~mon-sun)和天数(28-31)。例如:Monthrange(2017,1)回报(6,31)。

下面是使用此逻辑在两个月之间迭代的脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
from datetime import timedelta
import datetime as dt
from calendar import monthrange

def month_iterator(start_month, end_month):
    start_month = dt.datetime.strptime(start_month,
                                   '%Y-%m-%d').date().replace(day=1)
    end_month = dt.datetime.strptime(end_month,
                                 '%Y-%m-%d').date().replace(day=1)
    while start_month <= end_month:
        yield start_month
        start_month = start_month + timedelta(days=monthrange(start_month.year,
                                                         start_month.month)[1])

`


以下是如何处理熊猫FWIW的方法:

1
2
3
4
5
6
7
8
9
10
11
import pandas as pd
pd.date_range("1990/04/03","2014/12/31", freq="MS")

DatetimeIndex(['1990-05-01', '1990-06-01', '1990-07-01', '1990-08-01',
               '1990-09-01', '1990-10-01', '1990-11-01', '1990-12-01',
               '1991-01-01', '1991-02-01',
               ...
               '2014-03-01', '2014-04-01', '2014-05-01', '2014-06-01',
               '2014-07-01', '2014-08-01', '2014-09-01', '2014-10-01',
               '2014-11-01', '2014-12-01'],
              dtype='datetime64[ns]', length=296, freq='MS')

请注意,它从给定开始日期后的月份开始。


就像range函数一样,当月份为13时,转到下一年。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def year_month_range(start_date, end_date):
    '''
    start_date: datetime.date(2015, 9, 1) or datetime.datetime
    end_date: datetime.date(2016, 3, 1) or datetime.datetime
    return: datetime.date list of 201509, 201510, 201511, 201512, 201601, 201602
    '''

    start, end = start_date.strftime('%Y%m'), end_date.strftime('%Y%m')
    assert len(start) == 6 and len(end) == 6
    start, end = int(start), int(end)

    year_month_list = []
    while start < end:
        year, month = divmod(start, 100)
        if month == 13:
            start += 88  # 201513 + 88 = 201601
            continue
        year_month_list.append(datetime.date(year, month, 1))

        start += 1
    return year_month_list

python shell中的示例

1
2
3
4
5
6
>>> import datetime
>>> s = datetime.date(2015,9,1)
>>> e = datetime.date(2016, 3, 1)
>>> year_month_set_range(s, e)
[datetime.date(2015, 11, 1), datetime.date(2015, 9, 1), datetime.date(2016, 1, 1), datetime.date(2016, 2, 1),
 datetime.date(2015, 12, 1), datetime.date(2015, 10, 1)]

通常90天不是3个月,只是一个参考。

最后,您需要检查天数是否大于15才能将+1添加到月计数器中。或者更好的方法,添加另一个带有半月计数器的elif。

从另一个stackoverflow答案中,我最终得出了以下结论:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#/usr/bin/env python
# -*- coding: utf8 -*-

import datetime
from datetime import timedelta
from dateutil.relativedelta import relativedelta
import calendar

start_date = datetime.date.today()
end_date = start_date + timedelta(days=111)
start_month = calendar.month_abbr[int(start_date.strftime("%m"))]

print str(start_date) +" to" + str(end_date)

months = relativedelta(end_date, start_date).months
days = relativedelta(end_date, start_date).days

print months,"months", days,"days"

if days > 16:
    months += 1

print"around" + str(months) +" months","(",

for i in range(0, months):
    print calendar.month_abbr[int(start_date.strftime("%m"))],
    start_date = start_date + relativedelta(months=1)

print")"

输出:

1
2
3
2016-02-29 2016-06-14
3 months 16 days
around 4 months ( Feb Mar Apr May )

我注意到,如果您在本年剩余的天数超过天,这是不起作用的,这是出乎意料的。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from datetime import datetime

def diff_month(start_date,end_date):
    qty_month = ((end_date.year - start_date.year) * 12) + (end_date.month - start_date.month)

    d_days = end_date.day - start_date.day

    if d_days >= 0:
        adjust = 0
    else:
        adjust = -1
    qty_month += adjust

    return qty_month

diff_month(datetime.date.today(),datetime(2019,08,24))


#Examples:
#diff_month(datetime(2018,02,12),datetime(2019,08,24)) = 18
#diff_month(datetime(2018,02,12),datetime(2018,08,10)) = 5


这工作…

1
2
3
4
5
6
7
8
9
10
11
12
from datetime import datetime as dt
from dateutil.relativedelta import relativedelta
def number_of_months(d1, d2):
    months = 0
    r = relativedelta(d1,d2)
    if r.years==0:
        months = r.months
    if r.years>=1:
        months = 12*r.years+r.months
    return months
#example
number_of_months(dt(2017,9,1),dt(2016,8,1))

假设你想知道日期所在月份的"分数",我知道,那么你需要做更多的工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from datetime import datetime, date
import calendar

def monthdiff(start_period, end_period, decimal_places = 2):
    if start_period > end_period:
        raise Exception('Start is after end')
    if start_period.year == end_period.year and start_period.month == end_period.month:
        days_in_month = calendar.monthrange(start_period.year, start_period.month)[1]
        days_to_charge = end_period.day - start_period.day+1
        diff = round(float(days_to_charge)/float(days_in_month), decimal_places)
        return diff
    months = 0
    # we have a start date within one month and not at the start, and an end date that is not
    # in the same month as the start date
    if start_period.day > 1:
        last_day_in_start_month = calendar.monthrange(start_period.year, start_period.month)[1]
        days_to_charge = last_day_in_start_month - start_period.day +1
        months = months + round(float(days_to_charge)/float(last_day_in_start_month), decimal_places)
        start_period = datetime(start_period.year, start_period.month+1, 1)

    last_day_in_last_month = calendar.monthrange(end_period.year, end_period.month)[1]
    if end_period.day != last_day_in_last_month:
        # we have lest days in the last month
        months = months + round(float(end_period.day) / float(last_day_in_last_month), decimal_places)
        last_day_in_previous_month = calendar.monthrange(end_period.year, end_period.month - 1)[1]
        end_period = datetime(end_period.year, end_period.month - 1, last_day_in_previous_month)

    #whatever happens, we now have a period of whole months to calculate the difference between

    if start_period != end_period:
        months = months + (end_period.year - start_period.year) * 12 + (end_period.month - start_period.month) + 1

    # just counter for any final decimal place manipulation
    diff = round(months, decimal_places)
    return diff

assert monthdiff(datetime(2015,1,1), datetime(2015,1,31)) == 1
assert monthdiff(datetime(2015,1,1), datetime(2015,02,01)) == 1.04
assert monthdiff(datetime(2014,1,1), datetime(2014,12,31)) == 12
assert monthdiff(datetime(2014,7,1), datetime(2015,06,30)) == 12
assert monthdiff(datetime(2015,1,10), datetime(2015,01,20)) == 0.35
assert monthdiff(datetime(2015,1,10), datetime(2015,02,20)) == 0.71 + 0.71
assert monthdiff(datetime(2015,1,31), datetime(2015,02,01)) == round(1.0/31.0,2) + round(1.0/28.0,2)
assert monthdiff(datetime(2013,1,31), datetime(2015,02,01)) == 12*2 + round(1.0/31.0,2) + round(1.0/28.0,2)

提供一个示例,计算两个日期之间的月数(包括日期所在的每个月的分数)。这意味着您可以计算出在2015-01-20和2015-02-14之间有多少个月,其中1月份的日期分数由1月份的天数决定;或者同样考虑到2月份的天数可以逐年变化。

作为参考,此代码也位于github上-https://gist.github.com/andrewyager/6B9284A4F1CDB1779B10


我刚才真的需要做点类似的事情

最后编写了一个函数,该函数返回两组日期之间每个月的startend的元组列表,这样我就可以从后面写下一些SQL查询,用于每月的销售总额等。

我相信有人知道他们在做什么,但希望能有所帮助…

返回值如下(以今天到今天的365天为例生成)

1
2
3
4
5
6
7
8
9
10
11
12
13
[   (datetime.date(2013, 5, 1), datetime.date(2013, 5, 31)),
    (datetime.date(2013, 6, 1), datetime.date(2013, 6, 30)),
    (datetime.date(2013, 7, 1), datetime.date(2013, 7, 31)),
    (datetime.date(2013, 8, 1), datetime.date(2013, 8, 31)),
    (datetime.date(2013, 9, 1), datetime.date(2013, 9, 30)),
    (datetime.date(2013, 10, 1), datetime.date(2013, 10, 31)),
    (datetime.date(2013, 11, 1), datetime.date(2013, 11, 30)),
    (datetime.date(2013, 12, 1), datetime.date(2013, 12, 31)),
    (datetime.date(2014, 1, 1), datetime.date(2014, 1, 31)),
    (datetime.date(2014, 2, 1), datetime.date(2014, 2, 28)),
    (datetime.date(2014, 3, 1), datetime.date(2014, 3, 31)),
    (datetime.date(2014, 4, 1), datetime.date(2014, 4, 30)),
    (datetime.date(2014, 5, 1), datetime.date(2014, 5, 31))]

代码如下(包含一些可以删除的调试内容):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#! /usr/env/python
import datetime

def gen_month_ranges(start_date=None, end_date=None, debug=False):
    today = datetime.date.today()
    if not start_date: start_date = datetime.datetime.strptime(
       "{0}/01/01".format(today.year),"%Y/%m/%d").date()  # start of this year
    if not end_date: end_date = today
    if debug: print("Start: {0} | End {1}".format(start_date, end_date))

    # sense-check
    if end_date < start_date:
        print("Error. Start Date of {0} is greater than End Date of {1}?!".format(start_date, end_date))
        return None

    date_ranges = []  # list of tuples (month_start, month_end)

    current_year = start_date.year
    current_month = start_date.month

    while current_year <= end_date.year:
        next_month = current_month + 1
        next_year = current_year
        if next_month > 12:
            next_month = 1
            next_year = current_year + 1

        month_start = datetime.datetime.strptime(
           "{0}/{1}/01".format(current_year,
                                current_month),"%Y/%m/%d").date()  # start of month
        month_end = datetime.datetime.strptime(
           "{0}/{1}/01".format(next_year,
                                next_month),"%Y/%m/%d").date()  # start of next month
        month_end  = month_end+datetime.timedelta(days=-1)  # start of next month less one day

        range_tuple = (month_start, month_end)
        if debug: print("Month runs from {0} --> {1}".format(
            range_tuple[0], range_tuple[1]))
        date_ranges.append(range_tuple)

        if current_month == 12:
            current_month = 1
            current_year += 1
            if debug: print("End of year encountered, resetting months")
        else:
            current_month += 1
            if debug: print("Next iteration for {0}-{1}".format(
                current_year, current_month))

        if current_year == end_date.year and current_month > end_date.month:
            if debug: print("Final month encountered. Terminating loop")
            break

    return date_ranges


if __name__ == '__main__':
    print("Running in standalone mode. Debug set to True")
    from pprint import pprint
    pprint(gen_month_ranges(debug=True), indent=4)
    pprint(gen_month_ranges(start_date=datetime.date.today()+datetime.timedelta(days=-365),
                            debug=True), indent=4)

答案似乎不令人满意,因此我使用了自己的更容易理解的代码。

1
2
3
4
5
6
7
8
9
10
11
12
from datetime import datetime
from dateutil import relativedelta

date1 = datetime.strptime(str('2017-01-01'), '%Y-%m-%d')
date2 = datetime.strptime(str('2019-03-19'), '%Y-%m-%d')

difference = relativedelta.relativedelta(date2, date1)
months = difference.months
years = difference.years
# add in the number of months (12) for difference in years
months += 12 * difference.years
months

以下是一种方法:

1
2
3
4
5
6
7
8
def months_between(start_dt, stop_dt):
    month_list = []
    total_months = 12*(stop_dt.year-start_dt.year)+(stop_dt.month-start_d.month)+1
    if total_months > 0:
        month_list=[ datetime.date(start_dt.year+int((start_dt+i-1)/12),
                                   ((start_dt-1+i)%12)+1,
                                   1) for i in xrange(0,total_months) ]
    return month_list

这是首先计算两个日期之间(包括这两个日期)的月总数。然后,它使用第一个日期作为基础创建一个列表,并执行模运算来创建日期对象。


这是我的方法:

1
2
3
4
Start_date ="2000-06-01"
End_date   ="2001-05-01"

month_num  = len(pd.date_range(start = Start_date[:7], end = End_date[:7] ,freq='M'))+1

我只是用这个月来创建一个日期范围并计算长度。


试试这个:

1
2
3
4
5
6
7
8
9
10
 dateRange = [datetime.strptime(dateRanges[0],"%Y-%m-%d"),
             datetime.strptime(dateRanges[1],"%Y-%m-%d")]
delta_time = max(dateRange) - min(dateRange)
#Need to use min(dateRange).month to account for different length month
#Note that timedelta returns a number of days
delta_datetime = (datetime(1, min(dateRange).month, 1) + delta_time -
                           timedelta(days=1)) #min y/m/d are 1
months = ((delta_datetime.year - 1) * 12 + delta_datetime.month -
          min(dateRange).month)
print months

不管你输入日期的顺序是什么,它都会考虑月份长度的差异。


假设upperdate总是晚于lowerdate,并且两者都是datetime.date对象:

1
2
3
4
5
6
7
if lowerDate.year == upperDate.year:
    monthsInBetween = range( lowerDate.month + 1, upperDate.month )
elif upperDate.year > lowerDate.year:
    monthsInBetween = range( lowerDate.month + 1, 12 )
    for year in range( lowerDate.year + 1, upperDate.year ):
        monthsInBetween.extend( range(1,13) )
    monthsInBetween.extend( range( 1, upperDate.month ) )

我还没有彻底测试过这个,但看起来它应该起作用。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import datetime
from calendar import monthrange

def date_dif(from_date,to_date):   # Изчислява разлика между две дати
    dd=(to_date-from_date).days
    if dd>=0:
        fromDM=from_date.year*12+from_date.month-1
        toDM=to_date.year*12+to_date.month-1
        mlen=monthrange(int((toDM)/12),(toDM)%12+1)[1]
        d=to_date.day-from_date.day
        dm=toDM-fromDM
        m=(dm-int(d<0))%12
        y=int((dm-int(d<0))/12)
        d+=int(d<0)*mlen
        # diference in Y,M,D, diference months,diference  days, days in to_date month
        return[y,m,d,dm,dd,mlen]
    else:
        return[0,0,0,0,dd,0]


您可以使用如下内容:

1
2
3
4
5
6
import datetime
days_in_month = 365.25 / 12  # represent the average of days in a month by year
month_diff = lambda end_date, start_date, precision=0: round((end_date - start_date).days / days_in_month, precision)
start_date = datetime.date(1978, 12, 15)
end_date = datetime.date(2012, 7, 9)
month_diff(end_date, start_date)  # should show 403.0 months

更新2018-04-20:似乎OP@Joshkunz在询问两个日期之间的月份,而不是两个日期之间的"多少个月"。所以我不知道为什么@johnlarooy被投了100多票。@Joshkunz在最初问题下的评论中指出,他想要的是实际日期(或月份),而不是总的月份数。

因此,这似乎是一个需要解决的问题,因为在两个日期之间,2018-04-112018-06-01

1
Apr 2018, May 2018, June 2018

如果是在2014-04-112018-06-01之间呢?那么答案就是

1
Apr 2014, May 2014, ..., Dec 2014, Jan 2015, ..., Jan 2018, ..., June 2018

这就是为什么多年前我有下面的伪代码。它只是建议使用这两个月作为结束点并循环使用它们,每次增加一个月。@Joshkunz提到他想要"月",他还提到他想要"日期",在不知道确切日期的情况下,很难写出准确的代码,但其想法是使用一个简单的循环来循环遍历端点,并一次递增一个月。

答案是8年前的2010年:

如果增加一个星期,那么它将完成大约4.35倍的工作所需的工作。为什么不只是:

1
2
3
4
5
6
7
8
1. get start date in array of integer, set it to i: [2008, 3, 12],
       and change it to [2008, 3, 1]
2. get end date in array: [2010, 10, 26]
3. add the date to your result by parsing i
       increment the month in i
       if month is >= 13, then set it to 1, and increment the year by 1
   until either the year in i is > year in end_date,
           or (year in i == year in end_date and month in i > month in end_date)

只是现在的psedoo代码,还没有测试过,但我认为这条线的想法是可行的。