Beste manier om de maanden tussen twee datums te vinden

Ik heb de behoefte om de maanden tussen twee datums in python nauwkeurig te kunnen vinden. Ik heb een oplossing die werkt, maar het is niet erg goed (zoals in elegant) of snel.

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

Dus om het uit te leggen, wat ik hier doe, is de twee datums nemen en ze converteren van iso-formaat naar python datetime-objecten. Vervolgens voeg ik een week toe aan het start datetime-object en controleer ik of de numerieke waarde van de maand groter is (tenzij de maand december is, dan wordt gecontroleerd of de datum kleiner is). Als de waarde groter is, voeg ik deze toe aan de lijst maanden en blijf doorlopen tot ik mijn einddatum heb bereikt.

Het werkt perfect, het lijkt alleen geen goede manier om het te doen…


Antwoord 1, autoriteit 100%

Begin met het definiëren van enkele testgevallen, dan zul je zien dat de functie heel eenvoudig is en geen lussen nodig heeft

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

Je zou wat testgevallen aan je vraag moeten toevoegen, aangezien er veel potentiële hoekgevallen zijn om te behandelen – er is meer dan één manier om het aantal maanden tussen twee datums te bepalen.


Antwoord 2, autoriteit 24%

Eén regel om een ​​lijst met datums te vinden, opgehoogd per maand, tussen twee datums.

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

Antwoord 3, autoriteit 19%

Dit werkte voor mij –

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)

Antwoord 4, autoriteit 6%

Je kunt dit eenvoudig berekenen met rrule van de dateutilmodule:

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

geeft je:

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

Antwoord 5, autoriteit 4%

Krijg de eindmaand (ten opzichte van het jaar en de maand van de beginmaand, bijvoorbeeld: 2011 januari = 13 als uw startdatum begint op oktober 2010) en genereer vervolgens de datumtijden die beginnen met de beginmaand en die eindmaand als volgt:

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

als beide datums in hetzelfde jaar vallen, kan het ook gewoon worden geschreven als:

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

Antwoord 6, autoriteit 4%

from dateutil import relativedelta
r = relativedelta.relativedelta(date1, date2)
months_difference = (r.years * 12) + r.months

Antwoord 7, autoriteit 3%

Dit berichtmaakt het af! Gebruik 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

Antwoord 8, autoriteit 3%

Mijn eenvoudige oplossing:

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

Antwoord 9, autoriteit 3%

Definieer een “maand” als 1/12jaar en doe dan dit:

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

U kunt proberen een maand te definiëren als ‘een periode van 29, 28, 30 of 31 dagen (afhankelijk van het jaar)’. Maar als je dat doet, heb je een bijkomend probleem op te lossen.

Hoewel het meestal duidelijk is dat 15 junide+ 1 maand 15 julidemoet zijn, is het meestal niet duidelijk of 30 januaride+ 1 maand valt in februari of maart. In het laatste geval bent u mogelijk genoodzaakt om de datum te berekenen als 30 februarideen deze vervolgens te “corrigeren” tot 2 maartnd. Maar als je dat doet, zul je zien dat 2nd– 1 maand duidelijk 2 ndis. Ergo, reductio ad absurdum (deze operatie is niet goed gedefinieerd).


Antwoord 10, autoriteit 2%

Update 20-04-2018:het lijkt erop dat OP @Joshkunz vroeg om welke maandentussen twee datums liggen, in plaats van “hoeveel maanden” er tussen zitten twee data. Dus ik weet niet zeker waarom @JohnLaRooy meer dan 100 keer wordt gestemd. @Joshkunz gaf in de opmerking onder de oorspronkelijke vraag aan dat hij de werkelijke datums [of de maanden] wilde hebben, in plaats van het totale aantal maandente vinden.

Dus het leek erop dat de vraag gezocht werd, voor tussen twee data 2018-04-11tot 2018-06-01

Apr 2018, May 2018, June 2018 

En wat als het tussen 2014-04-11tot 2018-06-01is? Dan zou het antwoord zijn

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

Dus daarom had ik vele jaren geleden de volgende pseudo-code. Het stelde alleen voor om de twee maanden als eindpunten te gebruiken en er doorheen te bladeren, telkens met één maand. @Joshkunz zei dat hij de “maanden” wilde en hij zei ook dat hij de “datums” wilde hebben, zonder precies te weten, het was moeilijk om de exacte code te schrijven, maar het idee is om een ​​eenvoudige lus te gebruiken om door de eindpunten te lopen, en telkens met één maand oplopend.

Het antwoord 8 jaar geleden in 2010:

Als u een week toevoegt, zal het ongeveer 4,35 keer zoveel werk doen als nodig is. Waarom niet gewoon:

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)

voor nu alleen pseduo-code, niet getest, maar ik denk dat het idee in dezelfde lijn zal werken.


Antwoord 11, autoriteit 2%

Er is een eenvoudige oplossing gebaseerd op 360 dagen-jaren, waarbij alle maanden 30 dagen hebben.
Het past in de meeste gevallen waarin u, gegeven twee datums, het aantal volledige maanden plus de resterende dagen moet berekenen.

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

Antwoord 12, autoriteit 2%

Enigszins mooie oplossing van @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)

Antwoord 13, autoriteit 2%

Je kunt ook de bibliotheek arrowgebruiken. Dit is een eenvoudig voorbeeld:

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

Hiermee wordt afgedrukt:

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

Hopelijk helpt dit!


Antwoord 14, autoriteit 2%

Hier leest u hoe u dit doet met Panda’s 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')

Merk op dat het begint met de maand nade opgegeven startdatum.


Antwoord 15, autoriteit 2%

Veel mensen hebben je al goede antwoorden gegeven om dit op te lossen, maar ik heb geen lijstbegrip gelezen, dus ik geef je wat ik heb gebruikt voor een soortgelijk gebruik:


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

Antwoord 16

Probeer zoiets als dit. Het bevat momenteel de maand als beide datums in dezelfde maand vallen.

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

Uitvoer ziet er als volgt uit:

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

Antwoord 17

Je zou python-dateutilkunnen gebruiken. Zie Python: verschil van 2 datetimes in maanden


Antwoord 18

Het kan worden gedaan met datetime.timedelta, waar het aantal dagen voor het overslaan naar de volgende maand kan worden verkregen door calender.monthrange. maandbereik retourneert weekdag (0-6 ~ ma-zo) en aantal dagen (28-31) voor een bepaald jaar en maand.
Bijvoorbeeld: maandbereik(2017, 1) geeft als resultaat (6,31).

Hier is het script dat deze logica gebruikt om tussen twee maanden te herhalen.

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

`


Antwoord 19

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

Antwoord 20

Haal het verschil in aantal dagen, maanden en jaren tussen twee datums.

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)

Antwoord 21

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

Antwoord 22

net als de functie range, wanneer de maand 13is, ga naar volgend jaar

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

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

Antwoord 23

Gewoonlijk zijn 90 dagen GEEN 3 maanden letterlijk, slechts een referentie.

Dus ten slotte moet je controleren of dagen groter zijn dan 15 om +1 toe te voegen aan de maandteller. of beter, voeg nog een elif toe met een teller van een halve maand.

Van dit andere stackoverflow-antwoordben ik daar eindelijk mee geëindigd:

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

Uitvoer:

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

Ik heb gemerkt dat niet werkt als je meer toe te voegen dan dagen over in het lopende jaar, en dat is onverwacht.

Other episodes