Python gets the first and last day of the current calendar quarter

I built a function to get the first and last day of the current quarter, but it's a little long. I was wondering if there is a more concise way to do this?

I understand that pandas has a QuarterBegin() function, but I could not implement it in a more concise way.

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

EDIT: Please let me know if this is better suited for code review.

+5
source share
5 answers

You can do it as follows:

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

This solves the "first" case; I leave the β€œlast” case as an exercise, but I propose to keep it as an independent function for clarity (with your original version, it’s rather strange what happens if the arguments are not passed!).

+4
source

Why it is so difficult :-)

 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]) 
+4
source
  • avoid the yo-yo code . Do not convert the date object to a string just to reanalyze it back to a date object immediately, as the if last branch in the question does. Instead, return the date object and convert it to a string only if necessary (it’s easier to access the attributes of the object, such as .year , .month , than parse its string representation to extract the same information)
  • Avoid mutually exclusive logical keywords ( first , last ). This is an error prone interface and it causes duplication of code. It's easy to return both results here and access the appropriate attributes, such as .first_day later. Or if there are (unlikely) performance issues; you could create two functions like get_first_day_of_the_quarter() instead
  • To simplify the algorithm, you can add a little redundancy to the input, for example, see quarter_first_days in the code below ( 1 is mentioned twice in the list of months) - this allows you to unconditionally use 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 used to place the expression - DAY (it takes MINYEAR < MAXYEAR ). Quarterly Index Formula From Is there a Python function to determine which quarter of a year a date is in?

Example:

 >>> 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' 
+2
source

Why roll back?

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

You do not need to use unnecessary loops or large libraries like pandas for this. You can do this with simple integer division / arithmetic and just a datetime library (although using dateutil leads to cleaner code).

 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) 

Output:

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

Source: https://habr.com/ru/post/1247711/


All Articles