J'ai le besoin de pouvoir trouver avec précision les mois entre deux dates en python. J'ai une solution qui fonctionne mais ce n'est pas très bon (comme dans élégant) ou rapide.
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
Donc, juste pour expliquer, ce que je fais ici, c'est prendre les deux dates et les convertir du format iso en objets datetime Python. Ensuite, je passe en boucle en ajoutant une semaine à l'objet datetime de départ et vérifie si la valeur numérique du mois est supérieure (sauf si le mois est décembre, il vérifie si la date est inférieure), si la valeur est supérieure, je l'ajoute à la liste. de mois et continuer à boucler jusqu'à ce que j'arrive à ma date de fin.
Cela fonctionne parfaitement, cela ne semble pas être un bon moyen de le faire ...
Commencez par définir quelques cas de test, puis vous verrez que la fonction est très simple et ne nécessite aucune boucle.
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
Vous devez ajouter quelques cas de test à votre question, car il y a de nombreux cas de coin à couvrir - il existe plusieurs façons de définir le nombre de mois entre deux dates.
Une ligne pour trouver une liste de dates/heures, incrémentée par mois, entre deux dates.
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)]
Cela a fonctionné pour moi -
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 * (r.years+1)
Obtenez le mois de fin (par rapport à l'année et au mois du mois de début ex: 2011 janvier = 13 si votre date de début commence le 2010 octobre), puis générez les dates-heures commençant le mois de début et le mois de fin ainsi:
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)
)]
si les deux dates sont la même année, vous pouvez aussi simplement écrire:
dates=[datetime.datetime(year=dt1.year, month=mn, day=1) for mn in range(dt1.month, dt2.month + 1)]
Vous pouvez facilement le calculer en utilisant la règle de 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))))
te donnera:
[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)]
Ce post cloue-le! Utilisez 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
Une solution un peu piquée par @ 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)
Vous pouvez utiliser python-dateutil . Voir Python: Différence de 2 dates/mois
Vous pouvez également utiliser la bibliothèque arrow . Voici un exemple simple:
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')
Cela va imprimer:
1 January
2 February
3 March
4 April
5 May
6 June
J'espère que cela t'aides!
Essayez quelque chose comme ça. Il inclut actuellement le mois si les deux dates se trouvent dans le même mois.
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
La sortie ressemble à:
>>> start = datetime.now() - timedelta(days=120)
>>> end = datetime.now()
>>> months_between(start,end)
[6, 7, 8, 9, 10]
Il existe une solution simple basée sur des années de 360 jours, où tous les mois ont 30 jours . Elle convient à la plupart des cas d'utilisation où, à deux dates, vous devez calculer le nombre de mois complets plus les jours restants.
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))
Définir un "mois" comme 1/12 année, alors fais ceci:
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
Vous pouvez essayer de définir un mois comme "une période de 29, 28, 30 ou 31 jours (selon l'année)". Mais vous faites cela, vous avez un problème supplémentaire à résoudre.
Bien qu'il soit généralement clair que le 15 juinth + 1 mois devrait être le 15 juilletth, on ne sait généralement pas si le 30 janvierth + 1 mois est en février ou mars. Dans ce dernier cas, vous pouvez être obligé de calculer la date du 30 février.th, alors "corrige" au 2 marsdakota du Nord. Mais quand vous faites cela, vous constaterez que le 2 marsdakota du Nord - 1 mois est clairement le 2 févrierdakota du Nord. Ergo, reductio ad absurdum (cette opération n’est pas bien définie).
Habituellement, 90 jours ne sont PAS 3 mois à la lettre, mais une référence.
Donc, enfin, vous devez vérifier si les jours sont plus grands que 15 pour ajouter +1 au compteur de mois. ou mieux, ajoutez un autre Elif avec un compteur de demi-mois.
De cette autre réponse de stackoverflow j’ai enfin terminé avec cela:
#/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 ")"
Sortie:
2016-02-29 2016-06-14
3 months 16 days
around 4 months ( Feb Mar Apr May )
J'ai remarqué que cela ne fonctionne pas si vous ajoutez plus de jours que l'année en cours, ce qui est inattendu.
#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)
Voici comment faire cela avec 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')
Notez que cela commence par le mois après la date de début indiquée.
Cela peut être fait en utilisant datetime.timedelta, où le nombre de jours pour passer au mois suivant peut être obtenu par calender.monthrange. la plage mensuelle renvoie le jour de la semaine (0-6 ~ lun-dim) et le nombre de jours (28-31) pour une année et un mois donnés.
Par exemple: range de mois (2017, 1) renvoie (6,31).
Voici le script utilisant cette logique pour parcourir deux mois.
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])
`
Ma solution simple:
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))
comme pour la fonction range
, lorsque le mois est 13 , passez à l'année suivante
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
exemple en shell 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)]
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
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
En supposant que upperDate soit toujours postérieur à lowerDate et que les deux sont des objets 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 ) )
Je n'ai pas testé cela à fond, mais il semble que cela devrait suffire.
En supposant que vous vouliez connaître la "fraction" du mois dans laquelle se trouvaient les dates, ce que j'ai fait, vous devez alors travailler un peu plus.
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)
fournit un exemple qui calcule le nombre de mois entre deux dates inclusivement, y compris la fraction de chaque mois de la date. Cela signifie que vous pouvez calculer combien de mois se trouvent entre 2015-01-20 et 2015-02-14 , où la fraction de la date du mois de janvier est déterminée par le nombre de jours de janvier; ou également en tenant compte du fait que le nombre de jours en février peut changer d'année en année.
Pour ma référence, ce code est également sur github - https://Gist.github.com/andrewyager/6b9284a4f1cdb1779b10
Cela marche...
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))
C'est ma façon de faire ceci:
Start_date = "2000-06-01"
End_date = "2001-05-01"
month_num = len(pd.date_range(start = Start_date[:7], end = End_date[:7] ,freq='M'))+1
Je viens d'utiliser le mois pour créer une plage de dates et calculer la longueur.
Voici une méthode:
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
Il s’agit d’abord de calculer le nombre total de mois compris entre les deux dates. Il crée ensuite une liste en utilisant la première date comme base et effectue une arithmétique modula pour créer les objets de date.
Essaye ça:
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
Peu importe l’ordre dans lequel vous entrez les dates et il tient compte de la différence de longueur des mois.
il semble que les réponses ne soient pas satisfaisantes et j'utilise depuis mon propre code qui est plus facile à comprendre
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
En fait, j'avais besoin de faire quelque chose d'assez similaire tout à l'heure
A fini par écrire une fonction qui retourne une liste de nuplets indiquant les start
et end
de chaque mois entre deux séries de dates afin que je puisse écrire quelques requêtes SQL à l'arrière de celle-ci pour obtenir le total mensuel des ventes, etc.
Je suis sûr que quelqu'un qui sait ce qu'il fait peut améliorer la situation, mais espère que cela aidera ...
La valeur renvoyée se présente comme suit (génération pour aujourd'hui - 365 jours jusqu'à aujourd'hui, à titre d'exemple)
[ (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 comme suit (contient des éléments de débogage qui peuvent être supprimés):
#! /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 __== '__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)