Cách tốt nhất để tìm các tháng giữa hai ngày


91

Tôi có nhu cầu có thể tìm chính xác các tháng giữa hai ngày trong python. Tôi có một giải pháp hoạt động nhưng nó không tốt lắm (như trong thanh lịch) hoặc nhanh.

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

Vì vậy, chỉ để giải thích, những gì tôi đang làm ở đây là lấy hai ngày và chuyển đổi chúng từ định dạng iso thành các đối tượng datetime trong python. Sau đó, tôi lặp qua việc thêm một tuần vào đối tượng datetime bắt đầu và kiểm tra xem giá trị số của tháng có lớn hơn không (trừ khi tháng là tháng 12 thì nó sẽ kiểm tra xem ngày đó có nhỏ hơn không), nếu giá trị lớn hơn, tôi thêm nó vào danh sách trong nhiều tháng và tiếp tục lặp lại cho đến khi tôi đến ngày kết thúc.

Nó hoạt động hoàn hảo, nó chỉ có vẻ không phải là một cách tốt để làm điều đó ...


Bạn đang yêu cầu SỐ tháng giữa hai ngày hay số tháng thực tế là bao nhiêu?
Charles Hooper

trong giải pháp của tôi: Tôi không tăng thêm "giá trị của một tháng số giây". Tôi chỉ tăng số 1 lên 2, và sau đó từ 2 lên 3.
nonopolarity

Tôi chỉ muốn bạn biết rằng mặc dù bạn không thích câu trả lời của tôi vì nó "có một vòng lặp", bạn đã chọn một câu trả lời có HAI vòng lặp. Danh sách hiểu vẫn là vòng lặp.
Charles Hooper,

Câu trả lời:


0

Cập nhật 2018-04-20: có vẻ như OP @Joshkunz đang yêu cầu tìm những tháng nào nằm giữa hai ngày, thay vì "bao nhiêu tháng" nằm giữa hai ngày. Vì vậy, tôi không chắc tại sao @JohnLaRooy lại được ủng hộ hơn 100 lần. @Joshkunz đã chỉ ra trong nhận xét dưới câu hỏi ban đầu rằng anh ấy muốn ngày thực [hoặc tháng], thay vì tìm tổng số tháng .

Vì vậy, nó đã xuất hiện câu hỏi muốn, cho giữa hai ngày 2018-04-11tới2018-06-01

Apr 2018, May 2018, June 2018 

Và điều gì sẽ xảy ra nếu nó nằm giữa 2014-04-11đến 2018-06-01? Thì câu trả lời sẽ là

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

Vì vậy, đó là lý do tại sao tôi có mã giả sau nhiều năm trước. Nó chỉ đề xuất sử dụng hai tháng làm điểm kết thúc và lặp lại chúng, tăng dần một tháng một lần. @Joshkunz nói rằng anh ấy muốn có "tháng" và anh ấy cũng đề cập rằng anh ấy muốn "ngày tháng", mà không biết chính xác, rất khó để viết mã chính xác, nhưng ý tưởng là sử dụng một vòng lặp đơn giản để lặp qua các điểm cuối và tăng dần từng tháng một.

Câu trả lời 8 năm trước vào năm 2010:

Nếu cộng thêm một tuần, thì nó sẽ làm công việc xấp xỉ 4,35 lần công việc cần thiết. Tại sao không chỉ:

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)

chỉ là mã pseduo bây giờ, chưa thử nghiệm, nhưng tôi nghĩ rằng ý tưởng dọc theo cùng một dòng sẽ hoạt động.


1
Ok, tôi thấy một số vấn đề với các tháng như tháng 2 nếu quá trình tăng được thực hiện theo tháng chứ không phải tuần.
Joshkunz

Tôi không tăng thêm "số giây giá trị của một tháng". Tôi chỉ đang tăng số lượng 1lên 2, rồi từ đó 2đến 3sau.
nonopolarity

194

Bắt đầu bằng cách xác định một số trường hợp thử nghiệm, sau đó bạn sẽ thấy rằng chức năng này rất đơn giản và không cần vòng lặp

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

Bạn nên thêm một số trường hợp thử nghiệm vào câu hỏi của mình, vì có rất nhiều trường hợp tiềm ẩn cần giải quyết - có nhiều cách để xác định số tháng giữa hai ngày.


2
nó cho kết quả sai. kết quả là 1 tháng giữa "2015-04-30" và "2015-05-01" mà thực tế chỉ là 1 ngày.
Rao

21
@Rao. đó là lý do tại sao tôi đã nói "có nhiều hơn một cách để xác định số tháng giữa hai ngày". Câu hỏi vẫn còn thiếu một định nghĩa chính thức. Đây cũng là lý do tại sao tôi đề xuất các testcase nên được cung cấp cùng với định nghĩa.
John La Rooy

2
Tôi khuyên bạn nên thêm abs () xung quanh substractions cho phép d1 phải nhỏ hơn d2: abs return (d1.year - d2.year) * 12 + abs (d1.month - d2.month)
Lukas Schulze

Bạn có chắc không, @LukasSchulze? Nếu d1 nhỏ hơn d2, bạn sẽ phải trừ số đó cho số đầu tiên, phải không?
Lilith-Elina

Nhưng trong trường hợp của tôi, không thành vấn đề nếu d1 <= d2 hay d2 <= d1. Bạn chỉ quan tâm đến chênh lệch, bất kể ngày nào trong hai ngày nhỏ hơn / lớn hơn.
Lukas Schulze

40

Một lớp lót để tìm danh sách ngày giờ, tăng dần theo tháng, giữa hai ngày.

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

Điều này! Điều này điều chỉnh cho nhiều tình huống đặc biệt ưa thích nhờ sử dụng rrule. Lưu ý rằng các giá trị đầu ra là ngày giờ để bạn có thể chuyển đổi chúng thành bất kỳ thứ gì bạn thích (bao gồm cả chuỗi) để phù hợp với những gì người khác đang hiển thị.
Ezekiel Kruglick

Điều này không thành công khi strt_dt là 2001-1-29, do không có Ngày nhuận trong năm đó.
CS

2
OP yêu cầu danh sách các tháng giữa ngày bắt đầu và ngày kết thúc. Tháng Hai bị bỏ lỡ bằng cách sử dụng phương pháp của bạn trong ví dụ của tôi. Tất nhiên, giải pháp của bạn vẫn có thể được cứu vãn bằng cách điều chỉnh ngày bắt đầu thành ngày đầu tiên của tháng.
CS

2
Đó là một giải pháp tốt và nó giúp tôi tiết kiệm rất nhiều thời gian. Chúng ta có thể làm cho nó tốt hơn nhiều bằng cách cho ngày bắt đầu[dt for dt in rrule(MONTHLY, bymonthday=10,dtstart=strt_dt, until=end_dt)]
Pengju Zhao

1
Hãy nhận biết về nó. Họ thực sự có một ghi chú trong tài liệu nói rằng rrule có thể có "hành vi đáng ngạc nhiên, ví dụ: khi ngày bắt đầu xảy ra vào cuối tháng" (xem Lưu ý trong dateutil.readthedocs.io/en/stable/rrule.html ). Một cách để tránh nó là thay thế ngày bằng ngày đầu tiên của tháng: start_date_first = start_date.replace(day=1), end_date_first = end_date.replace(day=1)Sau đó, rrule đếm tháng đúng cách.
Alla Sorokina

36

Điều này đã làm việc cho tôi -

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)

loại bỏ các str()ký tự không cần thiết xung quanh chuỗi.
jfs

7
Không có gì đáng rằng nếu vùng đồng bằng là hơn 1 năm, r.monthssẽ bắt đầu từ 0
jbkkd

2
thường tôi làm r.months * (r.years + 1), như có thể điều chỉnh cho những gì @jbkkd đang nói về
triunenature

9
@triunenature vẻ sai này, chắc chắn nó phải là một cái gì đó giống như r.months + (r.years * 12)
Moataz Elmasry

2
Vâng, điều này không hiệu quả nếu các ngày cách nhau hơn một năm.
HansG600,

11

Bạn có thể dễ dàng tính toán điều này bằng cách sử dụng rrule từ mô-đun 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))))

sẽ cung cấp cho bạn:

 [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

Lấy tháng kết thúc (liên quan đến năm và tháng của tháng bắt đầu, ví dụ: 2011 tháng 1 = 13 nếu ngày bắt đầu của bạn bắt đầu vào tháng 10 năm 2010) và sau đó tạo lịch ngày bắt đầu từ tháng bắt đầu và tháng kết thúc như vậy:

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

nếu cả hai ngày đều vào cùng một năm, nó cũng có thể được viết đơn giản là:

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

8

Bài đăng này đóng đinh nó! Sử dụng 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 Như bạn có thể thấy trong ví dụ tôi đã đưa ra, ngày tháng ở các năm khác nhau. Tuy nhiên, str()dàn diễn viên mà tôi đã làm là hoàn toàn không cần thiết.
srodriguex

5
này không hoạt động nếu ngày bao gồm hơn một năm, bạn cần phải thêmrelativedelta.relativedelta(date2, date1).years * 12
muon

delta.years*12 + delta.months
user2682863

7

Giải pháp đơn giản của tôi:

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

giải pháp được đánh giá thấp
khởi động lại

4

Xác định một "tháng" như 1 / 12 năm, sau đó làm như sau:

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

Bạn có thể cố gắng xác định một tháng là "khoảng thời gian 29, 28, 30 hoặc 31 ngày (tùy thuộc vào năm)". Nhưng bạn làm điều đó, bạn có thêm một vấn đề cần giải quyết.

Trong khi nó thường là rõ ràng rằng 15 tháng 6 ngày tháng + 1 nên ngày 15 tháng 7 ngày , nó không phải là thường không rõ ràng nếu 30 tháng 1 ngày + 1 tháng là vào tháng Hai hoặc tháng Ba. Trong trường hợp sau, bạn có thể bị ép buộc để tính ngày như ngày 30 tháng 2 ngày , sau đó "đúng" nó vào ngày 2 tháng 3 thứ . Nhưng khi bạn làm điều đó, bạn sẽ thấy rằng 02 Tháng Ba nd - 1 tháng rõ ràng là 02 tháng 2 nd . Ergo, rút ​​gọn quảng cáo vô lý (hoạt động này không được xác định rõ).


4

Có một giải pháp đơn giản dựa trên năm 360 ngày, trong đó tất cả các tháng đều có 30 ngày. Nó phù hợp với hầu hết các trường hợp sử dụng, trong đó, với hai ngày, bạn cần tính số tháng đầy đủ cộng với số ngày còn lại.

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

Một chút giải pháp đã được kiểm chứng bởi @ 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

Bạn cũng có thể sử dụng thư viện mũi tên . Đây là một ví dụ đơn giản:

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

Điều này sẽ in:

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

Hi vọng điêu nay co ich!


4
from dateutil import relativedelta

relativedelta.relativedelta(date1, date2)

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

3

Hãy thử một cái gì đó như thế này. Nó hiện bao gồm tháng nếu cả hai ngày xảy ra trong cùng một tháng.

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

Đầu ra giống như sau:

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

Điều này vẫn có cách tiếp cận vòng lặp tương tự mặc dù, vì vậy tôi không nhất thiết phải nhận thấy lợi ích ...
Joshkunz

1
Tôi không hiểu đó là một vấn đề. Vòng lặp không phải là kẻ thù của bạn.
Charles Hooper

Điều này phải được thực hiện mỗi khi họ là một truy vấn ajax. Tôi biết rằng các vòng lặp không phải là kẻ thù nhưng có vẻ như chúng là một giải pháp chậm cho một vấn đề cần được giải quyết theo cách dễ dàng hơn nhiều.
Joshkunz


3

Đây là cách thực hiện việc này với 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')

Lưu ý rằng nó bắt đầu với tháng sau ngày bắt đầu nhất định.


2

Nó có thể được thực hiện bằng cách sử dụng datetime.timedelta, trong đó số ngày để chuyển sang tháng sau có thể được lấy bằng calender.monthrange. monthrange trả về ngày trong tuần (0-6 ~ T2-CN) và số ngày (28-31) cho một năm và tháng nhất định.
Ví dụ: monthrange (2017, 1) trả về (6,31).

Đây là tập lệnh sử dụng logic này để lặp lại giữa hai tháng.

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

`


Bạn có thể vui lòng giải thích cách này giải quyết vấn đề không? Chúng tôi khuyến khích mọi người thêm ngữ cảnh vào câu trả lời của họ; cảm ơn.
Gi0rgi0s

1
Thêm một lời giải thích
Pankaj Kumar

2

Nhiều người đã cho bạn những câu trả lời hay để giải quyết vấn đề này nhưng tôi chưa đọc bất kỳ cách sử dụng hiểu danh sách nào nên tôi cung cấp cho bạn những gì tôi đã sử dụng cho một trường hợp sử dụng tương tự:


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

giống như rangechức năng, khi tháng là 13 , chuyển sang năm sau

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

ví dụ trong vỏ trăn

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

Thông thường 90 ngày KHÔNG phải là 3 tháng theo nghĩa đen, chỉ là một tham chiếu.

Vì vậy, cuối cùng, bạn cần kiểm tra xem ngày có lớn hơn 15 hay không để thêm +1 vào bộ đếm tháng. hoặc tốt hơn, thêm một elif khác với bộ đếm nửa tháng.

Từ câu trả lời stackoverflow khác này, cuối cùng tôi đã kết thúc với điều đó:

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

Đầu ra:

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

Tôi nhận thấy rằng điều đó sẽ không hoạt động nếu bạn thêm nhiều hơn số ngày còn lại trong năm hiện tại và điều đó thật bất ngờ.


1

Có vẻ như các câu trả lời không thỏa đáng và tôi đã sử dụng mã của riêng mình, dễ hiểu hơn

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

Giả sử upperDate luôn muộn hơn lowDate và cả hai đều là đối tượng 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 ) )

Tôi đã không thử nghiệm điều này kỹ lưỡng, nhưng có vẻ như nó sẽ thực hiện thủ thuật.


0

Đây là một phương pháp:

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

Đây là lần đầu tiên tính toán tổng số tháng giữa hai ngày, bao gồm cả. Sau đó, nó tạo ra một danh sách sử dụng ngày đầu tiên làm cơ sở và thực hiện số học modula để tạo các đối tượng ngày.


0

Tôi thực sự cần làm một cái gì đó tương tự như vừa rồi

Kết thúc việc viết một hàm trả về một danh sách các bộ giá trị chỉ ra startend của mỗi tháng giữa hai nhóm ngày để tôi có thể viết một số truy vấn SQL ở mặt sau của nó cho tổng doanh số hàng tháng, v.v.

Tôi chắc rằng nó có thể được cải thiện bởi một người biết họ đang làm gì nhưng hy vọng nó sẽ giúp ...

Giá trị trả về trông như sau (tạo cho hôm nay - 365 ngày cho đến hôm nay làm ví dụ)

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

Mã như sau (có một số nội dung gỡ lỗi có thể được xóa):

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

Giả sử rằng bạn muốn biết "phần nhỏ" của tháng mà tôi đã làm, thì bạn cần phải làm thêm một chút.

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)

cung cấp một ví dụ tính toán hoàn toàn số tháng giữa hai ngày, bao gồm cả phần của mỗi tháng có ngày đó. Điều này có nghĩa là bạn có thể tính ra bao nhiêu tháng từ 2015-01-20 đến 2015-02-14 , trong đó phần của ngày trong tháng 1 được xác định bằng số ngày trong tháng 1; hoặc có tính đến số ngày trong tháng Hai có thể thay đổi từ năm này sang năm khác.

Để tôi tham khảo, mã này cũng có trên github - https://gist.github.com/andrewyager/6b9284a4f1cdb1779b10


0

Thử cái này:

 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

Không quan trọng thứ tự bạn nhập ngày tháng và nó phải tính đến sự khác biệt về độ dài tháng.


Lưu ý rằng điều này không giải thích cho các ngày của bạn giống nhau. Cách dễ nhất sẽ là if delta_time.days = 0: months = 0 else của quy trình.
om_henners

0

Những công việc này...

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.