Cara terbaik untuk menemukan bulan di antara dua tanggal

93

Saya memiliki kebutuhan untuk dapat secara akurat menemukan bulan antara dua tanggal dengan python. Saya punya solusi yang berhasil tetapi tidak terlalu bagus (seperti elegan) atau cepat.

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

Jadi hanya untuk menjelaskan, yang saya lakukan di sini adalah mengambil dua tanggal dan mengubahnya dari format iso menjadi objek datetime python. Kemudian saya mengulang menambahkan satu minggu ke objek awal waktu dan memeriksa apakah nilai numerik bulan itu lebih besar (kecuali bulan itu Desember maka ia memeriksa apakah tanggalnya kurang), Jika nilainya lebih besar saya menambahkannya ke daftar bulan dan terus berulang sampai saya mencapai tanggal akhir saya.

Ini bekerja dengan sempurna hanya saja sepertinya bukan cara yang baik untuk melakukannya ...

Joshkunz
sumber
Apakah Anda menanyakan JUMLAH bulan antara dua tanggal, atau berapa bulan sebenarnya?
Charles Hooper
dalam solusi saya: Saya tidak bertambah dengan "jumlah detik sebulan". Saya hanya menaikkan angka 1 menjadi 2, dan kemudian dari 2 menjadi 3 nanti.
nonopolaritas
Saya hanya ingin Anda tahu bahwa meskipun Anda tidak menyukai jawaban saya karena jawaban saya "memiliki putaran", Anda memilih jawaban yang memiliki DUA putaran. Pemahaman daftar masih berupa loop.
Charles Hooper

Jawaban:

0

Perbarui 2018-04-20: tampaknya OP @Joshkunz meminta untuk menemukan bulan mana di antara dua tanggal, alih-alih "berapa bulan" di antara dua tanggal. Jadi saya tidak yakin mengapa @JohnLaRooy diberi suara positif lebih dari 100 kali. @Joshkunz mengindikasikan dalam komentar di bawah pertanyaan awal dia menginginkan tanggal sebenarnya [atau bulan], alih-alih menemukan jumlah total bulan .

Jadi tampaknya pertanyaan itu diinginkan, antara dua tanggal 2018-04-11sampai2018-06-01

Apr 2018, May 2018, June 2018 

Dan bagaimana jika di antara 2014-04-11ke 2018-06-01? Maka jawabannya adalah

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

Jadi itulah mengapa saya memiliki kode semu berikut beberapa tahun yang lalu. Itu hanya menyarankan menggunakan dua bulan sebagai titik akhir dan mengulanginya, bertambah satu bulan pada satu waktu. @Joshkunz menyebutkan bahwa dia menginginkan "bulan" dan dia juga menyebutkan bahwa dia menginginkan "tanggal", tanpa mengetahui secara pasti, sulit untuk menulis kode yang tepat, tetapi idenya adalah menggunakan satu putaran sederhana untuk mengulang melalui titik akhir, dan bertambah satu bulan pada satu waktu.

Jawabannya 8 tahun lalu di tahun 2010:

Jika dijumlahkan seminggu, maka kira-kira akan melakukan pekerjaan 4,35 kali pekerjaan sesuai kebutuhan. Mengapa tidak hanya:

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)

hanya kode pseduo untuk saat ini, belum diuji, tetapi saya pikir ide di sepanjang baris yang sama akan berfungsi.

nonopolaritas
sumber
1
Oke, saya melihat beberapa masalah dengan bulan seperti Februari jika kenaikannya dilakukan berdasarkan bulan.
Joshkunz
Saya tidak bertambah dengan "jumlah detik selama sebulan". Saya hanya menaikkan angka 1menjadi 2, dan kemudian 2ke 3nanti.
nonopolaritas
199

Mulailah dengan menentukan beberapa kasus uji, kemudian Anda akan melihat bahwa fungsinya sangat sederhana dan tidak memerlukan putaran

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

Anda harus menambahkan beberapa kasus uji ke pertanyaan Anda, karena ada banyak kasus sudut potensial untuk dibahas - ada lebih dari satu cara untuk menentukan jumlah bulan antara dua tanggal.

John La Rooy
sumber
2
itu memberikan hasil yang salah. itu menghasilkan 1 bulan antara "2015-04-30" dan "2015-05-01" yang sebenarnya hanya 1 hari.
Rao
21
@Rao. itulah mengapa saya berkata "ada lebih dari satu cara untuk menentukan jumlah bulan antara dua tanggal". Pertanyaannya masih belum memiliki definisi formal. Ini juga mengapa saya menyarankan testcases harus disediakan di samping definisi.
John La Rooy
2
Saya merekomendasikan untuk menambahkan abs () di sekitar substraksi untuk memungkinkan d1 lebih kecil dari d2: return abs (d1.year - d2.year) * 12 + abs (d1.month - d2.month)
lszrh
Apakah Anda yakin, @LukasSchulze? Jika d1 lebih kecil dari d2, Anda harus mengurangkan angka itu dari yang pertama, bukan?
Lilith-Elina
41

Satu baris untuk menemukan daftar waktu, bertambah menurut bulan, antara dua tanggal.

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)]
N1B4
sumber
Ini! Ini menyesuaikan untuk banyak situasi khusus yang mewah berkat penggunaan aturan. Perhatikan nilai keluaran adalah waktu tanggal sehingga Anda dapat mengubahnya menjadi apa pun yang Anda suka (termasuk string) agar sesuai dengan yang ditampilkan orang lain.
Ezekiel Kruglick
Ini gagal ketika strt_dt adalah 2001-1-29, karena tidak ada Hari Kabisat tahun itu.
CS
2
OP meminta daftar bulan antara tanggal mulai dan berakhir. Februari terlewatkan menggunakan metode Anda pada contoh saya. Tentu saja, solusi Anda masih bisa diselamatkan dengan menyesuaikan tanggal mulai ke bulan pertama, misalnya.
CS
2
Ini adalah solusi yang bagus dan menghemat banyak waktu saya. Kita bisa membuatnya lebih baik dengan memberikan tanggal mulai[dt for dt in rrule(MONTHLY, bymonthday=10,dtstart=strt_dt, until=end_dt)]
Pengju Zhao
1
Sadarilah. Mereka sebenarnya memiliki catatan dalam dokumentasi yang mengatakan bahwa aturan dapat memiliki "perilaku mengejutkan ketika, misalnya, tanggal mulai terjadi di akhir bulan" (lihat Catatan di dateutil.readthedocs.io/en/stable/rrule.html ). Salah satu cara untuk menghindarinya adalah dengan mengganti tanggal pada hari pertama bulan: start_date_first = start_date.replace(day=1), end_date_first = end_date.replace(day=1)Kemudian aturan menghitung bulan dengan benar.
Alla Sorokina
38

Ini berhasil untuk saya -

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)
aamir23
sumber
hapus yang tidak perlu di str()sekitar string literal.
jfs
7
Tidak ada artinya bahwa jika delta lebih dari 1 tahun, r.monthsakan mulai dari 0
jbkkd
2
biasanya saya melakukan r.months * (r.years + 1), karena itu menyesuaikan dengan apa yang dibicarakan
@jbkkd
9
@triunenature ini terlihat salah, pasti itu harus seperti r.months + (r.years * 12)
Moataz Elmasry
2
Ya, ini tidak berhasil jika tanggal lebih dari satu tahun.
HansG600
11

Anda dapat dengan mudah menghitung ini menggunakan aturan dari modul 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))))

akan memberimu:

 [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)]
Nazarii Gudzovatyi
sumber
10

Dapatkan bulan akhir (relatif terhadap tahun dan bulan dari bulan mulai mis: 2011 Januari = 13 jika tanggal mulai Anda dimulai pada Oktober 2010) dan kemudian buat waktu tanggal mulai bulan awal dan bulan akhir seperti ini:

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

jika kedua tanggal berada pada tahun yang sama, dapat juga ditulis sebagai:

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

Posting ini berhasil! Gunakan 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
srodriguex.dll
sumber
1
@edouard Seperti yang Anda lihat dalam contoh yang saya berikan, tanggalnya berbeda tahun. Meskipun demikian, str()pemeran yang saya lakukan sama sekali tidak perlu.
srodriguex
6
ini tidak berfungsi jika tanggal mencakup lebih dari satu tahun, Anda perlu menambahkanrelativedelta.relativedelta(date2, date1).years * 12
muon
delta.years*12 + delta.months
pengguna2682863
7

Solusi sederhana saya:

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))
Hottabov
sumber
solusi yang kurang dihargai
reabow
6
from dateutil import relativedelta

relativedelta.relativedelta(date1, date2)

months_difference = (r.years * 12) + r.months
yatabani
sumber
4

Mendefinisikan "bulan" sebagai 1 / 12 tahun, kemudian melakukan hal ini:

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

Anda dapat mencoba menentukan bulan sebagai "periode 29, 28, 30 atau 31 hari (tergantung tahun)". Tetapi jika Anda melakukannya, Anda memiliki masalah tambahan yang harus dipecahkan.

Sementara itu biasanya jelas bahwa Juni 15 th + 1 bulan harus Juli 15 th , itu tidak biasanya tidak jelas apakah 30 Jan th + 1 bulan adalah bulan Februari atau Maret. Dalam kasus terakhir, Anda dapat dipaksa untuk menghitung tanggal sebagai Februari 30 th , maka "benar" itu 2 Maret nd . Tetapi ketika Anda melakukannya, Anda akan menemukan bahwa 2 Mar nd - 1 bulan jelas Februari 2 nd . Ergo, reductio ad absurdum (operasi ini tidak ditentukan dengan baik).

Seth
sumber
4

Ada solusi sederhana berdasarkan 360 hari dalam setahun, di mana semua bulan memiliki 30 hari. Ini cocok untuk sebagian besar kasus penggunaan di mana, dengan dua tanggal, Anda perlu menghitung jumlah bulan penuh ditambah hari yang tersisa.

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))
Daniel Reis
sumber
4

Sedikit solusi prettified oleh @ 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)
saaj
sumber
4

Anda juga bisa menggunakan perpustakaan panah . Ini adalah contoh sederhana:

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

Ini akan mencetak:

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

Semoga ini membantu!

Luis Alberto Santana
sumber
3

Cobalah sesuatu seperti ini. Ini sekarang termasuk bulan jika kedua tanggal kebetulan berada di bulan yang sama.

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

Outputnya terlihat seperti:

>>> start = datetime.now() - timedelta(days=120)
>>> end = datetime.now()
>>> months_between(start,end)
[6, 7, 8, 9, 10]
Charles Hooper
sumber
Ini masih mengambil pendekatan perulangan yang sama, jadi saya belum tentu melihat manfaatnya ...
Joshkunz
1
Saya tidak melihat bagaimana itu menjadi masalah. Loop bukanlah musuh Anda.
Charles Hooper
Nah ini harus dilakukan setiap kali mereka adalah kueri ajax. Saya tahu bahwa loop bukanlah musuh, tetapi sepertinya loop adalah solusi lambat untuk masalah yang harus diselesaikan dengan cara yang jauh lebih mudah.
Joshkunz
3

Berikut cara melakukan ini dengan 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')

Perhatikan itu dimulai dengan bulan setelah tanggal mulai yang ditentukan.

conner.xyz
sumber
2

Ini bisa dilakukan dengan menggunakan datetime.timedelta, di mana jumlah hari untuk melompat ke bulan berikutnya dapat diperoleh dengan calender.monthrange. monthrange mengembalikan hari kerja (0-6 ~ Sen-Ming) dan jumlah hari (28-31) untuk tahun dan bulan tertentu.
Misalnya: monthrange (2017, 1) mengembalikan (6,31).

Berikut ini skrip yang menggunakan logika ini untuk mengulang antara dua bulan.

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

`

pankaj kumar
sumber
Bisakah Anda menjelaskan bagaimana ini menyelesaikan masalah? Kami mendorong orang untuk menambahkan konteks pada jawaban mereka; Terima kasih.
Gi0rgi0s
1
Menambahkan penjelasan
pankaj kumar
2

Banyak orang telah memberi Anda jawaban yang baik untuk menyelesaikan ini tetapi saya belum membaca menggunakan pemahaman daftar jadi saya memberi Anda apa yang saya gunakan untuk kasus penggunaan serupa:


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

H4kim
sumber
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)
Mehmet Ercan
sumber
1

Sama seperti rangefungsinya, saat bulan 13 , lanjutkan ke tahun depan

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

contoh di 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)]
WeizhongTu
sumber
1

Biasanya 90 hari BUKAN 3 bulan secara harfiah, hanya referensi.

Jadi, akhirnya, Anda perlu memeriksa apakah hari lebih besar dari 15 untuk menambahkan +1 ke penghitung bulan. atau lebih baik, tambahkan elif lain dengan penghitung setengah bulan.

Dari jawaban stackoverflow lainnya ini, saya akhirnya mengakhiri dengan itu:

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

Keluaran:

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

Saya perhatikan itu tidak berfungsi jika Anda menambahkan lebih dari hari yang tersisa di tahun ini, dan itu tidak terduga.

m3nda
sumber
1

sepertinya jawabannya tidak memuaskan dan saya telah menggunakan kode saya sendiri yang lebih mudah dimengerti

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
grandia
sumber
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
Yogesh Kushwaha
sumber
1

Inilah solusi saya untuk ini:

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

Ini akan menangani beberapa kasus edge juga di mana perbedaan bulan antara 31 Des 2018 dan 1 Jan 2019 akan menjadi nol (karena perbedaannya hanya satu hari).

Gaurav Agarwal
sumber
0

Dengan asumsi upperDate selalu lebih baru dari lowerDate dan keduanya adalah objek 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 ) )

Saya belum menguji ini secara menyeluruh, tetapi sepertinya itu harus melakukan trik.

Scott
sumber
0

Berikut adalah metodenya:

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

Ini pertama kali menghitung jumlah bulan antara dua tanggal, inklusif. Kemudian membuat daftar menggunakan tanggal pertama sebagai basis dan melakukan aritmatika modula untuk membuat objek tanggal.

Dan
sumber
0

Saya sebenarnya perlu melakukan sesuatu yang sangat mirip sekarang

Akhirnya menulis fungsi yang mengembalikan daftar tupel yang menunjukkan startdan endsetiap bulan antara dua set tanggal sehingga saya bisa menulis beberapa kueri SQL di belakangnya untuk total penjualan bulanan, dll.

Saya yakin ini dapat ditingkatkan oleh seseorang yang tahu apa yang mereka lakukan tetapi berharap ini membantu ...

Nilai yang dikembalikan terlihat sebagai berikut (menghasilkan hari ini - 365 hari hingga hari ini sebagai contoh)

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

Kode sebagai berikut (memiliki beberapa hal debug yang dapat dihapus):

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

Dengan asumsi bahwa Anda ingin mengetahui "pecahan" dari bulan pada tanggal itu, yang saya lakukan, maka Anda perlu melakukan sedikit lebih banyak pekerjaan.

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)

memberikan contoh yang menghitung jumlah bulan antara dua tanggal secara inklusif, termasuk pecahan setiap bulan saat tanggal tersebut masuk. Ini berarti Anda dapat menghitung berapa bulan antara 2015-01-20 dan 2015-02-14 , dimana pecahan tanggal di bulan Januari ditentukan oleh jumlah hari di bulan Januari; atau dengan mempertimbangkan bahwa jumlah hari di bulan Februari dapat berubah dari tahun ke tahun.

Untuk referensi saya, kode ini juga ada di github - https://gist.github.com/andrewyager/6b9284a4f1cdb1779b10

Andrew Yager
sumber
0

Coba ini:

 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

Tidak masalah urutan apa yang Anda masukkan tanggal, dan itu memperhitungkan perbedaan panjang bulan.

om_henners
sumber
Perhatikan bahwa ini tidak memperhitungkan tanggal Anda yang sama. Cara termudah adalah dengan jika delta_time.days = 0: bulan = 0 lagi sisa rutinitas.
om_henners
0

Ini bekerja...

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))
George Pamfilis
sumber