2016-04-22 602 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) 
相关问题