Лучший способ найти месяцы между двумя датами - PullRequest
77 голосов
/ 28 октября 2010

У меня должна быть возможность точно найти месяцы между двумя датами в 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. Затем я циклически добавляю неделю к начальному объекту datetime и проверяю, больше ли числовое значение месяца (если месяц не декабрь, тогда он проверяет, меньше ли дата), Если значение больше, я добавляю его в список. месяцев и продолжайте цикл, пока я не достигну своей конечной даты.

Это прекрасно работает, просто не похоже на хороший способ сделать это ...

Ответы [ 31 ]

3 голосов
/ 28 октября 2010

Попробуйте что-нибудь подобное. В настоящее время он включает месяц, если обе даты совпадают.

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

Вывод выглядит так:

>>> start = datetime.now() - timedelta(days=120)
>>> end = datetime.now()
>>> months_between(start,end)
[6, 7, 8, 9, 10]
3 голосов
/ 29 апреля 2018

Мое простое решение:

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))
2 голосов
/ 28 октября 2010

Определите "месяц" как 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

Вы можете попытаться определить месяц как "период29, 28, 30 или 31 дней (в зависимости от года) ".Но если вы это сделаете, вам нужно решить еще одну проблему.

Хотя обычно ясно, что 15 июня th + 1 месяц должен быть 15 июля th , обычно не ясно, 30 января th + 1 месяц в феврале или марте.В последнем случае вы можете быть вынуждены вычислить дату как 30 февраля го , а затем "исправить" ее до 2 и .Но когда вы сделаете это, вы обнаружите, что 2 марта и - 1 месяц явно 2 февраля и .Ergo, reductio ad absurdum (эта операция четко не определена).

1 голос
/ 29 февраля 2016

Обычно 90 дней НЕ 3 месяца в буквальном смысле, просто ссылка.

Итак, наконец, вам нужно проверить, больше ли дней 15, чтобы добавить +1 к счетчику месяцев. или лучше, добавьте еще один elif со счетчиком полугодий.

С этот другой ответ stackoverflow Я наконец-то закончил с этим:

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

Выход:

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

Я заметил, что это не сработает, если вы добавите больше дней, оставшихся в текущем году, и это неожиданно.

1 голос
/ 17 августа 2012
#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)
1 голос
/ 24 июля 2018

Это можно сделать с помощью datetime.timedelta, где calender.monthrange может получить количество дней для пропуска до следующего месяца. monthrange возвращает день недели (0-6 ~ пн-вс) и количество дней (28-31) для данного года и месяца.
Например: monthrange (2017, 1) возвращает (6,31).

Вот скрипт, использующий эту логику для повторения между двумя месяцами.

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

`

1 голос
/ 14 марта 2019

кажется, что ответы неудовлетворительные, и с тех пор я использую свой собственный код, который легче понять

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 голос
/ 16 декабря 2015

как функция 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

пример в оболочке Python

>>> 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)]
1 голос
/ 26 апреля 2017

Вот как это сделать с 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')

Обратите внимание, что он начинается с месяца после указанной даты начала.

0 голосов
/ 26 апреля 2019

Это мой способ сделать это:

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

Я просто использую месяц, чтобы создать диапазон дат и вычислить длину.

...