2016-02-26 167 views
10

我想現在和2010年8月之間的所有月份,因爲格式化像這樣的列表:Python:獲取範圍內的所有月份?

['2010-08-01', '2010-09-01', .... , '2016-02-01'] 

現在這就是我:

months = [] 
for y in range(2010, 2016): 
    for m in range(1, 13): 
     if (y == 2010) and m < 8: 
      continue 
     if (y == 2016) and m > 2: 
      continue 
     month = '%s-%s-01' % (y, ('0%s' % (m)) if m < 10 else m) 
     months.append(month) 

什麼會是一個更好的辦法做這個?

回答

14

dateutil.relativedelta在這裏很方便。

我已經將格式化作爲練習。

from dateutil.relativedelta import relativedelta 
import datetime 

result = [] 

today = datetime.date.today() 
current = datetime.date(2010, 8, 1)  

while current <= today: 
    result.append(current) 
    current += relativedelta(months=1) 
+0

Ani,這幾乎是正確的,但您必須以ISO格式顯示的方式格式化日期時間。這可以通過追加isoformat來完成。你應該將行改爲'result.append(current.isoformat())' - [編輯:沒有看到你把格式化作爲練習......但是哦。] –

+0

謝謝,我其實已經' result.append(datetime.strftime(current,'%Y-%m-01'))'得到我想要的格式。 – Richard

0

您可以將if語句的數量減少爲兩行而不是四行,因爲第二個if語句與前面的if語句執行相同的操作有點多餘。

if (y == 2010 and m < 8) or (y == 2016 and m > 2): 
    continue 
4

使用datetimetimedelta標準Python的模塊 - 無需安裝任何新庫

from datetime import datetime, timedelta 

now = datetime(datetime.now().year, datetime.now().month, 1) 
ctr = datetime(2010, 8, 1) 
list = [ctr.strftime('%Y-%m-%d')] 

while ctr <= now: 
    ctr += timedelta(days=32) 
    list.append(datetime(ctr.year, ctr.month, 1).strftime('%Y-%m-%d')) 

我加入32天,每次進入新的月份(最長几個月31天)

+0

這是一個很好的解決方案,但你應該加上'CTR = ctr.replace(天= 1)''後,點擊率= timedelta(天= 32)',否則你會跳過在更長的一段時間。看看這個序列:[2018-10-28,2018-11-29,2018-12-31,2019-02-01,2019-03-05] - > 1月已在這裏跳過。設置日期= 1可以避免這個問題。 – jps

0

我不知道它是否是更好,但像下面的方法可能會被認爲是更「Python化」:

months = [ 
    '{}-{:0>2}-01'.format(year, month) 
     for year in xrange(2010, 2016 + 1) 
     for month in xrange(1, 12 + 1) 
     if not (year <= 2010 and month < 8 or year >= 2016 and month > 2) 
] 

主要這裏的區別是:

  • 由於我們希望迭代產生一個列表,因此使用list comprehension而不是彙總for循環中的列表元素。
  • 相反的明確使數區分低於10和號碼10及以上,使用format specification mini-language的能力爲.format() method of str指定
  • xrange而不是range返回一個生成器而不是一個列表,以便迭代值可以在它們被消耗時產生,並且不必保存在內存中。 (這個範圍並不重要,但是在Python 2中習慣這是一個好主意。)注意:在Python 3中,沒有xrangerange函數已經返回一個生成器而不是一個列表。
  • 使+ 1顯示上限。這使代碼的讀者更容易識別出我們想要指定一個包含邊界的方法(rangexrange),該方法將上限視爲排他性。否則,他們可能會想知道數字13是怎麼處理的。
+0

甚至更​​多pythonic當然使用'dateutil'和'datetime'來處理日期和時間差異,正如[Ani的回答](http://stackoverflow.com/a/35651063/674064)所做的那樣。 –

3

我看了一下dateutil文檔。原來,它提供了一個更方便的方法比使用dateutil.relativedeltarecurrence rulesexamples

手頭的任務,它是那麼容易,因爲

from dateutil.rrule import * 
from datetime import date 

months = map(
    date.isoformat, 
    rrule(MONTHLY, dtstart=date(2010, 8, 1), until=date.today()) 
) 

小字

注意,我們在這裏欺騙一點點。元素dateutil.rrule.rrule生成的類型爲datetime.datetime,即使我們通過dtstartuntil類型datetime.date,如上所述。我讓map將它們提供給dateisoformat函數,它只是將它們轉換爲字符串,就好像它們只是沒有任何時間信息的日期。

因此,表面上看似乎等價列表理解

[day.isoformat() 
    for day in rrule(MONTHLY, dtstart=date(2010, 8, 1), until=date.today())] 

會回到像

['2010-08-01T00:00:00', 
'2010-09-01T00:00:00', 
'2010-10-01T00:00:00', 
'2010-11-01T00:00:00', 
⋮ 
'2015-12-01T00:00:00', 
'2016-01-01T00:00:00', 
'2016-02-01T00:00:00'] 

列表因此,如果我們想用一個列表理解,而不是map,我們必須這樣做

[dt.date().isoformat() 
    for dt in rrule(MONTHLY, dtstart=date(2010, 8, 1), until=date.today())] 
+0

請注意,第一個列表理解中的迭代變量「day」是_deliberate_用詞不當。正如所解釋的,它擁有'datetime',而不是'date'。 –

0

我得到另一種方式使用datetime,timedelta和日曆:

from calendar import monthrange 
from datetime import datetime, timedelta 

def monthdelta(d1, d2): 
    delta = 0 
    while True: 
     mdays = monthrange(d1.year, d1.month)[1] 
     d1 += timedelta(days=mdays) 
     if d1 <= d2: 
      delta += 1 
     else: 
      break 
    return delta 

start_date = datetime(2016, 1, 1) 
end_date = datetime(2016, 12, 1) 

num_months = [i-12 if i>12 else i for i in range(start_date.month, monthdelta(start_date, end_date)+start_date.month+1)] 
monthly_daterange = [datetime(start_date.year,i, start_date.day, start_date.hour) for i in num_months] 
相關問題