2016-04-22 136 views
5

我已經構建了一個函數來獲取當前季度的第一天和最後一天,但它有點長。我想知道,是否有一個更簡潔的方法來完成這個?Python獲取當前日曆季度的第一天和最後一天

據我所知,pandas有一個QuarterBegin()函數,但我不能以更簡潔的方式實現它。

import datetime as dt 
from dateutil.relativedelta import relativedelta  

def get_q(first=None,last=None): 

    today = dt.date.today() 

    qmonth = [1, 4, 7, 10] 

    if first: 
     for i,v in enumerate(qmonth): 
      if (today.month-1)//3 == i: 
       return dt.date(today.year,qmonth[i],1).strftime("%Y-%m-%d") 

    if last: 
     firstday = dt.datetime.strptime(get_q(first=True),"%Y-%m-%d") 
     lastday = firstday + relativedelta(months=3, days=-1) 
     return lastday.strftime("%Y-%m-%d") 

編輯:請讓我知道這將是更適合於Code Review

+1

爲什麼'relativedelta(月= 3) - dt.timedelta(天= 1)',而不是'relativedelta(月= 3天= -1)'? – Paul

+0

@保羅 - 是的,那樣更好。謝謝。我將編輯帖子。 – Charon

回答

4

你可以這樣來做:

import bisect 
import datetime as dt 

def get_quarter_begin(): 
    today = dt.date.today() 

    qbegins = [dt.date(today.year, month, 1) for month in (1,4,7,10)] 

    idx = bisect.bisect(qbegins, today) 
    return str(qbegins[idx-1]) 

這解決了 「第一」 的情況;我將「最後一個」情況作爲練習,但我建議將它作爲一個獨立的函數來保持清晰(如果沒有參數傳遞,會發生什麼?)。

+0

如果您返回的是日期對象而不是字符串,它會使函數更一般化。 – jfs

+0

@ J.F.Sebastian:我完全同意 - 我在OP做了什麼之後模仿了這一點。但是,是的,在return語句中刪除'str()'調用對我來說也是可取的。 –

+0

不相關:對於一個小的'N'(例如在這種情況下),可以使用線性搜索來代替二分搜索:'return next(month(date(today.year,month,1)for month),next_month in zip(quarter_months ,quarter_months [1:])if today.month jfs

2
  • 避免Yo-Yo code。不要將日期對象轉換爲字符串,而只需立即將其解析回日期對象,就像if last分支在問題中所做的那樣。取而代之的是,返回一個日期對象並僅在必要時轉換爲字符串(它比訪問對象屬性更容易,比如解析其字符串表示以提取相同的信息)
  • 避免互斥布爾關鍵字參數(firstlast)。這是一個容易出錯的界面,它滋生了代碼重複。很容易在這裏返回兩個結果並稍後訪問相應的屬性,如.first_day。或者如果有(不太可能)的性能問題;你可以創建兩個函數,如get_first_day_of_the_quarter()而不是
  • 爲簡化算法,可以爲輸入數據添加一點冗餘,例如,請參閱下面代碼中的quarter_first_days1在幾個月的列表中提到兩次) - 允許使用i+1無條件:
#!/usr/bin/env python 
from collections import namedtuple 
from datetime import MINYEAR, date, timedelta 

DAY = timedelta(1) 
quarter_first_days = [date(MINYEAR+1, month, 1) for month in [1, 4, 7, 10, 1]]  
Quarter = namedtuple('Quarter', 'first_day last_day') 

def get_current_quarter(): 
    today = date.today() 
    i = (today.month - 1) // 3 # get quarter index 
    days = quarter_first_days[i], quarter_first_days[i+1] - DAY 
    return Quarter(*[day.replace(year=today.year) for day in days]) 

MINYEAR+1用於容納- DAY表達(它假定MINYEAR < MAXYEAR)。本季度指數的計算公式爲Is there a Python function to determine which quarter of the year a date is in?

例子:

>>> get_current_quarter() 
Quarter(first_day=datetime.date(2016, 4, 1), last_day=datetime.date(2016, 6, 30)) 
>>> str(get_current_quarter().last_day) 
'2016-06-30' 
+0

感謝發佈這個答案。我可以看到簡單地使用'date'對象並在必要時將它們轉換爲字符串會更好。我想我會把你的答案和上面的答案結合起來,使我的功能。 – Charon

4

爲什麼這麼複雜:-)

from datetime import date 
from calendar import monthrange 

quarter = 2 
year = 2016 
first_month_of_quarter = 3 * quarter - 2 
last_month_of_quarter = 3 * quarter 
date_of_first_day_of_quarter = date(year, first_month_of_quarter, 1) 
date_of_last_day_of_quarter = date(year, last_month_of_quarter, monthrange(year, last_month_of_quarter)[1]) 
2

爲什麼推出自己的?

import pandas as pd 

quarter_start = pd.to_datetime(pd.datetime.today() - pd.tseries.offsets.QuarterBegin(startingMonth=1)).date() 
+0

以及如何計算當前季度結束日期? –

+1

將QuarterBegin更改爲QuarterEnd並添加而不是減去 –

+0

感謝您在開始月份中進行了一次更改= 3 –

0

你不應該需要使用不必要的循環或像熊貓這樣的大型庫來做到這一點。你可以用簡單的整數除法/算術和只有日期時間庫(儘管使用更清晰的代碼使用dateutil結果)。

import datetime 

def getQuarterStart(dt=datetime.date.today()): 
    return datetime.date(dt.year, (dt.month - 1) // 3 * 3 + 1, 1) 

# using just datetime 
def getQuarterEnd1(dt=datetime.date.today()): 
    nextQtYr = dt.year + (1 if dt.month>9 else 0) 
    nextQtFirstMo = (dt.month - 1) // 3 * 3 + 4 
    nextQtFirstMo = 1 if nextQtFirstMo==13 else nextQtFirstMo 
    nextQtFirstDy = datetime.date(nextQtYr, nextQtFirstMo, 1) 
    return nextQtFirstDy - datetime.timedelta(days=1) 

# using dateutil 
from dateutil.relativedelta import relativedelta 

def getQuarterEnd2(dt=datetime.date.today()): 
    quarterStart = getQuarterStart(dt) 
    return quarterStart + relativedelta(months=3, days=-1) 

輸出:

>>> d1=datetime.date(2017,2,15) 
>>> d2=datetime.date(2017,1,1) 
>>> d3=datetime.date(2017,10,1) 
>>> d4=datetime.date(2017,12,31) 
>>> 
>>> getQuarterStart(d1) 
datetime.date(2017, 1, 1) 
>>> getQuarterStart(d2) 
datetime.date(2017, 1, 1) 
>>> getQuarterStart(d3) 
datetime.date(2017, 10, 1) 
>>> getQuarterStart(d4) 
datetime.date(2017, 10, 1) 
>>> getQuarterEnd1(d1) 
datetime.date(2017, 3, 31) 
>>> getQuarterEnd1(d2) 
datetime.date(2017, 3, 31) 
>>> getQuarterEnd1(d3) 
datetime.date(2017, 12, 31) 
>>> getQuarterEnd1(d4) 
datetime.date(2017, 12, 31) 
>>> getQuarterEnd2(d1) 
datetime.date(2017, 3, 31) 
>>> getQuarterEnd2(d2) 
datetime.date(2017, 3, 31) 
>>> getQuarterEnd2(d3) 
datetime.date(2017, 12, 31) 
>>> getQuarterEnd2(d4) 
datetime.date(2017, 12, 31) 
相關問題