计算两个日期之间的月份的最佳方法

145 投票
42 回答
283790 浏览
提问于 2025-04-16 06:12

我需要在Python中准确找到两个日期之间的月份。我有一个解决方案可以用,但感觉不太好(既不优雅也不快)。

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的日期时间对象。然后我通过一个循环,每次给开始的日期加上一周,检查这个月份的数字值是否更大(如果是12月,就检查日期是否更小)。如果值更大,我就把这个月份加到月份列表里,然后继续循环,直到到达结束日期。

这个方法是有效的,只是感觉不是一个好的做法……

42 个回答

63

这里有一个简单的代码,可以用来找出两个日期之间每个月的日期列表。

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

首先定义一些测试案例,这样你会发现这个函数非常简单,不需要使用循环。

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

你应该在你的问题中添加一些测试案例,因为有很多可能的特殊情况需要考虑——计算两个日期之间的月份数量的方法不止一种。

6

更新于2018年4月20日: 看起来提问者 @Joshkunz 是想知道在两个日期之间的 哪些月份,而不是“有多少个月”。所以我不太明白为什么 @JohnLaRooy 的回答会被点赞超过100次。@Joshkunz 在原问题下的评论中提到他想要的是实际的日期(或者月份),而不是计算 总共有多少个月

所以问题的意思是想知道在两个日期 2018-04-112018-06-01 之间的月份。

Apr 2018, May 2018, June 2018 

如果是从 2014-04-112018-06-01 呢?那么答案会是:

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

这就是我多年前写的伪代码的原因。它只是建议使用这两个日期作为起点,然后逐月循环,逐步增加一个月。@Joshkunz 提到他想要“月份”,还提到他想要“日期”,但没有明确说明,所以写出确切的代码比较困难。不过思路是用一个简单的循环来遍历这些日期,每次增加一个月。

8年前,也就是2010年的回答:

如果按周增加,那么大约需要做4.35倍的工作。为什么不直接:

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)

现在只是伪代码,没测试过,但我觉得这个思路应该是可行的。

撰写回答