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

编辑:请让我知道这是否更适合代码审查

为什么要自己滚动?

import pandas as pd
quarter_start = pd.to_datetime(pd.datetime.today() - pd.tseries.offsets.QuarterBegin(startingMonth=1)).date()

为什么这么复杂:-)

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

你可以这样做:

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

这解决了"第一种"情况;我将"最后一个"案例作为练习,但为了清楚起见,我建议将其保留为独立函数(对于您的原始版本,如果没有传递参数会发生什么,这很奇怪!

你不应该使用不必要的循环或像熊猫这样的大型库来做到这一点。您可以使用简单的整数除法/算术和日期时间库来做到这一点(尽管使用 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)
    避免使用溜
  • 溜球代码。不要将日期对象转换为字符串只是为了立即将其解析回日期对象if last就像分支在问题中所做的那样。相反,返回一个日期对象并仅在必要时转换为字符串(访问对象属性(如 .year.month比解析其字符串表示形式以提取相同的信息更容易)
  • 避免互斥布尔关键字参数(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)。季度指数公式来自 是否有一个 Python 函数来确定日期在一年中的哪个季度?

例:

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

使用 Pandas:

import pandas as pd
# current quarter start
pd.date_range(end=pd.Timestamp.now(), periods=1, freq='QS')
# current quarter end
pd.date_range(start=pd.Timestamp.now(), periods=1, freq='Q')

最新更新