如何在Python中获取当前季度的周数?
How to get the week number of the current quarter in Python?
我已经解决了每一个问题和每一个第三方图书馆,试图找出一种方法来做到这一点,而我不必手动映射日期。
我正在尝试获取当前财政季度的周数。
每个季度从 1 月、4 月、7 月或 10 月的 1 日开始。
给定一个日期(字符串或对象,无关紧要),我需要能够计算出它所在的财政季度的周数。
让事情变得有点复杂,财政年度从 4 月开始。
例如,今天 2020 年 7 月 9 日是本财政季度 (Q2) 的第 2 周,因为该季度从 4 月开始。同样,2020 年 6 月 29 日和 30 日是第 1 季度的第 14 周。
大多数时间格式化库甚至标准库都有像 ISO 日期这样的方法,我可以在其中提取周数。但它是从一年的第一天开始的周数。
我不能使用算术简单地删除到当前日期的周数,因为每个季度的周数不同。季度可以有 12、13 或 14 周,具体取决于年份。
我得到的最接近的是使用 FiscalYear 库,它很棒,因为它有一个财政季度 class。不幸的是,继承的方法 isoformat() 不适用于它。只有 FiscalDate class,它没有提供我需要的季度。
有人 运行 参与其中吗?有人能指出我正确的方向吗?
我会 post 代码片段,但这只是 Python 中获取当前周数(截至今天,为 28)的 100 种方法。
我试过在 dateutils 中使用 rrules 和 deltas,但我能得到的最接近的是使用偏移量的第一季度的周数。第二季,分崩离析
我很乐意使用 pandas 或任何其他第 3 方库,如果它能帮助我避免对季度日期或周数到日期映射进行硬编码的话。
任何正确方向的帮助将不胜感激。
编辑:以下所有三个答案都以不同的方式为我解决了这个问题。我纠结于要给出正确答案,但我把它给了@Paul 的答案,因为它是我作为非大四学生最能理解的答案。这也是适合我个人用例(我没有提到)的答案,它正在接收日期时间对象并获得结果。所以这给了它优势。对提供惊人答案的其他人表示抱歉。我很高兴得到代码,我所希望的只是朝着正确的方向轻推。谢谢大家
我认为这可以满足您的需求(或者至少是一个很好的开始):
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
除非这是计算周编号的一种非常常见的方法,否则我不知道您是否会找到一个可以完全为您执行此操作的库,但使用 dateutil
很容易完成的 relativedelta
和一点逻辑。这是一个简单的实现,return 是一个元组 (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
哪个 returns:
$ 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 年的第 4 季度,那么您可以更改 return quarter, week
最后一行(并更改 return 类型注释):
if quarter == 0:
year -= 1
quarter = 4
return year, quarter, week
将 return 值更改为:
$ 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_MONDAY
和LAST_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)
。
注意:如果我正在编写这个函数,我可能不会 return 一个纯整数元组,而是使用 attrs or a dataclass 创建一个简单的 class 的目的,像这样:
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
则不可用 — 只是这是一个简单的结构,我倾向于使用插槽 class es 用于简单结构)。
我已经解决了每一个问题和每一个第三方图书馆,试图找出一种方法来做到这一点,而我不必手动映射日期。
我正在尝试获取当前财政季度的周数。 每个季度从 1 月、4 月、7 月或 10 月的 1 日开始。
给定一个日期(字符串或对象,无关紧要),我需要能够计算出它所在的财政季度的周数。
让事情变得有点复杂,财政年度从 4 月开始。
例如,今天 2020 年 7 月 9 日是本财政季度 (Q2) 的第 2 周,因为该季度从 4 月开始。同样,2020 年 6 月 29 日和 30 日是第 1 季度的第 14 周。
大多数时间格式化库甚至标准库都有像 ISO 日期这样的方法,我可以在其中提取周数。但它是从一年的第一天开始的周数。
我不能使用算术简单地删除到当前日期的周数,因为每个季度的周数不同。季度可以有 12、13 或 14 周,具体取决于年份。
我得到的最接近的是使用 FiscalYear 库,它很棒,因为它有一个财政季度 class。不幸的是,继承的方法 isoformat() 不适用于它。只有 FiscalDate class,它没有提供我需要的季度。
有人 运行 参与其中吗?有人能指出我正确的方向吗?
我会 post 代码片段,但这只是 Python 中获取当前周数(截至今天,为 28)的 100 种方法。
我试过在 dateutils 中使用 rrules 和 deltas,但我能得到的最接近的是使用偏移量的第一季度的周数。第二季,分崩离析
我很乐意使用 pandas 或任何其他第 3 方库,如果它能帮助我避免对季度日期或周数到日期映射进行硬编码的话。
任何正确方向的帮助将不胜感激。
编辑:以下所有三个答案都以不同的方式为我解决了这个问题。我纠结于要给出正确答案,但我把它给了@Paul 的答案,因为它是我作为非大四学生最能理解的答案。这也是适合我个人用例(我没有提到)的答案,它正在接收日期时间对象并获得结果。所以这给了它优势。对提供惊人答案的其他人表示抱歉。我很高兴得到代码,我所希望的只是朝着正确的方向轻推。谢谢大家
我认为这可以满足您的需求(或者至少是一个很好的开始):
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
除非这是计算周编号的一种非常常见的方法,否则我不知道您是否会找到一个可以完全为您执行此操作的库,但使用 dateutil
很容易完成的 relativedelta
和一点逻辑。这是一个简单的实现,return 是一个元组 (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
哪个 returns:
$ 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 年的第 4 季度,那么您可以更改 return quarter, week
最后一行(并更改 return 类型注释):
if quarter == 0:
year -= 1
quarter = 4
return year, quarter, week
将 return 值更改为:
$ 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_MONDAY
和LAST_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)
。
注意:如果我正在编写这个函数,我可能不会 return 一个纯整数元组,而是使用 attrs or a dataclass 创建一个简单的 class 的目的,像这样:
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
则不可用 — 只是这是一个简单的结构,我倾向于使用插槽 class es 用于简单结构)。