Der beste Weg, um die Monate zwischen zwei Daten zu finden


91

Ich muss in der Lage sein, die Monate zwischen zwei Daten in Python genau zu finden. Ich habe eine Lösung, die funktioniert, aber nicht sehr gut (wie elegant) oder schnell ist.

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

Um dies zu erklären, nehme ich hier die beiden Daten und konvertiere sie aus dem ISO-Format in Python-Datetime-Objekte. Dann führe ich eine Schleife durch, indem ich dem Startobjekt datetime eine Woche hinzufüge und prüfe, ob der numerische Wert des Monats größer ist (es sei denn, der Monat ist Dezember, dann prüft er, ob das Datum kleiner ist). Wenn der Wert größer ist, füge ich ihn der Liste hinzu von Monaten und weiter durchschleifen, bis ich zu meinem Enddatum komme.

Es funktioniert perfekt, es scheint einfach kein guter Weg zu sein, es zu tun ...


Fragen Sie nach der ANZAHL der Monate zwischen zwei Daten oder nach den tatsächlichen Monaten?
Charles Hooper

in meiner Lösung: Ich werde nicht um "die Anzahl der Sekunden eines Monats" erhöht. Ich erhöhe lediglich die Zahl 1 auf 2 und später von 2 auf 3.
Unpolarität

Ich wollte nur, dass Sie wissen, dass Sie eine Antwort mit ZWEI Schleifen ausgewählt haben, obwohl Ihnen meine Antwort nicht gefallen hat, weil sie "eine Schleife hatte". Listenverständnisse sind immer noch Schleifen.
Charles Hooper

Antworten:


0

Update 2018-04-20: Es scheint, dass OP @Joshkunz gefragt hat, welche Monate zwischen zwei Daten liegen, anstatt "wie viele Monate" zwischen zwei Daten liegen. Ich bin mir also nicht sicher, warum @JohnLaRooy mehr als 100 Mal positiv bewertet wurde. @Joshkunz gab im Kommentar unter der ursprünglichen Frage an, dass er die tatsächlichen Daten [oder die Monate] haben wollte, anstatt die Gesamtzahl der Monate zu ermitteln .

So schien die Frage gewollt zu sein, zwischen zwei Terminen 2018-04-11zu2018-06-01

Apr 2018, May 2018, June 2018 

Und was , wenn es zwischen 2014-04-11zu 2018-06-01? Dann wäre die Antwort

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

Deshalb hatte ich vor vielen Jahren den folgenden Pseudocode. Es wurde lediglich vorgeschlagen, die zwei Monate als Endpunkte zu verwenden und sie jeweils um einen Monat zu durchlaufen. @Joshkunz erwähnte, dass er die "Monate" wollte und er erwähnte auch, dass er die "Daten" wollte, ohne genau zu wissen, es war schwierig, den genauen Code zu schreiben, aber die Idee ist, eine einfache Schleife zu verwenden, um die Endpunkte zu durchlaufen, und jeweils einen Monat inkrementieren.

Die Antwort vor 8 Jahren im Jahr 2010:

Wenn es um eine Woche hinzugefügt wird, erledigt es ungefähr das 4,35-fache der Arbeit nach Bedarf. Warum nicht einfach:

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)

Nur Pseduo-Code für den Moment, noch nicht getestet, aber ich denke, die Idee in die gleiche Richtung wird funktionieren.


1
Ok, ich sehe einige Probleme mit Monaten wie Februar, wenn die Inkrementierung von Monat zu Monat erfolgt. Statt Wochen.
Joshkunz

Ich werde nicht um "die Anzahl der Sekunden eines Monats" erhöht. Ich erhöhe lediglich die Zahl 1auf 2und dann von 2auf 3später.
Unpolarität

194

Definieren Sie zunächst einige Testfälle. Dann werden Sie feststellen, dass die Funktion sehr einfach ist und keine Schleifen benötigt

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

Sie sollten Ihrer Frage einige Testfälle hinzufügen, da viele potenzielle Eckfälle zu behandeln sind. Es gibt mehrere Möglichkeiten, die Anzahl der Monate zwischen zwei Daten zu definieren.


2
es gibt falsches Ergebnis. es ergibt sich 1 Monat zwischen "2015-04-30" und "2015-05-01", was eigentlich nur 1 Tag ist.
Rao

21
@ Rao. Deshalb sagte ich: "Es gibt mehr als eine Möglichkeit, die Anzahl der Monate zwischen zwei Daten zu definieren." Der Frage fehlt noch eine formale Definition. Aus diesem Grund schlage ich auch vor, neben der Definition auch Testfälle bereitzustellen.
John La Rooy

2
Ich empfehle, abs () um die Subtraktionen hinzuzufügen, damit d1 kleiner als d2 ist: return abs (d1.Jahr - d2.Jahr) * 12 + abs (d1.Monat - d2.Monat)
Lukas Schulze

Bist du sicher, @LukasSchulze? Wenn d1 kleiner als d2 ist, müssten Sie diese Zahl sowieso von der ersten subtrahieren, oder?
Lilith-Elina

In meinem Fall spielt es jedoch keine Rolle, ob d1 <= d2 oder d2 <= d1. Sie interessieren sich nur für den Unterschied, egal welches der beiden Daten kleiner / größer ist.
Lukas Schulze

40

Ein Liner, um eine Liste von Datumsangaben zu finden, die nach Monat zwischen zwei Datumsangaben erhöht sind.

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

Dies! Dies passt sich dank der Verwendung von Regeln vielen besonderen Situationen an. Beachten Sie, dass die Ausgabewerte Datumsangaben sind, sodass Sie sie in beliebige Werte (einschließlich Zeichenfolgen) konvertieren können, um sie an die angezeigten Werte anzupassen.
Ezekiel Kruglick

Dies schlägt fehl, wenn strt_dt der 29.11.2001 ist, da in diesem Jahr kein Schalttag vorliegt.
CS

2
OP bat um eine Liste der Monate zwischen dem Start- und Enddatum. Der Februar wird mit Ihrer Methode in meinem Beispiel verpasst. Natürlich kann Ihre Lösung weiterhin gerettet werden, indem Sie beispielsweise das Startdatum auf den ersten des Monats einstellen.
CS

2
Es ist eine schöne Lösung und spart mir viel Zeit. Wir können es besser machen, indem wir das Startdatum [dt for dt in rrule(MONTHLY, bymonthday=10,dtstart=strt_dt, until=end_dt)]
angeben

1
Sei dir dessen bewusst. Sie haben tatsächlich einen Hinweis in der Dokumentation, der besagt, dass Regel "überraschendes Verhalten haben könnte, wenn beispielsweise das Startdatum am Ende des Monats liegt" (siehe Hinweis in dateutil.readthedocs.io/en/stable/rrule.html ). Eine Möglichkeit, dies zu vermeiden, besteht darin, die Daten durch den ersten Tag des Monats zu ersetzen: start_date_first = start_date.replace(day=1), end_date_first = end_date.replace(day=1)Dann zählt die Regel die Monate ordnungsgemäß.
Alla Sorokina

36

Das hat bei mir funktioniert -

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)

Entfernen Sie unnötige str()Zeichenfolgenliterale.
JFS

7
Nichts wert, dass, wenn das Delta mehr als 1 Jahr ist, r.monthsvon 0
jbkkd

2
Normalerweise mache ich r.months * (r.years + 1), da dies dem entspricht, worüber @jbkkd sprach
Triunenature

9
@ Triunenature das sieht falsch aus, sicherlich sollte es so etwas wie r.months + (r.years * 12) sein
Moataz Elmasry

2
Ja, das funktioniert nicht, wenn die Daten mehr als ein Jahr voneinander entfernt sind.
HansG600

11

Sie können dies einfach mit der Regel aus dem dateutil- Modul berechnen :

from dateutil import rrule
from datetime import date

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

werde dir geben:

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

10

Ermitteln Sie den Endmonat (bezogen auf das Jahr und den Monat des Startmonats, z. B. 2011 Januar = 13, wenn Ihr Startdatum am Oktober 2010 beginnt) und generieren Sie dann die Datumsangaben, die mit dem Startmonat und dem Endmonat beginnen, wie folgt:

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

Wenn beide Daten im selben Jahr liegen, kann dies auch einfach wie folgt geschrieben werden:

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

8

Dieser Beitrag nagelt es! Verwenden Sie 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

1
@edouard Wie Sie in dem Beispiel sehen können, das ich gegeben habe, sind die Daten in verschiedenen Jahren. Trotzdem ist die str()Besetzung , die ich gemacht habe, völlig unnötig.
Srodriguex

5
Dies funktioniert nicht, wenn die Daten mehr als ein Jahr umfassen. Sie müssen hinzufügenrelativedelta.relativedelta(date2, date1).years * 12
Myon

delta.years*12 + delta.months
user2682863

7

Meine einfache Lösung:

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

unterschätzte Lösung
Reabow

4

Definieren Sie einen „Monat“ , wie 1 / 12 Jahr, dann wie folgt vorgehen:

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

Sie können versuchen, einen Monat als "Zeitraum von 29, 28, 30 oder 31 Tagen (je nach Jahr)" zu definieren. Aber wenn Sie das tun, müssen Sie ein zusätzliches Problem lösen.

Während es in der Regel klar, dass 15. Juni th + 1 Monat 15. Juli sein sollte th , es ist nicht in der Regel nicht klar , ob 30. Januar th + 1 Monat im Februar oder März. Im letzteren Fall können Sie das Datum als 30. Februar zu berechnen gezwungen werden , th , dann „richtig“ es bis 2. März nd . Aber wenn Sie das tun, werden Sie feststellen , dass 2. März nd - 1 Monat ist klar 2. Februar nd . Ergo, reductio ad absurdum (diese Operation ist nicht genau definiert).


4

Es gibt eine einfache Lösung, die auf 360-Tage-Jahren basiert, wobei alle Monate 30 Tage haben. Dies passt zu den meisten Anwendungsfällen, in denen Sie bei zwei Daten die Anzahl der vollen Monate plus die verbleibenden Tage berechnen müssen.

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

4

Etwas hübsche Lösung von @ 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)

4

Sie können auch die Pfeilbibliothek verwenden. Dies ist ein einfaches Beispiel:

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

Dies wird gedruckt:

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

Hoffe das hilft!


4
from dateutil import relativedelta

relativedelta.relativedelta(date1, date2)

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

3

Versuchen Sie so etwas. Es enthält derzeit den Monat, wenn beide Daten zufällig im selben Monat liegen.

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

Die Ausgabe sieht aus wie:

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

Dies ist jedoch immer noch der gleiche Loop-Ansatz, daher sehe ich nicht unbedingt den Vorteil ...
Joshkunz

1
Ich sehe nicht, wie das ein Problem ist. Loops sind nicht dein Feind.
Charles Hooper

Nun, dies muss jedes Mal gemacht werden, wenn es sich um eine Ajax-Abfrage handelt. Ich weiß, dass Loops nicht der Feind sind, aber es scheint, dass sie eine langsame Lösung für ein Problem sind, das auf viel einfachere Weise gelöst werden sollte.
Joshkunz


3

So geht's mit 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')

Beachten Sie, dass es mit dem Monat nach dem angegebenen Startdatum beginnt .


2

Dies kann mit datetime.timedelta erfolgen, wobei die Anzahl der Tage für das Überspringen zum nächsten Monat über calender.monthrange ermittelt werden kann. monthrange gibt den Wochentag (0-6 ~ Mo-So) und die Anzahl der Tage (28-31) für ein bestimmtes Jahr und einen bestimmten Monat zurück.
Beispiel: monthrange (2017, 1) gibt (6,31) zurück.

Hier ist das Skript, das diese Logik verwendet, um zwischen zwei Monaten zu iterieren.

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

`


Könnten Sie bitte erklären, wie dies das Problem löst? Wir ermutigen die Menschen, ihren Antworten einen Kontext hinzuzufügen. Vielen Dank.
Gi0rgi0s

1
Eine Erklärung wurde
hinzugefügt

2

Viele Leute haben Ihnen bereits gute Antworten gegeben, um dieses Problem zu lösen, aber ich habe keine mit Listenverständnis gelesen, daher gebe ich Ihnen, was ich für einen ähnlichen Anwendungsfall verwendet habe:


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

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

Genau wie bei der rangeFunktion, wenn der Monat 13 ist , gehe zum nächsten Jahr

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

Beispiel in der 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)]

1

Normalerweise sind 90 Tage NICHT 3 Monate, sondern nur eine Referenz.

Schließlich müssen Sie überprüfen, ob die Tage größer als 15 sind, um dem Monatszähler +1 hinzuzufügen. oder besser, fügen Sie einen weiteren Elif mit einem halben Monatszähler hinzu.

Von dieser anderen Stackoverflow-Antwort habe ich endlich damit geendet:

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

Ausgabe:

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

Ich habe festgestellt, dass dies nicht funktioniert, wenn Sie mehr als die im laufenden Jahr verbleibenden Tage hinzufügen, und das ist unerwartet.


1

Es scheint, dass die Antworten unbefriedigend sind und ich seitdem meinen eigenen Code verwendet habe, der leichter zu verstehen ist

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

0

Angenommen, UpperDate ist immer später als LowerDate und beide sind datetime.date-Objekte:

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

Ich habe das nicht gründlich getestet, aber es sieht so aus, als ob es den Trick machen sollte.


0

Hier ist eine Methode:

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

Hierbei wird zunächst die Gesamtzahl der Monate zwischen den beiden Daten einschließlich berechnet. Anschließend wird eine Liste mit dem ersten Datum als Basis erstellt und eine Modularithmetik zum Erstellen der Datumsobjekte ausgeführt.


0

Ich musste gerade etwas ziemlich Ähnliches tun

Am Ende habe ich eine Funktion geschrieben, die eine Liste von Tupeln zurückgibt, die das startund endjedes Monats zwischen zwei Datumsgruppen angeben, damit ich einige SQL-Abfragen für monatliche Gesamtumsätze usw. von hinten schreiben kann.

Ich bin sicher, dass es von jemandem verbessert werden kann, der weiß, was er tut, aber ich hoffe, es hilft ...

Der zurückgegebene Wert sieht wie folgt aus (Generierung für heute - 365 Tage bis heute als Beispiel)

[   (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 wie folgt (hat einige Debug-Sachen, die entfernt werden können):

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

0

Angenommen, Sie wollten wissen, in welchem ​​"Bruchteil" des Monats sich die Daten befanden, was ich getan habe, dann müssen Sie etwas mehr arbeiten.

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)

bietet ein Beispiel, das die Anzahl der Monate zwischen zwei Daten einschließlich berechnet, einschließlich des Bruchteils jedes Monats, in dem sich das Datum befindet. Dies bedeutet, dass Sie herausfinden können, wie viele Monate zwischen dem 20.01.2015 und dem 14.02.2015 liegen , wobei der Bruchteil des Datums im Januar durch die Anzahl der Tage im Januar bestimmt wird; oder gleichermaßen unter Berücksichtigung, dass sich die Anzahl der Tage im Februar von Jahr zu Jahr ändern kann.

Als Referenz befindet sich dieser Code auch auf github - https://gist.github.com/andrewyager/6b9284a4f1cdb1779b10


0

Versuche dies:

 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

Es sollte keine Rolle spielen, in welcher Reihenfolge Sie die Daten eingeben, und es wird der Unterschied in den Monatslängen berücksichtigt.


Beachten Sie, dass Ihre Daten nicht gleich sind. Der einfachste Weg wäre mit delta_time.days = 0: month = 0, sonst Rest der Routine.
om_henners

0

Das funktioniert...

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

0
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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.