Muszę być w stanie dokładnie znaleźć miesiące między dwiema datami w Pythonie. Mam rozwiązanie, które działa, ale nie jest zbyt dobre (jak eleganckie) lub szybkie.
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
Aby to wyjaśnić, robię tutaj dwie daty i konwertuję je z formatu iso na obiekty datetime w Pythonie. Następnie przechodzę w pętlę przez dodanie tygodnia do obiektu początkowej daty i godziny i sprawdzam, czy wartość liczbowa miesiąca jest większa (chyba że miesiącem jest grudzień, wtedy sprawdza, czy data jest mniejsza), jeśli wartość jest większa, dołączam ją do listy miesięcy i powtarzaj, aż dojdę do daty zakończenia.
Działa doskonale, ale nie wydaje się dobrym sposobem na zrobienie tego ...
python
datetime
monthcalendar
date-math
Joshkunz
źródło
źródło
Odpowiedzi:
Aktualizacja 2018-04-20: wygląda na to, że OP @Joshkunz pytał, które miesiące są między dwiema datami, zamiast „ile miesięcy” jest między dwiema datami. Więc nie jestem pewien, dlaczego @JohnLaRooy jest głosowany za ponad 100 razy. @Joshkunz wskazał w komentarzu pod pierwotnym pytaniem, że chciał poznać rzeczywiste daty [lub miesiące], zamiast znaleźć całkowitą liczbę miesięcy .
Wydawało się więc, że było to pożądane pytanie, od dwóch dat
2018-04-11
do2018-06-01
Apr 2018, May 2018, June 2018
A co, jeśli jest między
2014-04-11
do2018-06-01
? Wtedy odpowiedź byłabyApr 2014, May 2014, ..., Dec 2014, Jan 2015, ..., Jan 2018, ..., June 2018
Dlatego wiele lat temu miałem następujący pseudokod. Po prostu zasugerował użycie dwóch miesięcy jako punktów końcowych i zapętlenie ich, zwiększając o jeden miesiąc na raz. @Joshkunz wspomniał, że chciałby mieć „miesiące”, a także wspomniał, że chciałby mieć „daty”, nie wiedząc dokładnie. Trudno było napisać dokładny kod, ale chodzi o to, aby użyć jednej prostej pętli do zapętlenia punktów końcowych i narastająco o jeden miesiąc.
Odpowiedź 8 lat temu w 2010 roku:
Jeśli dodasz o tydzień, będzie działać około 4,35 razy więcej niż potrzeba. Dlaczego nie tylko:
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)
na razie tylko kod pseduo, nie testowałem, ale myślę, że pomysł z tej samej linii zadziała.
źródło
1
do2
, a potem od2
do3
później.Zacznij od zdefiniowania kilku przypadków testowych, a zobaczysz, że funkcja jest bardzo prosta i nie wymaga pętli
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
Powinieneś dodać kilka przypadków testowych do swojego pytania, ponieważ istnieje wiele potencjalnych przypadków narożnych do omówienia - istnieje więcej niż jeden sposób określenia liczby miesięcy między dwiema datami.
źródło
Jedna linijka, aby znaleźć listę dat dat, zwiększoną o miesiąc, między dwiema datami.
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)]
źródło
[dt for dt in rrule(MONTHLY, bymonthday=10,dtstart=strt_dt, until=end_dt)]
start_date_first = start_date.replace(day=1), end_date_first = end_date.replace(day=1)
wtedy reguła prawidłowo zlicza miesiące.To zadziałało dla mnie -
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)
źródło
str()
wokół literałów łańcuchowych.r.months
będzieMożesz to łatwo obliczyć używając rrule from dateutil module:
from dateutil import rrule from datetime import date print(list(rrule.rrule(rrule.MONTHLY, dtstart=date(2013, 11, 1), until=date(2014, 2, 1))))
da tobie:
[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)]
źródło
Pobierz miesiąc końcowy (w stosunku do roku i miesiąca miesiąca początkowego, np .: styczeń 2011 = 13, jeśli data początkowa zaczyna się w październiku 2010 r.), A następnie wygeneruj daty rozpoczynające się od miesiąca początkowego i kończącego w następujący sposób:
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) )]
jeśli obie daty przypadają w tym samym roku, można to również zapisać jako:
dates=[datetime.datetime(year=dt1.year, month=mn, day=1) for mn in range(dt1.month, dt2.month + 1)]
źródło
Ten post jest świetny! Użyj
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
źródło
str()
obsada, którą zrobiłem, jest zupełnie niepotrzebna.relativedelta.relativedelta(date2, date1).years * 12
delta.years*12 + delta.months
Moje proste rozwiązanie:
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))
źródło
from dateutil import relativedelta relativedelta.relativedelta(date1, date2) months_difference = (r.years * 12) + r.months
źródło
Zdefiniuj „miesiąc” jako 1 / 12 rok, to zrobić:
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
Możesz spróbować zdefiniować miesiąc jako „okres 29, 28, 30 lub 31 dni (w zależności od roku)”. Ale robisz to, masz dodatkowy problem do rozwiązania.
Choć jest to zazwyczaj jasne, że 15 czerwca th + 1 miesiąc powinien być 15 lipca th , to nie nie jest zazwyczaj jasne, czy 30 stycznia th + 1 miesiąc to w lutym lub marcu. W tym ostatnim przypadku może być zmuszany do obliczyć datę w lutym 30 th , to „poprawne” go do 2 marca nd . Ale gdy to zrobisz, przekonasz się, że 2 marca nd - 1 miesiąc to wyraźnie 02 lutego nd . Ergo, reductio ad absurdum (ta operacja nie jest dobrze zdefiniowana).
źródło
Istnieje proste rozwiązanie oparte na 360-dniowych latach, gdzie wszystkie miesiące mają 30 dni. Pasuje do większości przypadków użycia, w których biorąc pod uwagę dwie daty, należy obliczyć liczbę pełnych miesięcy plus pozostałe dni.
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))
źródło
Nieco upiększone rozwiązanie autorstwa @ 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)
źródło
Możesz także skorzystać z biblioteki strzałek . Oto prosty przykład:
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')
Spowoduje to wydrukowanie:
1 January 2 February 3 March 4 April 5 May 6 June
Mam nadzieję że to pomoże!
źródło
Spróbuj czegoś takiego. Obecnie obejmuje miesiąc, jeśli obie daty przypadają w tym samym miesiącu.
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
Wynik wygląda następująco:
>>> start = datetime.now() - timedelta(days=120) >>> end = datetime.now() >>> months_between(start,end) [6, 7, 8, 9, 10]
źródło
Możesz użyć python-dateutil . Zobacz Python: różnica 2 datime w miesiącach
źródło
Oto jak to zrobić z 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')
Zauważ, że zaczyna się od miesiąca następującego po podanej dacie rozpoczęcia.
źródło
Można to zrobić za pomocą datetime.timedelta, gdzie liczbę dni do przeskoczenia do następnego miesiąca można uzyskać za pomocą kalendarza.monthrange. Monthrange zwraca dzień tygodnia (0-6 ~ Pon-Nd) i liczbę dni (28-31) dla danego roku i miesiąca.
Na przykład: monthrange (2017, 1) zwraca (6,31).
Oto skrypt używający tej logiki do iteracji między dwoma miesiącami.
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])
`
źródło
Wiele osób już udzieliło Ci dobrych odpowiedzi, aby rozwiązać ten problem, ale nie czytałem żadnego przy użyciu rozumienia list, więc daję ci to, czego użyłem w podobnym przypadku użycia:
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']
źródło
#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)
źródło
podobnie jak
range
funkcja, gdy miesiąc jest 13 , przejdź do następnego rokudef 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
przykład w powłoce Pythona
>>> 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)]
źródło
Zwykle 90 dni NIE jest dosłownie 3 miesiącami, tylko odniesieniem.
Na koniec musisz sprawdzić, czy dni są większe niż 15, aby dodać +1 do licznika miesięcy. lub lepiej, dodaj kolejny elif z licznikiem pół miesiąca.
Z tej innej odpowiedzi na stosy w końcu skończyłem na tym:
#/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 ")"
Wynik:
2016-02-29 2016-06-14 3 months 16 days around 4 months ( Feb Mar Apr May )
Zauważyłem, że to nie działa, jeśli dodasz więcej dni niż pozostało w bieżącym roku i jest to nieoczekiwane.
źródło
wydaje się, że odpowiedzi są niezadowalające i od tego czasu używam własnego kodu, który jest łatwiejszy do zrozumienia
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
źródło
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
źródło
Oto moje rozwiązanie:
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
Będzie to dotyczyło również niektórych przypadków skrajnych, w których różnica w miesiącach między 31 grudnia 2018 r. A 1 stycznia 2019 r. Będzie wynosić zero (ponieważ różnica jest tylko jednym dniem).
źródło
Zakładając, że upperDate jest zawsze późniejsza niż lowerDate i oba są obiektami 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 ) )
Nie testowałem tego dokładnie, ale wygląda na to, że powinno to załatwić sprawę.
źródło
Oto metoda:
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
To jest pierwsze obliczenie całkowitej liczby miesięcy między dwiema datami włącznie. Następnie tworzy listę, używając pierwszej daty jako podstawy i wykonuje arytmetykę modulową, aby utworzyć obiekty daty.
źródło
Właściwie musiałem teraz zrobić coś podobnego
Skończyło się na napisaniu funkcji, która zwraca listę krotek wskazujących
start
iend
każdego miesiąca między dwoma zestawami dat, więc mogłem napisać kilka zapytań SQL z tyłu dla miesięcznych sum sprzedaży itp.Jestem pewien, że może to poprawić ktoś, kto wie, co robi, ale mam nadzieję, że to pomoże ...
Zwracana wartość wygląda następująco (generowanie na dziś - 365 dni do dzisiaj jako przykład)
[ (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))]
Kod w następujący sposób (zawiera pewne elementy debugowania, które można usunąć):
#! /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)
źródło
Zakładając, że chciałeś znać „ułamek” miesiąca, w którym były daty, a ja to zrobiłem, musisz wykonać trochę więcej pracy.
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)
zawiera przykład, który wylicza liczbę miesięcy między dwiema datami włącznie, w tym ułamek każdego miesiąca, w którym data się znajduje. Oznacza to, że można obliczyć, ile miesięcy przypada między 20.01.2015 a 14.02.2015 , gdzie ułamek dnia w miesiącu styczniu zależy od liczby dni w styczniu; lub też biorąc pod uwagę, że liczba dni w lutym może zmieniać się z roku na rok.
Dla mojego odniesienia ten kod jest również na github - https://gist.github.com/andrewyager/6b9284a4f1cdb1779b10
źródło
Spróbuj tego:
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
Nie powinno mieć znaczenia, w jakiej kolejności wprowadzasz daty, i uwzględnia różnicę w długościach miesięcy.
źródło
To działa...
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))
źródło