2016-04-22 2 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") 

EDIT: Пожалуйста, дайте мне знать, если это было бы лучше всего подходит для Code Review

+1

Почему 'relativedelta (месяцев = 3) - dt.timedelta (дни = 1)' вместо 'relativedelta (месяцев = 3, дней = -1)'? – Paul

+0

@Paul - да, это лучше. Спасибо. Я отредактирую сообщение. – 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. Но да, удаление вызова 'str()' в операторе return было бы предпочтительнее для меня тоже. –

+0

Несвязанный: для небольшого 'N' (например, в данном случае) вместо двоичного поиска может использоваться линейный поиск:' return next (date (today.year, month, 1) for month, next_month in zip (quarter_months) , quarter_months [1:]), если today.month jfs

2
  • избегать Yo-Yo code. Не конвертируйте объект даты в строку только для повторного анализа его обратно в объект даты сразу же, как и в случае вопроса if last. Вместо этого верните объект даты и конвертируйте в строку только при необходимости (легче получить доступ к таким атрибутам объекта, как .year, .month, чем анализировать его строковое представление для извлечения той же информации)
  • избегать взаимоисключающих логических ключевых слов (first, last). Это подверженный ошибкам интерфейс, и он порождает дублирование кода. Здесь легко вернуть оба результата и получить доступ к соответствующим атрибутам, таким как .first_day. Или если есть (маловероятные) проблемы с производительностью; вы могли бы создать две функции, такие как get_first_day_of_the_quarter() вместо
  • , чтобы упростить алгоритм, вы можете добавить немного избыточности к входным данным, например, см. quarter_first_days в приведенном ниже коде (1 упоминается дважды в списке месяцев) - разрешить использовать 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

Для этого вам не нужно использовать ненужные циклы или большие библиотеки, такие как панды. Вы можете сделать это с помощью простого целочисленного деления/арифметики и только библиотеки datetime (хотя использование 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) 
Смежные вопросы