Il modo migliore per trovare i mesi tra due date


91

Ho la necessità di poter trovare con precisione i mesi tra due date in Python. Ho una soluzione che funziona ma non è molto buona (come in elegante) o veloce.

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

Quindi, solo per spiegare, quello che sto facendo qui è prendere le due date e convertirle dal formato iso in oggetti datetime python. Quindi eseguo un ciclo aggiungendo una settimana all'oggetto datetime di inizio e controllo se il valore numerico del mese è maggiore (a meno che il mese non sia dicembre, controlla se la data è inferiore), Se il valore è maggiore lo aggiungo alla lista di mesi e continuo a scorrere fino alla data di fine.

Funziona perfettamente, semplicemente non sembra un buon modo per farlo ...


Stai chiedendo il NUMERO di mesi tra due date o quali sono i mesi effettivi?
Charles Hooper

nella mia soluzione: non sono incrementato del "numero di secondi di un mese". Sto semplicemente incrementando il numero da 1 a 2 e poi da 2 a 3 in seguito.
nonopolarità

Volevo solo farti sapere che anche se non ti è piaciuta la mia risposta perché "aveva un loop" hai selezionato una risposta che ha DUE loop. Le comprensioni delle liste sono ancora loop.
Charles Hooper

Risposte:


0

Aggiornamento 2018-04-20: sembra che OP @Joshkunz chiedesse di scoprire quali mesi sono tra due date, invece di "quanti mesi" sono tra due date. Quindi non sono sicuro del motivo per cui @JohnLaRooy è stato votato per oltre 100 volte. @Joshkunz ha indicato nel commento sotto la domanda originale che voleva le date effettive [oi mesi], invece di trovare il numero totale di mesi .

Quindi sembrava la domanda voluta, per tra due date 2018-04-11a2018-06-01

Apr 2018, May 2018, June 2018 

E se è tra 2014-04-11a 2018-06-01? Allora la risposta sarebbe

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

Ecco perché molti anni fa avevo il seguente pseudo codice. Suggeriva semplicemente di utilizzare i due mesi come punti finali e di passarli in rassegna, incrementando di un mese alla volta. @Joshkunz ha detto che voleva i "mesi" e ha anche detto che voleva le "date", senza sapere esattamente, era difficile scrivere il codice esatto, ma l'idea è di usare un semplice ciclo per scorrere i punti finali, e incrementando un mese alla volta.

La risposta 8 anni fa nel 2010:

Se si aggiunge di una settimana, funzionerà all'incirca 4,35 volte il lavoro necessario. Perché non solo:

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)

solo codice pseduo per ora, non ho testato, ma penso che l'idea sulla stessa linea funzionerà.


1
Ok, vedo alcuni problemi con mesi come febbraio se l'incremento viene fatto per mese, invece che per settimane.
Joshkunz

Non sono incrementato del "numero di secondi di un mese". Sto semplicemente incrementando il numero 1a 2, e poi da 2a in 3seguito.
nonopolarità

194

Inizia definendo alcuni casi di test, poi vedrai che la funzione è molto semplice e non necessita di loop

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

Dovresti aggiungere alcuni casi di test alla tua domanda, poiché ci sono molti potenziali casi d'angolo da coprire: c'è più di un modo per definire il numero di mesi tra due date.


2
dà un risultato sbagliato. risulta 1 mese tra "2015-04-30" e "2015-05-01", che in realtà è solo 1 giorno.
Rao

21
@Rao. ecco perché ho detto "c'è più di un modo per definire il numero di mesi tra due date". La domanda manca ancora di una definizione formale. Questo è anche il motivo per cui suggerisco che i casi di test dovrebbero essere forniti insieme alla definizione.
John La Rooy

2
Consiglio di aggiungere abs () attorno alle sottrazioni per consentire a d1 di essere più piccolo di d2: return abs (d1.year - d2.year) * 12 + abs (d1.month - d2.month)
Lukas Schulze

Sei sicuro, @LukasSchulze? Se d1 è minore di d2, dovresti comunque sottrarre quel numero dal primo, giusto?
Lilith-Elina

Ma nel mio caso, non importa se d1 <= d2 o d2 <= d1. Sei interessato solo al diff, indipendentemente da quale delle due date è più piccola / più grande.
Lukas Schulze

40

Una riga per trovare un elenco di datetimes, incrementato di mese, tra due date.

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

Questo! Questo si adatta a molte situazioni speciali fantasiose grazie all'uso di rrule. Nota che i valori di output sono datetimes quindi puoi convertirli in quello che ti piace (comprese le stringhe) per abbinare ciò che mostrano gli altri.
Ezekiel Kruglick

Questo fallisce quando strt_dt è 2001-1-29, a causa di nessun giorno bisestile quell'anno.
CS

2
OP ha chiesto un elenco dei mesi tra le date di inizio e fine. Febbraio è mancato usando il tuo metodo nel mio esempio. Ovviamente, la tua soluzione può ancora essere salvata adattando la data di inizio al primo del mese, ad esempio.
CS

2
È una bella soluzione e mi fa risparmiare molto tempo. Possiamo renderlo più migliore fornendo la data di inizio[dt for dt in rrule(MONTHLY, bymonthday=10,dtstart=strt_dt, until=end_dt)]
Pengju Zhao

1
Sii consapevole di ciò. In realtà hanno una nota nella documentazione che dice che rrule potrebbe avere "un comportamento sorprendente quando, ad esempio, la data di inizio si verifica alla fine del mese" (vedere la nota in dateutil.readthedocs.io/en/stable/rrule.html ). Un modo per evitarlo è sostituire le date entro il primo giorno del mese: start_date_first = start_date.replace(day=1), end_date_first = end_date.replace(day=1)quindi la regola conta i mesi correttamente.
Alla Sorokina il

36

Questo ha funzionato per 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)

rimuove inutili str()intorno alle stringhe letterali.
jfs

7
A nulla vale che se il delta è superiore a 1 anno, r.monthspartirà da 0
jbkkd

2
di solito faccio r.months * (r.years + 1), poiché ciò si adatta a ciò di cui parlava
@jbkkd

9
@triunenature sembra sbagliato, sicuramente dovrebbe essere qualcosa come r.months + (r.years * 12)
Moataz Elmasry

2
Sì, questo non funziona se le date sono a più di un anno di distanza.
HansG600

11

Puoi calcolarlo facilmente usando rrule dal modulo dateutil :

from dateutil import rrule
from datetime import date

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

ti darà:

 [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

Ottieni il mese di fine (relativo all'anno e al mese del mese di inizio es: 2011 gennaio = 13 se la tua data di inizio inizia il 2010 ottobre) e quindi genera i datetimes che iniziano il mese di inizio e quel mese di fine in questo modo:

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

se entrambe le date sono dello stesso anno, potrebbe anche essere scritto semplicemente come:

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

8

Questo post lo inchioda! Usa 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 Come puoi vedere nell'esempio che ho fornito, le date sono in anni diversi. Tuttavia, il str()cast che ho fatto è completamente inutile.
srodriguex

5
questo non funziona se le date coprono più di un anno, è necessario aggiungererelativedelta.relativedelta(date2, date1).years * 12
muon

delta.years*12 + delta.months
user2682863

7

La mia semplice soluzione:

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

soluzione
sottovalutata

4

Definire un "mese" come 1 / 12 anno, poi fare questo:

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

Potresti provare a definire un mese come "un periodo di 29, 28, 30 o 31 giorni (a seconda dell'anno)". Ma se lo fai, hai un ulteriore problema da risolvere.

Mentre è chiaro che di solito 15 giugno ° + 1 mese dovrebbe essere 15 luglio ° , non di solito non è chiaro se 30 gennaio ° + 1 mese è in febbraio o marzo. In quest'ultimo caso, si può essere costretti a calcolare la data come 30 Febbraio th , poi "corretto" è al 2 marzo ND . Ma quando lo fate, troverete che 2 marzo ° - 1 mese è chiaramente 2 febbraio ND . Ergo, reductio ad absurdum (questa operazione non è ben definita).


4

Esiste una soluzione semplice basata su 360 giorni / anni, in cui tutti i mesi hanno 30 giorni. Si adatta alla maggior parte dei casi d'uso in cui, date due date, è necessario calcolare il numero di mesi interi più i giorni rimanenti.

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

Una soluzione un po 'ottimizzata di @ 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

Puoi anche usare la libreria delle frecce . Questo è un semplice esempio:

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

Questo stamperà:

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

Spero che questo ti aiuti!


4
from dateutil import relativedelta

relativedelta.relativedelta(date1, date2)

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

3

Prova qualcosa di simile. Attualmente include il mese se entrambe le date si trovano nello stesso mese.

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

L'output è simile a:

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

Tuttavia, questo ha ancora lo stesso approccio a ciclo continuo, quindi non vedo necessariamente il vantaggio ...
Joshkunz

1
Non vedo come sia un problema. I loop non sono tuoi nemici.
Charles Hooper

Bene, questo deve essere fatto ogni volta che si tratta di una query ajax. So che i loop non sono il nemico ma sembra che siano una soluzione lenta a un problema che dovrebbe essere risolto in un modo molto più semplice.
Joshkunz


3

Ecco come farlo con 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')

Notare che inizia con il mese successivo alla data di inizio specificata.


2

Può essere fatto utilizzando datetime.timedelta, dove il numero di giorni per saltare al mese successivo può essere ottenuto da calender.monthrange. intervallo di mesi restituisce il giorno della settimana (0-6 ~ lunedì-domenica) e il numero di giorni (28-31) per un determinato anno e mese.
Ad esempio: intervallo di mesi (2017, 1) restituisce (6,31).

Ecco lo script che utilizza questa logica per iterare tra due mesi.

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

"


Potresti spiegare come questo risolve il problema? Incoraggiamo le persone ad aggiungere contesto alle loro risposte; Grazie.
Gi0rgi0s

1
Aggiunta una spiegazione
pankaj kumar

2

Molte persone ti hanno già dato buone risposte per risolvere questo problema, ma non ho letto alcuna comprensione dell'elenco utilizzando quindi ti do quello che ho usato per un caso d'uso simile:


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

proprio come la rangefunzione, quando il mese è 13 , vai all'anno successivo

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

esempio nella shell di 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

Di solito 90 giorni NON sono letteralmente 3 mesi, solo un riferimento.

Quindi, finalmente, devi controllare se i giorni sono più grandi di 15 per aggiungere +1 al contatore del mese. o meglio, aggiungi un altro elif con contatore di mezzo mese.

Da questa altra risposta di stackoverflow sono finalmente finito con quello:

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

Produzione:

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

Ho notato che non funziona se aggiungi più di giorni rimanenti nell'anno in corso, e questo è inaspettato.


1

sembra che le risposte siano insoddisfacenti e da allora ho usato il mio codice che è più facile da capire

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

Supponendo che upperDate sia sempre successivo a lowerDate ed entrambi siano oggetti 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 ) )

Non l'ho testato a fondo, ma sembra che dovrebbe funzionare.


0

Ecco un metodo:

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

Per prima cosa si calcola il numero totale di mesi tra le due date, incluse. Quindi crea un elenco utilizzando la prima data come base ed esegue l'aritmetica modula per creare gli oggetti data.


0

In realtà avevo bisogno di fare qualcosa di abbastanza simile proprio ora

Ho finito per scrivere una funzione che restituisce un elenco di tuple che indicano il starte enddi ogni mese tra due serie di date in modo da poter scrivere alcune query SQL sul retro per i totali mensili delle vendite ecc.

Sono sicuro che può essere migliorato da qualcuno che sa cosa sta facendo ma spero che aiuti ...

Il valore restituito appare come segue (generazione per oggi - 365 giorni fino ad oggi come esempio)

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

Codice come segue (ha alcune cose di debug che possono essere rimosse):

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

Supponendo che tu volessi conoscere la "frazione" del mese in cui si trovavano le date, cosa che ho fatto, allora devi fare un po 'più di lavoro.

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)

fornisce un esempio che calcola il numero di mesi tra due date inclusivamente, inclusa la frazione di ogni mese in cui si trova la data. Ciò significa che puoi calcolare quanti mesi sono tra 2015-01-20 e 2015-02-14 , dove la frazione della data nel mese di gennaio è determinata dal numero di giorni nel mese di gennaio; o ugualmente tenendo conto che il numero di giorni nel mese di febbraio può cambiare di anno in anno.

Per mio riferimento, questo codice è anche su github - https://gist.github.com/andrewyager/6b9284a4f1cdb1779b10


0

Prova questo:

 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

Non dovrebbe importare l'ordine in cui inserisci le date e tiene conto della differenza nella durata dei mesi.


Tieni presente che questo non tiene conto del fatto che le date sono le stesse. Il modo più semplice sarebbe se delta_time.days = 0: months = 0 else resto della routine.
om_henners

0

Funziona ...

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.