如何在Python中获取当前季度的周数?

2024-05-16 12:29:13 发布

您现在位置:Python中文网/ 问答频道 /正文

我已经通过了每一个问题和每一个第三方图书馆试图找出一种方法来做到这一点,我不必手动地图日期

我正在尝试获取当前财政季度的周数。 每个季度从1月1日、4月1日、7月1日或10月1日开始

给定一个日期(字符串或对象,这无关紧要),我需要能够计算它所在财政季度的周数

更为复杂的是,财政年度从4月份开始

例如,今天,2020年7月9日是本财政季度(第2季度)的第2周,因为该季度从4月份开始。同样,2020年6月29日和30日是第一季度的第14周

大多数时间格式库,甚至是标准库都有类似ISO date的方法,我可以从中提取周数。但这是一年中第一天起的周数

我不能使用算术简单地删除当前日期的周数,因为每个季度有不同的周数。根据年份的不同,季度可以有12周、13周或14周

我得到的最接近的是使用FiscalYear库,它非常棒,因为它有一个财政季度类。不幸的是,继承的方法isoformat()不适用于它。只有FiscalDate类,它没有提供我需要的季度

有人碰到过这个吗?有人能给我指出正确的方向吗

我会发布代码片段,但这只是Python中获取当前周数的100种方法(截至今天,为28)

我试过在dateutils中使用rrules和delta,但我能得到的最接近的是使用偏移量的第一季度的周数。第二季度,它崩溃了

我很乐意使用pandas或任何其他第三方库,如果它能帮助我避免硬编码季度日期或周数到日期映射的话

如果能在正确的方向上提供帮助,我们将不胜感激


编辑:下面三个答案以不同的方式为我解决了这个问题。我很难找到正确的答案,但我给了@Paul的答案,因为这是我作为一个非高年级学生最能遵循的答案。这也是与我的个人用例(我没有提到)相匹配的答案,它接收一个datetime对象并获得结果。这就给了它优势。对不起,其他人提供了惊人的答案。我很高兴得到了代码,我所希望的只是朝着正确的方向轻推。谢谢大家。


Tags: 对象方法字符串答案代码图书馆时间地图
3条回答

除非这是一种非常常见的计算周数的方法,否则我不知道您是否会找到一个库来为您精确地计算周数,但是使用dateutilrelativedelta和一点逻辑很容易完成。下面是一个返回元组(quarter, week)的简单实现。由于您说过Q1从4月1日开始,我假设从1月1日到4月1日的时间段称为Q0:

from datetime import date, datetime, timedelta
import typing

from dateutil import relativedelta

NEXT_MONDAY = relativedelta.relativedelta(weekday=relativedelta.MO)
LAST_MONDAY = relativedelta.relativedelta(weekday=relativedelta.MO(-1))
ONE_WEEK = timedelta(weeks=1)


def week_in_quarter(dt: datetime) -> typing.Tuple[int, int]:
    d: date = dt.date()
    year = d.year

    # Q0 = January 1, Q1 = April 1, Q2 = July 1, Q3 = October 1
    quarter = ((d.month - 1) // 3)
    quarter_start = date(year, (quarter * 3) + 1, 1)
    quarter_week_2_monday = quarter_start + NEXT_MONDAY

    if d < quarter_week_2_monday:
        week = 1
    else:
        cur_week_monday = d + LAST_MONDAY
        week = int((cur_week_monday - quarter_week_2_monday) / ONE_WEEK) + 2

    return quarter, week

返回:

$ python week_in_quarter.py 
2020-01-01: Q0-W01
2020-02-01: Q0-W05
2020-02-29: Q0-W09
2020-03-01: Q0-W09
2020-06-30: Q1-W14
2020-07-01: Q2-W01
2020-09-04: Q2-W10
2020-12-31: Q3-W14

如果我误解了日历年的第一个季度,而实际上X年的1月1日至4月1日被视为X-1年的第四季度,那么您可以将末尾的return quarter, week行更改为该行(并更改返回类型注释):

if quarter == 0:
    year -= 1
    quarter = 4

return year, quarter, week

将返回值更改为:

$ python week_in_quarter.py 
2020-01-01: FY2019-Q4-W01
2020-02-01: FY2019-Q4-W05
2020-02-29: FY2019-Q4-W09
2020-03-01: FY2019-Q4-W09
2020-06-30: FY2020-Q1-W14
2020-07-01: FY2020-Q2-W01
2020-09-04: FY2020-Q2-W10
2020-12-31: FY2020-Q3-W14

如果这是一个速度瓶颈,那么编写一个不使用dateutil.relativedelta的优化版本应该很容易,而是根据星期几、一年中的哪一天以及这是否是闰年来计算(如果可以尽早将Python中的日历计算转换为整数操作,那么通常会更快),但我认为在大多数情况下,这个版本应该是最容易阅读和理解的

如果要避免对dateutil的依赖,可以用简单函数替换NEXT_MONDAYLAST_MONDAY

def next_monday(dt: date) -> date:
    weekday = dt.weekday()
    return dt + timedelta(days=(7 - weekday) % 7)

def last_monday(dt: date) -> date:
    weekday = dt.weekday()
    return dt - timedelta(days=weekday)

在这种情况下,将两个_monday变量分别指定为quarter_week_2_monday = next_monday(quarter_start)cur_week_monday = last_monday(dt)

注意:如果我编写这个函数,我可能不会让它返回一个简单的整数元组,而是使用attrsdataclass创建一个简单的类,如下所示:

import attr

@attr.s(auto_attribs=True, frozen=True, slots=True)
class QuarterInWeek:
    year: int
    quarter: int
    week: int

    def __str__(self):
        return f"FY{self.year}-Q{self.quarter}-W{self.week:02d}"

(请注意slots=True是可选的,我认为如果使用dataclasses.dataclass则不可用-只是这是一个简单的结构,我倾向于使用slots类作为简单的结构)

我认为这正是你需要的(或者至少是一个很好的开始):

import datetime as dt

def quarter(date):
    return (date.month-1)//3 + 1 
    
def week_in_q(d):
    year=d.year
    soq={1:dt.date(year,1,1),
         2:dt.date(year,4,1),
         3:dt.date(year,7,1),
         4:dt.date(year,10,1)}
    for i, sow in enumerate(soq[quarter(d)]+dt.timedelta(weeks=x) for x in range(5*3)):
        if sow>=d: 
            return i+1
date=dt.date(2020, 1, 1)    

for d in (date+dt.timedelta(weeks=x) for x in range(53)):
    print(f"date: {d}, quarter: {quarter(d)}, week in that quarter: {week_in_q(d)}")

印刷品:

date: 2020-01-01, quarter: 1, week in that quarter: 1
date: 2020-01-08, quarter: 1, week in that quarter: 2
date: 2020-01-15, quarter: 1, week in that quarter: 3
date: 2020-01-22, quarter: 1, week in that quarter: 4
date: 2020-01-29, quarter: 1, week in that quarter: 5
date: 2020-02-05, quarter: 1, week in that quarter: 6
date: 2020-02-12, quarter: 1, week in that quarter: 7
date: 2020-02-19, quarter: 1, week in that quarter: 8
date: 2020-02-26, quarter: 1, week in that quarter: 9
date: 2020-03-04, quarter: 1, week in that quarter: 10
date: 2020-03-11, quarter: 1, week in that quarter: 11
date: 2020-03-18, quarter: 1, week in that quarter: 12
date: 2020-03-25, quarter: 1, week in that quarter: 13
date: 2020-04-01, quarter: 2, week in that quarter: 1
date: 2020-04-08, quarter: 2, week in that quarter: 2
date: 2020-04-15, quarter: 2, week in that quarter: 3
date: 2020-04-22, quarter: 2, week in that quarter: 4
date: 2020-04-29, quarter: 2, week in that quarter: 5
date: 2020-05-06, quarter: 2, week in that quarter: 6
date: 2020-05-13, quarter: 2, week in that quarter: 7
date: 2020-05-20, quarter: 2, week in that quarter: 8
date: 2020-05-27, quarter: 2, week in that quarter: 9
date: 2020-06-03, quarter: 2, week in that quarter: 10
date: 2020-06-10, quarter: 2, week in that quarter: 11
date: 2020-06-17, quarter: 2, week in that quarter: 12
date: 2020-06-24, quarter: 2, week in that quarter: 13
date: 2020-07-01, quarter: 3, week in that quarter: 1
date: 2020-07-08, quarter: 3, week in that quarter: 2
date: 2020-07-15, quarter: 3, week in that quarter: 3
date: 2020-07-22, quarter: 3, week in that quarter: 4
date: 2020-07-29, quarter: 3, week in that quarter: 5
date: 2020-08-05, quarter: 3, week in that quarter: 6
date: 2020-08-12, quarter: 3, week in that quarter: 7
date: 2020-08-19, quarter: 3, week in that quarter: 8
date: 2020-08-26, quarter: 3, week in that quarter: 9
date: 2020-09-02, quarter: 3, week in that quarter: 10
date: 2020-09-09, quarter: 3, week in that quarter: 11
date: 2020-09-16, quarter: 3, week in that quarter: 12
date: 2020-09-23, quarter: 3, week in that quarter: 13
date: 2020-09-30, quarter: 3, week in that quarter: 14
date: 2020-10-07, quarter: 4, week in that quarter: 2
date: 2020-10-14, quarter: 4, week in that quarter: 3
date: 2020-10-21, quarter: 4, week in that quarter: 4
date: 2020-10-28, quarter: 4, week in that quarter: 5
date: 2020-11-04, quarter: 4, week in that quarter: 6
date: 2020-11-11, quarter: 4, week in that quarter: 7
date: 2020-11-18, quarter: 4, week in that quarter: 8
date: 2020-11-25, quarter: 4, week in that quarter: 9
date: 2020-12-02, quarter: 4, week in that quarter: 10
date: 2020-12-09, quarter: 4, week in that quarter: 11
date: 2020-12-16, quarter: 4, week in that quarter: 12
date: 2020-12-23, quarter: 4, week in that quarter: 13
date: 2020-12-30, quarter: 4, week in that quarter: 14

下面是一个使用python的isocalendar库查找周数的简单解决方案:

注:本周从周一开始

from datetime import datetime

FISCAL_QUARTERS = [4, 7, 10, 1]  # April, July, October, January
FISCAL_PERIOD = 3

def _calc_quarter_week(day, month, year):
    fiscal_quarter = None
    # Find which quarter the given date falls in
    for fiscal_index in range(len(FISCAL_QUARTERS)):
        f_month = FISCAL_QUARTERS[fiscal_index]
        if month >= f_month and month < f_month + FISCAL_PERIOD:
            fiscal_quarter = fiscal_index + 1
            break

    quarter_start_day = datetime(
        year=year, month=FISCAL_QUARTERS[fiscal_quarter-1], day=1)
    # Quarter week number
    _, q_week_no, _ = quarter_start_day.isocalendar()

    given_date = datetime(year=year, month=month, day=day)
    # Given week number
    _, given_week_no, _ = given_date.isocalendar()

    return fiscal_quarter, given_week_no - q_week_no + 1


day, month, year = map(int, input('Day Month Year\n').strip().split())
fiscal_quarter, week_count = _calc_quarter_week(day, month, year)
print('Fiscal quarter: {}, Week: {}'.format(fiscal_quarter, week_count))

产出:

Day Month Year
29 6 2020
Fiscal quarter: 1, Week: 14
Day Month Year
9 7 2020
Fiscal quarter: 2, Week: 2

相关问题 更多 >