简体   繁体   English

查找两个日期之间的月份的最佳方法

[英]Best way to find the months between two dates

I have the need to be able to accurately find the months between two dates in python. I have a solution that works but its not very good (as in elegant) or fast.我需要能够准确地找到 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

So just to explain, what I'm doing here is taking the two dates and converting them from iso format into python datetime objects.所以只是为了解释一下,我在这里所做的是获取两个日期并将它们从 iso 格式转换为 python 日期时间对象。 Then I loop through adding a week to the start datetime object and check if the numerical value of the month is greater (unless the month is December then it checks if the date is less), If the value is greater I append it to the list of months and keep looping through until I get to my end date.然后我循环将一周添加到开始日期时间 object 并检查月份的数值是否更大(除非月份是十二月然后它检查日期是否更小),如果值更大我 append 它到列表几个月并不断循环直到我到达我的结束日期。

It works perfectly it just doesn't seem like a good way of doing it...它工作得很好,只是看起来不是一个好方法......

Start by defining some test cases, then you will see that the function is very simple and needs no loops首先定义一些测试用例,然后你会看到函数非常简单,不需要循环

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

You should add some test cases to your question, as there are lots of potential corner cases to cover - there is more than one way to define the number of months between two dates.您应该在您的问题中添加一些测试用例,因为有很多潜在的极端案例需要涵盖 - 定义两个日期之间的月数的方法不止一种。

One liner to find a list of datetimes, incremented by month, between two dates.一个班轮查找两个日期之间的日期时间列表,按月递增。

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

This worked for me -这对我有用 -

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 + (12*r.years)

You can easily calculate this using rrule from dateutil module:您可以使用dateutil模块中的 rrule 轻松计算:

from dateutil import rrule
from datetime import date

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

will give you:会给你:

 [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)]
from dateutil import relativedelta

r = relativedelta.relativedelta(date1, date2)

months_difference = (r.years * 12) + r.months

Get the ending month (relative to the year and month of the start month ex: 2011 January = 13 if your start date starts on 2010 Oct) and then generate the datetimes beginning the start month and that end month like so:获取结束月份(相对于开始月份的年份和月份,例如:如果您的开始日期从 2010 年 10 月开始,则 2011 年 1 月 = 13),然后生成开始月份和结束月份的日期时间,如下所示:

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

if both dates are on the same year, it could also be simply written as:如果两个日期都在同一年,也可以简单地写成:

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

This post nails it! 这篇文章一针见血! Use dateutil.relativedelta .使用dateutil.relativedelta

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

My simple solution:我的简单解决方案:

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

Update 2018-04-20: it seems that OP @Joshkunz was asking for finding which months are between two dates, instead of "how many months" are between two dates. 2018-04-20更新:似乎 OP @Joshkunz 要求查找两个日期之间的月份,而不是两个日期之间的“多少个月”。 So I am not sure why @JohnLaRooy is upvoted for more than 100 times.所以我不确定为什么@JohnLaRooy 被投票超过 100 次。 @Joshkunz indicated in the comment under the original question he wanted the actual dates [or the months], instead of finding the total number of months . @Joshkunz 在原始问题下的评论中指出,他想要实际日期 [或月份],而不是找到总月数

So it appeared the question wanted, for between two dates 2018-04-11 to 2018-06-01因此,在2018-04-11年 4 月 11 日到2018-06-01年 6 月 1 日这两个日期之间,出现了想要的问题

Apr 2018, May 2018, June 2018 

And what if it is between 2014-04-11 to 2018-06-01 ?如果它在2014-04-112018-06-01之间怎么办? Then the answer would be那么答案将是

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

So that's why I had the following pseudo code many years ago.所以这就是我多年前有以下伪代码的原因。 It merely suggested using the two months as end points and loop through them, incrementing by one month at a time.它只是建议使用两个月作为终点并循环它们,一次增加一个月。 @Joshkunz mentioned he wanted the "months" and he also mentioned he wanted the "dates", without knowing exactly, it was difficult to write the exact code, but the idea is to use one simple loop to loop through the end points, and incrementing one month at a time. @Joshkunz 提到他想要“月份”,他还提到他想要“日期”,在不确切知道的情况下,很难编写确切的代码,但想法是使用一个简单的循环来遍历端点,并且一个月递增一次。

The answer 8 years ago in 2010: 8年前的2010年的答案:

If adding by a week, then it will approximately do work 4.35 times the work as needed.如果增加一周,那么它的工作量大约是所需工作量的 4.35 倍。 Why not just:为什么不只是:

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)

just pseduo code for now, haven't tested, but i think the idea along the same line will work.现在只是伪代码,尚未测试,但我认为同一行的想法会奏效。

Define a "month" as 1 / 12 year, then do this:将“月”定义为1 / 12年,然后执行以下操作:

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

You might try to define a month as "a period of either 29, 28, 30 or 31 days (depending on the year)".您可以尝试将一个月定义为“29、28、30 或 31 天(取决于年份)的时间段”。 But you you do that, you have an additional problem to solve.但是你这样做了,你还有一个额外的问题要解决。

While it's usually clear that June 15 th + 1 month should be July 15 th , it's not usually not clear if January 30 th + 1 month is in February or March.虽然通常很清楚 6 月 15+ 1 个月应该是 7 月 15,但通常不清楚 1 月 30+ 1 个月是在 2 月还是 3 月。 In the latter case, you may be compelled to compute the date as February 30 th , then "correct" it to March 2 nd .在后一种情况下,您可能不得不将日期计算为 2 月 30,然后将其“更正”为 3 月 2 But when you do that, you'll find that March 2 nd - 1 month is clearly February 2 nd .但是当你这样做时,你会发现 March 2 nd - 1 月显然是 February 2 nd Ergo, reductio ad absurdum (this operation is not well defined).因此,reductio ad absurdum(这个操作没有很好的定义)。

Here's how to do this with Pandas FWIW:以下是如何使用 Pandas FWIW 执行此操作:

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

Notice it starts with the month after the given start date.请注意,它从给定开始日期的月份开始。

Many people have already given you good answers to solve this but I have not read any using list comprehension so I give you what I used for a similar use case :许多人已经给了你很好的答案来解决这个问题,但我还没有阅读任何使用列表理解的内容,所以我给你我在类似用例中使用的内容:


def compute_months(first_date, second_date):
    year1, month1, year2, month2 = map(
        int, 
        (first_date[:4], first_date[5:7], second_date[:4], second_date[5:7])
    )

    return [
        '{:0>4}-{:0>2}'.format(year, month)
        for year in range(year1, year2 + 1)
        for month in range(month1 if year == year1 else 1, month2 + 1 if year == year2 else 13)
    ]

>>> first_date = "2016-05"
>>> second_date = "2017-11"
>>> compute_months(first_date, second_date)
['2016-05',
 '2016-06',
 '2016-07',
 '2016-08',
 '2016-09',
 '2016-10',
 '2016-11',
 '2016-12',
 '2017-01',
 '2017-02',
 '2017-03',
 '2017-04',
 '2017-05',
 '2017-06',
 '2017-07',
 '2017-08',
 '2017-09',
 '2017-10',
 '2017-11']

Somewhat a little prettified solution by @Vin-G. @Vin-G 有点美化的解决方案。

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)

You can also use the arrow library.您还可以使用箭头库。 This is a simple example:这是一个简单的例子:

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

This will print:这将打印:

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

Hope this helps!希望这可以帮助!

There is a simple solution based on 360 day years, where all months have 30 days.有一个基于 360 天年的简单解决方案,其中所有月份都有 30 天。 It fits most use cases where, given two dates, you need to calculate the number of full months plus the remaining days.它适用于大多数用例,在这些用例中,给定两个日期,您需要计算整月数加上剩余天数。

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

Try something like this.尝试这样的事情。 It presently includes the month if both dates happen to be in the same month.如果两个日期恰好在同一个月份,则它目前包括月份。

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

Output looks like:输出如下所示:

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

Get difference in number of days, months and years between two dates.获取两个日期之间的天数、月数和年数的差异。

import datetime    
from dateutil.relativedelta import relativedelta


iphead_proc_dt = datetime.datetime.now()
new_date = iphead_proc_dt + relativedelta(months=+25, days=+23)

# Get Number of Days difference bewtween two dates
print((new_date - iphead_proc_dt).days)

difference = relativedelta(new_date, iphead_proc_dt)

# Get Number of Months difference bewtween two dates
print(difference.months + 12 * difference.years)

# Get Number of Years difference bewtween two dates
print(difference.years)

just like range function, when month is 13 , go to next year就像range函数一样,当月份为13时,转到下一年

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

example in python shell python shell中的示例

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

It can be done using datetime.timedelta, where the number of days for skipping to next month can be obtained by calender.monthrange.可以使用datetime.timedelta来完成,其中跳到下个月的天数可以通过calender.monthrange获取。 monthrange returns weekday (0-6 ~ Mon-Sun) and number of days (28-31) for a given year and month. monthrange 返回给定年份和月份的工作日 (0-6 ~ Mon-Sun) 和天数 (28-31)。
For example: monthrange(2017, 1) returns (6,31).例如:monthrange(2017, 1) 返回 (6,31)。

Here is the script using this logic to iterate between two months.这是使用此逻辑在两个月之间迭代的脚本。

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

` `

from datetime import datetime
from dateutil import relativedelta

def get_months(d1, d2):
    date1 = datetime.strptime(str(d1), '%Y-%m-%d')
    date2 = datetime.strptime(str(d2), '%Y-%m-%d')
    print (date2, date1)
    r = relativedelta.relativedelta(date2, date1)
    months = r.months +  12 * r.years
    if r.days > 0:
        months += 1
    print (months)
    return  months


assert  get_months('2018-08-13','2019-06-19') == 11
assert  get_months('2018-01-01','2019-06-19') == 18
assert  get_months('2018-07-20','2019-06-19') == 11
assert  get_months('2018-07-18','2019-06-19') == 12
assert  get_months('2019-03-01','2019-06-19') == 4
assert  get_months('2019-03-20','2019-06-19') == 3
assert  get_months('2019-01-01','2019-06-19') == 6
assert  get_months('2018-09-09','2019-06-19') == 10
#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)

Usually 90 days are NOT 3 months literally, just a reference.通常 90 天并不是字面上的 3 个月,只是一个参考。

So, finally, you need to check if days are bigger than 15 to add +1 to month counter.因此,最后,您需要检查天数是否大于 15 才能将 +1 添加到月份计数器。 or better, add another elif with half month counter.或者更好的是,添加另一个带有半个月计数器的 elif。

From this other stackoverflow answer i've finally ended with that:this other stackoverflow answer我终于结束了:

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

Output:输出:

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

I've noticed that doesn't work if you add more than days left in current year, and that's is unexpected.我注意到,如果您添加的天数超过当年剩余的天数,这将不起作用,这是出乎意料的。

it seems that the answers are unsatisfactory and I have since use my own code which is easier to understand似乎答案不能令人满意,我已经使用了我自己的代码,这更容易理解

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

Here is my solution for this:这是我的解决方案:

def calc_age_months(from_date, to_date):
    from_date = time.strptime(from_date, "%Y-%m-%d")
    to_date = time.strptime(to_date, "%Y-%m-%d")

    age_in_months = (to_date.tm_year - from_date.tm_year)*12 + (to_date.tm_mon - from_date.tm_mon)

    if to_date.tm_mday < from_date.tm_mday:
        return age_in_months -1
    else
        return age_in_months

This will handle some edge cases as well where the difference in months between 31st Dec 2018 and 1st Jan 2019 will be zero (since the difference is only a day).这也将处理一些极端情况,其中 2018 年 12 月 31 日和 2019 年 1 月 1 日之间的月份差异为零(因为差异只有一天)。

I actually needed to do something pretty similar just now我现在实际上需要做一些非常相似的事情

Ended up writing a function which returns a list of tuples indicating the start and end of each month between two sets of dates so I could write some SQL queries off the back of it for monthly totals of sales etc.最后写了一个函数,它返回一个元组列表,指示两组日期之间每个月的startend ,这样我就可以在它的后面写一些 SQL 查询来获取每月的销售总额等。

I'm sure it can be improved by someone who knows what they're doing but hope it helps...我敢肯定,知道自己在做什么的人可以改进它,但希望对您有所帮助...

The returned value look as follows (generating for today - 365days until today as an example)返回值如下(以今天生成 - 到今天为止的 365 天为例)

[   (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))]

Code as follows (has some debug stuff which can be removed):代码如下(有一些可以删除的调试内容):

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

Assuming that you wanted to know the "fraction" of the month that dates were in, which I did, then you need to do a bit more work.假设您想知道日期所在月份的“分数”,我就是这样做的,那么您需要做更多的工作。

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)

provides an example that works out the number of months between two dates inclusively, including the fraction of each month that the date is in. This means that you can work out how many months is between 2015-01-20 and 2015-02-14, where the fraction of the date in the month of January is determined by the number of days in January;提供了一个示例,计算两个日期之间的月数,包括日期所在的每个月的分数。这意味着您可以计算出 2015-01-20 和 2015-02-14 之间的月数,其中一月份日期的分数由一月份的天数确定; or equally taking into account that the number of days in February can change form year to year.或者同样考虑到二月份的天数可以逐年变化。

For my reference, this code is also on github - https://gist.github.com/andrewyager/6b9284a4f1cdb1779b10供我参考,此代码也在 github 上 - https://gist.github.com/andrewyager/6b9284a4f1cdb1779b10

This works...这有效...

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

Assuming upperDate is always later than lowerDate and both are datetime.date objects:假设 upperDate 总是晚于 lowerDate 并且两者都是 datetime.date 对象:

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

I haven't tested this thoroughly, but it looks like it should do the trick.我还没有彻底测试过,但看起来它应该可以解决问题。

Try this:尝试这个:

 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

Shouldn't matter what order you input the dates, and it takes into account the difference in month lengths.输入日期的顺序无关紧要,它会考虑月份长度的差异。

Here is a method:这是一个方法:

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

This is first computing the total number of months between the two dates, inclusive.这是首先计算两个日期之间的总月数,包括两个日期。 Then it creates a list using the first date as the base and performs modula arithmetic to create the date objects.然后它使用第一个日期作为基础创建一个列表,并执行模运算来创建日期对象。

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

This is my way to do this:这是我这样做的方法:

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

I just use the month to create a date range and calculate the length.我只是使用月份来创建日期范围并计算长度。

To get the number of full months between two dates:要获取两个日期之间的整月数:

import datetime

def difference_in_months(start, end):
    if start.year == end.year:
        months = end.month - start.month
    else:
        months = (12 - start.month) + (end.month)

    if start.day > end.day:
        months = months - 1

    return months

You can use the below code to get month between two dates:您可以使用以下代码获取两个日期之间的月份:

OrderedDict(((start_date + timedelta(_)).strftime(date_format), None) for _ in xrange((end_date - start_date).days)).keys()

where start_date and end_date must be proper date and date_format is the format in which you want your result of date..其中start_dateend_date必须是正确的日期,而 date_format 是您希望日期结果的格式。

In your case, date_format will be %b %Y .在您的情况下, date_format将为%b %Y

The question, is really asking about the total months between 2 dates and not the difference of it问题,实际上是询问两个日期之间的总月数,而不是它的差异

Hence a revisited answer with some extra functionallity,因此,具有一些额外功能的重新审视的答案

from datetime import date, datetime
from dateutil.rrule import rrule, MONTHLY

def month_get_list(dt_to, dt_from, return_datetime=False, as_month=True):
    INDEX_MONTH_MAPPING = {1: 'january', 2: 'february', 3: 'march', 4: 'april', 5: 'may', 6: 'june', 7: 'july',
                           8: 'august',
                           9: 'september', 10: 'october', 11: 'november', 12: 'december'}
    if return_datetime:
        return [dt for dt in rrule(MONTHLY, dtstart=dt_from, until=dt_to)]
    if as_month:
        return [INDEX_MONTH_MAPPING[dt.month] for dt in rrule(MONTHLY, dtstart=dt_from, until=dt_to)]

    return [dt.month for dt in rrule(MONTHLY, dtstart=dt_from, until=dt_to)]

month_list = month_get_list(date(2021, 12, 31), date(2021, 1, 1))
total_months = len(month_list)

Result结果

month_list = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december']
total_months = 12

With as_month set to False as_month设置为 False

month_list = month_get_list(date(2021, 12, 31), date(2021, 1, 1), month_list = month_get_list(日期(2021, 12, 31), 日期(2021, 1, 1),

as_month=False)
# month_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

Returing datetime instead而是返回日期时间

month_list = month_get_list(date(2021, 12, 31), date(2021, 1, 1), return_datetime=True)
month_list = [datetime.datetime(2021, 1, 1, 0, 0), datetime.datetime(2021, 2, 1, 0, 0), datetime.datetime(2021, 3, 1, 0, 0), datetime.datetime(2021, 4, 1, 0, 0), datetime.datetime(2021, 5, 1, 0, 0), datetime.datetime(2021, 6, 1, 0, 0), datetime.datetime(2021, 7, 1, 0, 0), datetime.datetime(2021, 8, 1, 0, 0), datetime.datetime(2021, 9, 1, 0, 0), datetime.datetime(2021, 10, 1, 0, 0), datetime.datetime(2021, 11, 1, 0, 0), datetime.datetime(2021, 12, 1, 0, 0)]

If fractions of month are important to you, leverage the first day of the next month to work around the number of days each month may have, on a step year or not:如果月份的小数部分对您很重要,请利用下个月的第一天来计算每个月可能有的天数,无论是否在一个步骤年:

from datetime import timedelta, datetime
from dateutil.relativedelta import relativedelta

def month_fraction(this_date):
        this_date_df=this_date.strftime("%Y-%m-%d")
        day1_same_month_as_this_date_df=this_date_df[:8]+'01'
        day1_same_month_as_this_date=datetime.strptime(day1_same_month_as_this_date_df, '%Y-%m-%d').date()
        next_mon    th_as_this_date=this_date+relativedelta(months=1)
        next_month_as_this_date_df=next_month_as_this_date.strftime("%Y-%m-%d")
        day1_next_month_this_date_df=next_month_as_this_date_df[:8]+'01'
        day1_next_month_this_date=datetime.strptime(day1_next_month_this_date_df, '%Y-%m-%d').date()
        last_day_same_month_this_date=day1_next_month_this_date-timedelta(days=1)
        delta_days_from_month_beginning=(this_date-day1_same_month_as_this_date).days
        delta_days_whole_month=(last_day_same_month_this_date-day1_same_month_as_this_date).days    
        fraction_beginning_of_month=round(delta_days_from_month_beginning/delta_days_whole_month,4)
        return fraction_beginning_of_month

def delta_months_JLR(second_date,first_date):
        return (second_date.year - first_date.year) * 12 + second_date.month - first_date.month

def delta_months_float(first_date,second_date):
        outgoing_fraction_first_date  =  month_fraction(first_date)
        incoming_fraction_second_date  =  month_fraction(second_date)
        delta_months=delta_months_JLR(second_date,first_date) #as on John La Rooy’s response
        months_float=round(delta_months-outgoing_fraction_first_date+incoming_fraction_second_date,4)
        return months_float


first_date_df='2021-12-28'
first_date=datetime.strptime(first_date, '%Y-%m-%d').date()
second_date_df='2022-01-02'
second_date=datetime.strptime(second_date, '%Y-%m-%d').date()

print (delta_months_float(first_date,second_date))
0.1333

please give start and end date of the and below function will fine all month starting and ending date.请给出以下功能的开始和结束日期,所有月份的开始和结束日期都可以。

$months = $this->getMonthsInRange('2022-03-15', '2022-07-12');
    
    
    public function getMonthsInRange($startDate, $endDate)
            {
                $months = array();
                while (strtotime($startDate) <= strtotime($endDate)) {
                    $start_date = date('Y-m-d', strtotime($startDate));
                    $end_date = date("Y-m-t", strtotime($start_date));
                    if(strtotime($end_date) >= strtotime($endDate)) {
                        $end_date = $endDate;
                    }
                    $months[] = array(
                        'start_date' => $start_date,
                        'end_date' => $end_date
                    );
                    $startDate = date('01 M Y', strtotime($startDate . '+ 1 month'));
                }
                return $months;
            }

This works fine for Python 3这适用于 Python 3

import datetime

start = datetime.datetime.today()
end = start + datetime.timedelta(days=365)
month_range = [start.date()]
diff = end - start

months = round(diff.days / 30)
temp = start
for i in range(months):
    temp = temp + datetime.timedelta(days=30)
    if temp <= end:
        month_range.append(temp.date())

print(month_range)

You could use something like:你可以使用类似的东西:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM