วิธีที่ดีที่สุดในการค้นหาเดือนระหว่างวันที่สองวัน


93

ฉันจำเป็นต้องสามารถค้นหาเดือนระหว่างวันที่สองวันใน python ได้อย่างแม่นยำ ฉันมีวิธีแก้ปัญหาที่ใช้งานได้ แต่ไม่ดีมาก (อย่างสวยหรู) หรือเร็ว

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

เพื่ออธิบายสิ่งที่ฉันกำลังทำอยู่ที่นี่คือการหาวันที่สองวันและแปลงจากรูปแบบ iso เป็นวัตถุ python datetime จากนั้นฉันวนซ้ำการเพิ่มสัปดาห์ให้กับวัตถุวันที่และเวลาเริ่มต้นและตรวจสอบว่าค่าตัวเลขของเดือนนั้นสูงกว่าหรือไม่ (เว้นแต่เดือนนั้นจะเป็นเดือนธันวาคมจากนั้นจะตรวจสอบว่าวันที่น้อยกว่าหรือไม่) หากค่ามากกว่านี้ฉันจะผนวกเข้ากับรายการ ของเดือนและวนซ้ำไปเรื่อย ๆ จนกว่าฉันจะถึงวันที่สิ้นสุด

มันทำงานได้อย่างสมบูรณ์ดูเหมือนจะไม่ใช่วิธีที่ดีในการทำ ...


คุณกำลังขอ NUMBER ของเดือนระหว่างวันที่สองวันหรือเดือนที่แท้จริงคืออะไร?
Charles Hooper

ในวิธีแก้ปัญหาของฉัน: ฉันไม่ได้เพิ่มขึ้นตาม "จำนวนวินาทีที่มีค่าต่อเดือน" ฉันแค่เพิ่มจำนวน 1 เป็น 2 แล้วเพิ่มจาก 2 เป็น 3 ในภายหลัง
nonopolarity

ฉันแค่อยากให้คุณรู้ว่าแม้ว่าคุณจะไม่ชอบคำตอบของฉันเพราะมัน "วนซ้ำ" คุณเลือกคำตอบที่มีสองลูป ความเข้าใจในรายการยังคงวนลูป
Charles Hooper

คำตอบ:


1

อัปเดต 2018-04-20:ดูเหมือนว่า OP @Joshkunz กำลังขอให้ค้นหาว่าเดือนใดอยู่ระหว่างวันที่สองวันแทนที่จะเป็น "กี่เดือน" ระหว่างวันที่สองวัน ดังนั้นฉันไม่แน่ใจว่าทำไม @JohnLaRooy ถึงได้รับการโหวตมากกว่า 100 ครั้ง @ Joshkunz ระบุไว้ในความคิดเห็นใต้คำถามเดิมว่าเขาต้องการวันที่จริง [หรือเดือน] แทนที่จะหาจำนวนเดือนทั้งหมด

ดังนั้นจึงปรากฏคำถามที่ต้องการระหว่างสองวัน2018-04-11ถึง2018-06-01

Apr 2018, May 2018, June 2018 

แล้วถ้าอยู่ระหว่าง2014-04-11ถึง2018-06-01ล่ะ? จากนั้นคำตอบจะเป็น

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

นั่นคือเหตุผลที่ฉันมีรหัสหลอกต่อไปนี้เมื่อหลายปีก่อน เพียงแค่แนะนำให้ใช้สองเดือนเป็นจุดสิ้นสุดแล้ววนซ้ำโดยเพิ่มทีละหนึ่งเดือนต่อครั้ง @ Joshkunz กล่าวว่าเขาต้องการ "เดือน" และเขายังบอกว่าเขาต้องการ "วันที่" โดยไม่รู้แน่ชัดว่ามันยากที่จะเขียนโค้ดที่แน่นอน แต่แนวคิดก็คือการใช้ลูปง่ายๆเพียงวงเดียวเพื่อวนรอบจุดสิ้นสุดและ เพิ่มขึ้นทีละหนึ่งเดือน

คำตอบเมื่อ 8 ปีที่แล้วในปี 2010:

หากเพิ่มสัปดาห์ละครั้งก็จะทำงานได้ประมาณ 4.35 เท่าของงานตามต้องการ ทำไมไม่เพียง:

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)

ตอนนี้แค่รหัส pseduo ยังไม่ได้ทดสอบ แต่ฉันคิดว่าแนวคิดในบรรทัดเดียวกันจะใช้ได้


1
โอเคฉันพบปัญหาบางอย่างในเดือนเช่นกุมภาพันธ์ถ้าการเพิ่มขึ้นเป็นรายเดือนแทนที่จะเป็นสัปดาห์
Joshkunz

ฉันไม่ได้เพิ่มขึ้นตาม "จำนวนวินาทีที่มีค่าต่อเดือน" ฉันแค่เพิ่มจำนวน1เป็น2และจากนั้น2ไปใน3ภายหลัง
nonopolarity

199

เริ่มต้นด้วยการกำหนดกรณีทดสอบจากนั้นคุณจะเห็นว่าฟังก์ชั่นนั้นง่ายมากและไม่จำเป็นต้องวนซ้ำ

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

คุณควรเพิ่มกรณีทดสอบบางกรณีในคำถามของคุณเนื่องจากมีหลายกรณีที่เป็นไปได้ที่จะครอบคลุม - มีมากกว่าหนึ่งวิธีในการกำหนดจำนวนเดือนระหว่างวันที่สองวัน


2
มันให้ผลลัพธ์ที่ไม่ถูกต้อง มันเป็นผล 1 เดือนระหว่าง "2015-04-30" และ "2015-05-01" ซึ่งเป็นเวลาเพียง 1 วัน
ราว

21
@ ราว. นั่นเป็นเหตุผลที่ฉันบอกว่า "มีมากกว่าหนึ่งวิธีในการกำหนดจำนวนเดือนระหว่างวันที่สองวัน" คำถามยังขาดคำจำกัดความที่เป็นทางการ นี่เป็นเหตุผลว่าทำไมฉันจึงแนะนำว่าควรระบุกรณีทดสอบควบคู่ไปกับคำจำกัดความ
John La Rooy

2
ฉันแนะนำให้เพิ่ม abs () รอบ ๆ ส่วนย่อยเพื่อให้ d1 มีขนาดเล็กกว่า d2: return abs (d1.year - d2.year) * 12 + abs (d1.month - d2.month)
lszrh

แน่ใจนะ @LukasSchulze ถ้า d1 น้อยกว่า d2 คุณจะต้องลบจำนวนนั้นออกจากตัวแรกใช่ไหม?
Lilith-Elina

41

หนึ่งซับเพื่อค้นหารายการวันที่โดยเพิ่มขึ้นทีละเดือนระหว่างวันที่สองวัน

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

นี้! สิ่งนี้ปรับให้เหมาะกับสถานการณ์พิเศษมากมายด้วยการใช้ rrule โปรดทราบว่าค่าเอาต์พุตเป็นวันที่เพื่อให้คุณสามารถแปลงเป็นอะไรก็ได้ที่คุณต้องการ (รวมถึงสตริง) เพื่อให้ตรงกับสิ่งที่ผู้อื่นแสดง
Ezekiel Kruglick

สิ่งนี้ล้มเหลวเมื่อ strt_dt คือ 2001-1-29 เนื่องจากไม่มี Leap Day ในปีนั้น
CS

2
OP ขอรายชื่อเดือนระหว่างวันที่เริ่มต้นและวันที่สิ้นสุด กุมภาพันธ์พลาดโดยใช้วิธีของคุณในตัวอย่างของฉัน แน่นอนว่าโซลูชันของคุณยังคงสามารถกู้คืนได้โดยการปรับวันที่เริ่มต้นเป็นวันแรกของเดือนเป็นต้น
CS

2
เป็นทางออกที่ดีและช่วยประหยัดเวลาได้มาก เราสามารถทำให้ดีขึ้นได้โดยให้วันที่เริ่มต้น[dt for dt in rrule(MONTHLY, bymonthday=10,dtstart=strt_dt, until=end_dt)]
Pengju Zhao

1
จงตระหนักถึงมัน จริงๆแล้วพวกเขามีหมายเหตุในเอกสารที่บอกว่า rrule อาจมี "พฤติกรรมที่น่าประหลาดใจเช่นเมื่อวันที่เริ่มต้นเกิดขึ้นเมื่อสิ้นเดือน" (ดูหมายเหตุในdateutil.readthedocs.io/en/stable/rrule.html ) วิธีหนึ่งในการหลีกเลี่ยงคือการแทนที่วันที่ภายในวันแรกของเดือน: start_date_first = start_date.replace(day=1), end_date_first = end_date.replace(day=1)จากนั้น rrule จะนับเดือนให้ถูกต้อง
Alla Sorokina

38

สิ่งนี้ได้ผลสำหรับฉัน -

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()ตัวอักษรสตริงที่ไม่จำเป็นออก
jfs

7
ไม่มีค่าอะไรถ้าเดลต้าเกิน 1 ปีr.monthsจะเริ่มตั้งแต่วันที่ 0
jbkkd

2
ปกติฉันจะใช้ r.months * (r.years + 1) เพราะมันปรับตามที่ @jbkkd พูดถึง
triunenature

9
@triunenature นี้ดูผิดแน่นอนมันควรจะเป็น r.months + (r.years * 12)
Moataz Elmasry

2
ใช่วิธีนี้ใช้ไม่ได้หากวันที่ห่างกันเกินหนึ่งปี
HansG600

11

คุณสามารถคำนวณสิ่งนี้ได้อย่างง่ายดายโดยใช้ rrule จากโมดูล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))))

จะให้คุณ:

 [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

รับเดือนสิ้นสุด (เทียบกับปีและเดือนของเดือนเริ่มต้นเช่น: 2011 มกราคม = 13 ถ้าวันที่เริ่มต้นของคุณเริ่มใน 2010 ต.ค. ) จากนั้นสร้างวันที่เริ่มต้นเดือนเริ่มต้นและเดือนปลายดังนี้:

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

หากทั้งสองวันอยู่ในปีเดียวกันก็สามารถเขียนได้ว่า:

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

8

โพสต์นี้เล็บมัน! ใช้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 ดังที่คุณเห็นในตัวอย่างที่ฉันให้ไว้วันที่อยู่ในปีที่แตกต่างกัน อย่างไรก็ตามstr()นักแสดงที่ฉันทำนั้นไม่จำเป็นเลย
srodriguex

6
จะใช้ไม่ได้ถ้าวันที่เกินหนึ่งปีคุณต้องเพิ่มrelativedelta.relativedelta(date2, date1).years * 12
muon

delta.years*12 + delta.months
user2682863

7

วิธีง่ายๆของฉัน:

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

วิธีแก้ปัญหาที่ไม่ได้รับความนิยม
reabow


4

กำหนด "เดือน" เป็น1 / 12ปีแล้วทำเช่นนี้:

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

คุณอาจลองกำหนดเดือนเป็น "ช่วงเวลา 29, 28, 30 หรือ 31 วัน (ขึ้นอยู่กับปี)" แต่คุณทำเช่นนั้นคุณมีปัญหาเพิ่มเติมที่ต้องแก้ไข

ในขณะที่มันมักจะชัดเจนว่า 15 มิถุนายนTH + 1 เดือนควรจะ 15 กรกฎาคมวันก็ไม่ได้มักจะไม่ชัดเจนว่า 30 มกราคมTH + 1 เดือนที่อยู่ในเดือนกุมภาพันธ์หรือมีนาคม ในกรณีหลังนี้คุณอาจจะถูกบังคับให้คำนวณวันที่ 30 กุมภาพันธ์วันที่แล้ว "ถูกต้อง" มันมีนาคม 2 ครั้ง แต่เมื่อคุณทำเช่นนั้นคุณจะพบว่า 2 มีนาคมND - 1 เดือนเป็นอย่างชัดเจนกุมภาพันธ์ 2 ครั้ง Ergo, reductio ad absurdum (การดำเนินการนี้ไม่ได้กำหนดไว้อย่างชัดเจน)


4

มีวิธีง่ายๆตาม 360 วันปีซึ่งทุกเดือนมี 30 วัน เหมาะกับกรณีการใช้งานส่วนใหญ่ที่กำหนดวันที่สองวันคุณต้องคำนวณจำนวนเดือนเต็มบวกจำนวนวันที่เหลือ

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

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

คุณยังสามารถใช้ไลบรารีลูกศร นี่เป็นตัวอย่างง่ายๆ:

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

สิ่งนี้จะพิมพ์:

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

หวังว่านี่จะช่วยได้!


3

ลองอะไรแบบนี้ ปัจจุบันรวมถึงเดือนหากวันที่ทั้งสองเป็นเดือนเดียวกัน

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

ผลลัพธ์ดูเหมือนว่า:

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

สิ่งนี้ยังคงใช้วิธีการวนซ้ำเหมือนเดิมดังนั้นฉันจึงไม่จำเป็นต้องเห็นประโยชน์ ...
Joshkunz

1
ฉันไม่เห็นว่านั่นเป็นปัญหาอย่างไร ลูปไม่ใช่ศัตรูของคุณ
Charles Hooper

สิ่งนี้จะต้องทำทุกครั้งที่เป็นแบบสอบถาม ajax ฉันรู้ว่าลูปไม่ใช่ศัตรู แต่ดูเหมือนว่าพวกเขาจะแก้ปัญหาได้ช้าซึ่งควรแก้ไขด้วยวิธีที่ง่ายกว่ามาก
Joshkunz


3

นี่คือวิธีดำเนินการกับ 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')

สังเกตว่าจะเริ่มต้นด้วยเดือนหลังจากวันที่เริ่มต้นที่กำหนด


2

สามารถทำได้โดยใช้ datetime.timedelta ซึ่งสามารถหาจำนวนวันในการข้ามไปยังเดือนถัดไปได้โดย calender.monthrange monthrange ส่งคืนวันธรรมดา (0-6 ~ จันทร์ - อาทิตย์) และจำนวนวัน (28-31) สำหรับปีและเดือนที่กำหนด
ตัวอย่างเช่น monthrange (2017, 1) ส่งกลับ (6,31)

นี่คือสคริปต์ที่ใช้ตรรกะนี้เพื่อวนซ้ำระหว่างสองเดือน

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

`


โปรดอธิบายว่าวิธีนี้แก้ปัญหาได้อย่างไร เราสนับสนุนให้ผู้คนเพิ่มบริบทให้กับคำตอบของพวกเขา ขอบคุณ.
Gi0rgi0s

1
เพิ่มคำอธิบาย
pankaj kumar

2

หลายคนให้คำตอบที่ดีแก่คุณในการแก้ปัญหานี้แล้ว แต่ฉันยังไม่ได้อ่านใด ๆ โดยใช้ list comp Understanding ดังนั้นฉันจึงให้สิ่งที่ฉันใช้สำหรับกรณีการใช้งานที่คล้ายกัน:


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

เช่นเดียวกับrangeฟังก์ชันเมื่อเดือน13ไปปีหน้า

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 shell

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

โดยปกติ 90 วันไม่ใช่ 3 เดือนตามตัวอักษรเป็นเพียงข้อมูลอ้างอิง

ในที่สุดคุณต้องตรวจสอบว่าวันมากกว่า 15 หรือไม่เพื่อเพิ่ม +1 ในตัวนับเดือน หรือดีกว่าเพิ่ม elif อื่นด้วยตัวนับครึ่งเดือน

จากคำตอบ stackoverflow อื่น ๆในที่สุดฉันก็จบลงด้วยสิ่งนั้น:

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

เอาท์พุต:

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

ฉันสังเกตเห็นว่าจะใช้ไม่ได้หากคุณเพิ่มเวลาที่เหลือมากกว่าวันในปีปัจจุบันและนั่นเป็นเรื่องที่ไม่คาดคิด


1

ดูเหมือนว่าคำตอบจะไม่น่าพอใจและฉันก็ใช้รหัสของตัวเองซึ่งเข้าใจง่ายกว่า

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

นี่คือทางออกของฉันสำหรับสิ่งนี้:

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

การดำเนินการนี้จะจัดการกับขอบบางกรณีเช่นกันโดยที่ความแตกต่างในเดือนระหว่างวันที่ 31 ธันวาคม 2018 ถึง 1 มกราคม 2019 จะเป็นศูนย์ (เนื่องจากความแตกต่างเป็นเพียงวันเดียว)


0

สมมติว่า upperDate ช้ากว่า lowerDate เสมอและทั้งสองเป็นวัตถุ 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 ) )

ฉันยังไม่ได้ทดสอบอย่างละเอียด แต่ดูเหมือนว่ามันควรจะเป็นเคล็ดลับ


0

นี่คือวิธีการ:

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

นี่คือการคำนวณจำนวนเดือนทั้งหมดระหว่างวันที่สองวันที่รวม จากนั้นจะสร้างรายการโดยใช้วันที่แรกเป็นฐานและทำการคำนวณทางคณิตศาสตร์แบบโมดูล่าเพื่อสร้างวัตถุวันที่


0

ตอนนี้ฉันต้องทำอะไรคล้าย ๆ กันจริงๆ

จบลงด้วยการเขียนฟังก์ชั่นที่ส่งคืนรายการสิ่งทอที่ระบุstartและendของแต่ละเดือนระหว่างวันที่สองชุดเพื่อให้ฉันสามารถเขียนคำสั่ง SQL บางส่วนที่ด้านหลังของมันสำหรับยอดขายรวมรายเดือนเป็นต้น

ฉันแน่ใจว่ามันจะดีขึ้นได้โดยคนที่รู้ว่ากำลังทำอะไรอยู่ แต่หวังว่ามันจะช่วยได้ ...

ค่าที่ส่งคืนมีลักษณะดังนี้ (สร้างสำหรับวันนี้ - 365 วันจนถึงวันนี้เป็นตัวอย่าง)

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

รหัสดังต่อไปนี้ (มีบางสิ่งที่ดีบักซึ่งสามารถลบออกได้):

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

สมมติว่าคุณต้องการทราบ "เศษส่วน" ของเดือนที่มีวันที่ซึ่งฉันทำแล้วคุณต้องทำงานเพิ่มอีกเล็กน้อย

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)

ให้ตัวอย่างที่คำนวณจำนวนเดือนระหว่างวันที่สองวันโดยรวมรวมเศษของแต่ละเดือนที่วันนั้นอยู่ด้วยซึ่งหมายความว่าคุณสามารถคำนวณจำนวนเดือนระหว่าง 2015-01-20 และ 2015-02-14 โดยที่เศษของวันที่ในเดือนมกราคมจะถูกกำหนดโดยจำนวนวันในเดือนมกราคม หรือพิจารณาเท่า ๆ กันว่าจำนวนวันในเดือนกุมภาพันธ์สามารถเปลี่ยนรูปแบบปีต่อปีได้

สำหรับการอ้างอิงของฉันรหัสนี้ยังอยู่ใน github - https://gist.github.com/andrewyager/6b9284a4f1cdb1779b10


0

ลองสิ่งนี้:

 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

ไม่ควรคำนึงถึงลำดับที่คุณป้อนวันที่และต้องคำนึงถึงความแตกต่างของความยาวของเดือนด้วย


โปรดทราบว่าสิ่งนี้ไม่ได้ระบุว่าวันที่ของคุณจะเหมือนกัน วิธีที่ง่ายที่สุดคือถ้า delta_time.days = 0: months = 0 else rest of routine
om_henners

0

งานนี้ ...

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))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.