İki tarih arasındaki ayları bulmanın en iyi yolu


95

Python'da iki tarih arasındaki ayları doğru bir şekilde bulabilmeye ihtiyacım var. Çalışan bir çözümüm var ama çok iyi değil (şıkta olduğu gibi) veya hızlı.

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

Yani sadece açıklamak gerekirse, burada yaptığım şey iki tarihi almak ve onları iso formatından python datetime nesnelerine dönüştürmek. Daha sonra başlangıç ​​datetime nesnesine bir hafta ekleyerek döngüye girerim ve ayın sayısal değerinin daha büyük olup olmadığını kontrol ederim (ay Aralık değilse, tarihin daha az olup olmadığını kontrol eder), Değer büyükse listeye eklerim ve bitiş tarihime gelene kadar döngüye devam et.

Mükemmel çalışıyor, bunu yapmanın iyi bir yolu gibi görünmüyor ...


İki tarih arasındaki ayların SAYISINI mı, yoksa gerçek ayların ne olduğunu mu soruyorsunuz?
Charles Hooper

benim çözümümde: "Bir aylık saniye değerinde" artış yapmıyorum. Ben sadece 1 sayısını 2'ye ve daha sonra 2'den 3'e yükseltiyorum.
nonopolarity

Cevabımı "döngü içerdiği" için beğenmeseniz bile, İKİ döngü içeren bir yanıt seçtiğinizi bilmenizi istedim. Liste anlayışları hâlâ döngü halinde.
Charles Hooper

Yanıtlar:


1

2018-04-20 Güncellemesi: Görünüşe göre OP @Joshkunz, iki tarih arasındaki "kaç ay" yerine hangi ayların iki tarih arasında olduğunu soruyor . Bu yüzden @JohnLaRooy'un neden 100 defadan fazla oy aldığından emin değilim. @Joshkunz, orijinal sorunun altındaki yorumda toplam ay sayısını bulmak yerine gerçek tarihleri ​​[veya ayları] istediğini belirtti .

İki tarih arasında aranan soru, ortaya Yani 2018-04-11etmek2018-06-01

Apr 2018, May 2018, June 2018 

Ve arasındaki buysa 2014-04-11için 2018-06-01? O zaman cevap olurdu

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

Bu yüzden yıllar önce aşağıdaki sözde koda sahiptim. Yalnızca iki ayın bitiş noktaları olarak kullanılması ve her seferinde bir ay artırılarak bunların üzerinden geçilmesi önerildi. @Joshkunz, "ayları" istediğini ve "tarihleri" istediğini de belirtti, tam olarak bilmeden, tam kodu yazmak zordu, ancak amaç, uç noktaları arasında döngü yapmak için basit bir döngü kullanmak ve her seferinde bir ay artarak.

Cevap 8 yıl önce 2010'da:

Haftaya kadar eklerseniz, gerektiği kadar işin yaklaşık 4.35 katı çalışacaktır. Neden sadece:

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)

Şimdilik sadece pseduo kodu, test etmedim, ancak aynı satırdaki fikrin işe yarayacağını düşünüyorum.


1
Tamam, artış haftalar yerine ay bazında yapılırsa Şubat gibi aylarda bazı sorunlar görüyorum.
Joshkunz

Ben "bir aylık saniye değerinde" artış yapmıyorum. Ben sadece numarasını artırarak am 1için 2ve o andan itibaren 2hiç 3sonradan.
nonopolarity

201

Bazı test senaryoları tanımlayarak başlayın, ardından işlevin çok basit olduğunu ve döngü gerektirmediğini göreceksiniz.

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

Kapsanacak çok sayıda olası köşe vakası olduğundan sorunuza bazı test senaryoları eklemelisiniz - iki tarih arasındaki ay sayısını tanımlamanın birden fazla yolu vardır.


3
yanlış sonuç verir. aslında sadece 1 gün olan "2015-04-30" ve "2015-05-01" arasında 1 ay sonuçlanır.
Rao

21
@Rao. bu yüzden "iki tarih arasındaki ay sayısını tanımlamanın birden fazla yolu var" dedim. Sorunun hala resmi bir tanımı yok. Bu aynı zamanda, tanımın yanında test senaryolarının da sunulmasını önermemin nedenidir.
John La Rooy

2
D1'in d2'den daha küçük olmasına izin vermek için çıkarmaların etrafına abs () eklemenizi öneririm: dönüş abs (d1.year - d2.year) * 12 + abs (d1.month - d2.month)
lszrh

Emin misin @LukasSchulze? D1, d2'den küçükse, bu sayıyı ilkinden çıkarmanız gerekir, değil mi?
Lilith-Elina

42

İki tarih arasında ay bazında artan veri zamanlarının bir listesini bulmak için bir satır.

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

Bu! Bu, rrule kullanımı sayesinde birçok süslü özel durum için ayarlanır. Çıktı değerlerinin veri saatleri olduğunu unutmayın, böylece diğerlerinin gösterdikleriyle eşleşecek şekilde onları istediğiniz şeye (dizeler dahil) dönüştürebilirsiniz.
Ezekiel Kruglick

Strt_dt 2001-1-29 olduğunda, o yıl Artık Gün olmadığı için bu başarısız olur.
CS

2
OP, başlangıç ​​ve bitiş tarihleri ​​arasındaki ayların bir listesini istedi. Benim örneğimdeki yöntemini kullanarak Şubat ayı kaçırıldı. Elbette, çözümünüz, örneğin başlangıç ​​tarihini ayın ilkine ayarlayarak da kurtarılabilir.
CS

2
Güzel bir çözüm ve bana çok zaman kazandırıyor. Başlangıç ​​tarihi vererek daha iyi hale getirebiliriz[dt for dt in rrule(MONTHLY, bymonthday=10,dtstart=strt_dt, until=end_dt)]
Pengju Zhao

1
Bunun farkında olun. Aslında dokümantasyonda, rrule'un "örneğin, başlangıç ​​tarihi ayın sonunda gerçekleştiğinde şaşırtıcı davranışlar sergileyebileceğini" belirten bir not var ( dateutil.readthedocs.io/en/stable/rrule.html'deki Not'a bakın ). Bundan kaçınmanın bir yolu, tarihleri ​​ayın ilk start_date_first = start_date.replace(day=1), end_date_first = end_date.replace(day=1)günüyle değiştirmektir : Daha sonra rrule, ayları doğru bir şekilde sayar.
Alla Sorokina

39

Bu benim için çalıştı -

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)

str()dize değişmezlerinin etrafındaki gereksizleri kaldırın .
jfs

7
Delta 1 yıldan fazla ise r.months0
jbkkd

2
genellikle r.months * (r.years + 1) yaparım, çünkü @jbkkd'nin bahsettiği konuya göre ayarlanır
triunenature

9
@triunenature bu yanlış görünüyor, kesinlikle r.months gibi bir şey olmalı + (r.years * 12)
Moataz Elmasry

2
Evet, tarihler bir yıldan fazla ise bu işe yaramaz.
HansG600

12

Bunu, dateutil modülünden rrule kullanarak kolayca hesaplayabilirsiniz :

from dateutil import rrule
from datetime import date

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

sana vereceğim:

 [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

Bitiş ayını alın (başlangıç ​​ayının yılı ve ayına göre örn: 2011 Ocak = 13, başlangıç ​​tarihiniz 2010 Ekim'de başlıyorsa) ve ardından başlangıç ​​ayından başlayıp bitiş ayına göre tarih saatlerini oluşturun:

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

her iki tarih aynı yıldaysa, şu şekilde de yazılabilir:

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

8

Bu gönderi onu tamamlıyor! Kullanın 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 Verdiğim örnekte de görebileceğiniz gibi tarihler farklı yıllarda. Yine de, str()yaptığım oyuncu kadrosu tamamen gereksiz.
srodriguex

6
tarihler bir yıldan fazla relativedelta.relativedelta(date2, date1).years * 12
muon

delta.years*12 + delta.months
user2682863

7

Benim basit çözümüm:

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

takdir
edilmeyen

6
from dateutil import relativedelta

relativedelta.relativedelta(date1, date2)

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

4

Bir "ay" olarak tanımlar 1 / 12 yıl sonra bunu:

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

Bir ayı "29, 28, 30 veya 31 günlük (yıla bağlı olarak) bir dönem" olarak tanımlamayı deneyebilirsiniz. Ama bunu yaparsan çözmen gereken ek bir problem var.

O 15 Haziran genellikle açık olsa inci + 1 ay 15 Temmuz olmalıdır inci 30 Ocak eğer, genellikle belli değil değil inci + 1 ay Şubat veya Mart ayında olduğunu. İkinci durumda, sen 30 Şubat olarak tarih hesaplamak için zorlanamaz th Mart 2'ye ardından "Doğru", bunu nd . Bunu yaparken Ama, 2 Mart bulacaksınız nd 1 ay açıkça 2 Şubat olduğunu - nd . Ergo, reduktio ad absurdum (bu işlem iyi tanımlanmamıştır).


4

Tüm ayların 30 gün olduğu 360 günlük yıllara dayalı basit bir çözüm var. İki tarih verildiğinde, tam ay sayısını artı kalan günleri hesaplamanız gereken çoğu kullanım durumuna uyar.

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

@ Vin-G tarafından biraz güzel bir çözüm.

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

Ok kitaplığını da kullanabilirsiniz . Bu basit bir örnek:

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

Bu yazdıracak:

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

Bu yardımcı olur umarım!


3

Bunun gibi bir şey dene. Şu anda, her iki tarihin de aynı ay olması durumunda ayı içerir.

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

Çıktı şöyle görünür:

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

Yine de bu aynı döngü yaklaşımını kullanıyor, bu yüzden faydasını görmüyorum ...
Joshkunz

1
Bunun nasıl bir sorun olduğunu anlamıyorum. Döngüler senin düşmanın değil.
Charles Hooper

Bu, ajax sorgusu her olduğunda yapılmalıdır. Döngülerin düşman olmadığını biliyorum, ancak çok daha kolay bir şekilde çözülmesi gereken bir soruna yavaş bir çözüm gibi görünüyorlar.
Joshkunz


3

Pandas FWIW ile bunu şu şekilde yapabilirsiniz:

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

Belirtilen başlangıç ​​tarihinden sonraki ayla başladığına dikkat edin .


2

Bir sonraki aya atlamak için gün sayısının calender.monthrange ile elde edilebildiği datetime.timedelta kullanılarak yapılabilir. monthrange, belirli bir yıl ve ay için haftanın gününü (0-6 ~ Pzt-Paz) ve gün sayısını (28-31) döndürür.
Örneğin: monthrange (2017, 1), (6,31) döndürür.

İşte iki ay arasında yinelemek için bu mantığı kullanan komut dosyası.

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

'


Bunun sorunu nasıl çözdüğünü açıklar mısınız? İnsanları cevaplarına bağlam eklemeye teşvik ediyoruz; Teşekkürler.
Gi0rgi0s

1
Bir açıklama eklendi
pankaj kumar

2

Pek çok kişi bunu çözmeniz için size zaten iyi cevaplar verdi, ancak listeyi anlama özelliğini kullanarak herhangi bir şey okumadım, bu yüzden size benzer bir kullanım örneği için kullandım:


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

tıpkı rangeişlev gibi , ay 13 olduğunda gelecek yıla git

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

python kabuğundaki örnek

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

Genellikle 90 gün kelimenin tam anlamıyla 3 ay DEĞİLDİR, sadece bir referanstır.

Son olarak, ay sayacına +1 eklemek için günlerin 15'ten büyük olup olmadığını kontrol etmeniz gerekir. veya daha iyisi, yarım ay sayacı olan başka bir elif ekleyin.

Gönderen bu diğer stackoverflow cevap nihayet o ile sona erdi ettik:

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

Çıktı:

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

Mevcut yılda kalan günden fazla gün eklerseniz bunun işe yaramadığını fark ettim ve bu beklenmedik bir durum.


1

Görünüşe göre cevaplar tatmin edici değil ve o zamandan beri anlaşılması daha kolay olan kendi kodumu kullanıyorum

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

1

İşte bunun için çözümüm:

def calc_age_months(from_date, to_date):
    from_date = time.strptime(from_date, "%Y-%m-%d")
    to_date = time.strptime(to_date, "%Y-%m-%d")

    age_in_months = (to_date.tm_year - from_date.tm_year)*12 + (to_date.tm_mon - from_date.tm_mon)

    if to_date.tm_mday < from_date.tm_mday:
        return age_in_months -1
    else
        return age_in_months

Bu, 31 Aralık 2018 ile 1 Ocak 2019 arasındaki aylardaki farkın sıfır olacağı (fark yalnızca bir gün olduğu için) bazı uç durumları da ele alacaktır.


0

ÜstDate'in her zaman lowerDate'ten sonra olduğunu ve her ikisinin de datetime.date nesneleri olduğunu varsayarsak:

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

Bunu tam olarak test etmedim, ancak hile yapması gerekiyor gibi görünüyor.


0

İşte bir yöntem:

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

Bu, ilk olarak iki tarih arasındaki toplam ay sayısını hesaplıyor. Daha sonra, temel olarak ilk tarihi kullanarak bir liste oluşturur ve tarih nesnelerini oluşturmak için modula aritmetiği gerçekleştirir.


0

Aslında şu anda oldukça benzer bir şey yapmam gerekiyordu

İki tarih kümesi arasında her ayın startve endayını gösteren bir tuple listesi döndüren bir işlevin yazılması sona erdi , böylece aylık toplam satışlar vb. İçin arkasından bazı SQL sorguları yazabilirdim.

Eminim ne yaptığını bilen ama yardımcı olacağını uman biri tarafından geliştirilebilir ...

Döndürülen değer aşağıdaki gibidir (örnek olarak bugün için - 365 günden bugüne kadar)

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

Aşağıdaki gibi kod (kaldırılabilen bazı hata ayıklama öğeleri vardır):

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

Tarihlerin geçtiği ayın "kesirini" bilmek istediğinizi varsayarsak, ki bunu yaptım, o zaman biraz daha fazla çalışmanız gerekir.

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)

tarihin bulunduğu her ayın kesri dahil olmak üzere iki tarih arasındaki ay sayısını hesaplayan bir örnek sağlar. Bu, 2015-01-20 ve 2015-02-14 arasında kaç ay olduğunu hesaplayabileceğiniz anlamına gelir. Ocak ayındaki tarihin kesirinin Ocak ayındaki gün sayısına göre belirlendiği; veya aynı şekilde, Şubat ayındaki gün sayısının yıldan yıla değişebileceğini hesaba katarak.

Referansım için, bu kod github'da da var - https://gist.github.com/andrewyager/6b9284a4f1cdb1779b10


0

Bunu dene:

 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

Tarihleri ​​hangi sırayla girdiğiniz önemli değildir ve ay uzunluklarındaki farkı hesaba katar.


Bunun tarihlerinizin aynı olmasını hesaba katmadığını unutmayın. Delta_time.days = 0: ay = 0, aksi takdirde rutinin geri kalanı ile en kolay yol olurdu.
om_henners

0

Bu çalışıyor...

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))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.